/** Given an SSL object and its last known state, attempt to write to it. * \param ssl pointer to SSL object. * \param state saved state of SSL object. * \param net_read_ready 1 if the underlying socket is ready for read. * \param net_write_ready 1 if the underlying socket is ready for write. * \param buf buffer to write. * \param bufsize length of buffer to write. * \param offset pointer to offset into buffer marking where to write next. * \return new state of SSL object, or -1 if the connection closed. */ int ssl_write(SSL *ssl, int state, int net_read_ready, int net_write_ready, const char *buf, int bufsize, int *offset) { int r; if ((net_write_ready && bufsize) || (net_read_ready && !(state & MYSSL_WBOR))) { state &= ~(MYSSL_WBOR | MYSSL_WB); r = SSL_write(ssl, buf + *offset, bufsize); switch (SSL_get_error(ssl, r)) { case SSL_ERROR_NONE: /* We wrote something, but maybe not all */ *offset += r; break; case SSL_ERROR_WANT_WRITE: /* Underlying socket isn't ready to be written to. */ ssl_debugdump("SSL_write wants write"); state |= MYSSL_WB; break; case SSL_ERROR_WANT_READ: /* More needs to be read from the underlying socket first. * This can happen during a rehandshake. */ ssl_debugdump("SSL_write wants read"); state |= MYSSL_WBOR; break; default: /* Should never happen */ ssl_errordump("Unknown ssl_write failure!"); } } return state; }
/** Perform an SSL handshake. * In some cases, a handshake may block, so we may have to call this * function again. Accordingly, we return state information that * tells us if we need to do that. * \param ssl pointer to an SSL object. * \return resulting state of the object. */ int ssl_handshake(SSL *ssl) { int ret; int state = 0; if ((ret = SSL_do_handshake(ssl)) <= 0) { switch (SSL_get_error(ssl, ret)) { case SSL_ERROR_WANT_READ: /* We must want for the socket to be readable, and then repeat * the call. */ ssl_debugdump("SSL_do_handshake wants read"); state = MYSSL_RB | MYSSL_HANDSHAKE; break; case SSL_ERROR_WANT_WRITE: /* We must want for the socket to be writable, and then repeat * the call. */ ssl_debugdump("SSL_do_handshake wants write"); state = MYSSL_WB | MYSSL_HANDSHAKE; break; default: /* Oops, don't know what's wrong */ ssl_errordump("Error in ssl_handshake"); state = -1; } } else { state = ssl_accept(ssl); } return state; }
/** Call SSL_accept and return the connection state. * \param ssl pointer to an SSL object. * \return ssl state flags indicating success, pending, or failure. */ int ssl_accept(SSL *ssl) { int ret; int state = 0; X509 *peer; char buf[256]; if ((ret = SSL_accept(ssl)) <= 0) { switch (SSL_get_error(ssl, ret)) { case SSL_ERROR_WANT_READ: /* We must want for the socket to be readable, and then repeat * the call. */ ssl_debugdump("SSL_accept wants read"); state = MYSSL_RB | MYSSL_ACCEPT; break; case SSL_ERROR_WANT_WRITE: /* We must want for the socket to be writable, and then repeat * the call. */ ssl_debugdump("SSL_accept wants write"); state = MYSSL_WB | MYSSL_ACCEPT; break; default: /* Oops, don't know what's wrong */ ssl_errordump("Error accepting connection"); return -1; } } else { /* Successful accept - report it */ if ((peer = SSL_get_peer_certificate(ssl))) { if (SSL_get_verify_result(ssl) == X509_V_OK) { /* The client sent a certificate which verified OK */ X509_NAME_oneline(X509_get_subject_name(peer), buf, 256); lock_file(stderr); fprintf(stderr, "SSL client certificate accepted: %s", buf); unlock_file(stderr); state |= MYSSL_VERIFIED; } } } return state; }
/** Given an SSL object and its last known state, attempt to read from it. * \param ssl pointer to SSL object. * \param state saved state of SSL object. * \param net_read_ready 1 if the underlying socket is ready for read. * \param net_write_ready 1 if the underlying socket is ready for write. * \param buf buffer to read into. * \param bufsize number of bytes to read. * \param bytes_read pointer to return the number of bytes read. * \return new state of SSL object, or -1 if the connection closed. */ int ssl_read(SSL *ssl, int state, int net_read_ready, int net_write_ready, char *buf, int bufsize, int *bytes_read) { if ((net_read_ready && !(state & MYSSL_WBOR)) || (net_write_ready && (state & MYSSL_RBOW))) { do { state &= ~(MYSSL_RB | MYSSL_RBOW); *bytes_read = SSL_read(ssl, buf, bufsize); switch (SSL_get_error(ssl, *bytes_read)) { case SSL_ERROR_NONE: /* Yay */ return state; case SSL_ERROR_ZERO_RETURN: /* End of data on this socket */ return -1; case SSL_ERROR_WANT_READ: /* More needs to be read from the underlying socket */ ssl_debugdump("SSL_read wants read"); state |= MYSSL_RB; break; case SSL_ERROR_WANT_WRITE: /* More needs to be written to the underlying socket. * This can happen during a rehandshake. */ ssl_debugdump("SSL_read wants write"); state |= MYSSL_RBOW; break; default: /* Should never happen */ ssl_errordump("Unknown ssl_read failure!"); return -1; } } while (SSL_pending(ssl) && !(state & MYSSL_RB)); } return state; }
/** Initialize the SSL context. * \return pointer to SSL context object. */ SSL_CTX * ssl_init(char *private_key_file, char *ca_file, int req_client_cert) { const SSL_METHOD *meth; /* If this const gives you a warning, you're using an old version of OpenSSL. Walker, this means you! */ /* uint8_t context[128]; */ DH *dh; unsigned int reps = 1; if (!bio_err) { if (!SSL_library_init()) return NULL; SSL_load_error_strings(); /* Error write context */ bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); } lock_file(stderr); fputs("Seeding OpenSSL random number pool.\n", stderr); unlock_file(stderr); while (!RAND_status()) { /* At this point, a system with /dev/urandom or a EGD file in the usual places will have enough entropy. Otherwise, be lazy and use random numbers until it's satisfied. */ uint32_t gibberish[4]; int n; /* sfmt_fill_array32 requires a much larger array. */ for (n = 0; n < 4; n++) gibberish[n] = sfmt_genrand_uint32(&rand_state); RAND_seed(gibberish, sizeof gibberish); reps += 1; } lock_file(stderr); fprintf(stderr, "Seeded after %u %s.\n", reps, reps > 1 ? "cycles" : "cycle"); unlock_file(stderr); /* Set up SIGPIPE handler here? */ /* Create context */ meth = SSLv23_server_method(); ctx = SSL_CTX_new(meth); /* Load keys/certs */ if (private_key_file && *private_key_file) { if (!SSL_CTX_use_certificate_chain_file(ctx, private_key_file)) { ssl_errordump ("Unable to load server certificate - only anonymous ciphers supported."); } if (!SSL_CTX_use_PrivateKey_file(ctx, private_key_file, SSL_FILETYPE_PEM)) { ssl_errordump ("Unable to load private key - only anonymous ciphers supported."); } } /* Load trusted CAs */ if (ca_file && *ca_file) { if (!SSL_CTX_load_verify_locations(ctx, ca_file, NULL)) { ssl_errordump("Unable to load CA certificates"); } else { if (req_client_cert) SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, client_verify_callback); else SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, client_verify_callback); #if (OPENSSL_VERSION_NUMBER < 0x0090600fL) SSL_CTX_set_verify_depth(ctx, 1); #endif } } SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE | SSL_OP_ALL); SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); /* Set up DH callback */ dh = get_dh1024(); SSL_CTX_set_tmp_dh(ctx, dh); /* The above function makes a private copy of this */ DH_free(dh); /* Set the cipher list to the usual default list, except that * we'll allow anonymous diffie-hellman, too. */ SSL_CTX_set_cipher_list(ctx, "ALL:ADH:RC4+RSA:+SSLv2:@STRENGTH"); /* Set up session cache if we can */ /* strncpy((char *) context, MUDNAME, 128); SSL_CTX_set_session_id_context(ctx, context, strlen(context)); */ return ctx; }
/** Initialize the SSL context. * \return pointer to SSL context object. */ SSL_CTX * ssl_init(char *private_key_file, char *ca_file, char *ca_dir, int req_client_cert) { const SSL_METHOD *meth; /* If this const gives you a warning, you're using an old version of OpenSSL. Walker, this means you! */ /* uint8_t context[128]; */ unsigned int reps = 1; pcg32_random_t rand_state; uint64_t seeds[2]; bool seeded = false; if (!bio_err) { if (!SSL_library_init()) return NULL; SSL_load_error_strings(); /* Error write context */ bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); } lock_file(stderr); fputs("Seeding OpenSSL random number pool.\n", stderr); unlock_file(stderr); while (!RAND_status()) { /* At this point, a system with /dev/urandom or a EGD file in the usual places will have enough entropy. Otherwise, be lazy and use random numbers until it's satisfied. */ uint32_t gibberish[8]; int n; if (!seeded) { generate_seed(seeds); pcg32_srandom_r(&rand_state, seeds[0], seeds[1]); seeded = 1; } for (n = 0; n < 8; n++) gibberish[n] = pcg32_random_r(&rand_state); RAND_seed(gibberish, sizeof gibberish); reps += 1; } lock_file(stderr); fprintf(stderr, "Seeded after %u %s.\n", reps, reps > 1 ? "cycles" : "cycle"); unlock_file(stderr); /* Set up SIGPIPE handler here? */ /* Create context */ #if OPENSSL_VERSION_NUMBER >= 0x10100000L meth = TLS_server_method(); #else meth = SSLv23_server_method(); #endif ctx = SSL_CTX_new(meth); SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); /* Load keys/certs */ if (private_key_file && *private_key_file) { if (!SSL_CTX_use_certificate_chain_file(ctx, private_key_file)) { ssl_errordump("Unable to load server certificate - only anonymous " "ciphers supported."); } if (!SSL_CTX_use_PrivateKey_file(ctx, private_key_file, SSL_FILETYPE_PEM)) { ssl_errordump( "Unable to load private key - only anonymous ciphers supported."); } } /* Load trusted CAs */ if ((ca_file && *ca_file) || (ca_dir && *ca_dir)) { if (!SSL_CTX_load_verify_locations(ctx, (ca_file && *ca_file) ? ca_file : NULL, (ca_dir && *ca_dir) ? ca_dir : NULL)) { ssl_errordump("Unable to load CA certificates"); } { STACK_OF(X509_NAME) *certs = NULL; if (ca_file && *ca_file) certs = SSL_load_client_CA_file(ca_file); if (certs) SSL_CTX_set_client_CA_list(ctx, certs); if (req_client_cert) SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, client_verify_callback); else SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, client_verify_callback); #if (OPENSSL_VERSION_NUMBER < 0x0090600fL) SSL_CTX_set_verify_depth(ctx, 1); #endif } } SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE | SSL_OP_ALL); SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); /* Set up DH key */ { DH *dh; dh = get_dh2048(); SSL_CTX_set_tmp_dh(ctx, dh); DH_free(dh); } #ifdef NID_X9_62_prime256v1 /* Set up ECDH key */ { EC_KEY *ecdh = NULL; ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); SSL_CTX_set_tmp_ecdh(ctx, ecdh); EC_KEY_free(ecdh); } #endif /* Set the cipher list to the usual default list, except that * we'll allow anonymous diffie-hellman, too. */ SSL_CTX_set_cipher_list(ctx, "ALL:ECDH:ADH:!LOW:!MEDIUM:@STRENGTH"); /* Set up session cache if we can */ /* strncpy((char *) context, MUDNAME, 128); SSL_CTX_set_session_id_context(ctx, context, strlen(context)); */ return ctx; }