/* * Amanda, The Advanced Maryland Automatic Network Disk Archiver * Copyright (c) 1999 University of Maryland * All Rights Reserved. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of U.M. not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. U.M. makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M. * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Authors: the Amanda Development Team. Its members are listed in a * file named AUTHORS, in the root directory of this distribution. */ /* * $Id$ * * ssl-security.c - security and transport over ssl or a ssl-like command. * * XXX still need to check for initial keyword on connect so we can skip * over shell garbage and other stuff that ssl might want to spew out. */ #include "amanda.h" #include "amutil.h" #include "event.h" #include "packet.h" #include "security.h" #include "security-util.h" #include "sockaddr-util.h" #include "stream.h" #include "version.h" #include #include #include /* * Number of seconds ssl has to start up */ #define CONNECT_TIMEOUT 20 /* * Interface functions */ static void ssl_accept(const struct security_driver *, char *(*)(char *, void *), int, int, void (*)(security_handle_t *, pkt_t *), void *); static void ssl_connect(const char *, char *(*)(char *, void *), void (*)(void *, security_handle_t *, security_status_t), void *, void *); static ssize_t ssl_data_write(void *c, struct iovec *iov, int iovcnt); static ssize_t ssl_data_write_non_blocking(void *c, struct iovec *iov, int iovcnt); static ssize_t ssl_data_read(void *c, void *bug, size_t size, int timeout); static void init_ssl(void); /* * This is our interface to the outside world. */ const security_driver_t ssl_security_driver = { "SSL", ssl_connect, ssl_accept, sec_get_authenticated_peer_name_hostname, sec_close, stream_sendpkt, stream_recvpkt, stream_recvpkt_cancel, tcpma_stream_server, tcpma_stream_accept, tcpma_stream_client, tcpma_stream_close, tcpma_stream_close_async, sec_stream_auth, sec_stream_id, tcpm_stream_write, tcpm_stream_write_async, tcpm_stream_read, tcpm_stream_read_sync, tcpm_stream_read_to_shm_ring, tcpm_stream_read_cancel, tcpm_stream_pause, tcpm_stream_resume, tcpm_close_connection, NULL, NULL, ssl_data_write, ssl_data_write_non_blocking, ssl_data_read }; static int newhandle = 1; /* * Local functions */ static int runssl(struct sec_handle *, in_port_t port, char *src_ip, char *ssl_fingerprint_file, char *ssl_cert_file, char *ssl_key_file, char *ssl_ca_cert_file, char *ssl_cipher_list, int ssl_check_certificate_host); /* * ssl version of a security handle allocator. Logically sets * up a network "connection". */ static void ssl_connect( const char *hostname, char * (*conf_fn)(char *, void *), void (*fn)(void *, security_handle_t *, security_status_t), void * arg, void * datap) { struct sec_handle *rh; int result; char *canonname; char *service; in_port_t port; char *src_ip = NULL; char *ssl_dir = NULL; char *ssl_fingerprint_file = NULL; char *ssl_cert_file = NULL; char *ssl_key_file = NULL; char *ssl_ca_cert_file = NULL; char *ssl_cipher_list = NULL; int ssl_check_certificate_host = 1; assert(fn != NULL); assert(hostname != NULL); auth_debug(1, _("ssl: ssl_connect: %s\n"), hostname); rh = g_new0(struct sec_handle, 1); security_handleinit(&rh->sech, &ssl_security_driver); rh->dle_hostname = g_strdup(hostname); rh->hostname = NULL; rh->rs = NULL; rh->ev_timeout = NULL; rh->rc = NULL; result = resolve_hostname(hostname, 0, NULL, &canonname); if(result != 0) { g_debug(_("resolve_hostname(%s): %s"), hostname, gai_strerror(result)); security_seterror(&rh->sech, _("resolve_hostname(%s): %s\n"), hostname, gai_strerror(result)); (*fn)(arg, &rh->sech, S_ERROR); return; } if (canonname == NULL) { g_debug(_("resolve_hostname(%s) did not return a canonical name"), hostname); security_seterror(&rh->sech, _("resolve_hostname(%s) did not return a canonical name\n"), hostname); (*fn)(arg, &rh->sech, S_ERROR); return; } rh->hostname = canonname; /* will be replaced */ canonname = NULL; /* steal reference */ rh->rs = tcpma_stream_client(rh, newhandle++); rh->rc->recv_security_ok = &bsd_recv_security_ok; rh->rc->prefix_packet = &bsd_prefix_packet; rh->rc->need_priv_port = 0; if (rh->rs == NULL) goto error; amfree(rh->hostname); rh->hostname = g_strdup(rh->rs->rc->hostname); ssl_dir = getconf_str(CNF_SSL_DIR); if (conf_fn) { service = conf_fn("client_port", datap); if (!service || strlen(service) <= 1) service = AMANDA_SERVICE_NAME; g_debug("Connecting to service '%s'", service); src_ip = conf_fn("src_ip", datap); ssl_fingerprint_file = g_strdup(conf_fn("ssl_fingerprint_file", datap)); ssl_cert_file = g_strdup(conf_fn("ssl_cert_file", datap)); ssl_key_file = g_strdup(conf_fn("ssl_key_file", datap)); ssl_ca_cert_file = g_strdup(conf_fn("ssl_ca_cert_file", datap)); ssl_cipher_list = conf_fn("ssl_cipher_list", datap); ssl_check_certificate_host = atoi(conf_fn("ssl_check_certificate_host", datap)); } else { service = AMANDA_SERVICE_NAME; } if (ssl_dir) { if (!ssl_cert_file || ssl_cert_file == '\0') { ssl_cert_file = g_strdup_printf("%s/me/crt.pem", ssl_dir); } if (!ssl_key_file || ssl_key_file == '\0') { ssl_key_file = g_strdup_printf("%s/me/private/key.pem", ssl_dir); } if (!ssl_ca_cert_file || ssl_ca_cert_file == '\0') { ssl_ca_cert_file = g_strdup_printf("%s/CA/crt.pem", ssl_dir); } if (!ssl_fingerprint_file || ssl_fingerprint_file == '\0') { struct stat statbuf; ssl_fingerprint_file = g_strdup_printf("%s/remote/%s/fingerprint", ssl_dir, rh->hostname); if (stat(ssl_fingerprint_file, &statbuf) == -1) { g_free(ssl_fingerprint_file); ssl_fingerprint_file = NULL; } } } port = find_port_for_service(service, "tcp"); if (port == 0) { security_seterror(&rh->sech, _("%s/tcp unknown protocol"), service); goto error; } /* * We need to open a new connection. */ if(rh->rc->read == -1) { if (runssl(rh, port, src_ip, ssl_fingerprint_file, ssl_cert_file, ssl_key_file, ssl_ca_cert_file, ssl_cipher_list, ssl_check_certificate_host) < 0) goto error; rh->rc->refcnt++; } g_free(ssl_fingerprint_file); g_free(ssl_cert_file); g_free(ssl_key_file); g_free(ssl_ca_cert_file); /* * The socket will be opened async so hosts that are down won't * block everything. We need to register a write event * so we will know when the socket comes alive. * * Overload rh->rs->ev_read to provide a write event handle. * We also register a timeout. */ g_mutex_lock(security_mutex); rh->fn.connect = fn; rh->arg = arg; rh->rs->rc->ev_write = event_create((event_id_t)(rh->rs->rc->write), EV_WRITEFD, sec_connect_callback, rh); rh->ev_timeout = event_create(CONNECT_TIMEOUT, EV_TIME, sec_connect_timeout, rh); event_activate(rh->rs->rc->ev_write); event_activate(rh->ev_timeout); g_mutex_unlock(security_mutex); return; error: (*fn)(arg, &rh->sech, S_ERROR); } static char *validate_fingerprints(X509 *cert, char *ssl_fingerprint_file); static char * validate_fingerprints( X509 *cert, char *ssl_fingerprint_file) { FILE *fingers; char fingerprint[32768]; char *errmsg = NULL; unsigned char md5[EVP_MAX_MD_SIZE + 1]; unsigned int len_md5; const EVP_MD *evp_md5; char *md5_fingerprint; unsigned char sha1[EVP_MAX_MD_SIZE + 1]; unsigned int len_sha1; const EVP_MD *evp_sha1; char *sha1_fingerprint; char *fp; unsigned int i; const char *md5_const = "MD5 Fingerprint="; const char *sha1_const = "SHA1 Fingerprint="; const size_t md5_const_len = strlen(md5_const); const size_t sha1_const_len = strlen(sha1_const); if (ssl_fingerprint_file == NULL) { g_debug("No fingerprint_file set"); return NULL; } evp_md5 = EVP_get_digestbyname("MD5"); if (!evp_md5) { auth_debug(1, _("EVP_get_digestbyname(MD5) failed")); } if (!X509_digest(cert, evp_md5, md5, &len_md5)) { auth_debug(1, _("cannot get MD5 digest")); } md5_fingerprint = malloc(len_md5*3 + 1); fp = md5_fingerprint; for (i=0; i < len_md5; i++) { snprintf(fp, 4, "%02X:", (unsigned) md5[i]); fp+=3; } /* remove latest ':' */ fp --; *fp = '\0'; auth_debug(1, _("md5: %s\n"), md5_fingerprint); evp_sha1 = EVP_get_digestbyname("SHA1"); if (!evp_sha1) { auth_debug(1, _("EVP_get_digestbyname(SHA1) failed")); } if (!X509_digest(cert, evp_sha1, sha1, &len_sha1)) { auth_debug(1, _("cannot get SHA1 digest")); } sha1_fingerprint = malloc(len_sha1*3 + 1); fp = sha1_fingerprint; for (i=0; i < len_sha1; i++) { snprintf(fp, 4, "%02X:", (unsigned) sha1[i]); fp+=3; } /* remove latest ':' */ fp --; *fp = '\0'; auth_debug(1, _("sha1: %s\n"), sha1_fingerprint); fingers = fopen(ssl_fingerprint_file, "r"); if (!fingers) { errmsg = g_strdup_printf("Failed open of %s: %s", ssl_fingerprint_file, strerror(errno)); g_debug("%s", errmsg); g_free(md5_fingerprint); g_free(sha1_fingerprint); return errmsg; } while (fgets(fingerprint, 32768, fingers) != NULL) { int len = strlen(fingerprint)-1; if (len > 0 && fingerprint[len] == '\n') fingerprint[len] = '\0'; if (strncmp(md5_const, fingerprint, md5_const_len) == 0) { if (strcmp(md5_fingerprint, fingerprint+md5_const_len) == 0) { g_debug("MD5 fingerprint '%s' match", md5_fingerprint); g_free(md5_fingerprint); g_free(sha1_fingerprint); fclose(fingers); return NULL; } } else if (strncmp(sha1_const, fingerprint, sha1_const_len) == 0) { if (strcmp(sha1_fingerprint, fingerprint+sha1_const_len) == 0) { g_debug("SHA1 fingerprint '%s' match", sha1_fingerprint); g_free(md5_fingerprint); g_free(sha1_fingerprint); fclose(fingers); return NULL; } } auth_debug(1, _("Fingerprint '%s' doesn't match\n"), fingerprint); } g_free(md5_fingerprint); g_free(sha1_fingerprint); fclose(fingers); return g_strdup_printf("No fingerprint match");; } /* * Setup to handle new incoming connections */ static void ssl_accept( const struct security_driver *driver, char * (*conf_fn)(char *, void *), int in, int out, void (*fn)(security_handle_t *, pkt_t *), void *datap) { sockaddr_union sin; socklen_t_equiv len = sizeof(struct sockaddr); struct tcp_conn *rc; char hostname[NI_MAXHOST]; int result; char *errmsg = NULL; int err; X509 *remote_cert; char *str; X509_NAME *x509_name; char *cert_hostname; SSL_CTX *ctx; SSL *ssl; int loc; char *ssl_dir = getconf_str(CNF_SSL_DIR); char *ssl_fingerprint_file = conf_fn("ssl_fingerprint_file", datap); char *ssl_cert_file = conf_fn("ssl_cert_file", datap); char *ssl_key_file = conf_fn("ssl_key_file", datap); char *ssl_ca_cert_file = conf_fn("ssl_ca_cert_file", datap); char *ssl_cipher_list = conf_fn("ssl_cipher_list", datap); int ssl_check_host = atoi(conf_fn("ssl_check_host", datap)); int ssl_check_certificate_host = atoi(conf_fn("ssl_check_certificate_host", datap)); if (getpeername(in, (struct sockaddr *)&sin, &len) < 0) { g_debug(_("getpeername returned: %s"), strerror(errno)); return; } if ((result = getnameinfo((struct sockaddr *)&sin, len, hostname, NI_MAXHOST, NULL, 0, 0) != 0)) { g_debug(_("getnameinfo failed: %s"), gai_strerror(result)); return; } if (ssl_check_host && check_name_give_sockaddr(hostname, (struct sockaddr *)&sin, &errmsg) < 0) { amfree(errmsg); return; } if (ssl_dir) { if (!ssl_cert_file || ssl_cert_file == '\0') { ssl_cert_file = g_strdup_printf("%s/me/crt.pem", ssl_dir); } if (!ssl_key_file || ssl_key_file == '\0') { ssl_key_file = g_strdup_printf("%s/me/private/key.pem", ssl_dir); } if (!ssl_ca_cert_file || ssl_ca_cert_file == '\0') { ssl_ca_cert_file = g_strdup_printf("%s/CA/crt.pem", ssl_dir); } } if (!ssl_cert_file) { g_debug(_("ssl-cert-file must be set in amanda-remote.conf")); return; } if (!ssl_key_file) { g_debug(_("ssl-key-file must be set in amanda-remote.conf")); return; } if (!ssl_ca_cert_file) { g_debug(_("ssl_ca_cert_file must be set in amanda-remote.conf")); return; } len = sizeof(sin); init_ssl(); /* Create a SSL_CTX structure */ #if OPENSSL_VERSION_NUMBER < 0x10100000L ctx = SSL_CTX_new(SSLv3_server_method()); #else ctx = SSL_CTX_new(TLS_server_method()); #endif if (!ctx) { g_debug(_("SSL_CTX_new failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); if (ssl_cipher_list) { g_debug("Set ssl_cipher_list to %s", ssl_cipher_list); if (SSL_CTX_set_cipher_list(ctx, ssl_cipher_list) == 0) { g_debug(_("SSL_CTX_set_cipher_list failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } } /* Load the me certificate into the SSL_CTX structure */ g_debug(_("Loading ssl-cert-file certificate %s"), ssl_cert_file); if (SSL_CTX_use_certificate_file(ctx, ssl_cert_file, SSL_FILETYPE_PEM) <= 0) { g_debug(_("Load ssl-cert-file failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } /* Load the private-key corresponding to the me certificate */ g_debug(_("Loading ssl-key-file private-key %s"), ssl_key_file); if (SSL_CTX_use_PrivateKey_file(ctx, ssl_key_file, SSL_FILETYPE_PEM) <= 0) { g_debug(_("Load ssl-key-file failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } if (ssl_ca_cert_file) { /* Load the RSA CA certificate into the SSL_CTX structure */ g_debug(_("Loading ssl-ca-cert-file ca certificate %s"), ssl_ca_cert_file); if (!SSL_CTX_load_verify_locations(ctx, ssl_ca_cert_file, NULL)) { g_debug(_("Load ssl-ca-cert-file failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } /* Set to require peer (remote) certificate verification */ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); /* Set the verification depth to 1 */ SSL_CTX_set_verify_depth(ctx,1); } ssl = SSL_new(ctx); if (!ssl) { g_debug(_("SSL_new failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } SSL_set_accept_state(ssl); /* Assign the socket into the SSL structure (SSL and socket without BIO) */ SSL_set_fd(ssl, in); /* Perform SSL Handshake on the SSL me */ err = SSL_accept(ssl); if (err == -1) { g_debug(_("SSL_accept failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } /* Get the me's certificate (optional) */ remote_cert = SSL_get_peer_certificate (ssl); if (remote_cert == NULL) { g_debug(_("remote doesn't sent a certificate")); return; } x509_name = X509_get_subject_name(remote_cert); str = X509_NAME_oneline(X509_get_subject_name(remote_cert), 0, 0); auth_debug(1, _("\t subject: %s\n"), str); amfree (str); str = X509_NAME_oneline(X509_get_issuer_name(remote_cert), 0, 0); auth_debug(1, _("\t issuer: %s\n"), str); amfree(str); loc = -1; loc = X509_NAME_get_index_by_NID(x509_name, NID_commonName, loc); if (loc != -1) { X509_NAME_ENTRY *x509_entry = X509_NAME_get_entry(x509_name, loc); ASN1_STRING *asn1_string = X509_NAME_ENTRY_get_data(x509_entry); cert_hostname = (char *)ASN1_STRING_data(asn1_string); auth_debug(1, "common_name: %s\n", cert_hostname); if (ssl_check_certificate_host && check_name_give_sockaddr((char*)cert_hostname, (struct sockaddr *)&sin, &errmsg) < 0) { g_debug("Common name of certicate (%s) doesn't resolv to IP (%s)", cert_hostname, str_sockaddr(&sin)); amfree(errmsg); X509_free(remote_cert); return; } } else { g_debug("Certificate have no common name"); X509_free(remote_cert); return; } if (ssl_dir) { if (!ssl_fingerprint_file || ssl_fingerprint_file == '\0') { struct stat statbuf; ssl_fingerprint_file = g_strdup_printf("%s/remote/%s/fingerprint", ssl_dir, cert_hostname); if (stat(ssl_fingerprint_file, &statbuf) == -1) { g_free(ssl_fingerprint_file); ssl_fingerprint_file = NULL; } } } if (ssl_fingerprint_file) { g_debug(_("Loading ssl-fingerprint-file %s"), ssl_fingerprint_file); str = validate_fingerprints(remote_cert, ssl_fingerprint_file); if (str) { g_debug("%s", str); amfree(str); X509_free(remote_cert); return; } } X509_free(remote_cert); rc = sec_tcp_conn_get(NULL, hostname, 0); rc->recv_security_ok = &bsd_recv_security_ok; rc->prefix_packet = &bsd_prefix_packet; rc->need_priv_port = 0; copy_sockaddr(&rc->peer, &sin); rc->read = in; rc->write = out; rc->accept_fn = fn; rc->driver = driver; rc->conf_fn = conf_fn; rc->datap = datap; rc->ctx = ctx; rc->ssl = ssl; strncpy(rc->hostname, cert_hostname, sizeof(rc->hostname)-1); g_debug(_("SSL_cipher: %s"), SSL_get_cipher(rc->ssl)); sec_tcp_conn_read(rc); } /* * Open a ssl connection to the host listed in rc->hostname * Returns negative on error, with an errmsg in rc->errmsg. */ static int runssl( struct sec_handle * rh, in_port_t port, char *src_ip, char *ssl_fingerprint_file, char *ssl_cert_file, char *ssl_key_file, char *ssl_ca_cert_file, char *ssl_cipher_list, int ssl_check_certificate_host) { int my_socket; in_port_t my_port; struct tcp_conn *rc = rh->rc; int err; X509 *remote_cert; sockaddr_union sin; socklen_t_equiv len; char *stream_msg = NULL; if (!ssl_key_file) { security_seterror(&rh->sech, _("ssl-key-file must be set")); return -1; } if (!ssl_cert_file) { security_seterror(&rh->sech, _("ssl-cert-file must be set")); return -1; } my_socket = stream_client(src_ip, rc->hostname, port, STREAM_BUFSIZE, STREAM_BUFSIZE, &my_port, 0, &stream_msg); if (stream_msg) { security_seterror(&rh->sech, "%s", stream_msg); g_free(stream_msg); return -1; } if (my_socket < 0) { security_seterror(&rh->sech, "%s", strerror(errno)); return -1; } rc->read = rc->write = my_socket; len = sizeof(sin); if (getpeername(my_socket, (struct sockaddr *)&sin, &len) < 0) { security_seterror(&rh->sech, _("getpeername returned: %s\n"), strerror(errno)); return -1; } copy_sockaddr(&rc->peer, &sin); init_ssl(); /* Create an SSL_CTX structure */ #if OPENSSL_VERSION_NUMBER < 0x10100000L rc->ctx = SSL_CTX_new(SSLv3_client_method()); #else rc->ctx = SSL_CTX_new(TLS_client_method()); #endif if (!rc->ctx) { security_seterror(&rh->sech, "%s", ERR_error_string(ERR_get_error(), NULL)); return -1; } SSL_CTX_set_mode(rc->ctx, SSL_MODE_AUTO_RETRY); if (ssl_cipher_list) { g_debug("Set ssl_cipher_list to %s", ssl_cipher_list); if (SSL_CTX_set_cipher_list(rc->ctx, ssl_cipher_list) == 0) { security_seterror(&rh->sech, "%s", ERR_error_string(ERR_get_error(), NULL)); return -1; } } /* Load the private-key corresponding to the remote certificate */ g_debug("Loading ssl-key-file private-key %s", ssl_key_file); if (SSL_CTX_use_PrivateKey_file(rc->ctx, ssl_key_file, SSL_FILETYPE_PEM) <= 0) { security_seterror(&rh->sech, "%s", ERR_error_string(ERR_get_error(), NULL)); return -1; } /* Load the me certificate into the SSL_CTX structure */ g_debug("Loading ssl-cert-file certificate %s", ssl_cert_file); if (SSL_CTX_use_certificate_file(rc->ctx, ssl_cert_file, SSL_FILETYPE_PEM) <= 0) { security_seterror(&rh->sech, "%s", ERR_error_string(ERR_get_error(), NULL)); return -1; } /* Check if the remote certificate and private-key matches */ if (ssl_cert_file) { if (!SSL_CTX_check_private_key(rc->ctx)) { security_seterror(&rh->sech, _("Private key does not match the certificate public key")); return -1; } } if (ssl_ca_cert_file) { /* Load the RSA CA certificate into the SSL_CTX structure */ /* This will allow this remote to verify the me's */ /* certificate. */ g_debug("Loading ssl-ca-cert-file ca %s", ssl_ca_cert_file); if (!SSL_CTX_load_verify_locations(rc->ctx, ssl_ca_cert_file, NULL)) { security_seterror(&rh->sech, "%s", ERR_error_string(ERR_get_error(), NULL)); return -1; } } else { g_debug(_("no ssl-ca-cert-file defined")); } /* Set flag in context to require peer (me) certificate */ /* verification */ if (ssl_ca_cert_file) { g_debug("Enabling certification verification"); SSL_CTX_set_verify(rc->ctx, SSL_VERIFY_PEER, NULL); SSL_CTX_set_verify_depth(rc->ctx, 1); } else { g_debug("Not enabling certification verification"); } /* ----------------------------------------------- */ rc->ssl = SSL_new(rc->ctx); if (!rc->ssl) { security_seterror(&rh->sech, _("SSL_new failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return -1; } SSL_set_connect_state(rc->ssl); /* Assign the socket into the SSL structure (SSL and socket without BIO) */ SSL_set_fd(rc->ssl, my_socket); /* Perform SSL Handshake on the SSL remote */ err = SSL_connect(rc->ssl); if (err == -1) { security_seterror(&rh->sech, _("SSL_connect failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return -1; } /* Get the me's certificate (optional) */ remote_cert = SSL_get_peer_certificate(rc->ssl); if (remote_cert == NULL) { security_seterror(&rh->sech, _("server have no certificate")); return -1; } else { char *str; str = X509_NAME_oneline(X509_get_subject_name(remote_cert), 0, 0); auth_debug(1, _("\t subject: %s\n"), str); amfree (str); str = X509_NAME_oneline(X509_get_issuer_name(remote_cert), 0, 0); auth_debug(1, _("\t issuer: %s\n"), str); amfree(str); if (ssl_check_certificate_host) { int loc = -1; char *errmsg = NULL; X509_NAME *x509_name = X509_get_subject_name(remote_cert); loc = X509_NAME_get_index_by_NID(x509_name, NID_commonName, loc); if (loc != -1) { X509_NAME_ENTRY *x509_entry = X509_NAME_get_entry(x509_name, loc); ASN1_STRING *asn1_string = X509_NAME_ENTRY_get_data(x509_entry); char *cert_hostname = (char *)ASN1_STRING_data(asn1_string); auth_debug(1, "common_name: %s\n", cert_hostname); if (check_name_give_sockaddr((char*)cert_hostname, (struct sockaddr *)&rc->peer, &errmsg) < 0) { security_seterror(&rh->sech, _("Common name of certicate (%s) doesn't resolv to IP (%s): %s"), cert_hostname, str_sockaddr(&rc->peer), errmsg); amfree(errmsg); return -1; } auth_debug(1, _("Certificate common name (%s) resolve to IP (%s)\n"), cert_hostname, str_sockaddr(&rc->peer)); } else { security_seterror(&rh->sech, _("Certificate have no common name")); g_debug("Certificate have no common name"); return -1; } } /* if (ssl_dir) { if (!ssl_fingerprint_file || ssl_fingerprint_file == '\0') { struct stat statbuf; ssl_fingerprint_file = g_strdup_printf("%s/remote/%s/fingerprint", ssl_dir, cert_hostname); if (stat(ssl_fingerprint_file, &statbuf) == -1) { g_free(ssl_fingerprint_file); ssl_fingerprint_file = NULL; } } } */ if (ssl_fingerprint_file) { g_debug(_("run_ssl: Loading ssl-fingerprint-file %s"), ssl_fingerprint_file); str = validate_fingerprints(remote_cert, ssl_fingerprint_file); if (str) { security_seterror(&rh->sech, "%s", str); amfree(str); return -1; } } X509_free (remote_cert); } g_debug(_("SSL_cipher: %s"), SSL_get_cipher(rc->ssl)); return 0; } static ssize_t ssl_data_write( void *c, struct iovec *iov, int iovcnt) { struct tcp_conn *rc = c; int i; int size; size = 0; for (i=0; i < iovcnt; i++) { size += SSL_write(rc->ssl, iov[i].iov_base, iov[i].iov_len); /* JLM check ERROR */ } return size; } static ssize_t ssl_data_write_non_blocking( void *c, struct iovec *iov, int iovcnt) { struct tcp_conn *rc = c; int i; int size; int r; int flags = fcntl(rc->write, F_GETFL, 0); flags = fcntl(rc->write, F_SETFL, flags|O_NONBLOCK); while(iovcnt>0 && iov->iov_len == 0) { iov++; iovcnt--; } size = 0; for (i=0; i < iovcnt; i++) { r = SSL_write(rc->ssl, iov[i].iov_base, iov[i].iov_len); if (r < 0) { return size; /* JLM check ERROR */ } else if (r == 0) { return size; /* JLM check ERROR */ } else if ((unsigned int)r < iov[i].iov_len) { iov[i].iov_len -= r; size += r; return size; } else { iov[i].iov_len = 0; size += r; } } return size; } static ssize_t ssl_data_read( void *c, void *buf, size_t size, int timeout G_GNUC_UNUSED) { struct tcp_conn *rc = c; return SSL_read(rc->ssl, buf, size); } static void init_ssl(void) { static int init_done = 0; if (init_done == 0) { /* Load encryption & hashing algorithms for the SSL program */ SSL_library_init(); /* Load the error strings for SSL & CRYPTO APIs */ SSL_load_error_strings(); init_done = 1; } }