I'm dusting off some ancient SecurityHI code; way back when I wrote
it,
it had to run on 10.3 and there were bugs in that OS that prevented
SecEditTrust() from working.
Hi Rich,
First off, if this is new code being written for 10.4+, I would try
to use SFCertificateTrustPanel instead. SecEditTrust() is part of a
frozen, dusty SecurityHI bridge layer to support Carbon access to
native Cocoa UI. It was always intended to be a temporary measure to
allow developers time to move from Carbon to Cocoa, and has various
limitations not present in the Cocoa UI (such as the one where you
don't get a Cancel button in that dialog.) Our recommendation has
always been to use the SecurityInterface framework APIs (Objective-C)
for certificate UI.
Consider a certificate from the server "mail.example.com" (note
fabricated domain name). It's self-signed (no trusted root) and
expired,
and the server name mismatches: the cert is signed for "example.com",
not "mail.example.com"
[...code removed...]
One problem with your code is that you are treating
kSecTrustResultUnspecified as an untrusted case. In fact, it's the
one case that means the certificate *is* trusted, all by itself,
without user trust settings having been specified. (If user trust
settings were specified, then you get a kSecTrustResultProceed or
kSecTrustResultDeny result instead.)
Here's how you should group the SecTrustEvaluate result cases:
case kSecTrustResultConfirm:
case kSecTrustResultRecoverableTrustFailure:
{
// the certificate itself is OK, but it's not trusted (i.e. it has
failed the requirements of the policy being evaluated);
// must ask the user if it's OK to use this certificate anyway
if (PutUpTrustPanelAndUserClickedOKButton())
GoAhead(...);
else
Fail(...);
break;
}
case kSecTrustResultUnspecified:
case kSecTrustResultProceed:
{
// the certificate is trusted (implicitly, in the former case;
explicitly via user trust settings in the latter)
GoAhead(...);
break;
}
case kSecTrustResultInvalid:
case kSecTrustResultDeny:
case kSecTrustResultFatalTrustFailure:
case kSecTrustResultOtherError:
{
// the certificate is either explicitly not trusted (Deny),
// or is otherwise unable to be used
Fail(...);
break;
}
The first time I run this code, I get the SecEditTrust() dialog.
There's
a check box to 'Always trust "example.com" when connecting to
"mail.example.com".' If I turn this on and click "Continue", I end up
getting the SecEditTrust() dialog once more. On the second invocation,
the icon next to the certificate name has a blue "+" overlaid,
indicating "This certificate is marked as trusted for this
account." The
check box title has changed to "Always trust these certificates"
and is
turned off. In between there are various prompts for my login
password,
to authenticate certificate and keychain changes.
The "2 attempts to get a certificate marked as trusted" issue is a
known Leopard bug, which is fixed in an upcoming 10.5.x update.
(You're evidently running Leopard since you are seeing the blue "+"
badge, indicating that there are user trust settings on this
certificate. On Tiger, user trust settings are advisory only... you
can check them yourself with SecTrustGetUserTrust if you get a
kSecTrustResultRecoverableTrustFailure result, but they do *not*
affect the outcome of a SecTrustEvaluate call as they will on Leopard.)
Despite all of this, however, the decisions I make in the dialogs
don't
stick: if I attempt a new connection to the same server, whether or
not
I quit and relaunch the application in between, I have to go
through the
same dance every time. I was expecting that checking "Always trust..."
would cause that certificate to be unconditionally accepted for
connections to that server, no matter what. Is there a technical
reason
that user trust decisions aren't persisting, or have I written (or
encountered) a bug?
You've likely encountered a bug. Almost all of the "trust settings
don't stick" bug reports we've seen fall into two groups:
1. If more than one app is running and has changed trust settings via
this UI in a given login session, one of them may have grabbed a lock
on the helper tool (kcproxy) which is used to change the trust
settings. If you see messages in your syslog which say, "Couldn't
register server "com.apple.KeychainProxyServer" on this host" then
you are hitting this bug. The workaround is to quit any other apps
which have put up the trust UI, then try again.
2. If your mail server is hosting more than one virtual domain (and
does not present a separate certificate for each), such that
successive connections to "mail.example.com" and "mail.another-
different-example.com" both resolve to the same server presenting the
same certificate for "example.com", then there is a known bug where
clicking the "Always trust <certificate> when connecting to <host>"
checkbox will overwrite previous trust settings for that certificate
with the current value of <host>, instead of appending it.
Note that the "always trust" checkbox has a couple of behaviors.
First, it will try to add the certificate to a keychain if isn't
already there. Self-signed root certificates are added to the System
keychain; non-roots are added to the user's default keychain.
Additionally, trust exceptions are applied which allow the
certificate to be expired or have a hostname mismatch when going to a
particular host. Since "add certificate to keychain" and "change
trust settings on certificate" are separate operations, they may each
require authentication. (We've mostly got that down to just one
dialog now.)
You can use the /usr/bin/security tool to dump a list of your current
trust settings (on Leopard and later). As an example, I clicked the
"Always trust..." checkbox on a certificate presented by an internal
web server, "clockwork.apple.com". This gives me the following output
from 'security dump-trust-settings':
$ security dump-trust-settings
Number of trusted certs = 1
Cert 0: clockwork.apple.com
Number of trust settings : 3
Trust Setting 0:
Policy OID : SSL
Policy String : clockwork.apple.com
Allowed Error : Host name mismatch
Result Type : kSecTrustSettingsResultTrustAsRoot
Trust Setting 1:
Policy OID : Apple X509 Basic
Result Type : kSecTrustSettingsResultTrustAsRoot
Trust Setting 2:
Allowed Error : CSSMERR_TP_CERT_EXPIRED
Result Type : kSecTrustSettingsResultTrustAsRoot
Notice that the certificate is allowed to have a hostname mismatch
error, but ONLY if we're going to "clockwork.apple.com" using SSL. It
is also marked as trusted for the X.509 Basic policy, and it is
allowed to be expired. Also note that this certificate is NOT self-
signed, so the result type here is kSecTrustSettingsResultTrustAsRoot
rather than kSecTrustSettingsResultTrustRoot (which you'd expect to
see if the certificate were self-signed.) "Trust as root" means that
we don't need to build the chain further back to an actual root in
order to consider this certificate trusted.
Also note that, by default, this command shows the trust settings for
the user's trust domain. You can pass the -d option to the command
and get the list of what's trusted in the admin trust domain
(typically, this will include any root certificates which you've
trusted manually.) Passing -s instead of -d shows the system trust
domain, which includes all the Apple-provided root certificates.
-ken
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Apple-cdsa mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden