1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * $Id: nss.c,v 1.1.1.1 2008-09-23 16:32:05 hoffman Exp $
22 ***************************************************************************/
25 * Source file for all NSS-specific code for the TLS/SSL layer. No code
26 * but sslgen.c should ever call or use these functions.
34 #ifdef HAVE_SYS_SOCKET_H
35 #include <sys/socket.h>
40 #include "formdata.h" /* for the boundary function */
41 #include "url.h" /* for the ssl config check function */
48 #define _MPRINTF_REPLACE /* use the internal *printf() functions */
49 #include <curl/mprintf.h>
69 #include "easyif.h" /* for Curl_convert_from_utf8 prototype */
71 /* The last #include file should be: */
74 #define SSL_DIR "/etc/pki/nssdb"
76 /* enough to fit the string "PEM Token #[0|1]" */
79 PRFileDesc
*PR_ImportTCPSocket(PRInt32 osfd
);
83 #define HANDSHAKE_TIMEOUT 30
87 struct SessionHandle
*data
;
93 PRInt32 version
; /* protocol version valid for this cipher */
96 #define PK11_SETATTRS(x,id,v,l) (x)->type = (id); \
97 (x)->pValue=(v); (x)->ulValueLen = (l)
99 #define CERT_NewTempCertificate __CERT_NewTempCertificate
101 enum sslversion
{ SSL2
= 1, SSL3
= 2, TLS
= 4 };
103 #define NUM_OF_CIPHERS sizeof(cipherlist)/sizeof(cipherlist[0])
104 static const cipher_s cipherlist
[] = {
105 /* SSL2 cipher suites */
106 {"rc4", SSL_EN_RC4_128_WITH_MD5
, SSL2
},
107 {"rc4-md5", SSL_EN_RC4_128_WITH_MD5
, SSL2
},
108 {"rc4export", SSL_EN_RC4_128_EXPORT40_WITH_MD5
, SSL2
},
109 {"rc2", SSL_EN_RC2_128_CBC_WITH_MD5
, SSL2
},
110 {"rc2export", SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5
, SSL2
},
111 {"des", SSL_EN_DES_64_CBC_WITH_MD5
, SSL2
},
112 {"desede3", SSL_EN_DES_192_EDE3_CBC_WITH_MD5
, SSL2
},
113 /* SSL3/TLS cipher suites */
114 {"rsa_rc4_128_md5", SSL_RSA_WITH_RC4_128_MD5
, SSL3
| TLS
},
115 {"rsa_rc4_128_sha", SSL_RSA_WITH_RC4_128_SHA
, SSL3
| TLS
},
116 {"rsa_3des_sha", SSL_RSA_WITH_3DES_EDE_CBC_SHA
, SSL3
| TLS
},
117 {"rsa_des_sha", SSL_RSA_WITH_DES_CBC_SHA
, SSL3
| TLS
},
118 {"rsa_rc4_40_md5", SSL_RSA_EXPORT_WITH_RC4_40_MD5
, SSL3
| TLS
},
119 {"rsa_rc2_40_md5", SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5
, SSL3
| TLS
},
120 {"rsa_null_md5", SSL_RSA_WITH_NULL_MD5
, SSL3
| TLS
},
121 {"rsa_null_sha", SSL_RSA_WITH_NULL_SHA
, SSL3
| TLS
},
122 {"fips_3des_sha", SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA
, SSL3
| TLS
},
123 {"fips_des_sha", SSL_RSA_FIPS_WITH_DES_CBC_SHA
, SSL3
| TLS
},
124 {"fortezza", SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA
, SSL3
| TLS
},
125 {"fortezza_rc4_128_sha", SSL_FORTEZZA_DMS_WITH_RC4_128_SHA
, SSL3
| TLS
},
126 {"fortezza_null", SSL_FORTEZZA_DMS_WITH_NULL_SHA
, SSL3
| TLS
},
127 /* TLS 1.0: Exportable 56-bit Cipher Suites. */
128 {"rsa_des_56_sha", TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA
, SSL3
| TLS
},
129 {"rsa_rc4_56_sha", TLS_RSA_EXPORT1024_WITH_RC4_56_SHA
, SSL3
| TLS
},
131 {"rsa_aes_128_sha", TLS_RSA_WITH_AES_128_CBC_SHA
, SSL3
| TLS
},
132 {"rsa_aes_256_sha", TLS_RSA_WITH_AES_256_CBC_SHA
, SSL3
| TLS
},
133 #ifdef NSS_ENABLE_ECC
135 {"ecdh_ecdsa_null_sha", TLS_ECDH_ECDSA_WITH_NULL_SHA
, TLS
},
136 {"ecdh_ecdsa_rc4_128_sha", TLS_ECDH_ECDSA_WITH_RC4_128_SHA
, TLS
},
137 {"ecdh_ecdsa_3des_sha", TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
, TLS
},
138 {"ecdh_ecdsa_aes_128_sha", TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
, TLS
},
139 {"ecdh_ecdsa_aes_256_sha", TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
, TLS
},
140 {"ecdhe_ecdsa_null_sha", TLS_ECDHE_ECDSA_WITH_NULL_SHA
, TLS
},
141 {"ecdhe_ecdsa_rc4_128_sha", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
, TLS
},
142 {"ecdhe_ecdsa_3des_sha", TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
, TLS
},
143 {"ecdhe_ecdsa_aes_128_sha", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
, TLS
},
144 {"ecdhe_ecdsa_aes_256_sha", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
, TLS
},
145 {"ecdh_rsa_null_sha", TLS_ECDH_RSA_WITH_NULL_SHA
, TLS
},
146 {"ecdh_rsa_128_sha", TLS_ECDH_RSA_WITH_RC4_128_SHA
, TLS
},
147 {"ecdh_rsa_3des_sha", TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
, TLS
},
148 {"ecdh_rsa_aes_128_sha", TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
, TLS
},
149 {"ecdh_rsa_aes_256_sha", TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
, TLS
},
150 {"echde_rsa_null", TLS_ECDHE_RSA_WITH_NULL_SHA
, TLS
},
151 {"ecdhe_rsa_rc4_128_sha", TLS_ECDHE_RSA_WITH_RC4_128_SHA
, TLS
},
152 {"ecdhe_rsa_3des_sha", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
, TLS
},
153 {"ecdhe_rsa_aes_128_sha", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
, TLS
},
154 {"ecdhe_rsa_aes_256_sha", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
, TLS
},
155 {"ecdh_anon_null_sha", TLS_ECDH_anon_WITH_NULL_SHA
, TLS
},
156 {"ecdh_anon_rc4_128sha", TLS_ECDH_anon_WITH_RC4_128_SHA
, TLS
},
157 {"ecdh_anon_3des_sha", TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA
, TLS
},
158 {"ecdh_anon_aes_128_sha", TLS_ECDH_anon_WITH_AES_128_CBC_SHA
, TLS
},
159 {"ecdh_anon_aes_256_sha", TLS_ECDH_anon_WITH_AES_256_CBC_SHA
, TLS
},
163 #ifdef HAVE_PK11_CREATEGENERICOBJECT
164 static const char* pem_library
= "libnsspem.so";
166 SECMODModule
* mod
= NULL
;
168 static SECStatus
set_ciphers(struct SessionHandle
*data
, PRFileDesc
* model
,
172 PRBool cipher_state
[NUM_OF_CIPHERS
];
177 /* First disable all ciphers. This uses a different max value in case
178 * NSS adds more ciphers later we don't want them available by
181 for(i
=0; i
<SSL_NumImplementedCiphers
; i
++) {
182 SSL_CipherPrefSet(model
, SSL_ImplementedCiphers
[i
], SSL_NOT_ALLOWED
);
185 /* Set every entry in our list to false */
186 for(i
=0; i
<NUM_OF_CIPHERS
; i
++) {
187 cipher_state
[i
] = PR_FALSE
;
190 cipher
= cipher_list
;
192 while(cipher_list
&& (cipher_list
[0])) {
193 while((*cipher
) && (ISSPACE(*cipher
)))
196 if((cipher_list
= strchr(cipher
, ','))) {
197 *cipher_list
++ = '\0';
202 for(i
=0; i
<NUM_OF_CIPHERS
; i
++) {
203 if(strequal(cipher
, cipherlist
[i
].name
)) {
204 cipher_state
[i
] = PR_TRUE
;
210 if(found
== PR_FALSE
) {
211 failf(data
, "Unknown cipher in list: %s", cipher
);
216 cipher
= cipher_list
;
220 /* Finally actually enable the selected ciphers */
221 for(i
=0; i
<NUM_OF_CIPHERS
; i
++) {
222 rv
= SSL_CipherPrefSet(model
, cipherlist
[i
].num
, cipher_state
[i
]);
223 if(rv
!= SECSuccess
) {
224 failf(data
, "Unknown cipher in cipher list");
233 * Determine whether the nickname passed in is a filename that needs to
234 * be loaded as a PEM or a regular NSS nickname.
236 * returns 1 for a file
237 * returns 0 for not a file (NSS nickname)
239 static int is_file(const char *filename
)
246 if(stat(filename
, &st
) == 0)
247 if(S_ISREG(st
.st_mode
))
254 nss_load_cert(const char *filename
, PRBool cacert
)
256 #ifdef HAVE_PK11_CREATEGENERICOBJECT
258 PK11SlotInfo
* slot
= NULL
;
259 PK11GenericObject
*rv
;
261 CK_ATTRIBUTE theTemplate
[20];
262 CK_BBOOL cktrue
= CK_TRUE
;
263 CK_BBOOL ckfalse
= CK_FALSE
;
264 CK_OBJECT_CLASS objClass
= CKO_CERTIFICATE
;
265 char *slotname
= NULL
;
267 CERTCertificate
*cert
;
268 char *nickname
= NULL
;
271 /* If there is no slash in the filename it is assumed to be a regular
274 if(is_file(filename
)) {
275 n
= strrchr(filename
, '/');
282 /* A nickname from the NSS internal database */
284 return 0; /* You can't specify an NSS CA nickname this way */
285 nickname
= strdup(filename
);
289 #ifdef HAVE_PK11_CREATEGENERICOBJECT
292 /* All CA and trust objects go into slot 0. Other slots are used
293 * for storing certificates. With each new user certificate we increment
294 * the slot count. We only support 1 user certificate right now.
301 slotname
= (char *)malloc(SLOTSIZE
);
302 nickname
= (char *)malloc(PATH_MAX
);
303 snprintf(slotname
, SLOTSIZE
, "PEM Token #%ld", slotID
);
304 snprintf(nickname
, PATH_MAX
, "PEM Token #%ld:%s", slotID
, n
);
306 slot
= PK11_FindSlotByName(slotname
);
314 PK11_SETATTRS(attrs
, CKA_CLASS
, &objClass
, sizeof(objClass
) );
316 PK11_SETATTRS(attrs
, CKA_TOKEN
, &cktrue
, sizeof(CK_BBOOL
) );
318 PK11_SETATTRS(attrs
, CKA_LABEL
, (unsigned char *)filename
,
322 PK11_SETATTRS(attrs
, CKA_TRUST
, &cktrue
, sizeof(CK_BBOOL
) );
325 PK11_SETATTRS(attrs
, CKA_TRUST
, &ckfalse
, sizeof(CK_BBOOL
) );
329 /* This load the certificate in our PEM module into the appropriate
332 rv
= PK11_CreateGenericObject(slot
, theTemplate
, 4, PR_FALSE
/* isPerm */);
342 /* We don't have PK11_CreateGenericObject but a file-based cert was passed
343 * in. We need to fail.
349 /* Double-check that the certificate or nickname requested exists in
350 * either the token or the NSS certificate database.
353 cert
= PK11_FindCertFromNickname((char *)nickname
, NULL
);
355 /* An invalid nickname was passed in */
358 PR_SetError(SEC_ERROR_UNKNOWN_CERT
, 0);
362 CERT_DestroyCertificate(cert
);
370 static int nss_load_crl(char* crlfilename
, PRBool ascii
)
378 CERTSignedCrl
*crl
=NULL
;
379 PK11SlotInfo
*slot
=NULL
;
381 infile
= PR_Open(crlfilename
,PR_RDONLY
,0);
386 prstat
= PR_GetOpenFileInfo(infile
,&info
);
387 if (prstat
!=PR_SUCCESS
)
392 filedata
.data
= NULL
;
393 if (!SECITEM_AllocItem(NULL
,&filedata
,info
.size
))
395 nb
= PR_Read(infile
,filedata
.data
,info
.size
);
398 asc
= (char*)filedata
.data
;
402 body
=strstr(asc
,"-----BEGIN");
406 body
= PORT_Strchr(asc
,'\n');
408 body
= PORT_Strchr(asc
,'\r');
410 trailer
= strstr(++body
,"-----END");
419 rv
= ATOB_ConvertAsciiToItem(&crlDER
,body
);
420 PORT_Free(filedata
.data
);
425 if (!SECITEM_AllocItem(NULL
,&crlDER
,info
.size
))
427 nb
= PR_Read(infile
,crlDER
.data
,info
.size
);
432 slot
= PK11_GetInternalKeySlot();
433 crl
= PK11_ImportCRL(slot
,&crlDER
,
435 NULL
,CRL_IMPORT_DEFAULT_OPTIONS
,
436 NULL
,(CRL_DECODE_DEFAULT_OPTIONS
|
437 CRL_DECODE_DONT_COPY_DER
));
438 if (slot
) PK11_FreeSlot(slot
);
444 static int nss_load_key(struct connectdata
*conn
, char *key_file
)
446 #ifdef HAVE_PK11_CREATEGENERICOBJECT
447 PK11SlotInfo
* slot
= NULL
;
448 PK11GenericObject
*rv
;
450 CK_ATTRIBUTE theTemplate
[20];
451 CK_BBOOL cktrue
= CK_TRUE
;
452 CK_OBJECT_CLASS objClass
= CKO_PRIVATE_KEY
;
454 char *slotname
= NULL
;
455 pphrase_arg_t
*parg
= NULL
;
459 /* FIXME: grok the various file types */
461 slotID
= 1; /* hardcoded for now */
463 slotname
= (char *)malloc(SLOTSIZE
);
464 snprintf(slotname
, SLOTSIZE
, "PEM Token #%ld", slotID
);
466 slot
= PK11_FindSlotByName(slotname
);
472 PK11_SETATTRS(attrs
, CKA_CLASS
, &objClass
, sizeof(objClass
) ); attrs
++;
473 PK11_SETATTRS(attrs
, CKA_TOKEN
, &cktrue
, sizeof(CK_BBOOL
) ); attrs
++;
474 PK11_SETATTRS(attrs
, CKA_LABEL
, (unsigned char *)key_file
,
475 strlen(key_file
)+1); attrs
++;
477 /* When adding an encrypted key the PKCS#11 will be set as removed */
478 rv
= PK11_CreateGenericObject(slot
, theTemplate
, 3, PR_FALSE
/* isPerm */);
480 PR_SetError(SEC_ERROR_BAD_KEY
, 0);
484 /* This will force the token to be seen as re-inserted */
485 SECMOD_WaitForAnyTokenEvent(mod
, 0, 0);
486 PK11_IsPresent(slot
);
488 parg
= (pphrase_arg_t
*) malloc(sizeof(*parg
));
489 parg
->retryCount
= 0;
490 parg
->data
= conn
->data
;
491 /* parg is initialized in nss_Init_Tokens() */
492 if(PK11_Authenticate(slot
, PR_TRUE
, parg
) != SECSuccess
) {
500 /* If we don't have PK11_CreateGenericObject then we can't load a file-based
503 (void)conn
; /* unused */
504 (void)key_file
; /* unused */
509 static int display_error(struct connectdata
*conn
, PRInt32 err
,
510 const char *filename
)
513 case SEC_ERROR_BAD_PASSWORD
:
514 failf(conn
->data
, "Unable to load client key: Incorrect password");
516 case SEC_ERROR_UNKNOWN_CERT
:
517 failf(conn
->data
, "Unable to load certificate %s", filename
);
522 return 0; /* The caller will print a generic error */
525 static int cert_stuff(struct connectdata
*conn
, char *cert_file
, char *key_file
)
527 struct SessionHandle
*data
= conn
->data
;
531 rv
= nss_load_cert(cert_file
, PR_FALSE
);
533 if(!display_error(conn
, PR_GetError(), cert_file
))
534 failf(data
, "Unable to load client cert %d.", PR_GetError());
538 if(key_file
|| (is_file(cert_file
))) {
540 rv
= nss_load_key(conn
, key_file
);
542 /* In case the cert file also has the key */
543 rv
= nss_load_key(conn
, cert_file
);
545 if(!display_error(conn
, PR_GetError(), key_file
))
546 failf(data
, "Unable to load client key %d.", PR_GetError());
554 static char * nss_get_password(PK11SlotInfo
* slot
, PRBool retry
, void *arg
)
557 parg
= (pphrase_arg_t
*) arg
;
559 (void)slot
; /* unused */
562 if(parg
->data
->set
.str
[STRING_KEY_PASSWD
])
563 return (char *)PORT_Strdup((char *)parg
->data
->set
.str
[STRING_KEY_PASSWD
]);
568 /* No longer ask for the password, parg has been freed */
569 static char * nss_no_password(PK11SlotInfo
*slot
, PRBool retry
, void *arg
)
571 (void)slot
; /* unused */
572 (void)retry
; /* unused */
573 (void)arg
; /* unused */
577 static SECStatus
nss_Init_Tokens(struct connectdata
* conn
)
579 PK11SlotList
*slotList
;
580 PK11SlotListElement
*listEntry
;
581 SECStatus ret
, status
= SECSuccess
;
582 pphrase_arg_t
*parg
= NULL
;
584 parg
= (pphrase_arg_t
*) malloc(sizeof(*parg
));
585 parg
->retryCount
= 0;
586 parg
->data
= conn
->data
;
588 PK11_SetPasswordFunc(nss_get_password
);
591 PK11_GetAllTokens(CKM_INVALID_MECHANISM
, PR_FALSE
, PR_TRUE
, NULL
);
593 for(listEntry
= PK11_GetFirstSafe(slotList
);
594 listEntry
; listEntry
= listEntry
->next
) {
595 PK11SlotInfo
*slot
= listEntry
->slot
;
597 if(PK11_NeedLogin(slot
) && PK11_NeedUserInit(slot
)) {
598 if(slot
== PK11_GetInternalKeySlot()) {
599 failf(conn
->data
, "The NSS database has not been initialized");
602 failf(conn
->data
, "The token %s has not been initialized",
603 PK11_GetTokenName(slot
));
609 ret
= PK11_Authenticate(slot
, PR_TRUE
, parg
);
610 if(SECSuccess
!= ret
) {
611 if(PR_GetError() == SEC_ERROR_BAD_PASSWORD
)
612 infof(conn
->data
, "The password for token '%s' is incorrect\n",
613 PK11_GetTokenName(slot
));
617 parg
->retryCount
= 0; /* reset counter to 0 for the next token */
626 static SECStatus
BadCertHandler(void *arg
, PRFileDesc
*sock
)
628 SECStatus success
= SECSuccess
;
629 struct connectdata
*conn
= (struct connectdata
*)arg
;
630 PRErrorCode err
= PR_GetError();
631 CERTCertificate
*cert
= NULL
;
632 char *subject
, *issuer
;
634 if(conn
->data
->set
.ssl
.certverifyresult
!=0)
637 conn
->data
->set
.ssl
.certverifyresult
=err
;
638 cert
= SSL_PeerCertificate(sock
);
639 subject
= CERT_NameToAscii(&cert
->subject
);
640 issuer
= CERT_NameToAscii(&cert
->issuer
);
641 CERT_DestroyCertificate(cert
);
644 case SEC_ERROR_CA_CERT_INVALID
:
645 infof(conn
->data
, "Issuer certificate is invalid: '%s'\n", issuer
);
646 if(conn
->data
->set
.ssl
.verifypeer
)
647 success
= SECFailure
;
649 case SEC_ERROR_UNTRUSTED_ISSUER
:
650 if(conn
->data
->set
.ssl
.verifypeer
)
651 success
= SECFailure
;
652 infof(conn
->data
, "Certificate is signed by an untrusted issuer: '%s'\n",
655 case SSL_ERROR_BAD_CERT_DOMAIN
:
656 if(conn
->data
->set
.ssl
.verifypeer
)
657 success
= SECFailure
;
658 infof(conn
->data
, "common name: %s (does not match '%s')\n",
659 subject
, conn
->host
.dispname
);
661 case SEC_ERROR_EXPIRED_CERTIFICATE
:
662 if(conn
->data
->set
.ssl
.verifypeer
)
663 success
= SECFailure
;
664 infof(conn
->data
, "Remote Certificate has expired.\n");
667 if(conn
->data
->set
.ssl
.verifypeer
)
668 success
= SECFailure
;
669 infof(conn
->data
, "Bad certificate received. Subject = '%s', "
670 "Issuer = '%s'\n", subject
, issuer
);
673 if(success
== SECSuccess
)
674 infof(conn
->data
, "SSL certificate verify ok.\n");
682 * Inform the application that the handshake is complete.
684 static SECStatus
HandshakeCallback(PRFileDesc
*sock
, void *arg
)
691 static void display_conn_info(struct connectdata
*conn
, PRFileDesc
*sock
)
693 SSLChannelInfo channel
;
694 SSLCipherSuiteInfo suite
;
695 CERTCertificate
*cert
;
696 char *subject
, *issuer
, *common_name
;
697 PRExplodedTime printableTime
;
698 char timeString
[256];
699 PRTime notBefore
, notAfter
;
701 if(SSL_GetChannelInfo(sock
, &channel
, sizeof channel
) ==
702 SECSuccess
&& channel
.length
== sizeof channel
&&
703 channel
.cipherSuite
) {
704 if(SSL_GetCipherSuiteInfo(channel
.cipherSuite
,
705 &suite
, sizeof suite
) == SECSuccess
) {
706 infof(conn
->data
, "SSL connection using %s\n", suite
.cipherSuiteName
);
710 infof(conn
->data
, "Server certificate:\n");
712 cert
= SSL_PeerCertificate(sock
);
713 subject
= CERT_NameToAscii(&cert
->subject
);
714 issuer
= CERT_NameToAscii(&cert
->issuer
);
715 common_name
= CERT_GetCommonName(&cert
->subject
);
716 infof(conn
->data
, "\tsubject: %s\n", subject
);
718 CERT_GetCertTimes(cert
, ¬Before
, ¬After
);
719 PR_ExplodeTime(notBefore
, PR_GMTParameters
, &printableTime
);
720 PR_FormatTime(timeString
, 256, "%b %d %H:%M:%S %Y GMT", &printableTime
);
721 infof(conn
->data
, "\tstart date: %s\n", timeString
);
722 PR_ExplodeTime(notAfter
, PR_GMTParameters
, &printableTime
);
723 PR_FormatTime(timeString
, 256, "%b %d %H:%M:%S %Y GMT", &printableTime
);
724 infof(conn
->data
, "\texpire date: %s\n", timeString
);
725 infof(conn
->data
, "\tcommon name: %s\n", common_name
);
726 infof(conn
->data
, "\tissuer: %s\n", issuer
);
730 PR_Free(common_name
);
732 CERT_DestroyCertificate(cert
);
739 * Check that the Peer certificate's issuer certificate matches the one found
740 * by issuer_nickname. This is not exactly the way OpenSSL and GNU TLS do the
741 * issuer check, so we provide comments that mimic the OpenSSL
742 * X509_check_issued function (in x509v3/v3_purp.c)
744 static SECStatus
check_issuer_cert(PRFileDesc
*sock
,
745 char* issuer_nickname
)
747 CERTCertificate
*cert
,*cert_issuer
,*issuer
;
748 SECStatus res
=SECSuccess
;
749 void *proto_win
= NULL
;
752 PRArenaPool *tmpArena = NULL;
753 CERTAuthKeyID *authorityKeyID = NULL;
754 SECITEM *caname = NULL;
757 cert
= SSL_PeerCertificate(sock
);
758 cert_issuer
= CERT_FindCertIssuer(cert
,PR_Now(),certUsageObjectSigner
);
760 proto_win
= SSL_RevealPinArg(sock
);
762 issuer
= PK11_FindCertFromNickname(issuer_nickname
, proto_win
);
764 if ((!cert_issuer
) || (!issuer
))
766 else if (SECITEM_CompareItem(&cert_issuer
->derCert
,
767 &issuer
->derCert
)!=SECEqual
)
770 CERT_DestroyCertificate(cert
);
771 CERT_DestroyCertificate(issuer
);
772 CERT_DestroyCertificate(cert_issuer
);
778 * Callback to pick the SSL client certificate.
780 static SECStatus
SelectClientCert(void *arg
, PRFileDesc
*sock
,
781 struct CERTDistNamesStr
*caNames
,
782 struct CERTCertificateStr
**pRetCert
,
783 struct SECKEYPrivateKeyStr
**pRetKey
)
785 CERTCertificate
*cert
;
786 SECKEYPrivateKey
*privKey
;
787 char *nickname
= (char *)arg
;
788 void *proto_win
= NULL
;
789 SECStatus secStatus
= SECFailure
;
793 proto_win
= SSL_RevealPinArg(sock
);
798 cert
= PK11_FindCertFromNickname(nickname
, proto_win
);
801 if(!strncmp(nickname
, "PEM Token", 9)) {
802 CK_SLOT_ID slotID
= 1; /* hardcoded for now */
803 char * slotname
= (char *)malloc(SLOTSIZE
);
804 snprintf(slotname
, SLOTSIZE
, "PEM Token #%ld", slotID
);
805 slot
= PK11_FindSlotByName(slotname
);
806 privKey
= PK11_FindPrivateKeyFromCert(slot
, cert
, NULL
);
810 secStatus
= SECSuccess
;
814 privKey
= PK11_FindKeyByAnyCert(cert
, proto_win
);
816 secStatus
= SECSuccess
;
820 if(secStatus
== SECSuccess
) {
826 CERT_DestroyCertificate(cert
);
835 * @retval 0 error initializing SSL
836 * @retval 1 SSL initialized successfully
838 int Curl_nss_init(void)
841 PR_Init(PR_USER_THREAD
, PR_PRIORITY_NORMAL
, 256);
843 /* We will actually initialize NSS later */
849 void Curl_nss_cleanup(void)
856 * This function uses SSL_peek to determine connection status.
859 * 1 means the connection is still in place
860 * 0 means the connection has been closed
861 * -1 means the connection status is unknown
864 Curl_nss_check_cxn(struct connectdata
*conn
)
870 PR_Recv(conn
->ssl
[FIRSTSOCKET
].handle
, (void *)&buf
, 1, PR_MSG_PEEK
,
871 PR_SecondsToInterval(1));
873 return 1; /* connection still in place */
876 return 0; /* connection has been closed */
878 return -1; /* connection status unknown */
882 * This function is called when an SSL connection is closed.
884 void Curl_nss_close(struct connectdata
*conn
, int sockindex
)
886 struct ssl_connect_data
*connssl
= &conn
->ssl
[sockindex
];
888 if(connssl
->handle
) {
889 PR_Close(connssl
->handle
);
890 if(connssl
->client_nickname
!= NULL
) {
891 free(connssl
->client_nickname
);
892 connssl
->client_nickname
= NULL
;
894 connssl
->handle
= NULL
;
899 * This function is called when the 'data' struct is going away. Close
900 * down everything and free all resources!
902 int Curl_nss_close_all(struct SessionHandle
*data
)
908 CURLcode
Curl_nss_connect(struct connectdata
*conn
, int sockindex
)
911 PRFileDesc
*model
= NULL
;
912 PRBool ssl2
, ssl3
, tlsv1
;
913 struct SessionHandle
*data
= conn
->data
;
914 curl_socket_t sockfd
= conn
->sock
[sockindex
];
915 struct ssl_connect_data
*connssl
= &conn
->ssl
[sockindex
];
917 #ifdef HAVE_PK11_CREATEGENERICOBJECT
918 char *configstring
= NULL
;
920 char *certDir
= NULL
;
923 curlerr
= CURLE_SSL_CONNECT_ERROR
;
925 if (connssl
->state
== ssl_connection_complete
)
928 /* FIXME. NSS doesn't support multiple databases open at the same time. */
932 certDir
= getenv("SSL_DIR"); /* Look in $SSL_DIR */
937 if(stat(SSL_DIR
, &st
) == 0)
938 if(S_ISDIR(st
.st_mode
)) {
939 certDir
= (char *)SSL_DIR
;
944 rv
= NSS_NoDB_Init(NULL
);
947 rv
= NSS_Initialize(certDir
, NULL
, NULL
, "secmod.db",
950 if(rv
!= SECSuccess
) {
951 infof(conn
->data
, "Unable to initialize NSS database\n");
952 curlerr
= CURLE_SSL_CACERT_BADFILE
;
956 NSS_SetDomesticPolicy();
958 #ifdef HAVE_PK11_CREATEGENERICOBJECT
959 configstring
= (char *)malloc(PATH_MAX
);
961 PR_snprintf(configstring
, PATH_MAX
, "library=%s name=PEM", pem_library
);
963 mod
= SECMOD_LoadUserModule(configstring
, NULL
, PR_FALSE
);
965 if(!mod
|| !mod
->loaded
) {
967 SECMOD_DestroyModule(mod
);
970 infof(data
, "WARNING: failed to load NSS PEM library %s. Using OpenSSL "
971 "PEM certificates will not work.\n", pem_library
);
976 model
= PR_NewTCPSocket();
979 model
= SSL_ImportFD(NULL
, model
);
981 if(SSL_OptionSet(model
, SSL_SECURITY
, PR_TRUE
) != SECSuccess
)
983 if(SSL_OptionSet(model
, SSL_HANDSHAKE_AS_SERVER
, PR_FALSE
) != SECSuccess
)
985 if(SSL_OptionSet(model
, SSL_HANDSHAKE_AS_CLIENT
, PR_TRUE
) != SECSuccess
)
988 ssl2
= ssl3
= tlsv1
= PR_FALSE
;
990 switch (data
->set
.ssl
.version
) {
992 case CURL_SSLVERSION_DEFAULT
:
993 ssl3
= tlsv1
= PR_TRUE
;
995 case CURL_SSLVERSION_TLSv1
:
998 case CURL_SSLVERSION_SSLv2
:
1001 case CURL_SSLVERSION_SSLv3
:
1006 if(SSL_OptionSet(model
, SSL_ENABLE_SSL2
, ssl2
) != SECSuccess
)
1008 if(SSL_OptionSet(model
, SSL_ENABLE_SSL3
, ssl3
) != SECSuccess
)
1010 if(SSL_OptionSet(model
, SSL_ENABLE_TLS
, tlsv1
) != SECSuccess
)
1013 if(SSL_OptionSet(model
, SSL_V2_COMPATIBLE_HELLO
, ssl2
) != SECSuccess
)
1016 if(data
->set
.ssl
.cipher_list
) {
1017 if(set_ciphers(data
, model
, data
->set
.ssl
.cipher_list
) != SECSuccess
) {
1018 curlerr
= CURLE_SSL_CIPHER
;
1023 data
->set
.ssl
.certverifyresult
=0; /* not checked yet */
1024 if(SSL_BadCertHook(model
, (SSLBadCertHandler
) BadCertHandler
, conn
)
1028 if(SSL_HandshakeCallback(model
, (SSLHandshakeCallback
) HandshakeCallback
,
1029 NULL
) != SECSuccess
)
1032 if(!data
->set
.ssl
.verifypeer
)
1033 /* skip the verifying of the peer */
1035 else if(data
->set
.ssl
.CAfile
) {
1036 int rc
= nss_load_cert(data
->set
.ssl
.CAfile
, PR_TRUE
);
1038 curlerr
= CURLE_SSL_CACERT_BADFILE
;
1042 else if(data
->set
.ssl
.CApath
) {
1047 if(stat(data
->set
.ssl
.CApath
, &st
) == -1) {
1048 curlerr
= CURLE_SSL_CACERT_BADFILE
;
1052 if(S_ISDIR(st
.st_mode
)) {
1055 dir
= PR_OpenDir(data
->set
.ssl
.CApath
);
1057 entry
= PR_ReadDir(dir
, PR_SKIP_BOTH
| PR_SKIP_HIDDEN
);
1060 char fullpath
[PATH_MAX
];
1062 snprintf(fullpath
, sizeof(fullpath
), "%s/%s", data
->set
.ssl
.CApath
,
1064 rc
= nss_load_cert(fullpath
, PR_TRUE
);
1065 /* FIXME: check this return value! */
1067 /* This is purposefully tolerant of errors so non-PEM files
1068 * can be in the same directory */
1069 } while(entry
!= NULL
);
1076 data
->set
.ssl
.CAfile
? data
->set
.ssl
.CAfile
: "none",
1077 data
->set
.ssl
.CApath
? data
->set
.ssl
.CApath
: "none");
1079 if (data
->set
.ssl
.CRLfile
) {
1080 int rc
= nss_load_crl(data
->set
.ssl
.CRLfile
, PR_FALSE
);
1082 curlerr
= CURLE_SSL_CRL_BADFILE
;
1087 data
->set
.ssl
.CRLfile
? data
->set
.ssl
.CRLfile
: "none");
1090 if(data
->set
.str
[STRING_CERT
]) {
1094 nickname
= (char *)malloc(PATH_MAX
);
1095 if(is_file(data
->set
.str
[STRING_CERT
])) {
1096 n
= strrchr(data
->set
.str
[STRING_CERT
], '/');
1098 n
++; /* skip last slash */
1099 snprintf(nickname
, PATH_MAX
, "PEM Token #%ld:%s", 1, n
);
1103 strncpy(nickname
, data
->set
.str
[STRING_CERT
], PATH_MAX
);
1104 nickname
[PATH_MAX
-1]=0; /* make sure this is zero terminated */
1106 if(nss_Init_Tokens(conn
) != SECSuccess
) {
1110 if(!cert_stuff(conn
, data
->set
.str
[STRING_CERT
],
1111 data
->set
.str
[STRING_KEY
])) {
1112 /* failf() is already done in cert_stuff() */
1114 return CURLE_SSL_CERTPROBLEM
;
1117 connssl
->client_nickname
= strdup(nickname
);
1118 if(SSL_GetClientAuthDataHook(model
,
1119 (SSLGetClientAuthData
) SelectClientCert
,
1122 curlerr
= CURLE_SSL_CERTPROBLEM
;
1128 PK11_SetPasswordFunc(nss_no_password
);
1131 connssl
->client_nickname
= NULL
;
1134 /* Import our model socket onto the existing file descriptor */
1135 connssl
->handle
= PR_ImportTCPSocket(sockfd
);
1136 connssl
->handle
= SSL_ImportFD(model
, connssl
->handle
);
1137 if(!connssl
->handle
)
1139 PR_Close(model
); /* We don't need this any more */
1141 /* Force handshake on next I/O */
1142 SSL_ResetHandshake(connssl
->handle
, /* asServer */ PR_FALSE
);
1144 SSL_SetURL(connssl
->handle
, conn
->host
.name
);
1146 /* Force the handshake now */
1147 if(SSL_ForceHandshakeWithTimeout(connssl
->handle
,
1148 PR_SecondsToInterval(HANDSHAKE_TIMEOUT
))
1150 if(conn
->data
->set
.ssl
.certverifyresult
!=0)
1151 curlerr
= CURLE_SSL_CACERT
;
1155 connssl
->state
= ssl_connection_complete
;
1157 display_conn_info(conn
, connssl
->handle
);
1159 if (data
->set
.str
[STRING_SSL_ISSUERCERT
]) {
1162 nickname
= (char *)malloc(PATH_MAX
);
1163 if(is_file(data
->set
.str
[STRING_SSL_ISSUERCERT
])) {
1164 n
= strrchr(data
->set
.str
[STRING_SSL_ISSUERCERT
], '/');
1166 n
++; /* skip last slash */
1167 snprintf(nickname
, PATH_MAX
, "PEM Token #%ld:%s", 1, n
);
1171 strncpy(nickname
, data
->set
.str
[STRING_SSL_ISSUERCERT
], PATH_MAX
);
1172 nickname
[PATH_MAX
-1]=0; /* make sure this is zero terminated */
1174 if (check_issuer_cert(connssl
->handle
, nickname
) == SECFailure
) {
1175 infof(data
,"SSL certificate issuer check failed\n");
1177 curlerr
= CURLE_SSL_ISSUER_ERROR
;
1181 infof(data
, "SSL certificate issuer check ok\n");
1188 err
= PR_GetError();
1189 infof(data
, "NSS error %d\n", err
);
1195 /* return number of sent (non-SSL) bytes */
1196 int Curl_nss_send(struct connectdata
*conn
, /* connection data */
1197 int sockindex
, /* socketindex */
1198 const void *mem
, /* send this data */
1199 size_t len
) /* amount to write */
1202 struct SessionHandle
*data
= conn
->data
;
1206 if(data
->set
.timeout
)
1207 timeout
= PR_MillisecondsToInterval(data
->set
.timeout
);
1209 timeout
= PR_MillisecondsToInterval(DEFAULT_CONNECT_TIMEOUT
);
1211 rc
= PR_Send(conn
->ssl
[sockindex
].handle
, mem
, (int)len
, 0, timeout
);
1214 err
= PR_GetError();
1216 if(err
== PR_IO_TIMEOUT_ERROR
) {
1217 failf(data
, "SSL connection timeout");
1218 return CURLE_OPERATION_TIMEDOUT
;
1221 failf(conn
->data
, "SSL write: error %d", err
);
1224 return rc
; /* number of bytes */
1228 * If the read would block we return -1 and set 'wouldblock' to TRUE.
1229 * Otherwise we return the amount of data read. Other errors should return -1
1230 * and set 'wouldblock' to FALSE.
1232 ssize_t
Curl_nss_recv(struct connectdata
* conn
, /* connection data */
1233 int num
, /* socketindex */
1234 char *buf
, /* store read data here */
1235 size_t buffersize
, /* max amount to read */
1239 struct SessionHandle
*data
= conn
->data
;
1242 if(data
->set
.timeout
)
1243 timeout
= PR_SecondsToInterval(data
->set
.timeout
);
1245 timeout
= PR_MillisecondsToInterval(DEFAULT_CONNECT_TIMEOUT
);
1247 nread
= PR_Recv(conn
->ssl
[num
].handle
, buf
, (int)buffersize
, 0, timeout
);
1248 *wouldblock
= FALSE
;
1250 /* failed SSL read */
1251 PRInt32 err
= PR_GetError();
1253 if(err
== PR_WOULD_BLOCK_ERROR
) {
1255 return -1; /* basically EWOULDBLOCK */
1257 if(err
== PR_IO_TIMEOUT_ERROR
) {
1258 failf(data
, "SSL connection timeout");
1259 return CURLE_OPERATION_TIMEDOUT
;
1261 failf(conn
->data
, "SSL read: errno %d", err
);
1267 size_t Curl_nss_version(char *buffer
, size_t size
)
1269 return snprintf(buffer
, size
, "NSS/%s", NSS_VERSION
);
1271 #endif /* USE_NSS */