/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "socket.h" static ErrType err_type = STD_ERROR; static unsigned int g_use_ssl = 0; static char *g_ssl_pathstore = NULL; /* * Tools functions */ const char * socket_strerror(int err) { /* ----------------------------------------------------------------------------- * Get str error, that can be strerror or ssl error * * @param int, not used on OPENSSL_ERROR * @return char *, a static buffer * -------------------------------------------------------------------------- */ switch (err_type) { case STD_ERROR: return (const char *)strerror(err); break; case OPENSSL_ERROR: #ifdef USE_SOCKET_SSL err_type = STD_ERROR; /* Reinit to errno as it is the more frequent one */ return ERR_reason_error_string(ERR_get_error()); #endif break; } return NULL; } void set_socket_ssl(unsigned int use) { /* ----------------------------------------------------------------------------- * Toggle the variable g_use_ssl * * @param unsigned int * -------------------------------------------------------------------------- */ g_use_ssl = use; } unsigned int get_socket_ssl() { /* ----------------------------------------------------------------------------- * Get the variable g_use_ssl * * @return unsigned int * -------------------------------------------------------------------------- */ return g_use_ssl; } void set_ssl_pathstore(char * pathstore) { /* ----------------------------------------------------------------------------- * Toggle the variable g_ssl_pathstore * * @param char * * !! The pointer must contain a dynamically memory allocate * and must not be freed after * -------------------------------------------------------------------------- */ if (g_ssl_pathstore != NULL) free(g_ssl_pathstore); g_ssl_pathstore = pathstore; } char * get_ssl_pathstore() { /* ----------------------------------------------------------------------------- * Get the variable g_ssl_pathstore * * @return char * * -------------------------------------------------------------------------- */ return g_ssl_pathstore; } #ifdef USE_SOCKET_SSL int BIO_read_buffer(BIO *bio, void * buffer, int count) { /* ----------------------------------------------------------------------------- * Read to buffer * * @param BIO *, the SSL BIO pointer to read on * @param void *, the buffer where to store * @param int, the max number of character to read * @return int the number of character read or -1 if an error occurss * -------------------------------------------------------------------------- */ int status = 0, n; while(status < count) { n = BIO_read(bio, (void *)&((char *)buffer)[status], count - status); switch (n) { case 0: /* end-of-file (0) */ return n; break; case -1: if (!BIO_should_retry(bio)) { err_type = OPENSSL_ERROR; return n; } break; default: status += n; } } return status; } int BIO_readline(BIO *bio, char * buffer, int maxlen) { /* ----------------------------------------------------------------------------- * Read line to buffer * * @param BIO *, the SSL BIO pointer to read on * @param char *, the buffer where to store * @param int, the max number of character to read * @return int the number of character read * -------------------------------------------------------------------------- */ int count = 0, status; while(count < maxlen) { /* Read one character */ if ((status = BIO_read_buffer(bio, buffer + count, 1)) <= 0) { /* Nothing to read */ return count; } else { if (buffer[count] == '\n'){ buffer[count + 1] = 0; return count + 1; } } count++; } buffer[count] = 0; return 0; } char * X509_get_verify_error_string(unsigned int verify) { /* ----------------------------------------------------------------------------- * Return an error string * * @param * @param unsigned int, the verify error number, from SSL_get_verify_result() * @return char *, error string * -------------------------------------------------------------------------- */ static char verify_err_string[256]; verify_err_string[0] = '\0'; switch(verify) { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: strlcpy(verify_err_string, "unable to get issuer certificate", sizeof(verify_err_string)); break; case X509_V_ERR_UNABLE_TO_GET_CRL: strlcpy(verify_err_string, "unable to get certificate CRL", sizeof(verify_err_string)); break; case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: strlcpy(verify_err_string, "nable to decrypt certificate's signature", sizeof(verify_err_string)); break; case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: strlcpy(verify_err_string, "unable to decrypt CRL's signature", sizeof(verify_err_string)); break; case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: strlcpy(verify_err_string, "unable to decode issuer public key", sizeof(verify_err_string)); break; case X509_V_ERR_CERT_SIGNATURE_FAILURE: strlcpy(verify_err_string, "certificate signature failure", sizeof(verify_err_string)); break; case X509_V_ERR_CRL_SIGNATURE_FAILURE: strlcpy(verify_err_string, "CRL signature failure", sizeof(verify_err_string)); break; case X509_V_ERR_CERT_NOT_YET_VALID: strlcpy(verify_err_string, "certificate is not yet valid", sizeof(verify_err_string)); break; case X509_V_ERR_CERT_HAS_EXPIRED: strlcpy(verify_err_string, "certificate has expired", sizeof(verify_err_string)); break; case X509_V_ERR_CRL_NOT_YET_VALID: strlcpy(verify_err_string, "CRL is not yet valid", sizeof(verify_err_string)); break; case X509_V_ERR_CRL_HAS_EXPIRED: strlcpy(verify_err_string, "CRL has expired", sizeof(verify_err_string)); break; case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: strlcpy(verify_err_string, "format error in certificate's notBefore field", sizeof(verify_err_string)); break; case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: strlcpy(verify_err_string, "format error in certificate's notAfter field", sizeof(verify_err_string)); break; case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: strlcpy(verify_err_string, "format error in CRL's lastUpdate field", sizeof(verify_err_string)); break; case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: strlcpy(verify_err_string, "format error in CRL's nextUpdate field", sizeof(verify_err_string)); break; case X509_V_ERR_OUT_OF_MEM: strlcpy(verify_err_string, "out of memory", sizeof(verify_err_string)); break; case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: strlcpy(verify_err_string, "self signed certificate", sizeof(verify_err_string)); break; case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: strlcpy(verify_err_string, "self signed certificate in certificate chain", sizeof(verify_err_string)); break; case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: strlcpy(verify_err_string, "unable to get issuer certificate locally", sizeof(verify_err_string)); break; case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: strlcpy(verify_err_string, "unable to verify the first certificate", sizeof(verify_err_string)); break; case X509_V_ERR_CERT_CHAIN_TOO_LONG: strlcpy(verify_err_string, "certificate chain too long", sizeof(verify_err_string)); break; case X509_V_ERR_CERT_REVOKED: strlcpy(verify_err_string, "certificate revoked", sizeof(verify_err_string)); break; case X509_V_ERR_INVALID_CA: strlcpy(verify_err_string, "invalid CA certificate", sizeof(verify_err_string)); break; case X509_V_ERR_PATH_LENGTH_EXCEEDED: strlcpy(verify_err_string, "path length constraint exceeded", sizeof(verify_err_string)); break; case X509_V_ERR_INVALID_PURPOSE: strlcpy(verify_err_string, "unsupported certificate purpose", sizeof(verify_err_string)); break; case X509_V_ERR_CERT_UNTRUSTED: strlcpy(verify_err_string, "certificate not trusted", sizeof(verify_err_string)); break; case X509_V_ERR_CERT_REJECTED: strlcpy(verify_err_string, "certificate rejected", sizeof(verify_err_string)); break; case X509_V_ERR_SUBJECT_ISSUER_MISMATCH: strlcpy(verify_err_string, "subject issuer mismatch", sizeof(verify_err_string)); break; case X509_V_ERR_AKID_SKID_MISMATCH: strlcpy(verify_err_string, "authority and subject key identifier mismatch", sizeof(verify_err_string)); break; case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: strlcpy(verify_err_string, "authority and issuer serial number mismatch", sizeof(verify_err_string)); break; case X509_V_ERR_KEYUSAGE_NO_CERTSIGN: strlcpy(verify_err_string, "key usage does not include certificate signing", sizeof(verify_err_string)); break; case X509_V_ERR_APPLICATION_VERIFICATION: strlcpy(verify_err_string, "application verification failure", sizeof(verify_err_string)); break; } return verify_err_string; } #endif /* * Server Socket functions */ ServerSocket *server_socket_new(const char * host, const char * port, const char * proto) { /* ----------------------------------------------------------------------------- * Create a server socket * * @param const char *, the address to bind to * @param const char *, port number or service name * @param const char *, name protocol * @return ServerSocket * or NULL * -------------------------------------------------------------------------- */ struct sockaddr_in socketaddr; int mastersock; int trueval = TRUE; ServerSocket * new_socket = NULL; u_long ip_addr; new_socket = (ServerSocket *)malloc(sizeof(ServerSocket)); if (new_socket == NULL) return NULL; socketaddr_init(&socketaddr); if (host == NULL) { socketaddr.sin_addr.s_addr = INADDR_ANY; new_socket->addr = NULL; } else { if (isipv4(host)) { ip_addr = inet_addr(host); socketaddr.sin_addr.s_addr = ip_addr; new_socket->addr = (char *)strdup(host); if (new_socket->addr == NULL) return NULL; } else { if (!socketaddr_host(&socketaddr, host)) { /* host cannot be resolved */ socketaddr.sin_addr.s_addr = INADDR_ANY; new_socket->addr = NULL; } else { new_socket->addr = (char *)strdup(host); if (new_socket->addr == NULL) return NULL; } } } if (port != NULL) { socketaddr_service(&socketaddr, port, proto); new_socket->port = (char *)strdup(port); if (new_socket->port == NULL) return NULL; } else new_socket->port = NULL; new_socket->proto = (char *)strdup(proto); if (new_socket->proto == NULL) return NULL; mastersock = socket(PF_INET, prototype(proto), resolveproto(proto)); if (mastersock < 0) { return NULL; } setsockopt(mastersock, SOL_SOCKET, SO_REUSEADDR, &trueval, sizeof(trueval)); if (bind(mastersock, (struct sockaddr *) &socketaddr, sizeof(socketaddr)) < 0) { return NULL; } if (prototype(proto) == SOCK_STREAM) { if (listen(mastersock, 5) < 0) { return NULL; } } new_socket->fd = mastersock; return new_socket; } Socket *server_socket_accept(ServerSocket *socket) { /* ----------------------------------------------------------------------------- * Do an accept() to the server socket * * @param ServerSocket * * @return Socket *, client socket * -------------------------------------------------------------------------- */ Socket * new_sock = NULL; char port[5]; int sockid; struct sockaddr_in socketaddr; socklen_t size = sizeof(socketaddr); if ((sockid = accept(socket->fd, (struct sockaddr *)&socketaddr, &size)) >= 0) { new_sock = (Socket *)malloc(sizeof(Socket)); if (new_sock != NULL) { new_sock->fd = sockid; new_sock->host = (char *)strdup(inet_ntoa(socketaddr.sin_addr)); memset(&port, 0, 5); snprintf(port, sizeof(port), "%d", ntohs(socketaddr.sin_port)); new_sock->port = (char *)strdup(port); new_sock->proto = (char *)strdup("tcp"); new_sock->state = SOCK_CONNECTED; return new_sock; } } return NULL; } int server_socket_disconnect(ServerSocket *socket) { /* ----------------------------------------------------------------------------- * Close a server socket * * @param ServerSocket * * @return int * -------------------------------------------------------------------------- */ if (close(socket->fd) == 0) { return 1; } return 0; } int server_socket_destroy(ServerSocket *socket) { /* ----------------------------------------------------------------------------- * Destroy a server socket * * @param ServerSocket * * @return int * -------------------------------------------------------------------------- */ server_socket_disconnect(socket); if (socket->addr != NULL) free(socket->addr); free(socket->port); free(socket->proto); free(socket); socket = NULL; return 1; } /* * Client Socket functions */ Socket *socket_new(const char *host, const char * port, const char * proto) { /* ----------------------------------------------------------------------------- * Create a client socket * * @param const char *, the hostname or IP address * @param const char *, the port number or service name * @param const char *, name protocol * @return Socket * or NULL * -------------------------------------------------------------------------- */ int sockid = -1; Socket * new_socket = NULL; #ifdef USE_SOCKET_SSL char *pathstore = NULL; SSL *ssl; #endif new_socket = (Socket *)malloc(sizeof(Socket)); if (new_socket == NULL) return NULL; new_socket->host = (char *)strdup(host); if (new_socket->host == NULL) { free(new_socket); return NULL; } new_socket->port = (char *)strdup(port); if (new_socket->port == NULL) { free(new_socket->host); free(new_socket); return NULL; } new_socket->proto = (char *)strdup(proto); if (new_socket->proto == NULL) { free(new_socket->host); free(new_socket->port); free(new_socket); return NULL; } new_socket->state = SOCK_DISCONNECTED; new_socket->use_ssl = 0; #ifdef USE_SOCKET_SSL if (get_socket_ssl()) { pathstore = get_ssl_pathstore(); if (pathstore != NULL) { new_socket->ctx = SSL_CTX_new(SSLv23_client_method()); /* Load the Certificate Trust store */ if (!SSL_CTX_load_verify_locations(new_socket->ctx, pathstore, NULL)) { err_type = OPENSSL_ERROR; SSL_CTX_free(new_socket->ctx); new_socket->ctx = NULL; free(new_socket->host); free(new_socket->port); free(new_socket->proto); free(new_socket); return NULL; } /* Create the BIO structure */ new_socket->bio = BIO_new_ssl_connect(new_socket->ctx); /* Auto responding in background, if the server requested another handshake */ BIO_get_ssl(new_socket->bio, &ssl); SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); new_socket->use_ssl = 1; } else { logmsg(LOG_ERR, "an SSL certificate trust store must be specified."); free(new_socket->host); free(new_socket->port); free(new_socket->proto); free(new_socket); return NULL; } } else { new_socket->bio = NULL; new_socket->ctx = NULL; #else if (get_socket_ssl()) { logmsg(LOG_ERR, "Warning : socket creation with SSL is requested but SSL " "is not supported. Define the USE_SOCKET_SSL."); } #endif sockid = socket(PF_INET, prototype(proto), resolveproto(proto)); if (sockid < 0) { free(new_socket->host); free(new_socket->port); free(new_socket->proto); free(new_socket); return NULL; } #ifdef USE_SOCKET_SSL } #endif new_socket->fd = sockid; return new_socket; } int socket_bind(Socket *socket, const char *host, const char *port) { /* ----------------------------------------------------------------------------- * Bind a socket to a particular port (and host) * * @param Socket * * @param const char *, the ip / host address * if NULL, INADDR_ANY will be used * @param const char *, the port * @return int * -------------------------------------------------------------------------- */ struct sockaddr_in socketaddr; u_long ip_addr; socketaddr_init(&socketaddr); socketaddr_service(&socketaddr, port, socket->proto); if (host == NULL) socketaddr.sin_addr.s_addr = INADDR_ANY; else { if (isipv4(host)) { ip_addr = inet_addr(host); socketaddr.sin_addr.s_addr = ip_addr; } else { if (!socketaddr_host(&socketaddr, host)) /* host cannot be resolved */ socketaddr.sin_addr.s_addr = INADDR_ANY; } } return bind(socket->fd, (struct sockaddr *)&socketaddr, sizeof(socketaddr)); } int socket_connect(Socket *socket) { /* ----------------------------------------------------------------------------- * Connect a client socket * * @param Socket * * @return int (see SocketConnectStatus enum) * -------------------------------------------------------------------------- */ struct sockaddr_in socketaddr; int retcode = SOCK_OK; #ifdef USE_SOCKET_SSL SSL *ssl; X509 *peerCertificate; char commonName[512]; X509_NAME *name; if ((socket->use_ssl) && (socket->bio != NULL)) { if (isipv4(socket->host)) BIO_set_conn_ip(socket->bio, socket->host); else BIO_set_conn_hostname(socket->bio, socket->host); BIO_set_conn_port(socket->bio, socket->port); /* Verify the connection opened */ if (BIO_do_connect(socket->bio) <= 0) { err_type = OPENSSL_ERROR; return SOCK_ERR; } /* Verify the certificate validity * Note that the connection can still be used * The caller must chose to be paranoid and refuse the connection or not */ BIO_get_ssl(socket->bio, &ssl); peerCertificate = SSL_get_peer_certificate(ssl); if (peerCertificate != NULL) { socket->ssl_verify = SSL_get_verify_result(ssl); if (socket->ssl_verify != X509_V_OK) { logmsg(LOG_ERR, "Warning : certificate cannot be verified with trust store : %s", X509_get_verify_error_string(socket->ssl_verify)); retcode = SOCK_SSL_VERIFY_ERR; } name = X509_get_subject_name(peerCertificate); X509_NAME_get_text_by_NID(name, NID_commonName, commonName, 512); if(strncmp(commonName, socket->host, 512) != 0) { logmsg(LOG_ERR, "Warning : peer certificate name verification failed for %s" " : commonName mis-match : %s", socket->host, commonName); retcode = SOCK_SSL_CN_MISMATCH_ERR; } } else { logmsg(LOG_ERR, "Warning : no peer certificate found"); retcode = SOCK_SSL_NO_PEER_CERT_ERR; } /* Get the file descriptor, useful for select() loop */ socket->fd = SSL_get_fd(ssl); } else { #endif socketaddr_init(&socketaddr); socketaddr_service(&socketaddr, socket->port, socket->proto); socketaddr_host(&socketaddr, socket->host); if (connect(socket->fd, (struct sockaddr *)&socketaddr, sizeof(socketaddr)) < 0) return SOCK_ERR; #ifdef USE_SOCKET_SSL } #endif socket->state = SOCK_CONNECTED; return retcode; } int socket_disconnect(Socket *socket) { /* ----------------------------------------------------------------------------- * Close a client socket * * @param Socket * * @return int * -------------------------------------------------------------------------- */ #ifdef USE_SOCKET_SSL if ((socket->use_ssl) && (socket->bio != NULL)) { BIO_reset(socket->bio); socket->state = SOCK_DISCONNECTED; return 1; } else { #endif if (close(socket->fd) == 0) { socket->state = SOCK_DISCONNECTED; return 1; } #ifdef USE_SOCKET_SSL } #endif return 0; } int socket_destroy(Socket *socket) { /* ----------------------------------------------------------------------------- * Destroy a client socket * * @param Socket * * @return int * -------------------------------------------------------------------------- */ socket_disconnect(socket); free(socket->host); free(socket->port); free(socket->proto); #ifdef USE_SOCKET_SSL if ((socket->use_ssl) && (socket->bio != NULL)) { BIO_free_all(socket->bio); SSL_CTX_free(socket->ctx); } #endif free(socket); socket = NULL; return 1; } int socket_readline(Socket *socket, char * buffer, int maxlen) { /* ----------------------------------------------------------------------------- * Read line from a client socket * * @param Socket * * @param char *, buffer to fill * @param int, maxlen to read * @return int, number of characters read * -------------------------------------------------------------------------- */ #ifdef USE_SOCKET_SSL if ((socket->use_ssl) && (socket->bio != NULL)) { return BIO_readline(socket->bio, buffer, maxlen); } else { #endif return readline(socket->fd, buffer, maxlen); #ifdef USE_SOCKET_SSL } #endif } int socket_read(Socket *socket, char * buffer, int maxlen) { /* ----------------------------------------------------------------------------- * Read from a client socket * * @param Socket * * @param char *, buffer to fill * @param int, maxlen to read * @return int, number of characters read * -------------------------------------------------------------------------- */ #ifdef USE_SOCKET_SSL if ((socket->use_ssl) && (socket->bio != NULL)) { return BIO_read_buffer(socket->bio, buffer, maxlen); } else { #endif return read_buffer(socket->fd, buffer, maxlen); #ifdef USE_SOCKET_SSL } #endif } int socket_write(Socket *socket, int flags, const char * str, ...) { /* ----------------------------------------------------------------------------- * Write to client socket * * @param Socket * * @param int, flags passed to send() * @param const char *, printf format * @return int, number of characters written * -------------------------------------------------------------------------- */ va_list args; char *result = NULL; int retval; va_start(args, str); vasprintf(&result, str, args); va_end(args); if (result != NULL) { #ifdef USE_SOCKET_SSL if ((socket->use_ssl) && (socket->bio != NULL)) { retval = BIO_write(socket->bio, result, strlen(result)); if (retval <= 0) { if (!BIO_should_retry(socket->bio)) { err_type = OPENSSL_ERROR; } } } else { #endif retval = send(socket->fd, result, strlen(result), flags); free(result); switch(errno) { case EBADF: case ENOTSOCK: case EFAULT: case EMSGSIZE: case EAGAIN: case ENOBUFS: case EINTR: case ENOMEM: case EINVAL: case EPIPE: return -1; break; default: return retval; break; } #ifdef USE_SOCKET_SSL } #endif } return -1; } int socket_rawrite(Socket *socket, int flags, const char * data, int size) { /* ----------------------------------------------------------------------------- * Write to client socket * * @param Socket * * @param int, flags passed to send() * @param const void *, data to write * @param int, number of bytes to write * @return int, number of characters written * -------------------------------------------------------------------------- */ int retval; #ifdef USE_SOCKET_SSL if ((socket->use_ssl) && (socket->bio != NULL)) { retval = BIO_write(socket->bio, data, size); if (retval <= 0) { if (!BIO_should_retry(socket->bio)) { err_type = OPENSSL_ERROR; } } } else { #endif retval = send(socket->fd, data, size, flags); switch(errno) { case EBADF: case ENOTSOCK: case EFAULT: case EMSGSIZE: case EAGAIN: case ENOBUFS: case EINTR: case ENOMEM: case EINVAL: case EPIPE: return -1; break; default: return retval; break; } #ifdef USE_SOCKET_SSL } #endif return -1; }