/* * The copyright holder gives permission to link this code with the OpenSSL * library and distribute linked combinations including the two. You must obey * the GNU General Public License in all respects for all of the code used other * than OpenSSL. If you modify this file, you may extend this exception to your * version of the file, but you are not obligated to do so. If you do not wish to * do so, delete this exception statement from your version. * */ #include "config.h" #ifdef HAVE_SSL #include #include #include #include #include #include #include "alternative.h" #include "libssl.h" static pthread_mutex_t *locks; static int lockcount; #ifdef DEBUG int print_ssl_error(SSL *ssl, int code) { int result; switch (result = SSL_get_error(ssl, code)) { case SSL_ERROR_ZERO_RETURN: fprintf(stderr, "connection closed\n"); break; case SSL_ERROR_WANT_READ: fprintf(stderr, "read incomplete\n"); break; case SSL_ERROR_WANT_WRITE: fprintf(stderr, "write incomplete\n"); break; case SSL_ERROR_WANT_CONNECT: fprintf(stderr, "connect incomplete\n"); break; case SSL_ERROR_WANT_ACCEPT: fprintf(stderr, "accept incomplete\n"); break; case SSL_ERROR_WANT_X509_LOOKUP: fprintf(stderr, "X509 lookup incomplete\n"); break; case SSL_ERROR_SYSCALL: fprintf(stderr, "I/O error\n"); break; case SSL_ERROR_SSL: fprintf(stderr, "protocol error\n"); break; default: fprintf(stderr, "unknown error\n"); break; } return result; } #endif /* SSL multithread locking callback */ static void locking_callback(int mode, int n, const char *file, int line) { if ((n >= 0) && (n < lockcount)) { if (mode & CRYPTO_LOCK) { pthread_mutex_lock(&locks[n]); } else { pthread_mutex_unlock(&locks[n]); } } else { syslog(LOG_DAEMON | LOG_ALERT, "libssl::locking_callback() error!"); exit(EXIT_FAILURE); } } /* SSL thread ID callback */ static unsigned long id_callback() { return (unsigned long)pthread_self(); } static int password_callback(char *buffer, int size, int rwflag, void *data) { int len; if ((len = (int)strlen((char*)data) + 1) > size) { return 0; } memcpy(buffer, (char*)data, len); return len; } void ssl_init(char *buffer, int size) { int i; SSL_library_init(); SSL_load_error_strings(); if (buffer != NULL) { RAND_seed(buffer, size); } lockcount = CRYPTO_num_locks(); if (lockcount > 0) { if ((locks = malloc(lockcount * sizeof(pthread_mutex_t))) != NULL) { for (i = 0; i < lockcount; i++) { pthread_mutex_init(&locks[i], NULL); } } } CRYPTO_set_locking_callback(locking_callback); CRYPTO_set_id_callback(id_callback); } static int load_dh_params(SSL_CTX *context, char *file) { DH *dh = NULL; BIO *bio; if ((bio = BIO_new_file(file,"r")) == NULL) { fprintf(stderr, "Couldn't open DH file"); return -1; } dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); BIO_free(bio); if (SSL_CTX_set_tmp_dh(context, dh) != 1) { fprintf(stderr, "Couldn't set DH parameters"); return -1; } return 0; } SSL_CTX *ssl_binding(char *keyfile, char *CA_cert, int verify_depth, char *dh_file, char *ciphers) { SSL_METHOD *meth; SSL_CTX *context; STACK_OF(X509_NAME) *ca_list; if ((meth = SSLv23_method()) == NULL) { fprintf(stderr, "SSLv23_method() error\n"); return NULL; } if ((context = SSL_CTX_new(meth)) == NULL) { fprintf(stderr, "SSL_CTX_new() error\n"); return NULL; } SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); if (SSL_CTX_use_certificate_chain_file(context, keyfile) != 1) { fprintf(stderr, "Error while reading certificate from %s\n", keyfile); return NULL; } SSL_CTX_set_default_passwd_cb(context, password_callback); /* SSL_CTX_set_default_passwd_cb_userdata(context, (void*)password); */ if (SSL_CTX_use_PrivateKey_file(context, keyfile, SSL_FILETYPE_PEM) != 1) { fprintf(stderr, "Error while reading private key from %s\n", keyfile); return NULL; } if (SSL_CTX_check_private_key(context) != 1) { fprintf(stderr, "Private key does not match the certificate\n"); return NULL; } if (CA_cert != NULL) { SSL_CTX_set_verify_depth(context, verify_depth); SSL_CTX_set_verify(context, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0); if (SSL_CTX_load_verify_locations(context, CA_cert, NULL) != 1) { fprintf(stderr, "Error while setting CA verify locations\n"); return NULL; } if ((ca_list = SSL_load_client_CA_file(CA_cert)) == NULL) { fprintf(stderr, "Error while loading CA certificate file\n"); return NULL; } SSL_CTX_set_client_CA_list(context, ca_list); } if (dh_file != NULL) { if (load_dh_params(context, dh_file) == -1) { fprintf(stderr, "Error while loading DH file\n"); return NULL; } } if (ciphers != NULL) { if (SSL_CTX_set_cipher_list(context, ciphers) == 0) { fprintf(stderr, "Error while setting cipher list\n"); return NULL; } } return context; } bool ssl_accept(int sock, SSL **ssl, SSL_CTX *context) { BIO * bio; int result; bio = BIO_new_socket(sock, 0); *ssl = SSL_new(context); SSL_set_bio(*ssl, bio, bio); if ((result = SSL_accept(*ssl)) != 1) { #ifdef DEBUG fprintf(stderr, "SSL_accept(): "); print_ssl_error(*ssl, result); #endif SSL_free(*ssl); return false; } return true; } int ssl_receive(SSL *ssl, char *buffer, unsigned int maxlength) { int result; result = SSL_read(ssl, buffer, maxlength); #ifdef DEBUG if (result < 0) { fprintf(stderr, "SSL_read(): "); print_ssl_error(ssl, result); } #endif return result; } int ssl_send(SSL *ssl, char *buffer, unsigned int length) { int result; result = SSL_write(ssl, buffer, length); #ifdef DEBUG if (result < 0) { fprintf(stderr, "SSL_write(): "); print_ssl_error(ssl, result); } #endif return result; } int ssl_close(SSL *ssl) { return SSL_shutdown(ssl); } void ssl_cleanup(SSL_CTX *sslContext) { int i; SSL_CTX_free(sslContext); for (i = 0; i < lockcount; i++) { pthread_mutex_destroy(&locks[i]); } } int get_client_certificate(SSL *ssl_data, int size, char *subject, char *issuer) { X509 *cert; subject[size - 1] = '\0'; issuer[size - 1] = '\0'; if ((cert = SSL_get_peer_certificate(ssl_data)) == NULL) { return -1; } X509_NAME_oneline(X509_get_subject_name(cert), subject, size - 1); X509_NAME_oneline(X509_get_issuer_name(cert), issuer, size - 1); return 0; } #endif