CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) { PRErrorCode err = 0; PRFileDesc *model = NULL; PRBool ssl2 = PR_FALSE; PRBool ssl3 = PR_FALSE; PRBool tlsv1 = PR_FALSE; PRBool ssl_no_cache; PRBool ssl_cbc_random_iv; struct SessionHandle *data = conn->data; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; CURLcode curlerr; const int *cipher_to_enable; PRSocketOptionData sock_opt; long time_left; PRUint32 timeout; if(connssl->state == ssl_connection_complete) return CURLE_OK; connssl->data = data; /* list of all NSS objects we need to destroy in Curl_nss_close() */ connssl->obj_list = Curl_llist_alloc(nss_destroy_object); if(!connssl->obj_list) return CURLE_OUT_OF_MEMORY; /* FIXME. NSS doesn't support multiple databases open at the same time. */ PR_Lock(nss_initlock); curlerr = nss_init(conn->data); if(CURLE_OK != curlerr) { PR_Unlock(nss_initlock); goto error; } curlerr = CURLE_SSL_CONNECT_ERROR; 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); } } PK11_SetPasswordFunc(nss_get_password); PR_Unlock(nss_initlock); model = PR_NewTCPSocket(); if(!model) goto error; model = SSL_ImportFD(NULL, model); if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess) goto error; /* do not use SSL cache if disabled or we are not going to verify peer */ ssl_no_cache = (conn->ssl_config.sessionid && data->set.ssl.verifypeer) ? PR_FALSE : PR_TRUE; if(SSL_OptionSet(model, SSL_NO_CACHE, ssl_no_cache) != SECSuccess) goto error; switch (data->set.ssl.version) { default: case CURL_SSLVERSION_DEFAULT: ssl3 = PR_TRUE; if(data->state.ssl_connect_retry) infof(data, "TLS disabled due to previous handshake failure\n"); else tlsv1 = PR_TRUE; break; case CURL_SSLVERSION_TLSv1: tlsv1 = PR_TRUE; break; case CURL_SSLVERSION_SSLv2: ssl2 = PR_TRUE; break; case CURL_SSLVERSION_SSLv3: ssl3 = PR_TRUE; break; case CURL_SSLVERSION_TLSv1_0: case CURL_SSLVERSION_TLSv1_1: case CURL_SSLVERSION_TLSv1_2: failf(data, "TLS minor version cannot be set\n"); curlerr = CURLE_SSL_CONNECT_ERROR; goto error; } if(SSL_OptionSet(model, SSL_ENABLE_SSL2, ssl2) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_ENABLE_SSL3, ssl3) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_ENABLE_TLS, tlsv1) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_V2_COMPATIBLE_HELLO, ssl2) != SECSuccess) goto error; ssl_cbc_random_iv = !data->set.ssl_enable_beast; #ifdef SSL_CBC_RANDOM_IV /* unless the user explicitly asks to allow the protocol vulnerability, we use the work-around */ if(SSL_OptionSet(model, SSL_CBC_RANDOM_IV, ssl_cbc_random_iv) != SECSuccess) infof(data, "warning: failed to set SSL_CBC_RANDOM_IV = %d\n", ssl_cbc_random_iv); #else if(ssl_cbc_random_iv) infof(data, "warning: support for SSL_CBC_RANDOM_IV not compiled in\n"); #endif /* reset the flag to avoid an infinite loop */ data->state.ssl_connect_retry = FALSE; /* enable all ciphers from enable_ciphers_by_default */ cipher_to_enable = enable_ciphers_by_default; while(SSL_NULL_WITH_NULL_NULL != *cipher_to_enable) { if(SSL_CipherPrefSet(model, *cipher_to_enable, PR_TRUE) != SECSuccess) { curlerr = CURLE_SSL_CIPHER; goto error; } cipher_to_enable++; } if(data->set.ssl.cipher_list) { if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) { curlerr = CURLE_SSL_CIPHER; goto error; } } if(!data->set.ssl.verifypeer && data->set.ssl.verifyhost) infof(data, "warning: ignoring value of ssl.verifyhost\n"); /* bypass the default SSL_AuthCertificate() hook in case we do not want to * verify peer */ if(SSL_AuthCertificateHook(model, nss_auth_cert_hook, conn) != SECSuccess) goto error; data->set.ssl.certverifyresult=0; /* not checked yet */ if(SSL_BadCertHook(model, BadCertHandler, conn) != SECSuccess) goto error; if(SSL_HandshakeCallback(model, HandshakeCallback, NULL) != SECSuccess) goto error; if(data->set.ssl.verifypeer) { const CURLcode rv = nss_load_ca_certificates(conn, sockindex); if(CURLE_OK != rv) { curlerr = rv; goto error; } } if(data->set.ssl.CRLfile) { if(SECSuccess != nss_load_crl(data->set.ssl.CRLfile)) { curlerr = CURLE_SSL_CRL_BADFILE; goto error; } infof(data, " CRLfile: %s\n", data->set.ssl.CRLfile ? data->set.ssl.CRLfile : "none"); } if(data->set.str[STRING_CERT]) { char *nickname = dup_nickname(data, STRING_CERT); if(nickname) { /* we are not going to use libnsspem.so to read the client cert */ connssl->obj_clicert = NULL; } else { CURLcode rv = cert_stuff(conn, sockindex, data->set.str[STRING_CERT], data->set.str[STRING_KEY]); if(CURLE_OK != rv) { /* failf() is already done in cert_stuff() */ curlerr = rv; goto error; } } /* store the nickname for SelectClientCert() called during handshake */ connssl->client_nickname = nickname; } else connssl->client_nickname = NULL; if(SSL_GetClientAuthDataHook(model, SelectClientCert, (void *)connssl) != SECSuccess) { curlerr = CURLE_SSL_CERTPROBLEM; goto error; } /* Import our model socket onto the existing file descriptor */ connssl->handle = PR_ImportTCPSocket(sockfd); connssl->handle = SSL_ImportFD(model, connssl->handle); if(!connssl->handle) goto error; PR_Close(model); /* We don't need this any more */ model = NULL; /* This is the password associated with the cert that we're using */ if(data->set.str[STRING_KEY_PASSWD]) { SSL_SetPKCS11PinArg(connssl->handle, data->set.str[STRING_KEY_PASSWD]); } /* Force handshake on next I/O */ SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE); SSL_SetURL(connssl->handle, conn->host.name); /* check timeout situation */ time_left = Curl_timeleft(data, NULL, TRUE); if(time_left < 0L) { failf(data, "timed out before SSL handshake"); curlerr = CURLE_OPERATION_TIMEDOUT; goto error; } timeout = PR_MillisecondsToInterval((PRUint32) time_left); /* Force the handshake now */ if(SSL_ForceHandshakeWithTimeout(connssl->handle, timeout) != SECSuccess) { if(conn->data->set.ssl.certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN) curlerr = CURLE_PEER_FAILED_VERIFICATION; else if(conn->data->set.ssl.certverifyresult!=0) curlerr = CURLE_SSL_CACERT; goto error; } /* switch the SSL socket into non-blocking mode */ sock_opt.option = PR_SockOpt_Nonblocking; sock_opt.value.non_blocking = PR_TRUE; if(PR_SetSocketOption(connssl->handle, &sock_opt) != PR_SUCCESS) goto error; connssl->state = ssl_connection_complete; conn->recv[sockindex] = nss_recv; conn->send[sockindex] = nss_send; display_conn_info(conn, connssl->handle); if(data->set.str[STRING_SSL_ISSUERCERT]) { SECStatus ret = SECFailure; char *nickname = dup_nickname(data, STRING_SSL_ISSUERCERT); if(nickname) { /* we support only nicknames in case of STRING_SSL_ISSUERCERT for now */ ret = check_issuer_cert(connssl->handle, nickname); free(nickname); } if(SECFailure == ret) { infof(data,"SSL certificate issuer check failed\n"); curlerr = CURLE_SSL_ISSUER_ERROR; goto error; } else { infof(data, "SSL certificate issuer check ok\n"); } } return CURLE_OK; error: /* reset the flag to avoid an infinite loop */ data->state.ssl_connect_retry = FALSE; if(is_nss_error(curlerr)) { /* read NSPR error code */ err = PR_GetError(); if(is_cc_error(err)) curlerr = CURLE_SSL_CERTPROBLEM; /* print the error number and error string */ infof(data, "NSS error %d (%s)\n", err, nss_error_to_name(err)); /* print a human-readable message describing the error if available */ nss_print_error_message(data, err); } if(model) PR_Close(model); /* cleanup on connection failure */ Curl_llist_destroy(connssl->obj_list, NULL); connssl->obj_list = NULL; if(ssl3 && tlsv1 && isTLSIntoleranceError(err)) { /* schedule reconnect through Curl_retry_request() */ data->state.ssl_connect_retry = TRUE; infof(data, "Error in TLS handshake, trying SSLv3...\n"); return CURLE_OK; } return curlerr; }
int main(int argc, char **argv) { PRHostEnt he; PRStatus status; PRIntn next_index; PRUint16 port_number; char netdb_buf[PR_NETDB_BUF_SIZE]; PRNetAddr client_addr, server_addr; PRThread *client_thread, *server_thread; PRIntervalTime delta = PR_MillisecondsToInterval(500); err_out = PR_STDERR; std_out = PR_STDOUT; accept_timeout = PR_SecondsToInterval(2); if (argc != 2 && argc != 3) port_number = DEFAULT_PORT; else port_number = (PRUint16)atoi(argv[(argc == 2) ? 1 : 2]); status = PR_InitializeNetAddr(PR_IpAddrAny, port_number, &server_addr); if (PR_SUCCESS != status) { PL_FPrintError(err_out, "PR_InitializeNetAddr failed"); PR_ProcessExit(1); } if (argc < 3) { status = PR_InitializeNetAddr( PR_IpAddrLoopback, port_number, &client_addr); if (PR_SUCCESS != status) { PL_FPrintError(err_out, "PR_InitializeNetAddr failed"); PR_ProcessExit(1); } } else { status = PR_GetHostByName( argv[1], netdb_buf, sizeof(netdb_buf), &he); if (status == PR_FAILURE) { PL_FPrintError(err_out, "PR_GetHostByName failed"); PR_ProcessExit(1); } next_index = PR_EnumerateHostEnt(0, &he, port_number, &client_addr); if (next_index == -1) { PL_FPrintError(err_out, "PR_EnumerateHostEnt failed"); PR_ProcessExit(1); } } for ( write_dally = 0; write_dally < accept_timeout + (2 * delta); write_dally += delta) { PR_fprintf( std_out, "Testing w/ write_dally = %d msec\n", PR_IntervalToMilliseconds(write_dally)); server_thread = PR_CreateThread( PR_USER_THREAD, AcceptingThread, &server_addr, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); if (server_thread == NULL) { PL_FPrintError(err_out, "PR_CreateThread (server) failed"); PR_ProcessExit(1); } PR_Sleep(delta); /* let the server pot thicken */ client_thread = PR_CreateThread( PR_USER_THREAD, ConnectingThread, &client_addr, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); if (client_thread == NULL) { PL_FPrintError(err_out, "PR_CreateThread (client) failed"); PR_ProcessExit(1); } if (PR_JoinThread(client_thread) == PR_FAILURE) PL_FPrintError(err_out, "PR_JoinThread (client) failed"); if (PR_JoinThread(server_thread) == PR_FAILURE) PL_FPrintError(err_out, "PR_JoinThread (server) failed"); } return 0; }
int poll(struct pollfd *filedes, unsigned long nfds, int timeout) #endif { #ifdef AIX struct pollfd *filedes = (struct pollfd *) listptr; #endif struct pollfd *pfd, *epfd; _PRUnixPollDesc *unixpds, *unixpd, *eunixpd; PRIntervalTime ticks; PRInt32 pdcnt; int ready; /* * Easy special case: zero timeout. Simply call the native * poll() with no fear of blocking. */ if (timeout == 0) { #if defined(AIX) return _MD_POLL(listptr, nfds, timeout); #else return _MD_POLL(filedes, nfds, timeout); #endif } if (!_pr_initialized) { _PR_ImplicitInitialization(); } #ifndef _PR_LOCAL_THREADS_ONLY if (_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) { return _MD_POLL(filedes, nfds, timeout); } #endif /* We do not support the pollmsg structures on AIX */ #ifdef AIX PR_ASSERT((nfds & 0xff00) == 0); #endif if (timeout < 0 && timeout != -1) { errno = EINVAL; return -1; } /* Convert timeout from miliseconds to ticks */ if (timeout == -1) { ticks = PR_INTERVAL_NO_TIMEOUT; } else { ticks = PR_MillisecondsToInterval(timeout); } /* Check for no descriptor case (just do a timeout) */ if (nfds == 0) { PR_Sleep(ticks); return 0; } unixpds = (_PRUnixPollDesc *) PR_MALLOC(nfds * sizeof(_PRUnixPollDesc)); if (NULL == unixpds) { errno = EAGAIN; return -1; } pdcnt = 0; epfd = filedes + nfds; unixpd = unixpds; for (pfd = filedes; pfd < epfd; pfd++) { /* * poll() ignores negative fd's. */ if (pfd->fd >= 0) { unixpd->osfd = pfd->fd; #ifdef _PR_USE_POLL unixpd->in_flags = pfd->events; #else /* * Map the poll events to one of the three that can be * represented by the select fd_sets: * POLLIN, POLLRDNORM ===> readable * POLLOUT, POLLWRNORM ===> writable * POLLPRI, POLLRDBAND ===> exception * POLLNORM, POLLWRBAND (and POLLMSG on some platforms) * are ignored. * * The output events POLLERR and POLLHUP are never turned on. * POLLNVAL may be turned on. */ unixpd->in_flags = 0; if (pfd->events & (POLLIN #ifdef POLLRDNORM | POLLRDNORM #endif )) { unixpd->in_flags |= _PR_UNIX_POLL_READ; } if (pfd->events & (POLLOUT #ifdef POLLWRNORM | POLLWRNORM #endif )) { unixpd->in_flags |= _PR_UNIX_POLL_WRITE; } if (pfd->events & (POLLPRI #ifdef POLLRDBAND | POLLRDBAND #endif )) { unixpd->in_flags |= PR_POLL_EXCEPT; } #endif /* _PR_USE_POLL */ unixpd->out_flags = 0; unixpd++; pdcnt++; } } ready = _PR_WaitForMultipleFDs(unixpds, pdcnt, ticks); if (-1 == ready) { if (PR_GetError() == PR_PENDING_INTERRUPT_ERROR) { errno = EINTR; /* XXX we aren't interrupted by a signal, but... */ } else { errno = PR_GetOSError(); } } if (ready <= 0) { goto done; } /* * Copy the out_flags from the _PRUnixPollDesc structures to the * user's pollfd structures and free the allocated memory */ unixpd = unixpds; for (pfd = filedes; pfd < epfd; pfd++) { pfd->revents = 0; if (pfd->fd >= 0) { #ifdef _PR_USE_POLL pfd->revents = unixpd->out_flags; #else if (0 != unixpd->out_flags) { if (unixpd->out_flags & _PR_UNIX_POLL_READ) { if (pfd->events & POLLIN) { pfd->revents |= POLLIN; } #ifdef POLLRDNORM if (pfd->events & POLLRDNORM) { pfd->revents |= POLLRDNORM; } #endif } if (unixpd->out_flags & _PR_UNIX_POLL_WRITE) { if (pfd->events & POLLOUT) { pfd->revents |= POLLOUT; } #ifdef POLLWRNORM if (pfd->events & POLLWRNORM) { pfd->revents |= POLLWRNORM; } #endif } if (unixpd->out_flags & _PR_UNIX_POLL_EXCEPT) { if (pfd->events & POLLPRI) { pfd->revents |= POLLPRI; } #ifdef POLLRDBAND if (pfd->events & POLLRDBAND) { pfd->revents |= POLLRDBAND; } #endif } if (unixpd->out_flags & _PR_UNIX_POLL_ERR) { pfd->revents |= POLLERR; } if (unixpd->out_flags & _PR_UNIX_POLL_NVAL) { pfd->revents |= POLLNVAL; } if (unixpd->out_flags & _PR_UNIX_POLL_HUP) { pfd->revents |= POLLHUP; } } #endif /* _PR_USE_POLL */ unixpd++; } } done: PR_DELETE(unixpds); return ready; }
int main(int argc, char **argv) { PRThread **threads; int num_thread_funcs = sizeof(threadFuncs)/sizeof(NSPRThreadFunc); int num_thread_scopes = sizeof(threadScopes)/sizeof(PRThreadScope); int i, j; int idx; PRInt32 secs; PLOptStatus os; PLOptState *opt = PL_CreateOptState(argc, argv, "dl:t:h"); while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { if (PL_OPT_BAD == os) continue; switch (opt->option) { case 'd': /* debug mode */ debug_mode = PR_TRUE; break; case 'l': /* lead time */ lead_time_secs = atoi(opt->value); break; case 't': /* tolerance */ tolerance_msecs = atoi(opt->value); break; case 'h': default: Help(); return 2; } } PL_DestroyOptState(opt); if (debug_mode) { fprintf(stderr, "lead time: %d secs\n", lead_time_secs); fprintf(stderr, "tolerance: %d msecs\n", tolerance_msecs); } start_time = PR_IntervalNow(); #if defined(XP_UNIX) gettimeofday(&start_time_tv, NULL); #endif #if defined(WIN32) _ftime(&start_time_tb); #endif tolerance = PR_MillisecondsToInterval(tolerance_msecs); threads = PR_Malloc( num_thread_scopes * num_thread_funcs * sizeof(PRThread*)); if (threads == NULL) { fprintf(stderr, "PR_Malloc failed\n"); exit(1); } /* start to time out 5 seconds after a rollover date */ secs = lead_time_secs + 5; idx = 0; for (i = 0; i < num_thread_scopes; i++) { for (j = 0; j < num_thread_funcs; j++) { threads[idx] = PR_CreateThread(PR_USER_THREAD, threadFuncs[j], (void*)PR_SecondsToInterval(secs), PR_PRIORITY_NORMAL, threadScopes[i], PR_JOINABLE_THREAD, 0); if (threads[idx] == NULL) { fprintf(stderr, "PR_CreateThread failed\n"); exit(1); } secs++; idx++; } } for (idx = 0; idx < num_thread_scopes*num_thread_funcs; idx++) { if (PR_JoinThread(threads[idx]) == PR_FAILURE) { fprintf(stderr, "PR_JoinThread failed\n"); exit(1); } } PR_Free(threads); printf("PASS\n"); return 0; }