/** @file nanohttp-ssl.c SSL wrapper */ /****************************************************************** * $Id: nanohttp-ssl.c,v 1.38 2007/11/03 22:40:15 m0gg Exp $ * * CSOAP Project: A http client/server library in C * Copyright (C) 2001-2005 Rochester Institute of Technology * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Author: Matt Campbell ******************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_TIME_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_IO_H #include #endif #ifdef HAVE_OPENSSL_SSL_H #include #endif #ifdef HAVE_OPENSSL_RAND_H #include #endif #ifdef HAVE_OPENSSL_ERR_H #include #endif #include "nanohttp-error.h" #include "nanohttp-common.h" #include "nanohttp-socket.h" #include "nanohttp-logging.h" #include "nanohttp-ssl.h" #define CERT_SUBJECT 1 static char *_hssl_certificate = NULL; static char *_hssl_certpass = NULL; static char *_hssl_ca_list = NULL; static SSL_CTX *_hssl_context = NULL; static int _hssl_enabled = 0; static int _hssl_dummy_verify_cert(X509 * cert) { /* TODO: Make sure that the client is providing a client cert, or that the Module is providing the Module cert */ /* connect to anyone */ log_verbose("_Not_ validating certificate."); return 1; } int (*_hssl_verify_cert) (X509 * cert) = _hssl_dummy_verify_cert; static int _hssl_cert_verify_callback(int prev_ok, X509_STORE_CTX * ctx) { /* if ((X509_STORE_CTX_get_error(ctx) = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN)) { log_verbose("Self signed cert in chain"); return 1; } */ log_verbose("Cert depth = %d", X509_STORE_CTX_get_error_depth(ctx)); if (X509_STORE_CTX_get_error_depth(ctx) == 0) { return _hssl_verify_cert(X509_STORE_CTX_get_current_cert(ctx)); } else { log_verbose("Cert ok (prev)"); return prev_ok; } } static void _hssl_superseed(void) { int buf[256], i; srand(time(NULL)); for (i = 0; i < 256; i++) { buf[i] = rand(); } RAND_seed((unsigned char *) buf, sizeof(buf)); return; } static char * _hssl_get_error(SSL * ssl, int ret) { switch (SSL_get_error(ssl, ret)) { case SSL_ERROR_NONE: return "None"; case SSL_ERROR_ZERO_RETURN: return "Zero return"; case SSL_ERROR_WANT_READ: return "Want read"; case SSL_ERROR_WANT_WRITE: return "Want write"; case SSL_ERROR_WANT_X509_LOOKUP: return "Want x509 lookup"; case SSL_ERROR_SYSCALL: if (ERR_get_error() == 0 && ret == -1) { return strerror(errno); } return "Syscall failed"; case SSL_ERROR_SSL: return "SSL error"; default: return "Unkown error"; } } static int _hssl_password_callback(char *buf, int num, int rwflag, void *userdata) { int ret; if (!_hssl_certpass) return 0; ret = strlen(_hssl_certpass); if (num < ret + 1) return 0; strcpy(buf, _hssl_certpass); return ret; } int verify_sn(X509 * cert, int who, int nid, char *str) { char name[256]; char buf[256]; memset(name, '\0', 256); memset(buf, '\0', 256); if (who == CERT_SUBJECT) { X509_NAME_oneline(X509_get_subject_name(cert), name, 256); } else { X509_NAME_oneline(X509_get_issuer_name(cert), name, 256); } buf[0] = '/'; strcat(buf, OBJ_nid2sn(nid)); strcat(buf, "="); strcat(buf, str); return strstr(name, buf) ? 1 : 0; } void hssl_set_hssl_verify_cert(int func(X509 * cert)) { _hssl_verify_cert = func; return; } void hssl_set_certificate(const char *filename) { if (_hssl_certificate) free(_hssl_certificate); _hssl_certificate = strdup(filename); return; } void hssl_set_certpass(const char *password) { if (_hssl_certpass) free(_hssl_certpass); _hssl_certpass = strdup(password); return; } void hssl_set_ca_list(const char *filename) { if (_hssl_ca_list) free(_hssl_ca_list); _hssl_ca_list = strdup(filename); return; } void hssl_enable(void) { _hssl_enabled = 1; return; } static void _hssl_parse_arguments(int argc, char **argv) { int i; for (i=1; isock); if ((ret = SSL_connect(ssl)) <= 0) { herror_t err; log_error("SSL connect error (%s)", _hssl_get_error(ssl, -1)); err = herror_new("hssl_client_ssl", HSSL_ERROR_CONNECT, "SSL_connect failed (%s)", _hssl_get_error(ssl, ret)); SSL_free(ssl); return err; } /* SSL_connect should take care of this for us. if (SSL_get_peer_certificate(ssl) == NULL) { log_error("No certificate provided"); SSL_free(ssl); return herror_new("hssl_client_ssl", HSSL_ERROR_CERTIFICATE, "No certificate provided"); } if (SSL_get_verify_result(ssl) != X509_V_OK) { log_error("Certificate did not verify"); SSL_free(ssl); return herror_new("hssl_client_ssl", HSSL_ERROR_CERTIFICATE, "Verfiy certificate failed"); } */ log_verbose("SSL client initialization completed"); sock->ssl = ssl; return H_OK; } static int _hssl_bio_read(BIO * b, char *out, int outl) { return hsocket_select_recv(b->num, out, outl);; } herror_t hssl_server_ssl(struct hsocket_t *sock) { SSL *ssl; int ret; BIO *sbio; if (!_hssl_enabled) return H_OK; log_verbose("Starting SSL initialization for socket %d", sock->sock); if (!(ssl = SSL_new(_hssl_context))) { log_warn("SSL_new failed"); return herror_new("hssl_server_ssl", HSSL_ERROR_SERVER, "Cannot create SSL object"); } /* SSL_set_fd(ssl, sock->sock); */ sbio = BIO_new_socket(sock->sock, BIO_NOCLOSE); if (sbio == NULL) { log_error("BIO_new_socket failed"); return NULL; } /* BIO_set_callback(sbio, hssl_bio_cb); */ sbio->method->bread = _hssl_bio_read; SSL_set_bio(ssl, sbio, sbio); if ((ret = SSL_accept(ssl)) <= 0) { herror_t err; log_error("SSL_accept failed (%s)", _hssl_get_error(ssl, ret)); err = herror_new("hssl_server_ssl", HSSL_ERROR_SERVER, "SSL_accept failed (%s)", _hssl_get_error(ssl, ret)); SSL_free(ssl); return err; } sock->ssl = ssl; return H_OK; } void hssl_cleanup(struct hsocket_t * sock) { if (sock->ssl) { SSL_shutdown(sock->ssl); SSL_free(sock->ssl); sock->ssl = NULL; } return; } herror_t hssl_read(struct hsocket_t * sock, char *buf, size_t len, size_t * received) { int count; /* log_verbose("sock->sock=%d sock->ssl=%p, len=%li", sock->sock, sock->ssl, len); */ if (sock->ssl) { if ((count = SSL_read(sock->ssl, buf, len)) < 1) return herror_new("hssl_read", HSOCKET_ERROR_RECEIVE, "SSL_read failed (%s)", _hssl_get_error(sock->ssl, count)); } else { if ((count = hsocket_select_recv(sock->sock, buf, len)) == -1) return herror_new("hssl_read", HSOCKET_ERROR_RECEIVE, "recv failed (%s)", strerror(errno)); } *received = count; return H_OK; } herror_t hssl_write(struct hsocket_t * sock, const char *buf, size_t len, size_t * sent) { int count; /* log_verbose("sock->sock=%d, sock->ssl=%p, len=%li", sock->sock, sock->ssl, len); */ if (sock->ssl) { if ((count = SSL_write(sock->ssl, buf, len)) == -1) return herror_new("hssl_write", HSOCKET_ERROR_SEND, "SSL_write failed (%s)", _hssl_get_error(sock->ssl, count)); } else { if ((count = send(sock->sock, buf, len, 0)) == -1) return herror_new("hssl_write", HSOCKET_ERROR_SEND, "send failed (%s)", strerror(errno)); } *sent = count; return H_OK; }