diff options
-rw-r--r-- | camel/ChangeLog | 13 | ||||
-rw-r--r-- | camel/camel-tcp-stream-ssl.c | 133 | ||||
-rw-r--r-- | camel/camel.c | 2 |
3 files changed, 134 insertions, 14 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog index ff5caf930d..9074e6dd7c 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,16 @@ +2001-03-15 Jeffrey Stedfast <fejj@ximian.com> + + * camel.c (camel_init): Don't use the hardcoded cert db directory, + use the one passed in. + + * camel-tcp-stream-ssl.c (ssl_get_client_auth): Wrote the default + implementation. Not that we'll use this though, since this is the + default implementation provided by NSS anyway. This more or less + serves as a reference in case we want to change anything. + (ssl_auth_cert): Same. + (ssl_bad_cert): Changed the prompt string and free it when we're + done. + 2001-03-15 Dan Winship <danw@ximian.com> First batch of disconnected IMAP-related stuff. This adds local diff --git a/camel/camel-tcp-stream-ssl.c b/camel/camel-tcp-stream-ssl.c index 243c68403b..332234291c 100644 --- a/camel/camel-tcp-stream-ssl.c +++ b/camel/camel-tcp-stream-ssl.c @@ -53,10 +53,6 @@ static int stream_connect (CamelTcpStream *stream, struct hostent *host, int static int stream_getsockopt (CamelTcpStream *stream, CamelSockOptData *data); static int stream_setsockopt (CamelTcpStream *stream, const CamelSockOptData *data); -/* callbacks */ -static SECStatus ssl_bad_cert (void *data, PRFileDesc *fd); -static SECStatus ssl_auth_cert (void *data, PRFileDesc *fd, PRBool checksig, PRBool is_server); - static void camel_tcp_stream_ssl_class_init (CamelTcpStreamSSLClass *camel_tcp_stream_ssl_class) @@ -210,36 +206,146 @@ stream_close (CamelStream *stream) return 0; } +#if 0 +/* Since this is default implementation, let NSS handle it. */ +static SECStatus +ssl_get_client_auth (void *data, PRFileDesc *sockfd, + struct CERTDistNamesStr *caNames, + struct CERTCertificateStr **pRetCert, + struct SECKEYPrivateKeyStr **pRetKey) +{ + SECStatus status = SECFailure; + SECKEYPrivateKey *privkey; + CERTCertificate *cert; + void *proto_win; + + proto_win = SSL_RevealPinArg (sockfd); + + if ((char *)data) { + cert = PK11_FindCertFromNickname ((char *)data, proto_win); + if (cert) { + privKey = PK11_FindKeyByAnyCert (cert, proto_win); + if (privkey) { + status = SECSuccess; + } else { + CERT_DestroyCertificate (cert); + } + } + } else { + /* no nickname given, automatically find the right cert */ + CERTCertNicknames *names; + int i; + + names = CERT_GetCertNicknames (CERT_GetDefaultCertDB (), + SEC_CERT_NICKNAMES_USER, + proto_win); + + if (names != NULL) { + for (i = 0; i < names->numnicknames; i++) { + + cert = PK11_FindCertFromNickname (names->nicknames[i], + proto_win); + if (!cert) + continue; + + /* Only check unexpired certs */ + if (CERT_CheckCertValidTimes (cert, PR_Now (), PR_FALSE) != secCertTimeValid) { + CERT_DestroyCertificate (cert); + continue; + } + + status = NSS_CmpCertChainWCANames (cert, caNames); + if (status == SECSuccess) { + privkey = PK11_FindKeyByAnyCert (cert, proto_win); + if (privkey) + break; + + status = SECFailure; + break; + } + + CERT_FreeNicknames (names); + } + } + } + + if (status == SECSuccess) { + *pRetCert = cert; + *pRetKey = privkey; + } + + return status; +} +#endif +#if 0 +/* Since this is the default NSS implementation, no need for us to use this. */ static SECStatus -ssl_auth_cert (void *data, PRFileDesc *fd, PRBool checksig, PRBool is_server) +ssl_auth_cert (void *data, PRFileDesc *sockfd, PRBool checksig, PRBool is_server) { - return SSL_AuthCertificate (NULL, fd, TRUE, FALSE); + CERTCertificate *cert; + SECStatus status; + void *pinarg; + char *host; + + cert = SSL_PeerCertificate (sockfd); + pinarg = SSL_RevealPinArg (sockfd); + status = CERT_VerifyCertNow ((CERTCertDBHandle *)data, cert, + checksig, certUsageSSLClient, pinarg); + + if (status != SECSuccess) + return SECFailure; + + /* Certificate is OK. Since this is the client side of an SSL + * connection, we need to verify that the name field in the cert + * matches the desired hostname. This is our defense against + * man-in-the-middle attacks. + */ + + /* SSL_RevealURL returns a hostname, not a URL. */ + host = SSL_RevealURL (sockfd); + + if (host && *host) { + status = CERT_VerifyCertName (cert, host); + } else { + PR_SetError (SSL_ERROR_BAD_CERT_DOMAIN, 0); + status = SECFailure; + } + + if (host) + PR_Free (hostName); + + return secStatus; } +#endif static SECStatus -ssl_bad_cert (void *data, PRFileDesc *fd) +ssl_bad_cert (void *data, PRFileDesc *sockfd) { CamelService *service; - char *string, *err; + char *prompt, *err; gpointer accept; - PRInt32 len; + PRUint32 len; g_return_val_if_fail (data != NULL, SECFailure); g_return_val_if_fail (CAMEL_IS_SERVICE (data), SECFailure); service = CAMEL_SERVICE (data); - /* FIXME: International issues here?? */ len = PR_GetErrorTextLength (); err = g_malloc0 (len + 1); PR_GetErrorText (err); - string = g_strdup_printf (_("Do you wish to accept this certificate from %s?\n\n%s"), + /* construct our user prompt */ + prompt = g_strdup_printf (_("Bad certificate from %s:%s\n\nDo you wish to accept anyway?"), service->url->host, err); + g_free (err); + /* query the user to find out if we want to accept this certificate */ accept = camel_session_query_authenticator (service->session, CAMEL_AUTHENTICATOR_ACCEPT, - string, FALSE, service, NULL, NULL); + prompt, FALSE, service, NULL, NULL); + + g_free (prompt); if (GPOINTER_TO_INT (accept)) return SECSuccess; @@ -275,7 +381,8 @@ stream_connect (CamelTcpStream *stream, struct hostent *host, int port) return -1; } - /*SSL_AuthCertificateHook (ssl_fd, ssl_auth_cert, NULL);*/ + /*SSL_GetClientAuthDataHook (sslSocket, ssl_get_client_auth, (void *)certNickname);*/ + /*SSL_AuthCertificateHook (ssl_fd, ssl_auth_cert, (void *) CERT_GetDefaultCertDB ());*/ SSL_BadCertHook (ssl_fd, ssl_bad_cert, ssl->service); ssl->sockfd = ssl_fd; diff --git a/camel/camel.c b/camel/camel.c index 5b102473e6..ba70162aca 100644 --- a/camel/camel.c +++ b/camel/camel.c @@ -55,7 +55,7 @@ camel_init (const char *certdb_dir, gboolean nss_init) if (nss_init) { PR_Init (PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 10); - if (NSS_Init ("/home/fejj/.mozilla/default") == SECFailure) { + if (NSS_Init (certdb_dir) == SECFailure) { g_warning ("Failed to initialize NSS"); return -1; } |