static SQRESULT sq_ssl_get_session_id(HSQUIRRELVM v){ SQ_FUNC_VARS_NO_TOP(v); GET_ssl_INSTANCE(); const uint8_t * result = ssl_get_session_id(self); sq_pushstring(v, (char *)result, ssl_get_session_id_size(self)); return 1; }
/*---------------------------------------------------------------------- | NPT_TlsSessionImpl::GetSessionId +---------------------------------------------------------------------*/ NPT_Result NPT_TlsSessionImpl::GetSessionId(NPT_DataBuffer& session_id) { if (m_SSL == NULL) { // no handshake done session_id.SetDataSize(0); return NPT_ERROR_INVALID_STATE; } // return the session id session_id.SetData(ssl_get_session_id(m_SSL), ssl_get_session_id_size(m_SSL)); return NPT_SUCCESS; }
/** * Display what session id we have. */ static void display_session_id(SSL *ssl) { int i; const uint8_t *session_id = ssl_get_session_id(ssl); int sess_id_size = ssl_get_session_id_size(ssl); if (sess_id_size > 0) { printf("-----BEGIN SSL SESSION PARAMETERS-----\n"); for (i = 0; i < sess_id_size; i++) { printf("%02x", session_id[i]); } printf("\n-----END SSL SESSION PARAMETERS-----\n"); } }
/* * This function is called after the TCP connect has completed. Setup the TLS * layer and do all necessary magic. */ CURLcode Curl_axtls_connect(struct connectdata *conn, int sockindex) { struct SessionHandle *data = conn->data; SSL_CTX *ssl_ctx; SSL *ssl; int cert_types[] = {SSL_OBJ_X509_CERT, SSL_OBJ_PKCS12, 0}; int key_types[] = {SSL_OBJ_RSA_KEY, SSL_OBJ_PKCS8, SSL_OBJ_PKCS12, 0}; int i, ssl_fcn_return; const uint8_t *ssl_sessionid; size_t ssl_idsize; const char *peer_CN; uint32_t dns_altname_index; const char *dns_altname; int8_t found_subject_alt_names = 0; int8_t found_subject_alt_name_matching_conn = 0; /* Assuming users will not compile in custom key/cert to axTLS */ uint32_t client_option = SSL_NO_DEFAULT_KEY|SSL_SERVER_VERIFY_LATER; if(conn->ssl[sockindex].state == ssl_connection_complete) /* to make us tolerant against being called more than once for the same connection */ return CURLE_OK; /* axTLS only supports TLSv1 */ /* check to see if we've been told to use an explicit SSL/TLS version */ switch(data->set.ssl.version) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: break; default: failf(data, "axTLS only supports TLSv1"); return CURLE_SSL_CONNECT_ERROR; } #ifdef AXTLSDEBUG client_option |= SSL_DISPLAY_STATES | SSL_DISPLAY_RSA | SSL_DISPLAY_CERTS; #endif /* AXTLSDEBUG */ /* Allocate an SSL_CTX struct */ ssl_ctx = ssl_ctx_new(client_option, SSL_DEFAULT_CLNT_SESS); if(ssl_ctx == NULL) { failf(data, "unable to create client SSL context"); return CURLE_SSL_CONNECT_ERROR; } /* Load the trusted CA cert bundle file */ if(data->set.ssl.CAfile) { if(ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CACERT, data->set.ssl.CAfile, NULL) != SSL_OK) { infof(data, "error reading ca cert file %s \n", data->set.ssl.CAfile); if(data->set.ssl.verifypeer) { Curl_axtls_close(conn, sockindex); return CURLE_SSL_CACERT_BADFILE; } } else infof(data, "found certificates in %s\n", data->set.ssl.CAfile); } /* curl_gtls.c tasks we're skipping for now: * 1) certificate revocation list checking * 2) dns name assignment to host * 3) set protocol priority. axTLS is TLSv1 only, so can probably ignore * 4) set certificate priority. axTLS ignores type and sends certs in * order added. can probably ignore this. */ /* Load client certificate */ if(data->set.str[STRING_CERT]) { i=0; /* Instead of trying to analyze cert type here, let axTLS try them all. */ while(cert_types[i] != 0) { ssl_fcn_return = ssl_obj_load(ssl_ctx, cert_types[i], data->set.str[STRING_CERT], NULL); if(ssl_fcn_return == SSL_OK) { infof(data, "successfully read cert file %s \n", data->set.str[STRING_CERT]); break; } i++; } /* Tried all cert types, none worked. */ if(cert_types[i] == 0) { failf(data, "%s is not x509 or pkcs12 format", data->set.str[STRING_CERT]); Curl_axtls_close(conn, sockindex); return CURLE_SSL_CERTPROBLEM; } } /* Load client key. If a pkcs12 file successfully loaded a cert, then there's nothing to do because the key has already been loaded. */ if(data->set.str[STRING_KEY] && cert_types[i] != SSL_OBJ_PKCS12) { i=0; /* Instead of trying to analyze key type here, let axTLS try them all. */ while(key_types[i] != 0) { ssl_fcn_return = ssl_obj_load(ssl_ctx, key_types[i], data->set.str[STRING_KEY], NULL); if(ssl_fcn_return == SSL_OK) { infof(data, "successfully read key file %s \n", data->set.str[STRING_KEY]); break; } i++; } /* Tried all key types, none worked. */ if(key_types[i] == 0) { failf(data, "Failure: %s is not a supported key file", data->set.str[STRING_KEY]); Curl_axtls_close(conn, sockindex); return CURLE_SSL_CONNECT_ERROR; } } /* curl_gtls.c does more here that is being left out for now * 1) set session credentials. can probably ignore since axtls puts this * info in the ssl_ctx struct * 2) setting up callbacks. these seem gnutls specific */ /* In axTLS, handshaking happens inside ssl_client_new. */ if(!Curl_ssl_getsessionid(conn, (void **) &ssl_sessionid, &ssl_idsize)) { /* we got a session id, use it! */ infof (data, "SSL re-using session ID\n"); ssl = ssl_client_new(ssl_ctx, conn->sock[sockindex], ssl_sessionid, (uint8_t)ssl_idsize); } else ssl = ssl_client_new(ssl_ctx, conn->sock[sockindex], NULL, 0); /* Check to make sure handshake was ok. */ ssl_fcn_return = ssl_handshake_status(ssl); if(ssl_fcn_return != SSL_OK) { Curl_axtls_close(conn, sockindex); ssl_display_error(ssl_fcn_return); /* goes to stdout. */ return map_error_to_curl(ssl_fcn_return); } infof (data, "handshake completed successfully\n"); /* Here, curl_gtls.c gets the peer certificates and fails out depending on * settings in "data." axTLS api doesn't have get cert chain fcn, so omit? */ /* Verify server's certificate */ if(data->set.ssl.verifypeer) { if(ssl_verify_cert(ssl) != SSL_OK) { Curl_axtls_close(conn, sockindex); failf(data, "server cert verify failed"); return CURLE_SSL_CONNECT_ERROR; } } else infof(data, "\t server certificate verification SKIPPED\n"); /* Here, curl_gtls.c does issuer verification. axTLS has no straightforward * equivalent, so omitting for now.*/ /* Here, curl_gtls.c does the following * 1) x509 hostname checking per RFC2818. axTLS doesn't support this, but * it seems useful. This is now implemented, by Oscar Koeroo * 2) checks cert validity based on time. axTLS does this in ssl_verify_cert * 3) displays a bunch of cert information. axTLS doesn't support most of * this, but a couple fields are available. */ /* There is no (DNS) Altnames count in the version 1.4.8 API. There is a risk of an inifite loop */ for(dns_altname_index = 0; ; dns_altname_index++) { dns_altname = ssl_get_cert_subject_alt_dnsname(ssl, dns_altname_index); if(dns_altname == NULL) { break; } found_subject_alt_names = 1; infof(data, "\tComparing subject alt name DNS with hostname: %s <-> %s\n", dns_altname, conn->host.name); if(Curl_cert_hostcheck(dns_altname, conn->host.name)) { found_subject_alt_name_matching_conn = 1; break; } } /* RFC2818 checks */ if(found_subject_alt_names && !found_subject_alt_name_matching_conn) { /* Break connection ! */ Curl_axtls_close(conn, sockindex); failf(data, "\tsubjectAltName(s) do not match %s\n", conn->host.dispname); return CURLE_PEER_FAILED_VERIFICATION; } else if(found_subject_alt_names == 0) { /* Per RFC2818, when no Subject Alt Names were available, examine the peer CN as a legacy fallback */ peer_CN = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME); if(peer_CN == NULL) { /* Similar behaviour to the OpenSSL interface */ Curl_axtls_close(conn, sockindex); failf(data, "unable to obtain common name from peer certificate"); return CURLE_PEER_FAILED_VERIFICATION; } else { if(!Curl_cert_hostcheck((const char *)peer_CN, conn->host.name)) { if(data->set.ssl.verifyhost) { /* Break connection ! */ Curl_axtls_close(conn, sockindex); failf(data, "\tcommon name \"%s\" does not match \"%s\"\n", peer_CN, conn->host.dispname); return CURLE_PEER_FAILED_VERIFICATION; } else infof(data, "\tcommon name \"%s\" does not match \"%s\"\n", peer_CN, conn->host.dispname); } } } /* General housekeeping */ conn->ssl[sockindex].state = ssl_connection_complete; conn->ssl[sockindex].ssl = ssl; conn->ssl[sockindex].ssl_ctx = ssl_ctx; conn->recv[sockindex] = axtls_recv; conn->send[sockindex] = axtls_send; /* Put our freshly minted SSL session in cache */ ssl_idsize = ssl_get_session_id_size(ssl); ssl_sessionid = ssl_get_session_id(ssl); if(Curl_ssl_addsessionid(conn, (void *) ssl_sessionid, ssl_idsize) != CURLE_OK) infof (data, "failed to add session to cache\n"); return CURLE_OK; }
/* * For both blocking and non-blocking connects, this function finalizes the * SSL connection. */ static CURLcode connect_finish(struct connectdata *conn, int sockindex) { struct SessionHandle *data = conn->data; SSL *ssl = conn->ssl[sockindex].ssl; const uint8_t *ssl_sessionid; size_t ssl_idsize; const char *peer_CN; uint32_t dns_altname_index; const char *dns_altname; int8_t found_subject_alt_names = 0; int8_t found_subject_alt_name_matching_conn = 0; /* Here, gtls.c gets the peer certificates and fails out depending on * settings in "data." axTLS api doesn't have get cert chain fcn, so omit? */ /* Verify server's certificate */ if(data->set.ssl.verifypeer) { if(ssl_verify_cert(ssl) != SSL_OK) { Curl_axtls_close(conn, sockindex); failf(data, "server cert verify failed"); return CURLE_PEER_FAILED_VERIFICATION; } } else infof(data, "\t server certificate verification SKIPPED\n"); /* Here, gtls.c does issuer verification. axTLS has no straightforward * equivalent, so omitting for now.*/ /* Here, gtls.c does the following * 1) x509 hostname checking per RFC2818. axTLS doesn't support this, but * it seems useful. This is now implemented, by Oscar Koeroo * 2) checks cert validity based on time. axTLS does this in ssl_verify_cert * 3) displays a bunch of cert information. axTLS doesn't support most of * this, but a couple fields are available. */ /* There is no (DNS) Altnames count in the version 1.4.8 API. There is a risk of an inifite loop */ for(dns_altname_index = 0; ; dns_altname_index++) { dns_altname = ssl_get_cert_subject_alt_dnsname(ssl, dns_altname_index); if(dns_altname == NULL) { break; } found_subject_alt_names = 1; infof(data, "\tComparing subject alt name DNS with hostname: %s <-> %s\n", dns_altname, conn->host.name); if(Curl_cert_hostcheck(dns_altname, conn->host.name)) { found_subject_alt_name_matching_conn = 1; break; } } /* RFC2818 checks */ if(found_subject_alt_names && !found_subject_alt_name_matching_conn) { if(data->set.ssl.verifyhost) { /* Break connection ! */ Curl_axtls_close(conn, sockindex); failf(data, "\tsubjectAltName(s) do not match %s\n", conn->host.dispname); return CURLE_PEER_FAILED_VERIFICATION; } else infof(data, "\tsubjectAltName(s) do not match %s\n", conn->host.dispname); } else if(found_subject_alt_names == 0) { /* Per RFC2818, when no Subject Alt Names were available, examine the peer CN as a legacy fallback */ peer_CN = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME); if(peer_CN == NULL) { if(data->set.ssl.verifyhost) { Curl_axtls_close(conn, sockindex); failf(data, "unable to obtain common name from peer certificate"); return CURLE_PEER_FAILED_VERIFICATION; } else infof(data, "unable to obtain common name from peer certificate"); } else { if(!Curl_cert_hostcheck((const char *)peer_CN, conn->host.name)) { if(data->set.ssl.verifyhost) { /* Break connection ! */ Curl_axtls_close(conn, sockindex); failf(data, "\tcommon name \"%s\" does not match \"%s\"\n", peer_CN, conn->host.dispname); return CURLE_PEER_FAILED_VERIFICATION; } else infof(data, "\tcommon name \"%s\" does not match \"%s\"\n", peer_CN, conn->host.dispname); } } } /* General housekeeping */ conn->ssl[sockindex].state = ssl_connection_complete; conn->recv[sockindex] = axtls_recv; conn->send[sockindex] = axtls_send; /* Put our freshly minted SSL session in cache */ ssl_idsize = ssl_get_session_id_size(ssl); ssl_sessionid = ssl_get_session_id(ssl); if(Curl_ssl_addsessionid(conn, (void *) ssl_sessionid, ssl_idsize) != CURLE_OK) infof (data, "failed to add session to cache\n"); return CURLE_OK; }
/*** get/set session ***/ SSL_SESSION *SSL_get1_session(SSL *ssl) { return (SSL_SESSION *)ssl_get_session_id(ssl); /* note: wrong cast */ }
/** * Implement the SSL client logic. */ static void do_client(int argc, char *argv[]) { #ifdef CONFIG_SSL_ENABLE_CLIENT int res, i = 2; uint16_t port = 4433; uint32_t options = SSL_SERVER_VERIFY_LATER | SSL_DISPLAY_CERTS; int client_fd; char *private_key_file = NULL; struct sockaddr_in client_addr; struct hostent *hostent; int reconnect = 0; uint32_t sin_addr; SSL_CTX *ssl_ctx; SSL *ssl = NULL; int quiet = 0; int cert_index = 0, ca_cert_index = 0; int cert_size, ca_cert_size; char **ca_cert, **cert; uint8_t session_id[SSL_SESSION_ID_SIZE]; fd_set read_set; const char *password = NULL; FD_ZERO(&read_set); sin_addr = inet_addr("127.0.0.1"); cert_size = ssl_get_config(SSL_MAX_CERT_CFG_OFFSET); ca_cert_size = ssl_get_config(SSL_MAX_CA_CERT_CFG_OFFSET); ca_cert = (char **)calloc(1, sizeof(char *)*ca_cert_size); cert = (char **)calloc(1, sizeof(char *)*cert_size); while (i < argc) { if (strcmp(argv[i], "-connect") == 0) { char *host, *ptr; if (i >= argc - 1) { print_client_options(argv[i]); } host = argv[++i]; if ((ptr = strchr(host, ':')) == NULL) { print_client_options(argv[i]); } *ptr++ = 0; port = atoi(ptr); hostent = gethostbyname(host); if (hostent == NULL) { print_client_options(argv[i]); } sin_addr = *((uint32_t **)hostent->h_addr_list)[0]; } else if (strcmp(argv[i], "-cert") == 0) { if (i >= argc - 1 || cert_index >= cert_size) { print_client_options(argv[i]); } cert[cert_index++] = argv[++i]; } else if (strcmp(argv[i], "-key") == 0) { if (i >= argc - 1) { print_client_options(argv[i]); } private_key_file = argv[++i]; options |= SSL_NO_DEFAULT_KEY; } else if (strcmp(argv[i], "-CAfile") == 0) { if (i >= argc - 1 || ca_cert_index >= ca_cert_size) { print_client_options(argv[i]); } ca_cert[ca_cert_index++] = argv[++i]; } else if (strcmp(argv[i], "-verify") == 0) { options &= ~SSL_SERVER_VERIFY_LATER; } else if (strcmp(argv[i], "-reconnect") == 0) { reconnect = 4; } else if (strcmp(argv[i], "-quiet") == 0) { quiet = 1; options &= ~SSL_DISPLAY_CERTS; } else if (strcmp(argv[i], "-pass") == 0) { if (i >= argc - 1) { print_client_options(argv[i]); } password = argv[++i]; } #ifdef CONFIG_SSL_FULL_MODE else if (strcmp(argv[i], "-debug") == 0) { options |= SSL_DISPLAY_BYTES; } else if (strcmp(argv[i], "-state") == 0) { options |= SSL_DISPLAY_STATES; } else if (strcmp(argv[i], "-show-rsa") == 0) { options |= SSL_DISPLAY_RSA; } #endif else { /* don't know what this is */ print_client_options(argv[i]); } i++; } if ((ssl_ctx = ssl_ctx_new(options, SSL_DEFAULT_CLNT_SESS)) == NULL) { fprintf(stderr, "Error: Client context is invalid\n"); exit(1); } if (private_key_file) { int obj_type = SSL_OBJ_RSA_KEY; /* auto-detect the key type from the file extension */ if (strstr(private_key_file, ".p8")) { obj_type = SSL_OBJ_PKCS8; } else if (strstr(private_key_file, ".p12")) { obj_type = SSL_OBJ_PKCS12; } if (ssl_obj_load(ssl_ctx, obj_type, private_key_file, password)) { fprintf(stderr, "Error: Private key '%s' is undefined.\n", private_key_file); exit(1); } } for (i = 0; i < cert_index; i++) { if (ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, cert[i], NULL)) { printf("Certificate '%s' is undefined.\n", cert[i]); exit(1); } } for (i = 0; i < ca_cert_index; i++) { if (ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CACERT, ca_cert[i], NULL)) { printf("Certificate '%s' is undefined.\n", ca_cert[i]); exit(1); } } free(cert); free(ca_cert); /************************************************************************* * This is where the interesting stuff happens. Up until now we've * just been setting up sockets etc. Now we do the SSL handshake. *************************************************************************/ client_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); memset(&client_addr, 0, sizeof(client_addr)); client_addr.sin_family = AF_INET; client_addr.sin_port = htons(port); client_addr.sin_addr.s_addr = sin_addr; if (connect(client_fd, (struct sockaddr *)&client_addr, sizeof(client_addr)) < 0) { perror("connect"); exit(1); } if (!quiet) { printf("CONNECTED\n"); TTY_FLUSH(); } /* Try session resumption? */ if (reconnect) { while (reconnect--) { ssl = ssl_client_new(ssl_ctx, client_fd, session_id, sizeof(session_id)); if ((res = ssl_handshake_status(ssl)) != SSL_OK) { if (!quiet) { ssl_display_error(res); } ssl_free(ssl); exit(1); } display_session_id(ssl); memcpy(session_id, ssl_get_session_id(ssl), SSL_SESSION_ID_SIZE); if (reconnect) { ssl_free(ssl); SOCKET_CLOSE(client_fd); client_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); connect(client_fd, (struct sockaddr *)&client_addr, sizeof(client_addr)); } } } else { ssl = ssl_client_new(ssl_ctx, client_fd, NULL, 0); } /* check the return status */ if ((res = ssl_handshake_status(ssl)) != SSL_OK) { if (!quiet) { ssl_display_error(res); } exit(1); } if (!quiet) { const char *common_name = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME); if (common_name) { printf("Common Name:\t\t\t%s\n", common_name); } display_session_id(ssl); display_cipher(ssl); } for (;;) { uint8_t buf[1024]; /* allow parallel reading of server and standard input */ FD_SET(client_fd, &read_set); #ifndef WIN32 /* win32 doesn't like mixing up stdin and sockets */ FD_SET(STDIN_FILENO, &read_set); if ((res = select(client_fd + 1, &read_set, NULL, NULL, NULL)) > 0) { /* read standard input? */ if (FD_ISSET(STDIN_FILENO, &read_set)) #endif { if (fgets((char *)buf, sizeof(buf), stdin) == NULL) { /* bomb out of here */ ssl_free(ssl); break; } else { /* small hack to check renegotiation */ if (buf[0] == 'R' && (buf[1] == '\n' || buf[1] == '\r')) { res = ssl_renegotiate(ssl); } else { res = ssl_write(ssl, buf, strlen((char *)buf)); } } } #ifndef WIN32 else { /* a socket read */ uint8_t *read_buf; res = ssl_read(ssl, &read_buf); if (res > 0) { /* display our interesting output */ int written = 0; while (written < res) { written += write(STDOUT_FILENO, read_buf + written, res - written); } TTY_FLUSH(); } } } #endif if (res < 0) { if (!quiet) { ssl_display_error(res); } break; /* get outta here */ } } ssl_ctx_free(ssl_ctx); SOCKET_CLOSE(client_fd); #else print_client_options(argv[1]); #endif }