diff options
Diffstat (limited to 'lib/nss.c')
-rw-r--r-- | lib/nss.c | 354 |
1 files changed, 205 insertions, 149 deletions
@@ -63,6 +63,7 @@ #include <secport.h> #include <certdb.h> #include <base64.h> +#include <cert.h> #include "curl_memory.h" #include "rawstr.h" @@ -79,6 +80,7 @@ PRFileDesc *PR_ImportTCPSocket(PRInt32 osfd); PRLock * nss_initlock = NULL; +PRLock * nss_crllock = NULL; volatile int initialized = 0; @@ -411,78 +413,90 @@ static int nss_load_cert(struct ssl_connect_data *ssl, return 1; } -static int nss_load_crl(const char* crlfilename, PRBool ascii) +/* add given CRL to cache if it is not already there */ +static SECStatus nss_cache_crl(SECItem *crlDER) { - PRFileDesc *infile; - PRStatus prstat; - PRFileInfo info; - PRInt32 nb; - int rv; - SECItem crlDER; - CERTSignedCrl *crl=NULL; - PK11SlotInfo *slot=NULL; - - infile = PR_Open(crlfilename,PR_RDONLY,0); - if (!infile) { - return 0; + CERTCertDBHandle *db = CERT_GetDefaultCertDB(); + CERTSignedCrl *crl = SEC_FindCrlByDERCert(db, crlDER, 0); + if(crl) { + /* CRL already cached */ + SEC_DestroyCrl(crl); + SECITEM_FreeItem(crlDER, PR_FALSE); + return SECSuccess; } - crlDER.data = NULL; - prstat = PR_GetOpenFileInfo(infile,&info); - if (prstat!=PR_SUCCESS) - return 0; - if (ascii) { - SECItem filedata; - char *asc,*body; - filedata.data = NULL; - if (!SECITEM_AllocItem(NULL,&filedata,info.size)) - return 0; - nb = PR_Read(infile,filedata.data,info.size); - if (nb!=info.size) - return 0; - asc = (char*)filedata.data; - if (!asc) - return 0; - body=strstr(asc,"-----BEGIN"); - if (body != NULL) { - char *trailer=NULL; - asc = body; - body = PORT_Strchr(asc,'\n'); - if (!body) - body = PORT_Strchr(asc,'\r'); - if (body) - trailer = strstr(++body,"-----END"); - if (trailer!=NULL) - *trailer='\0'; - else - return 0; - } - else { - body = asc; - } - rv = ATOB_ConvertAsciiToItem(&crlDER,body); - PORT_Free(filedata.data); - if (rv) - return 0; + /* acquire lock before call of CERT_CacheCRL() */ + PR_Lock(nss_crllock); + if(SECSuccess != CERT_CacheCRL(db, crlDER)) { + /* unable to cache CRL */ + PR_Unlock(nss_crllock); + SECITEM_FreeItem(crlDER, PR_FALSE); + return SECFailure; } - else { - if (!SECITEM_AllocItem(NULL,&crlDER,info.size)) - return 0; - nb = PR_Read(infile,crlDER.data,info.size); - if (nb!=info.size) - return 0; + + /* we need to clear session cache, so that the CRL could take effect */ + SSL_ClearSessionCache(); + PR_Unlock(nss_crllock); + return SECSuccess; +} + +static SECStatus nss_load_crl(const char* crlfilename) +{ + PRFileDesc *infile; + PRFileInfo info; + SECItem filedata = { 0, NULL, 0 }; + SECItem crlDER = { 0, NULL, 0 }; + char *body; + + infile = PR_Open(crlfilename, PR_RDONLY, 0); + if(!infile) + return SECFailure; + + if(PR_SUCCESS != PR_GetOpenFileInfo(infile, &info)) + goto fail; + + if(!SECITEM_AllocItem(NULL, &filedata, info.size + /* zero ended */ 1)) + goto fail; + + if(info.size != PR_Read(infile, filedata.data, info.size)) + goto fail; + + /* place a trailing zero right after the visible data */ + body = (char*)filedata.data; + body[--filedata.len] = '\0'; + + body = strstr(body, "-----BEGIN"); + if(body) { + /* assume ASCII */ + char *trailer; + char *begin = PORT_Strchr(body, '\n'); + if(!begin) + begin = PORT_Strchr(body, '\r'); + if(!begin) + goto fail; + + trailer = strstr(++begin, "-----END"); + if(!trailer) + goto fail; + + /* retrieve DER from ASCII */ + *trailer = '\0'; + if(ATOB_ConvertAsciiToItem(&crlDER, begin)) + goto fail; + + SECITEM_FreeItem(&filedata, PR_FALSE); } + else + /* assume DER */ + crlDER = filedata; - slot = PK11_GetInternalKeySlot(); - crl = PK11_ImportCRL(slot,&crlDER, - NULL,SEC_CRL_TYPE, - NULL,CRL_IMPORT_DEFAULT_OPTIONS, - NULL,(CRL_DECODE_DEFAULT_OPTIONS| - CRL_DECODE_DONT_COPY_DER)); - if (slot) PK11_FreeSlot(slot); - if (!crl) return 0; - SEC_DestroyCrl(crl); - return 1; + PR_Close(infile); + return nss_cache_crl(&crlDER); + +fail: + PR_Close(infile); + SECITEM_FreeItem(&filedata, PR_FALSE); + return SECFailure; } static int nss_load_key(struct connectdata *conn, int sockindex, @@ -877,6 +891,57 @@ isTLSIntoleranceError(PRInt32 err) } } +static CURLcode init_nss(struct SessionHandle *data) +{ + char *cert_dir; + struct_stat st; + if(initialized) + return CURLE_OK; + + /* First we check if $SSL_DIR points to a valid dir */ + cert_dir = getenv("SSL_DIR"); + if(cert_dir) { + if((stat(cert_dir, &st) != 0) || + (!S_ISDIR(st.st_mode))) { + cert_dir = NULL; + } + } + + /* Now we check if the default location is a valid dir */ + if(!cert_dir) { + if((stat(SSL_DIR, &st) == 0) && + (S_ISDIR(st.st_mode))) { + cert_dir = (char *)SSL_DIR; + } + } + + if(!NSS_IsInitialized()) { + SECStatus rv; + initialized = 1; + infof(data, "Initializing NSS with certpath: %s\n", + cert_dir ? cert_dir : "none"); + if(!cert_dir) { + rv = NSS_NoDB_Init(NULL); + } + else { + char *certpath = + PR_smprintf("%s%s", NSS_VersionCheck("3.12.0") ? "sql:" : "", cert_dir); + rv = NSS_Initialize(certpath, "", "", "", NSS_INIT_READONLY); + PR_smprintf_free(certpath); + } + if(rv != SECSuccess) { + infof(data, "Unable to initialize NSS database\n"); + initialized = 0; + return CURLE_SSL_CACERT_BADFILE; + } + } + + if(num_enabled_ciphers() == 0) + NSS_SetDomesticPolicy(); + + return CURLE_OK; +} + /** * Global SSL init * @@ -889,6 +954,7 @@ int Curl_nss_init(void) if (nss_initlock == NULL) { PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 256); nss_initlock = PR_NewLock(); + nss_crllock = PR_NewLock(); } /* We will actually initialize NSS later */ @@ -896,6 +962,21 @@ int Curl_nss_init(void) return 1; } +CURLcode Curl_nss_force_init(struct SessionHandle *data) +{ + CURLcode rv; + if(!nss_initlock) { + failf(data, "unable to initialize NSS, curl_global_init() should have been " + "called with CURL_GLOBAL_SSL or CURL_GLOBAL_ALL"); + return CURLE_OUT_OF_MEMORY; + } + + PR_Lock(nss_initlock); + rv = init_nss(data); + PR_Unlock(nss_initlock); + return rv; +} + /* Global cleanup */ void Curl_nss_cleanup(void) { @@ -918,6 +999,7 @@ void Curl_nss_cleanup(void) PR_Unlock(nss_initlock); PR_DestroyLock(nss_initlock); + PR_DestroyLock(nss_crllock); nss_initlock = NULL; initialized = 0; @@ -1010,6 +1092,9 @@ static bool handle_cc_error(PRInt32 err, struct SessionHandle *data) } } +static Curl_recv nss_recv; +static Curl_send nss_send; + CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) { PRInt32 err; @@ -1020,15 +1105,12 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) struct SessionHandle *data = conn->data; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - SECStatus rv; - char *certDir = NULL; int curlerr; const int *cipher_to_enable; PRSocketOptionData sock_opt; + long time_left; PRUint32 timeout; - curlerr = CURLE_SSL_CONNECT_ERROR; - if (connssl->state == ssl_connection_complete) return CURLE_OK; @@ -1042,76 +1124,36 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) /* FIXME. NSS doesn't support multiple databases open at the same time. */ PR_Lock(nss_initlock); - if(!initialized) { - struct_stat st; - - /* First we check if $SSL_DIR points to a valid dir */ - certDir = getenv("SSL_DIR"); - if(certDir) { - if((stat(certDir, &st) != 0) || - (!S_ISDIR(st.st_mode))) { - certDir = NULL; - } - } - - /* Now we check if the default location is a valid dir */ - if(!certDir) { - if((stat(SSL_DIR, &st) == 0) && - (S_ISDIR(st.st_mode))) { - certDir = (char *)SSL_DIR; - } - } - - if (!NSS_IsInitialized()) { - initialized = 1; - infof(conn->data, "Initializing NSS with certpath: %s\n", - certDir ? certDir : "none"); - if(!certDir) { - rv = NSS_NoDB_Init(NULL); - } - else { - char *certpath = PR_smprintf("%s%s", - NSS_VersionCheck("3.12.0") ? "sql:" : "", - certDir); - rv = NSS_Initialize(certpath, "", "", "", NSS_INIT_READONLY); - PR_smprintf_free(certpath); - } - if(rv != SECSuccess) { - infof(conn->data, "Unable to initialize NSS database\n"); - curlerr = CURLE_SSL_CACERT_BADFILE; - initialized = 0; - PR_Unlock(nss_initlock); - goto error; - } - } + curlerr = init_nss(conn->data); + if(CURLE_OK != curlerr) { + PR_Unlock(nss_initlock); + goto error; + } - if(num_enabled_ciphers() == 0) - NSS_SetDomesticPolicy(); + curlerr = CURLE_SSL_CONNECT_ERROR; #ifdef HAVE_PK11_CREATEGENERICOBJECT - if(!mod) { - char *configstring = aprintf("library=%s name=PEM", pem_library); - if(!configstring) { - PR_Unlock(nss_initlock); - goto error; - } - mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE); - free(configstring); + if(!mod) { + char *configstring = aprintf("library=%s name=PEM", pem_library); + if(!configstring) { + PR_Unlock(nss_initlock); + goto error; + } + mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE); + free(configstring); - if(!mod || !mod->loaded) { - if(mod) { - SECMOD_DestroyModule(mod); - mod = NULL; - } - infof(data, "WARNING: failed to load NSS PEM library %s. Using " - "OpenSSL PEM certificates will not work.\n", pem_library); + if(!mod || !mod->loaded) { + if(mod) { + SECMOD_DestroyModule(mod); + mod = NULL; } + infof(data, "WARNING: failed to load NSS PEM library %s. Using " + "OpenSSL PEM certificates will not work.\n", pem_library); } + } #endif - PK11_SetPasswordFunc(nss_get_password); - - } + PK11_SetPasswordFunc(nss_get_password); PR_Unlock(nss_initlock); model = PR_NewTCPSocket(); @@ -1243,8 +1285,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) data->set.ssl.CApath ? data->set.ssl.CApath : "none"); if (data->set.ssl.CRLfile) { - int rc = nss_load_crl(data->set.ssl.CRLfile, PR_FALSE); - if (!rc) { + if(SECSuccess != nss_load_crl(data->set.ssl.CRLfile)) { curlerr = CURLE_SSL_CRL_BADFILE; goto error; } @@ -1302,8 +1343,15 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) SSL_SetURL(connssl->handle, conn->host.name); + /* check timeout situation */ + time_left = Curl_timeleft(conn, NULL, TRUE); + if(time_left < 0L) { + failf(data, "timed out before SSL handshake"); + goto error; + } + timeout = PR_MillisecondsToInterval((PRUint32) time_left); + /* Force the handshake now */ - timeout = PR_MillisecondsToInterval((PRUint32)Curl_timeleft(conn, NULL, TRUE)); if(SSL_ForceHandshakeWithTimeout(connssl->handle, timeout) != SECSuccess) { if(conn->data->set.ssl.certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN) curlerr = CURLE_PEER_FAILED_VERIFICATION; @@ -1313,6 +1361,8 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) } connssl->state = ssl_connection_complete; + conn->recv[sockindex] = nss_recv; + conn->send[sockindex] = nss_send; display_conn_info(conn, connssl->handle); @@ -1365,12 +1415,11 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) return curlerr; } -/* for documentation see Curl_ssl_send() in sslgen.h */ -int Curl_nss_send(struct connectdata *conn, /* connection data */ - int sockindex, /* socketindex */ - const void *mem, /* send this data */ - size_t len, /* amount to write */ - int *curlcode) +static ssize_t nss_send(struct connectdata *conn, /* connection data */ + int sockindex, /* socketindex */ + const void *mem, /* send this data */ + size_t len, /* amount to write */ + CURLcode *curlcode) { int rc; @@ -1379,7 +1428,7 @@ int Curl_nss_send(struct connectdata *conn, /* connection data */ if(rc < 0) { PRInt32 err = PR_GetError(); if(err == PR_WOULD_BLOCK_ERROR) - *curlcode = -1; /* EWOULDBLOCK */ + *curlcode = CURLE_AGAIN; else if(handle_cc_error(err, conn->data)) *curlcode = CURLE_SSL_CERTPROBLEM; else { @@ -1391,12 +1440,11 @@ int Curl_nss_send(struct connectdata *conn, /* connection data */ return rc; /* number of bytes */ } -/* for documentation see Curl_ssl_recv() in sslgen.h */ -ssize_t Curl_nss_recv(struct connectdata * conn, /* connection data */ - int num, /* socketindex */ - char *buf, /* store read data here */ - size_t buffersize, /* max amount to read */ - int *curlcode) +static ssize_t nss_recv(struct connectdata * conn, /* connection data */ + int num, /* socketindex */ + char *buf, /* store read data here */ + size_t buffersize, /* max amount to read */ + CURLcode *curlcode) { ssize_t nread; @@ -1406,7 +1454,7 @@ ssize_t Curl_nss_recv(struct connectdata * conn, /* connection data */ PRInt32 err = PR_GetError(); if(err == PR_WOULD_BLOCK_ERROR) - *curlcode = -1; /* EWOULDBLOCK */ + *curlcode = CURLE_AGAIN; else if(handle_cc_error(err, conn->data)) *curlcode = CURLE_SSL_CERTPROBLEM; else { @@ -1422,4 +1470,12 @@ size_t Curl_nss_version(char *buffer, size_t size) { return snprintf(buffer, size, "NSS/%s", NSS_VERSION); } + +int Curl_nss_seed(struct SessionHandle *data) +{ + /* TODO: implement? */ + (void) data; + return 0; +} + #endif /* USE_NSS */ |