/* * Copyright (c) 2007, Cameron Rich * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the axTLS project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * Demonstrate the use of the axTLS library in C with a set of * command-line parameters similar to openssl. In fact, openssl clients * should be able to communicate with axTLS servers and visa-versa. * * This code has various bits enabled depending on the configuration. To enable * the most interesting version, compile with the 'full mode' enabled. * * To see what options you have, run the following: * > axssl s_server -? * > axssl s_client -? * * The axtls shared library must be in the same directory or be found * by the OS. */ #include #include #include #include "os_port.h" #include "ssl.h" /* define standard input */ #ifndef STDIN_FILENO #define STDIN_FILENO 0 #endif static void do_server(int argc, char *argv[]); static void print_options(char *option); static void print_server_options(char *option); static void do_client(int argc, char *argv[]); static void print_client_options(char *option); static void display_cipher(SSL *ssl); static void display_session_id(SSL *ssl); /** * Main entry point. Doesn't do much except works out whether we are a client * or a server. */ int main(int argc, char *argv[]) { #ifdef WIN32 WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2, 2); WSAStartup(wVersionRequested, &wsaData); #elif !defined(CONFIG_PLATFORM_SOLARIS) signal(SIGPIPE, SIG_IGN); /* ignore pipe errors */ #endif if (argc == 2 && strcmp(argv[1], "version") == 0) { printf("axssl %s %s\n", ssl_version(), __DATE__); exit(0); } if (argc < 2 || ( strcmp(argv[1], "s_server") && strcmp(argv[1], "s_client"))) print_options(argc > 1 ? argv[1] : ""); strcmp(argv[1], "s_server") ? do_client(argc, argv) : do_server(argc, argv); return 0; } /** * Implement the SSL server logic. */ static void do_server(int argc, char *argv[]) { int i = 2; uint16_t port = 4433; uint32_t options = SSL_DISPLAY_CERTS; int client_fd; SSL_CTX *ssl_ctx; int server_fd, res = 0; socklen_t client_len; #ifndef CONFIG_SSL_SKELETON_MODE char *private_key_file = NULL; const char *password = NULL; char **cert; int cert_index = 0; int cert_size = ssl_get_config(SSL_MAX_CERT_CFG_OFFSET); #endif #ifdef WIN32 char yes = 1; #else int yes = 1; #endif struct sockaddr_in serv_addr; struct sockaddr_in client_addr; int quiet = 0; #ifdef CONFIG_SSL_CERT_VERIFICATION int ca_cert_index = 0; int ca_cert_size = ssl_get_config(SSL_MAX_CA_CERT_CFG_OFFSET); char **ca_cert = (char **)calloc(1, sizeof(char *)*ca_cert_size); #endif fd_set read_set; #ifndef CONFIG_SSL_SKELETON_MODE cert = (char **)calloc(1, sizeof(char *)*cert_size); #endif while (i < argc) { if (strcmp(argv[i], "-accept") == 0) { if (i >= argc-1) { print_server_options(argv[i]); } port = atoi(argv[++i]); } #ifndef CONFIG_SSL_SKELETON_MODE else if (strcmp(argv[i], "-cert") == 0) { if (i >= argc-1 || cert_index >= cert_size) { print_server_options(argv[i]); } cert[cert_index++] = argv[++i]; } else if (strcmp(argv[i], "-key") == 0) { if (i >= argc-1) { print_server_options(argv[i]); } private_key_file = argv[++i]; options |= SSL_NO_DEFAULT_KEY; } else if (strcmp(argv[i], "-pass") == 0) { if (i >= argc-1) { print_server_options(argv[i]); } password = argv[++i]; } #endif else if (strcmp(argv[i], "-quiet") == 0) { quiet = 1; options &= ~SSL_DISPLAY_CERTS; } #ifdef CONFIG_SSL_CERT_VERIFICATION else if (strcmp(argv[i], "-verify") == 0) { options |= SSL_CLIENT_AUTHENTICATION; } else if (strcmp(argv[i], "-CAfile") == 0) { if (i >= argc-1 || ca_cert_index >= ca_cert_size) { print_server_options(argv[i]); } ca_cert[ca_cert_index++] = argv[++i]; } #endif #ifdef CONFIG_SSL_FULL_MODE else if (strcmp(argv[i], "-debug") == 0) { options |= SSL_DISPLAY_BYTES; } else if (strcmp(argv[i], "-state") == 0) { options |= SSL_DISPLAY_STATES; } else if (strcmp(argv[i], "-show-rsa") == 0) { options |= SSL_DISPLAY_RSA; } #endif else /* don't know what this is */ { print_server_options(argv[i]); } i++; } if ((ssl_ctx = ssl_ctx_new(options, SSL_DEFAULT_SVR_SESS)) == NULL) { fprintf(stderr, "Error: Server context is invalid\n"); exit(1); } #ifndef CONFIG_SSL_SKELETON_MODE if (private_key_file) { int obj_type = SSL_OBJ_RSA_KEY; /* auto-detect the key type from the file extension */ if (strstr(private_key_file, ".p8")) obj_type = SSL_OBJ_PKCS8; else if (strstr(private_key_file, ".p12")) obj_type = SSL_OBJ_PKCS12; if (ssl_obj_load(ssl_ctx, obj_type, private_key_file, password)) { fprintf(stderr, "Error: Private key '%s' is undefined.\n", private_key_file); exit(1); } } for (i = 0; i < cert_index; i++) { if (ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, cert[i], NULL)) { printf("Certificate '%s' is undefined.\n", cert[i]); exit(1); } } #endif #ifdef CONFIG_SSL_CERT_VERIFICATION for (i = 0; i < ca_cert_index; i++) { if (ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CACERT, ca_cert[i], NULL)) { printf("Certificate '%s' is undefined.\n", ca_cert[i]); exit(1); } } free(ca_cert); #endif #ifndef CONFIG_SSL_SKELETON_MODE free(cert); #endif /* Create socket for incoming connections */ if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return; } setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); /* Construct local address structure */ memset(&serv_addr, 0, sizeof(serv_addr)); /* Zero out structure */ serv_addr.sin_family = AF_INET; /* Internet address family */ serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ serv_addr.sin_port = htons(port); /* Local port */ /* Bind to the local address */ if (bind(server_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { perror("bind"); exit(1); } if (listen(server_fd, 5) < 0) { perror("listen"); exit(1); } client_len = sizeof(client_addr); /************************************************************************* * This is where the interesting stuff happens. Up until now we've * just been setting up sockets etc. Now we do the SSL handshake. *************************************************************************/ for (;;) { SSL *ssl; int reconnected = 0; if (!quiet) { printf("ACCEPT\n"); TTY_FLUSH(); } if ((client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len)) < 0) { break; } ssl = ssl_server_new(ssl_ctx, client_fd); /* now read (and display) whatever the client sends us */ for (;;) { /* allow parallel reading of client and standard input */ FD_ZERO(&read_set); FD_SET(client_fd, &read_set); #ifndef WIN32 /* win32 doesn't like mixing up stdin and sockets */ if (isatty(STDIN_FILENO))/* but only if we are in an active shell */ { FD_SET(STDIN_FILENO, &read_set); } if ((res = select(client_fd+1, &read_set, NULL, NULL, NULL)) > 0) { uint8_t buf[1024]; /* read standard input? */ if (FD_ISSET(STDIN_FILENO, &read_set)) { if (fgets((char *)buf, sizeof(buf), stdin) == NULL) { res = SSL_ERROR_CONN_LOST; } else { /* small hack to check renegotiation */ if (buf[0] == 'r' && (buf[1] == '\n' || buf[1] == '\r')) { res = ssl_renegotiate(ssl); } else /* write our ramblings to the client */ { res = ssl_write(ssl, buf, strlen((char *)buf)+1); } } } else /* a socket read */ #endif { /* keep reading until we get something interesting */ uint8_t *read_buf; if ((res = ssl_read(ssl, &read_buf)) == SSL_OK) { /* are we in the middle of doing a handshake? */ if (ssl_handshake_status(ssl) != SSL_OK) { reconnected = 0; } else if (!reconnected) { /* we are connected/reconnected */ if (!quiet) { display_session_id(ssl); display_cipher(ssl); } reconnected = 1; } } if (res > SSL_OK) /* display our interesting output */ { int written = 0; while (written < res) { written += write(STDOUT_FILENO, read_buf+written, res-written); } TTY_FLUSH(); } else if (res == SSL_CLOSE_NOTIFY) { printf("shutting down SSL\n"); TTY_FLUSH(); } else if (res < SSL_OK && !quiet) { ssl_display_error(res); } } #ifndef WIN32 } #endif if (res < SSL_OK) { if (!quiet) { printf("CONNECTION CLOSED\n"); TTY_FLUSH(); } break; } } /* client was disconnected or the handshake failed. */ ssl_free(ssl); SOCKET_CLOSE(client_fd); } ssl_ctx_free(ssl_ctx); } /** * Implement the SSL client logic. */ static void do_client(int argc, char *argv[]) { #ifdef CONFIG_SSL_ENABLE_CLIENT int res, i = 2; uint16_t port = 4433; uint32_t options = SSL_SERVER_VERIFY_LATER|SSL_DISPLAY_CERTS; int client_fd; char *private_key_file = NULL; struct sockaddr_in client_addr; struct hostent *hostent; int reconnect = 0; uint32_t sin_addr; SSL_CTX *ssl_ctx; SSL *ssl = NULL; int quiet = 0; int cert_index = 0, ca_cert_index = 0; int cert_size, ca_cert_size; char **ca_cert, **cert; uint8_t session_id[SSL_SESSION_ID_SIZE]; fd_set read_set; const char *password = NULL; FD_ZERO(&read_set); sin_addr = inet_addr("127.0.0.1"); cert_size = ssl_get_config(SSL_MAX_CERT_CFG_OFFSET); ca_cert_size = ssl_get_config(SSL_MAX_CA_CERT_CFG_OFFSET); ca_cert = (char **)calloc(1, sizeof(char *)*ca_cert_size); cert = (char **)calloc(1, sizeof(char *)*cert_size); while (i < argc) { if (strcmp(argv[i], "-connect") == 0) { char *host, *ptr; if (i >= argc-1) { print_client_options(argv[i]); } host = argv[++i]; if ((ptr = strchr(host, ':')) == NULL) { print_client_options(argv[i]); } *ptr++ = 0; port = atoi(ptr); hostent = gethostbyname(host); if (hostent == NULL) { print_client_options(argv[i]); } sin_addr = *((uint32_t **)hostent->h_addr_list)[0]; } else if (strcmp(argv[i], "-cert") == 0) { if (i >= argc-1 || cert_index >= cert_size) { print_client_options(argv[i]); } cert[cert_index++] = argv[++i]; } else if (strcmp(argv[i], "-key") == 0) { if (i >= argc-1) { print_client_options(argv[i]); } private_key_file = argv[++i]; options |= SSL_NO_DEFAULT_KEY; } else if (strcmp(argv[i], "-CAfile") == 0) { if (i >= argc-1 || ca_cert_index >= ca_cert_size) { print_client_options(argv[i]); } ca_cert[ca_cert_index++] = argv[++i]; } else if (strcmp(argv[i], "-verify") == 0) { options &= ~SSL_SERVER_VERIFY_LATER; } else if (strcmp(argv[i], "-reconnect") == 0) { reconnect = 4; } else if (strcmp(argv[i], "-quiet") == 0) { quiet = 1; options &= ~SSL_DISPLAY_CERTS; } else if (strcmp(argv[i], "-pass") == 0) { if (i >= argc-1) { print_client_options(argv[i]); } password = argv[++i]; } #ifdef CONFIG_SSL_FULL_MODE else if (strcmp(argv[i], "-debug") == 0) { options |= SSL_DISPLAY_BYTES; } else if (strcmp(argv[i], "-state") == 0) { options |= SSL_DISPLAY_STATES; } else if (strcmp(argv[i], "-show-rsa") == 0) { options |= SSL_DISPLAY_RSA; } #endif else /* don't know what this is */ { print_client_options(argv[i]); } i++; } if ((ssl_ctx = ssl_ctx_new(options, SSL_DEFAULT_CLNT_SESS)) == NULL) { fprintf(stderr, "Error: Client context is invalid\n"); exit(1); } if (private_key_file) { int obj_type = SSL_OBJ_RSA_KEY; /* auto-detect the key type from the file extension */ if (strstr(private_key_file, ".p8")) obj_type = SSL_OBJ_PKCS8; else if (strstr(private_key_file, ".p12")) obj_type = SSL_OBJ_PKCS12; if (ssl_obj_load(ssl_ctx, obj_type, private_key_file, password)) { fprintf(stderr, "Error: Private key '%s' is undefined.\n", private_key_file); exit(1); } } for (i = 0; i < cert_index; i++) { if (ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, cert[i], NULL)) { printf("Certificate '%s' is undefined.\n", cert[i]); exit(1); } } for (i = 0; i < ca_cert_index; i++) { if (ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CACERT, ca_cert[i], NULL)) { printf("Certificate '%s' is undefined.\n", ca_cert[i]); exit(1); } } free(cert); free(ca_cert); /************************************************************************* * This is where the interesting stuff happens. Up until now we've * just been setting up sockets etc. Now we do the SSL handshake. *************************************************************************/ client_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); memset(&client_addr, 0, sizeof(client_addr)); client_addr.sin_family = AF_INET; client_addr.sin_port = htons(port); client_addr.sin_addr.s_addr = sin_addr; if (connect(client_fd, (struct sockaddr *)&client_addr, sizeof(client_addr)) < 0) { perror("connect"); exit(1); } if (!quiet) { printf("CONNECTED\n"); TTY_FLUSH(); } /* Try session resumption? */ if (reconnect) { while (reconnect--) { ssl = ssl_client_new(ssl_ctx, client_fd, session_id, sizeof(session_id)); if ((res = ssl_handshake_status(ssl)) != SSL_OK) { if (!quiet) { ssl_display_error(res); } ssl_free(ssl); exit(1); } display_session_id(ssl); memcpy(session_id, ssl_get_session_id(ssl), SSL_SESSION_ID_SIZE); if (reconnect) { ssl_free(ssl); SOCKET_CLOSE(client_fd); client_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); connect(client_fd, (struct sockaddr *)&client_addr, sizeof(client_addr)); } } } else { ssl = ssl_client_new(ssl_ctx, client_fd, NULL, 0); } /* check the return status */ if ((res = ssl_handshake_status(ssl)) != SSL_OK) { if (!quiet) { ssl_display_error(res); } exit(1); } if (!quiet) { const char *common_name = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME); if (common_name) { printf("Common Name:\t\t\t%s\n", common_name); } display_session_id(ssl); display_cipher(ssl); } for (;;) { uint8_t buf[1024]; /* allow parallel reading of server and standard input */ FD_SET(client_fd, &read_set); #ifndef WIN32 /* win32 doesn't like mixing up stdin and sockets */ FD_SET(STDIN_FILENO, &read_set); if ((res = select(client_fd+1, &read_set, NULL, NULL, NULL)) > 0) { /* read standard input? */ if (FD_ISSET(STDIN_FILENO, &read_set)) #endif { if (fgets((char *)buf, sizeof(buf), stdin) == NULL) { /* bomb out of here */ ssl_free(ssl); break; } else { /* small hack to check renegotiation */ if (buf[0] == 'R' && (buf[1] == '\n' || buf[1] == '\r')) { res = ssl_renegotiate(ssl); } else { res = ssl_write(ssl, buf, strlen((char *)buf)); } } } #ifndef WIN32 else /* a socket read */ { uint8_t *read_buf; res = ssl_read(ssl, &read_buf); if (res > 0) /* display our interesting output */ { int written = 0; while (written < res) { written += write(STDOUT_FILENO, read_buf+written, res-written); } TTY_FLUSH(); } } } #endif if (res < 0) { if (!quiet) { ssl_display_error(res); } break; /* get outta here */ } } ssl_ctx_free(ssl_ctx); SOCKET_CLOSE(client_fd); #else print_client_options(argv[1]); #endif } /** * We've had some sort of command-line error. Print out the basic options. */ static void print_options(char *option) { printf("axssl: Error: '%s' is an invalid command.\n", option); printf("usage: axssl [s_server|s_client|version] [args ...]\n"); exit(1); } /** * We've had some sort of command-line error. Print out the server options. */ static void print_server_options(char *option) { #ifndef CONFIG_SSL_SKELETON_MODE int cert_size = ssl_get_config(SSL_MAX_CERT_CFG_OFFSET); #endif #ifdef CONFIG_SSL_CERT_VERIFICATION int ca_cert_size = ssl_get_config(SSL_MAX_CA_CERT_CFG_OFFSET); #endif printf("unknown option %s\n", option); printf("usage: s_server [args ...]\n"); printf(" -accept arg\t- port to accept on (default is 4433)\n"); #ifndef CONFIG_SSL_SKELETON_MODE printf(" -cert arg\t- certificate file to add (in addition to default)" " to chain -\n" "\t\t Can repeat up to %d times\n", cert_size); printf(" -key arg\t- Private key file to use\n"); printf(" -pass\t\t- private key file pass phrase source\n"); #endif printf(" -quiet\t\t- No server output\n"); #ifdef CONFIG_SSL_CERT_VERIFICATION printf(" -verify\t- turn on peer certificate verification\n"); printf(" -CAfile arg\t- Certificate authority\n"); printf("\t\t Can repeat up to %d times\n", ca_cert_size); #endif #ifdef CONFIG_SSL_FULL_MODE printf(" -debug\t\t- Print more output\n"); printf(" -state\t\t- Show state messages\n"); printf(" -show-rsa\t- Show RSA state\n"); #endif exit(1); } /** * We've had some sort of command-line error. Print out the client options. */ static void print_client_options(char *option) { #ifdef CONFIG_SSL_ENABLE_CLIENT int cert_size = ssl_get_config(SSL_MAX_CERT_CFG_OFFSET); int ca_cert_size = ssl_get_config(SSL_MAX_CA_CERT_CFG_OFFSET); #endif printf("unknown option %s\n", option); #ifdef CONFIG_SSL_ENABLE_CLIENT printf("usage: s_client [args ...]\n"); printf(" -connect host:port - who to connect to (default " "is localhost:4433)\n"); printf(" -verify\t- turn on peer certificate verification\n"); printf(" -cert arg\t- certificate file to use\n"); printf("\t\t Can repeat up to %d times\n", cert_size); printf(" -key arg\t- Private key file to use\n"); printf(" -CAfile arg\t- Certificate authority\n"); printf("\t\t Can repeat up to %d times\n", ca_cert_size); printf(" -quiet\t\t- No client output\n"); printf(" -reconnect\t- Drop and re-make the connection " "with the same Session-ID\n"); printf(" -pass\t\t- private key file pass phrase source\n"); #ifdef CONFIG_SSL_FULL_MODE printf(" -debug\t\t- Print more output\n"); printf(" -state\t\t- Show state messages\n"); printf(" -show-rsa\t- Show RSA state\n"); #endif #else printf("Change configuration to allow this feature\n"); #endif exit(1); } /** * Display what cipher we are using */ static void display_cipher(SSL *ssl) { printf("CIPHER is "); switch (ssl_get_cipher_id(ssl)) { case SSL_AES128_SHA: printf("AES128-SHA"); break; case SSL_AES256_SHA: printf("AES256-SHA"); break; case SSL_RC4_128_SHA: printf("RC4-SHA"); break; case SSL_RC4_128_MD5: printf("RC4-MD5"); break; default: printf("Unknown - %d", ssl_get_cipher_id(ssl)); break; } printf("\n"); TTY_FLUSH(); } /** * Display what session id we have. */ static void display_session_id(SSL *ssl) { int i; const uint8_t *session_id = ssl_get_session_id(ssl); int sess_id_size = ssl_get_session_id_size(ssl); if (sess_id_size > 0) { printf("-----BEGIN SSL SESSION PARAMETERS-----\n"); for (i = 0; i < sess_id_size; i++) { printf("%02x", session_id[i]); } printf("\n-----END SSL SESSION PARAMETERS-----\n"); TTY_FLUSH(); } }