#if defined(HAVE_CONFIG_H) #include "config.h" #endif /* HAVE_CONFIG_H */ #ifndef HAVE_TLS #include IIIMF_status iiimf_tls_set_certificate( IIIMF_stream *stream, const char *certfile, const char *keyfile, const char *cafile, const char *capath ) { return IIIMF_STATUS_FAIL; } IIIMF_status iiimf_tls_supported(void) { return IIIMF_STATUS_FAIL; } IIIMF_status iiimf_accept_tls_stream( IIIMF_stream *stream, IIIMF_stream **stream_ret ) { return IIIMF_STATUS_FAIL; } IIIMF_status iiimf_listen_tls_stream( const char *node, const char *service, int timeout, IIIMF_stream **ret ) { return IIIMF_STATUS_FAIL; } IIIMF_status iiimf_connect_tls_stream( const char *node, const char *service, int timeout, IIIMF_stream **ret ) { return IIIMF_STATUS_FAIL; } IIIMF_status iiimf_delete_tls_stream( IIIMF_stream *stream ) { return IIIMF_STATUS_FAIL; } #else #include #include #include #include #include #include #include #include #include #if defined(HAVE_UNIX_SOCKET) #if defined(HAVE_STDDEF_H) #include #endif #include #endif #if defined(HAVE_POLL) #include #else /* !HAVE_POLL */ #include #endif /* !HAVE_POLL */ #if defined(WIN32) #include #endif /* WIN32 */ #include #include "stream.h" #include #include static int ssl_initialized = 0; /* function declarations */ static IIIMF_status tls_socket_read( IIIMF_stream_private private, void *buf, size_t nbyte ); static IIIMF_status tls_stream_write( IIIMF_stream_private private, const void* buf, size_t nbyte ); /* enum and structure */ enum IIIMF_STREAM_TLS_FLAGS { IIIMF_STREAM_TLS_LISTEN, IIIMF_STREAM_TLS_OPEN }; typedef struct IIIMF_stream_tls_private IIIMF_stream_tls_private; struct IIIMF_stream_tls_private { SSL_CTX *ctx; SSL *ssl; int handshake; int flags; int fd; int timeout; }; static IIIMF_status do_handshake( IIIMF_stream_tls_private *ptlspriv ) { X509 *cert; /* create SSL */ if (!ptlspriv->ssl) { ptlspriv->ssl = SSL_new(ptlspriv->ctx); SSL_set_fd(ptlspriv->ssl, ptlspriv->fd); } /* now handshake with peer */ switch (ptlspriv->flags) { case IIIMF_STREAM_TLS_OPEN: SSL_connect(ptlspriv->ssl); break; case IIIMF_STREAM_TLS_LISTEN: SSL_accept(ptlspriv->ssl); break; default: break; } /* get peer certificate */ cert = SSL_get_peer_certificate(ptlspriv->ssl); if (cert) { /* verify peer's certificate */ if (SSL_get_verify_result(ptlspriv->ssl)) { #if 0 fprintf (stderr, "%s\n", X509_NAME_oneline(X509_get_subject_name(cert), 0, 0)); fprintf (stderr, "%s\n", X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0)); #endif X509_free(cert); } } ptlspriv->handshake = 1; return IIIMF_STATUS_SUCCESS; } static int verify_callback( int ok, X509_STORE_CTX *ctx ) { return ok; } static IIIMF_stream_tls_private* create_tlspriv( int flags, int fd, int timeout ) { IIIMF_stream_tls_private *p; if (!ssl_initialized) { SSL_load_error_strings(); SSL_library_init(); ssl_initialized = 1; } p = (IIIMF_stream_tls_private*) malloc(sizeof(*p)); if (!p) return NULL; /* create ssl context */ switch (flags) { case IIIMF_STREAM_TLS_OPEN: p->ctx = SSL_CTX_new(SSLv23_client_method()); break; case IIIMF_STREAM_TLS_LISTEN: p->ctx = SSL_CTX_new(SSLv23_server_method()); break; default: /* TODO */ break; } /* set timeout */ SSL_CTX_set_timeout(p->ctx, (long) timeout / 1000); /* * we shouldn't create SSL at now. * user may or not set certificate by calling iiimp_tls_set_certificate, * so deley until do_handshake is called. */ p->ssl = NULL; p->timeout = timeout; p->flags = flags; p->fd = fd; p->handshake = 0; return p; } static void delete_tlspriv( IIIMF_stream_tls_private *ptlspriv ) { if (ptlspriv->ssl) { SSL_shutdown(ptlspriv->ssl); SSL_free(ptlspriv->ssl); } if (ptlspriv->ctx) SSL_CTX_free(ptlspriv->ctx); close(ptlspriv->fd); free(ptlspriv); } #if defined(HAVE_UNIX_SOCKET) static IIIMF_status create_tls_stream_unix( const char *node, const char *service, int timeout, IIIMF_stream ** stream_ret ) { int fd; int fd_flag; int r; size_t size; struct sockaddr_un sun_addr; fd = -1; fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd == -1) { return IIIMF_STATUS_STREAM; } sun_addr.sun_family = AF_UNIX; if ((NULL == service) || ('\0' == (*service))) { strncpy(sun_addr.sun_path, node, sizeof(sun_addr.sun_path)); } else { snprintf(sun_addr.sun_path, sizeof(sun_addr.sun_path), "%s/%s", node, service); } size = (offsetof(struct sockaddr_un, sun_path) + strlen (sun_addr.sun_path) + 1); r = connect(fd, (struct sockaddr *)(&sun_addr), size); if (r < 0) { (void)close(fd); return IIIMF_STATUS_STREAM; } fd_flag = fcntl(fd, F_GETFD); fd_flag |= FD_CLOEXEC; (void)fcntl(fd, F_SETFD, fd_flag); { IIIMF_status st; IIIMF_stream *stream; IIIMF_stream_tls_private *tlspriv; tlspriv = create_tlspriv(IIIMF_STREAM_TLS_OPEN, fd, timeout); if (!tlspriv) { close(fd); return IIIMF_STATUS_MALLOC; } st = iiimf_create_stream(tls_socket_read, tls_stream_write, tlspriv, timeout, &stream); if (st != IIIMF_STATUS_SUCCESS) return st; *stream_ret = stream; } return IIIMF_STATUS_SUCCESS; } #endif IIIMF_status iiimf_connect_tls_stream( const char * node, const char * service, int timeout, IIIMF_stream ** stream_ret ) { int fd; int fd_flag; #if defined(HAVE_GETADDRINFO) int e; struct addrinfo * res; struct addrinfo * aip; struct addrinfo hints; #if defined(HAVE_UNIX_SOCKET) if (*node == '/') { return create_tls_stream_unix(node, service, timeout, stream_ret); } #endif fd = -1; (void)memset(&hints, 0, sizeof (hints)); hints.ai_flags = AI_CANONNAME; hints.ai_socktype = SOCK_STREAM; e = getaddrinfo(node, service, &hints, &res); if (0 != e) { return -1; } for (aip = res; NULL != aip; aip = aip->ai_next) { fd = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); if (-1 == fd) { break; } if (-1 == connect(fd, aip->ai_addr, aip->ai_addrlen)) { (void)close(fd); fd = -1; continue; } else { break; } } freeaddrinfo(res); if (-1 == fd) return IIIMF_STATUS_STREAM; #else /* !HAVE_GETADDRINFO */ #if defined(WIN32) unsigned short int port_number; #else /* !WIN32 */ int port_number; #endif /* !WIN32 */ int r; int optval; unsigned long in_addr; struct hostent * hostp; struct sockaddr_in addr; struct servent * srvp; #if defined(HAVE_UNIX_SOCKET) if (*node == '/') { return create_tls_stream_unix(node, service, timeout, stream_ret); } #endif #if defined(WIN32) start_winsock(1, 1); #endif fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0 ) return IIIMF_STATUS_STREAM; port_number = 0; (void)memset((char *)(&addr), 0, sizeof (addr)); addr.sin_family = PF_INET; if (0 < strlen(service)) { srvp = getservbyname(service, "tcp"); if (NULL != srvp) { port_number = srvp->s_port; } (void)endservent(); if (0 == port_number) { port_number = atoi(service); } } if (0 == port_number) { port_number = 9010; } addr.sin_port = htons(port_number); in_addr = inet_addr(node); if ((unsigned long)(-1) != in_addr) { (void)memcpy(&addr.sin_addr, &in_addr, sizeof (in_addr)); } else { hostp = gethostbyname(node); if (NULL == hostp) { #if defined(WIN32) (void)sock_close(fd); #else /* !WIN32 */ (void)close(fd); #endif /* !WIN32 */ return IIIMF_STATUS_STREAM; } if (0 < hostp->h_length) { (void)memcpy(&addr.sin_addr, hostp->h_addr, hostp->h_length); } else { #if defined(WIN32) sock_close(fd); #else /* !WIN32 */ (void)close(fd); #endif /* !WIN32 */ return IIIMF_STATUS_STREAM; } } r = connect(fd, (struct sockaddr *)(&addr), sizeof (addr)); #if defined(WIN32) if (SOCKET_ERROR == r) { sock_close(fd); return IIIMF_STATUS_STREAM; } #else /* !WIN32 */ if (r < 0) { (void)close(fd); return IIIMF_STATUS_STREAM; } #endif /* !WIN32 */ #endif /* !HAVE_GETADDRINFO */ fd_flag = fcntl(fd, F_GETFD); fd_flag |= FD_CLOEXEC; (void)fcntl(fd, F_SETFD, fd_flag); { IIIMF_status st; IIIMF_stream *stream; IIIMF_stream_tls_private *tlspriv; tlspriv = create_tlspriv(IIIMF_STREAM_TLS_OPEN, fd, timeout); if (!tlspriv) { close(fd); return IIIMF_STATUS_MALLOC; } st = iiimf_create_stream(tls_socket_read, tls_stream_write, tlspriv, timeout, &stream); if (st != IIIMF_STATUS_SUCCESS) return st; *stream_ret = stream; } return IIIMF_STATUS_SUCCESS; } IIIMF_status iiimf_listen_tls_stream( const char * node, const char * service, int timeout, IIIMF_stream ** stream_ret ) { int fd; int fd_flag; int optval; int r = 0; #if defined(HAVE_GETADDRINFO) int e; struct addrinfo * a; struct addrinfo * aip; struct addrinfo hints; fd = -1; (void)memset(&hints, 0, sizeof (hints)); hints.ai_flags = AI_PASSIVE; hints.ai_socktype = SOCK_STREAM; e = getaddrinfo(NULL, service, &hints, &aip); if (0 != e) return IIIMF_STATUS_STREAM; for (a = aip; NULL != a; a = a->ai_next) { fd = socket(a->ai_family, a->ai_socktype, a->ai_protocol); if (-1 != fd) { r = bind(fd, aip->ai_addr, sizeof (struct sockaddr)); if (-1 != r) break; (void)close(fd); fd = -1; } } freeaddrinfo(aip); if ((-1 == fd) || (-1 == r)) return IIIMF_STATUS_STREAM; r = listen(fd, 5); if (-1 == r) return IIIMF_STATUS_STREAM; #else /* !HAVE_GETADDRINFO */ #if defined(WIN32) unsigned short int port_number; #else /* !WIN32 */ int port_number; #endif /* !WIN32 */ #if defined(PF_INET6) && defined(ENABLE_IPV6) struct sockaddr_in6 addr; #else /* !PF_INET6 && ENABLE_IPV6 */ struct sockaddr_in addr; #endif /* !PF_INET6 && ENABLE_IPV6 */ struct servent * srvp; #if defined(WIN32) start_winsock(1, 1); #endif #if defined(PF_INET6) && defined(ENABLE_IPV6) fd = socket(PF_INET6, SOCK_STREAM, 0); #else /* !PF_INET6 && ENABLE_IPV6 */ fd = socket(PF_INET, SOCK_STREAM, 0); #endif /* !PF_INET6 && ENABLE_IPV6 */ if (fd < 0 ) return IIIMF_STATUS_STREAM; port_number = 0; if (0 < strlen(service)) { srvp = getservbyname(service, "tcp"); if (NULL != srvp) { port_number = srvp->s_port; } (void)endservent(); if (0 == port_number) { port_number = atoi(service); } } if (0 == port_number) { port_number = 9010; } (void)memset((char *)(&addr), 0, sizeof (addr)); #if defined(PF_INET6) && defined(ENABLE_IPV6) addr.sin6_family = PF_INET6; addr.sin6_flowinfo = 0; addr.sin6_port = htons(port_number); addr.sin6_addr = in6addr_any; #else /* !PF_INET6 && ENABLE_IPV6 */ addr.sin_family = PF_INET; addr.sin_port = htons(port_number); #endif /* !PF_INET6 && ENABLE_IPV6 */ if ((bind(fd, (struct sockaddr *)(&addr), sizeof (addr)) < 0) || (listen(fd, 5) < 0) ) { #if defined(WIN32) sock_close(fd); #else /* !WIN32 */ (void)close(fd); #endif /* !WIN32 */ return IIIMF_STATUS_STREAM; } #endif /* !HAVE_GETADDRINFO */ optval = 1; r = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof (int)); if (-1 == r) { #if defined(WIN32) sock_close(fd); #else /* !WIN32 */ (void)close(fd); #endif /* !WIN32 */ return IIIMF_STATUS_STREAM; } fd_flag = fcntl(fd, F_GETFD); fd_flag |= FD_CLOEXEC; (void)fcntl(fd, F_SETFD, fd_flag); { IIIMF_status st; IIIMF_stream *stream; IIIMF_stream_tls_private *tlspriv; tlspriv = create_tlspriv(IIIMF_STREAM_TLS_LISTEN, fd, timeout); if (!tlspriv) { close(fd); return IIIMF_STATUS_MALLOC; } st = iiimf_create_stream(NULL, NULL, tlspriv, timeout, &stream); if (st != IIIMF_STATUS_SUCCESS) return st; *stream_ret = stream; } return IIIMF_STATUS_SUCCESS; } IIIMF_status iiimf_accept_tls_stream( IIIMF_stream * stream, IIIMF_stream ** stream_ret ) { IIIMF_stream_tls_private *tlspriv = (IIIMF_stream_tls_private*) stream->private_data; IIIMF_status status; struct sockaddr address; socklen_t address_len; int fd; #if defined(HAVE_POLL) struct pollfd fds[1]; int poll_ret; #else /* !HAVE_POLL */ fd_set fdvar; struct timeval timeout; int select_ret; timeout.tv_sec = tlspriv->timeout / 1000; timeout.tv_usec = tlspriv->timeout % 1000; #endif /* !HAVE_POLL */ if (tlspriv->flags != IIIMF_STREAM_TLS_LISTEN) return IIIMF_STATUS_ARGUMENT; if (0 <= tlspriv->timeout) { #if defined(HAVE_POLL) fds[0].fd = tlspriv->fd; fds[0].events = POLLIN; poll_ret = poll(fds, 1, tlspriv->timeout); if (0 == poll_ret) { return IIIMF_STATUS_TIMEOUT; } else if (-1 == poll_ret) { return IIIMF_STATUS_STREAM; } #else /* !HAVE_POLL */ do { FD_ZERO(&fdvar); FD_SET(tlspriv->fd, &fdvar); select_ret = select(tlspriv->fd + 1, NULL, &fdvar, NULL, &timeout); } while ((-1 == select_ret) && (EINTR == errno)); if (-1 == select_ret) return IIIMF_STATUS_TIMEOUT; #endif /* !HAVE_POLL */ } address_len = (sizeof (address)); fd = accept(tlspriv->fd, &address, &address_len); if (-1 == fd) return IIIMF_STATUS_STREAM; { IIIMF_stream *stream_new; IIIMF_stream_tls_private *tlspriv_new; /* we need a special handling for accepted socket */ tlspriv_new = malloc(sizeof(*tlspriv_new)); if (!tlspriv_new) { close(fd); return IIIMF_STATUS_MALLOC; } tlspriv_new->ctx = NULL; tlspriv_new->ssl = SSL_new(tlspriv->ctx); SSL_set_fd(tlspriv_new->ssl, fd); tlspriv_new->timeout = tlspriv->timeout; tlspriv_new->flags = IIIMF_STREAM_TLS_OPEN; tlspriv_new->fd = fd; tlspriv_new->handshake = 0; do_handshake(tlspriv_new); status = iiimf_create_stream(tls_socket_read, tls_stream_write, tlspriv_new, tlspriv->timeout, &stream_new); if (status != IIIMF_STATUS_SUCCESS) return status; *stream_ret = stream_new; } return IIIMF_STATUS_SUCCESS; } IIIMF_status iiimf_delete_tls_stream( IIIMF_stream * stream ) { IIIMF_stream_tls_private *tlspriv = (IIIMF_stream_tls_private*) stream->private_data; delete_tlspriv(tlspriv); iiimf_stream_delete(stream); return IIIMF_STATUS_SUCCESS; } IIIMF_status iiimf_tls_supported(void) { return IIIMF_STATUS_SUCCESS; } IIIMF_status iiimf_tls_set_certificate( IIIMF_stream *stream, const char *certfile, const char *keyfile, const char *cafile, const char *capath ) { IIIMF_stream_tls_private *tlspriv = (IIIMF_stream_tls_private*) stream->private_data; if (tlspriv->handshake) return IIIMF_STATUS_FAIL; /* stream already handshaked with peer */ if (!certfile && !keyfile) return IIIMF_STATUS_FAIL; /* set certificate */ if (certfile) { if (!SSL_CTX_use_certificate_file(tlspriv->ctx, certfile, SSL_FILETYPE_PEM)) { } } /* set private key */ if (!SSL_CTX_use_PrivateKey_file(tlspriv->ctx, keyfile ? keyfile : certfile, SSL_FILETYPE_PEM)) { } /* check private key */ if (!SSL_CTX_check_private_key(tlspriv->ctx)) { } SSL_CTX_set_verify(tlspriv->ctx, SSL_VERIFY_PEER, verify_callback); // SSL_CTX_set_verify_depth(tlspriv->ctx, 1); /* set ca */ if (cafile || capath) { if (!SSL_CTX_load_verify_locations(tlspriv->ctx, cafile, capath) || !SSL_CTX_set_default_verify_paths(tlspriv->ctx)) { } if (tlspriv->flags == IIIMF_STREAM_TLS_LISTEN) { /* if the stream is server mode */ STACK_OF(X509_NAME) *ca_list = NULL; if (cafile) { ca_list = SSL_load_client_CA_file(cafile); } if (capath) { if (!ca_list) ca_list = sk_X509_NAME_new_null(); if (!SSL_add_dir_cert_subjects_to_stack (ca_list, capath)) { } } if (ca_list) SSL_CTX_set_client_CA_list(tlspriv->ctx, ca_list); } } return IIIMF_STATUS_SUCCESS; } static IIIMF_status tls_socket_read( IIIMF_stream_private private, void * buf, size_t nbyte) { IIIMF_stream_tls_private *tlspriv = (IIIMF_stream_tls_private*) private; char * p; ssize_t n; ssize_t r; #if 0 #if defined(HAVE_POLL) struct pollfd fds[1]; int poll_ret; #else /* !HAVE_POLL */ fd_set fdvar; struct timeval timeout; int select_ret; timeout.tv_sec = tlspriv->timeout / 1000; timeout.tv_usec = tlspriv->timeout % 1000; #endif /* !HAVE_POLL */ #endif if (!tlspriv->handshake) do_handshake(tlspriv); for (p = buf, n = nbyte; 0 < n; p += r, n -= r) { #if 0 if (0 <= tlspriv->timeout) { #if defined(HAVE_POLL) fds[0].fd = SSL_get_fd(tlspriv->ssl); fds[0].events = POLLIN; fprintf (stderr, "poll\n"); poll_ret = poll(fds, 1, tlspriv->timeout); if (0 == poll_ret) { return IIIMF_STATUS_TIMEOUT; } else if (-1 == poll_ret) { return IIIMF_STATUS_STREAM; } #else /* !HAVE_POLL */ do { FD_ZERO(&fdvar); FD_SET(tlspriv->fd, &fdvar); select_ret = select(tlspriv->fd + 1, NULL, &fdvar, NULL, &timeout); } while ((-1 == select_ret) && (EINTR == errno)); #endif /* !HAVE_POLL */ } #endif r = SSL_read(tlspriv->ssl, p, n); if (r == 0) { return IIIMF_STATUS_CONNECTION_CLOSED; } if (r < 0) { if (EINTR == errno) { continue; } else { return IIIMF_STATUS_STREAM_RECEIVE; } } } return IIIMF_STATUS_SUCCESS; } static IIIMF_status tls_stream_write( IIIMF_stream_private private, const void * buf, size_t nbyte) { IIIMF_stream_tls_private *tlspriv = (IIIMF_stream_tls_private*) private; const char * p; ssize_t n; ssize_t r; if (!tlspriv->handshake) do_handshake(tlspriv); for (p = buf, n = nbyte; 0 < n; p += r, n -= r) { r = SSL_write(tlspriv->ssl, p, n); if (r < 0) return IIIMF_STATUS_STREAM_SEND; } return IIIMF_STATUS_SUCCESS; } #if defined(WIN32) static int start_winsock(int major, int minor) { WORD wVersionRequested; WSADATA wsaData; int r; wVersionRequested = MAKEWORD(major, minor); r = WSAStartup(wVersionRequested, &wsaData); if (0 != r) return -1; #ifdef CHK_WSOCK_VERSION if ((1 != LOBYTE(wnsaData.wVersion)) || (1 != HIBYTE(wsaData.wVersion))) { /* wrong version */ return 1; } #endif /* CHK_WSOCK_VERSION */ return 0; } static int end_winsock() { return WSACleanup(); } static int sock_close(int fd) { int r; if (INVALID_SOCKET != fd) { r = closesocket(fd); end_winsock(); fd = INVALID_SOCKET; return r; } return 0; } #endif /* WIN32 */ #endif /* HAVE_TLS */ /* Local Variables: */ /* c-file-style: "iiim-project" */ /* End: */