static void ssl_handshake(struct ssl_proxy *proxy) { int ret, dir; ret = gnutls_handshake(proxy->session); if (ret >= 0) { /* handshake done, now we can start reading */ if (proxy->io_ssl != NULL) io_remove(proxy->io_ssl); proxy->io_plain = io_add(proxy->fd_plain, IO_READ, plain_input, proxy); proxy->io_ssl = io_add(proxy->fd_ssl, IO_READ, ssl_input, proxy); return; } if (handle_ssl_error(proxy, ret) < 0) return; /* i/o interrupted */ dir = gnutls_record_get_direction(proxy->session) == 0 ? IO_READ : IO_WRITE; if (proxy->io_ssl_dir != dir) { if (proxy->io_ssl != NULL) io_remove(proxy->io_ssl); proxy->io_ssl = io_add(proxy->fd_ssl, dir, ssl_handshake, proxy); proxy->io_ssl_dir = dir; } }
static int proxy_recv_ssl(struct ssl_proxy *proxy, void *data, size_t size) { int rcvd; rcvd = gnutls_record_recv(proxy->session, data, size); if (rcvd > 0) return rcvd; if (rcvd == 0 || rcvd == GNUTLS_E_UNEXPECTED_PACKET_LENGTH) { /* disconnected, either by nicely telling us that we'll close the connection, or by simply killing the connection which gives us the packet length error. */ ssl_proxy_destroy(proxy); return -1; } return handle_ssl_error(proxy, rcvd); }
static int proxy_send_ssl(struct ssl_proxy *proxy, const void *data, size_t size) { int sent; sent = gnutls_record_send(proxy->session, data, size); if (sent >= 0) return sent; if (sent == GNUTLS_E_PUSH_ERROR || sent == GNUTLS_E_INVALID_SESSION) { /* don't warn about errors related to unexpected disconnection */ ssl_proxy_destroy(proxy); return -1; } return handle_ssl_error(proxy, sent); }
ssl_server_con * ssl_server_accept(ssl_server_client * scli, int fd){ BIO_set_fd(SSL_get_rbio(scli->ssl), fd, BIO_NOCLOSE); BIO_ctrl(SSL_get_rbio(scli->ssl), BIO_CTRL_DGRAM_SET_CONNECTED, 0, &scli->addr); int ret = 0; do{ret = SSL_accept(scli->ssl);} while(ret == 0); if(ret < 0){ handle_ssl_error(scli->ssl, ret); return NULL; } ASSERT(ret > 0); struct timeval timeout; timeout.tv_sec = 5; timeout.tv_usec = 0; BIO_ctrl(SSL_get_rbio(scli->ssl), BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout); ssl_server_con * con = alloc0(sizeof(ssl_server_con)); con->ssl = scli->ssl; return con; }
ssl_client * ssl_start_client(int fd, struct sockaddr * remote_addr){ ssl_ensure_initialized(); SSL_CTX * ctx = SSL_CTX_new(DTLSv1_client_method()); SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL:!MD5:!RC4"); if (!SSL_CTX_use_certificate_file(ctx, "certs/client-cert.pem", SSL_FILETYPE_PEM)) printf("\nERROR: no certificate found!"); if (!SSL_CTX_use_PrivateKey_file(ctx, "certs/client-key.pem", SSL_FILETYPE_PEM)) printf("\nERROR: no private key found!"); if (!SSL_CTX_check_private_key (ctx)) printf("\nERROR: invalid private key!"); SSL_CTX_set_verify_depth (ctx, 2); SSL_CTX_set_read_ahead(ctx, 1); SSL * ssl = SSL_new(ctx); // Create BIO, connect and set to already connected. BIO * bio = BIO_new_dgram(fd, BIO_CLOSE); BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, remote_addr); SSL_set_bio(ssl, bio, bio); int ret = SSL_connect(ssl); if(ret < 0){ handle_ssl_error(ssl, ret); } { struct timeval timeout; timeout.tv_sec = 3; timeout.tv_usec = 0; BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout); } ssl_client * cli = alloc0(sizeof(ssl_client)); cli->ssl = ssl; cli->ctx = ctx; return cli; }
/** * Check the transmitted client certs and a compare with client cert database */ static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { char subject[STRLEN]; X509_OBJECT found_cert; /* RATS: ignore */ /* buffer size is limited by STRLEN */ X509_NAME_oneline(X509_get_subject_name(ctx->current_cert), subject, STRLEN-1); if(!preverify_ok) { if (!check_preverify(ctx)) { goto reject; } } if(ctx->error_depth==0 && X509_STORE_get_by_subject(ctx, X509_LU_X509, X509_get_subject_name(ctx->current_cert), &found_cert)!=1) { handle_ssl_error("verify_callback()"); log("%s: verify_callback(): SSL connection rejected. No matching " "certificate found.", prog); goto reject; } return 1; reject: return 0; }
/** * Handle errors during read, write, connect and accept * @return TRUE if non fatal, FALSE if non fatal and retry */ static int handle_connection_error(int code, ssl_connection *ssl, char *operation, int timeout) { int ssl_error= 0; switch ((ssl_error= SSL_get_error(ssl->handler, code))) { case SSL_ERROR_WANT_READ: if (can_read(ssl->socket, timeout)) return TRUE; else log("%s: %s: Openssl read timeout error!\n", prog, operation); return FALSE; case SSL_ERROR_WANT_WRITE: if (can_read(ssl->socket, timeout)) return TRUE; else log("%s: %s: Openssl write timeout error!\n", prog, operation); return FALSE; case SSL_ERROR_SYSCALL: log("%s: %s: Openssl syscall error: %s!\n", prog, operation, STRERROR); return FALSE; case SSL_ERROR_SSL: handle_ssl_error(operation); return FALSE; default: log("%s: %s: Openssl error!\n", prog, operation); } return FALSE; }
/** * Initializes a ssl connection for server use. * @param pemfilename Filename for the key/cert file * @return An ssl connection, or NULL if an error occured. */ ssl_server_connection *init_ssl_server (char *pemfile, char *clientpemfile) { #ifdef HAVE_OPENSSL ssl_server_connection *ssl_server = new_ssl_server_connection(pemfile, clientpemfile); ASSERT(pemfile); if (!ssl_initilized) { start_ssl(); } if ((ssl_server->method= SSLv23_server_method()) == NULL ) { handle_ssl_error("init_ssl_server()"); log("%s: init_ssl_server (): Cannot initialize the SSL method!\n", prog); goto sslerror; } if ((ssl_server->ctx= SSL_CTX_new(ssl_server->method)) == NULL ) { handle_ssl_error("init_ssl_server()"); log("%s: init_ssl_server (): Cannot initialize SSL server" " certificate handler!\n" , prog); goto sslerror; } if (SSL_CTX_use_certificate_file(ssl_server->ctx, pemfile, SSL_FILETYPE_PEM) <= 0) { handle_ssl_error("init_ssl_server()"); log("%s: init_ssl_server(): Cannot initialize SSL server" " certificate!\n", prog); goto sslerror; } if (SSL_CTX_use_PrivateKey_file(ssl_server->ctx, pemfile, SSL_FILETYPE_PEM) <= 0) { handle_ssl_error("init_ssl_server()"); log("%s: init_ssl_server(): Cannot initialize SSL server" " private key!\n", prog); goto sslerror; } if (!SSL_CTX_check_private_key(ssl_server->ctx)) { handle_ssl_error("init_ssl_server()"); log("%s: init_ssl_server(): The private key does not match the" " certificate public key!\n", prog); goto sslerror; } /* * We need this to force transmission of client certs */ if (!verify_init(ssl_server)) { handle_ssl_error("init_ssl_server()"); log("%s: init_ssl_server(): Verification engine was not" " properly initilized!\n", prog); goto sslerror; } if (ssl_server->clientpemfile != NULL) { verify_info(ssl_server); } return ssl_server; sslerror: cleanup_ssl_server_socket(ssl_server); return NULL; #else return NULL; #endif }
/** * Embeds a socket in a ssl connection. * @param socket the socket to be used. * @return The ssl connection or NULL if an error occured. */ int embed_ssl_socket (ssl_connection *ssl, int socket) { #ifdef HAVE_OPENSSL int ssl_error; time_t ssl_time; if ( ssl == NULL ) { return FALSE; } if (!ssl_initilized) { start_ssl(); } if ( socket >= 0 ) { ssl->socket= socket; } else { log("%s: embed_ssl_socket (): Socket error!\n", prog); goto sslerror; } if ((ssl->handler= SSL_new (ssl->ctx)) == NULL ) { handle_ssl_error("embed_ssl_socket()"); log("%s: embed_ssl_socket (): Cannot initialize the SSL handler!\n", prog); goto sslerror; } set_noblock(ssl->socket); if((ssl->socket_bio= BIO_new_socket(ssl->socket, BIO_NOCLOSE)) == NULL) { handle_ssl_error("embed_ssl_socket()"); log("%s: embed_ssl_socket (): Cannot generate IO buffer!\n", prog); goto sslerror; } SSL_set_bio(ssl->handler, ssl->socket_bio, ssl->socket_bio); ssl_time=time(NULL); while((ssl_error= SSL_connect (ssl->handler)) < 0) { if((time(NULL)-ssl_time) > SSL_TIMEOUT) { log("%s: embed_ssl_socket (): SSL service timeout!\n", prog); goto sslerror; } if (!handle_connection_error(ssl_error, ssl, "embed_ssl_socket()", SSL_TIMEOUT)) { goto sslerror; } if (!BIO_should_retry(ssl->socket_bio)) { goto sslerror; } } ssl->cipher= (char *) SSL_get_cipher(ssl->handler); if (! update_ssl_cert_data(ssl)) { log("%s: embed_ssl_socket (): Cannot get the SSL server certificate!\n", prog); goto sslerror; } return TRUE; sslerror: cleanup_ssl_socket(ssl); return FALSE; #else return FALSE; #endif }
/** * Init verification of transmitted client certs */ static int verify_init(ssl_server_connection *ssl_server) { struct stat stat_buf; if (ssl_server->clientpemfile==NULL) { allow_any_purpose= TRUE; SSL_CTX_set_verify(ssl_server->ctx, SSL_VERIFY_PEER , verify_callback_noclientcert); goto end_success; /* No verification, but we have to call the callback! */ } if ( -1 == stat(ssl_server->clientpemfile, &stat_buf )) { log("%s: verify_init(): Cannot stat the SSL pem path '%s' -- %s\n", prog, Run.httpsslclientpem, STRERROR); goto end_error; } if (S_ISDIR(stat_buf.st_mode)) { if (!SSL_CTX_load_verify_locations(ssl_server->ctx, NULL , ssl_server->clientpemfile)) { handle_ssl_error("verify_init()"); log("%s: verify_init(): Error setting verify directory to %s\n", Run.httpsslclientpem); goto end_error; } log("%s: verify_init(): Loaded SSL client pem directory '%s'\n", prog, ssl_server->clientpemfile); /* Monits server cert for cli support ! */ if(!SSL_CTX_load_verify_locations(ssl_server->ctx, ssl_server->pemfile, NULL)) { handle_ssl_error("verify_init()"); log("%s: verify_init(): Error loading verify certificates from %s\n", prog, ssl_server->pemfile); goto end_error; } log("%s: verify_init(): Loaded monit's SSL pem server file '%s'\n", prog, ssl_server->pemfile); } else if (S_ISREG(stat_buf.st_mode)) { if(!SSL_CTX_load_verify_locations(ssl_server->ctx, ssl_server->clientpemfile, NULL)) { handle_ssl_error("verify_init()"); log("%s: verify_init(): Error loading verify certificates from %s\n", prog, Run.httpsslclientpem); goto end_error; } log("%s: verify_init(): Loaded SSL pem client file '%s'\n", prog, ssl_server->clientpemfile); /* Monits server cert for cli support ! */ if(!SSL_CTX_load_verify_locations(ssl_server->ctx, ssl_server->pemfile, NULL)) { handle_ssl_error("verify_init()"); log("%s: verify_init(): Error loading verify certificates from %s\n", prog, ssl_server->pemfile); goto end_error; } log("%s: verify_init(): Loaded monit's SSL pem server file '%s'\n", prog, ssl_server->pemfile); SSL_CTX_set_client_CA_list(ssl_server->ctx, SSL_load_client_CA_file(ssl_server->clientpemfile)); } else { log("%s: verify_init(): SSL client pem path is no file or directory %s\n", prog, ssl_server->clientpemfile); goto end_error; } allow_any_purpose= FALSE; SSL_CTX_set_verify(ssl_server->ctx, SSL_VERIFY_PEER , verify_callback); end_success: return TRUE; end_error: return FALSE; }
/** * Generate a new ssl connection * @return ssl connection container */ ssl_connection *new_ssl_connection(char *clientpemfile, int sslversion) { #ifdef HAVE_OPENSSL ssl_connection *ssl = (ssl_connection *) NEW(ssl); if (!ssl_initilized) { start_ssl(); } ssl->socket_bio= NULL; ssl->handler= NULL; ssl->cert= NULL; ssl->cipher= NULL; ssl->socket= 0; ssl->next = NULL; ssl->accepted = FALSE; ssl->cert_md5 = NULL; ssl->cert_md5_len = 0; if(clientpemfile!=NULL) { ssl->clientpemfile= xstrdup(clientpemfile); } else { ssl->clientpemfile= NULL; } switch (sslversion) { case SSL_VERSION_AUTO: ssl->method = SSLv23_client_method(); break; case SSL_VERSION_SSLV2: ssl->method = SSLv2_client_method(); break; case SSL_VERSION_SSLV3: ssl->method = SSLv3_client_method(); break; case SSL_VERSION_TLS: ssl->method = TLSv1_client_method(); break; default: log("%s: new_ssl_connection(): Unknown SSL version!\n", prog); goto sslerror; } if (ssl->method == NULL ) { handle_ssl_error("new_ssl_connection()"); log("%s: new_ssl_connection(): Cannot initilize SSL method!\n", prog); goto sslerror; } if ((ssl->ctx= SSL_CTX_new (ssl->method)) == NULL ) { handle_ssl_error("new_ssl_connection()"); log("%s: new_ssl_connection(): Cannot initilize SSL server certificate" " handler!\n", prog); goto sslerror; } if ( ssl->clientpemfile!=NULL ) { if (SSL_CTX_use_certificate_file(ssl->ctx, ssl->clientpemfile, SSL_FILETYPE_PEM) <= 0) { handle_ssl_error("new_ssl_connection()"); log("%s: new_ssl_connection(): Cannot initilize SSL server" " certificate!\n", prog); goto sslerror; } if (SSL_CTX_use_PrivateKey_file(ssl->ctx, ssl->clientpemfile, SSL_FILETYPE_PEM) <= 0) { handle_ssl_error("new_ssl_connection()"); log("%s: new_ssl_connection(): Cannot initilize SSL server" " private key!\n", prog); goto sslerror; } if (!SSL_CTX_check_private_key(ssl->ctx)) { handle_ssl_error("new_ssl_connection()"); log("%s: new_ssl_connection(): Private key does not match the" " certificate public key!\n", prog); goto sslerror; } } return ssl; sslerror: delete_ssl_socket(ssl); return NULL; #else return NULL; #endif }
/** * Embeds an accepted server socket in an existing ssl connection. * @param ssl ssl connection * @param socket the socket to be used. * @return TRUE, or FALSE if an error has occured. */ int embed_accepted_ssl_socket(ssl_connection *ssl, int socket) { #ifdef HAVE_OPENSSL int ssl_error; time_t ssl_time; ASSERT(ssl); ssl->socket=socket; if(!ssl_initilized) { start_ssl(); } if((ssl->handler= SSL_new(ssl->ctx)) == NULL) { handle_ssl_error("embed_accepted_ssl_socket()"); log("%s: embed_accepted_ssl_socket(): Cannot initialize the" " SSL handler!\n", prog); goto sslerror; } if(socket < 0) { log("Socket error!\n"); goto sslerror; } set_noblock(ssl->socket); if((ssl->socket_bio= BIO_new_socket(ssl->socket, BIO_NOCLOSE)) == NULL) { handle_ssl_error("embed_accepted_ssl_socket()"); log("%s: embed_accepted_ssl_socket(): Cannot generate IO buffer!\n", prog); goto sslerror; } SSL_set_bio(ssl->handler, ssl->socket_bio, ssl->socket_bio); ssl_time= time(NULL); while((ssl_error= SSL_accept(ssl->handler)) < 0) { if((time(NULL)-ssl_time) > SSL_TIMEOUT) { log("%s: embed_accepted_ssl_socket(): SSL service timeout!\n", prog); goto sslerror; } if (!handle_connection_error(ssl_error, ssl, "embed_accepted_ssl_socket()", SSL_TIMEOUT)) { goto sslerror; } if (!BIO_should_retry(ssl->socket_bio)) { goto sslerror; } } ssl->cipher= (char *) SSL_get_cipher(ssl->handler); if(!update_ssl_cert_data(ssl) && (ssl->clientpemfile != NULL)) { log("%s: The client did not supply a required client certificate!\n", prog); goto sslerror; } if (SSL_get_verify_result(ssl->handler)>0) { log("%s: Verification of the certificate has failed!\n", prog); goto sslerror; } return TRUE; sslerror: return FALSE; #else return FALSE; #endif }