/** * @return >0: the version that was negotiated * 0: no agreement on version was reached * -1: error */ int ServerNegotiateProtocol(const ConnectionInfo *conn_info) { int ret; char input[CF_SMALLBUF] = ""; /* Send "CFE_v%d cf-serverd version". */ char version_string[CF_MAXVARSIZE]; int len = snprintf(version_string, sizeof(version_string), "CFE_v%d cf-serverd %s\n", SERVER_PROTOCOL_VERSION, VERSION); ret = TLSSend(conn_info->ssl, version_string, len); if (ret != len) { Log(LOG_LEVEL_ERR, "Connection was hung up!"); return -1; } /* Receive CFE_v%d ... */ ret = TLSRecvLine(conn_info->ssl, input, sizeof(input)); if (ret <= 0) { Log(LOG_LEVEL_ERR, "Client closed connection early! He probably does not trust our key..."); return -1; } int version_received = -1; ret = sscanf(input, "CFE_v%d", &version_received); if (ret != 1) { Log(LOG_LEVEL_ERR, "Protocol version negotiation failed! Received: %s", input); return -1; } /* For now we support only one version, so just check they match... */ if (version_received == SERVER_PROTOCOL_VERSION) { char s[] = "OK\n"; TLSSend(conn_info->ssl, s, sizeof(s)-1); } else { char s[] = "BAD unsupported protocol version\n"; TLSSend(conn_info->ssl, s, sizeof(s)-1); Log(LOG_LEVEL_ERR, "Client advertises unsupported protocol version: %d", version_received); version_received = 0; } return version_received; }
/** * @brief Return the client's username into the #username variable * @TODO More protocol identity: IDENTIFY USERNAME=xxx HOSTNAME=xxx CUSTOMNAME=xxx * @return 1 if IDENTITY command was parsed correctly. Identity fields * (only #username for now) have the respective string values, or they are * empty if field was not on IDENTITY line. * @return -1 in case of error. */ int ServerIdentifyClient(const ConnectionInfo *conn_info, char *username, size_t username_size) { char line[1024], word1[1024], word2[1024]; int line_pos = 0, chars_read = 0; int ret; /* Reset all identity variables, we'll set them later according to fields * on IDENTITY line. For now only "username" setting exists... */ username[0] = '\0'; ret = TLSRecvLine(conn_info->ssl, line, sizeof(line)); if (ret <= 0) { return -1; } /* Assert sscanf() is safe to use. */ assert(sizeof(word1)>=sizeof(line) && sizeof(word2)>=sizeof(line)); ret = sscanf(line, "IDENTITY %[^=]=%s %n", word1, word2, &chars_read); while (ret >= 2) { /* Found USERNAME identity setting */ if (strcmp(word1, "USERNAME") == 0) { if (strlen(word2) < username_size) { strcpy(username, word2); } else { Log(LOG_LEVEL_ERR, "IDENTITY parameter too long: %s=%s", word1, word2); return -1; } Log(LOG_LEVEL_VERBOSE, "Setting IDENTITY: %s=%s", word1, word2); } else { Log(LOG_LEVEL_VERBOSE, "Received unknown IDENTITY parameter: %s=%s", word1, word2); } line_pos += chars_read; ret = sscanf(&line[line_pos], " %[^=]=%s %n", word1, word2, &chars_read); } return 1; }
/* * This test checks for the three basic operations: * - TLSSend * - TLSRecv * - TLSRecvLine * It is difficult to test each one separatedly, so we test all at once. * The test consists on establishing a connection to our child process and then * sending and receiving data. We switch between the original functions and the * mock functions. * We do not test SSL_new, SSL_accept and such because those will be covered by either * the client or server tests. */ static void test_TLSBasicIO(void) { ASSERT_IF_NOT_INITIALIZED; RESET_STATUS; SSL *ssl = NULL; char output_buffer[] = "this is a buffer"; int output_buffer_length = strlen(output_buffer); char input_buffer[4096]; int result = 0; /* * Open a socket and establish a tcp connection. */ struct sockaddr_in server_addr; int server = 0; memset(&server_addr, 0, sizeof(struct sockaddr_in)); server = socket(AF_INET, SOCK_STREAM, 0); assert_int_not_equal(-1, server); server_addr.sin_family = AF_INET; /* We should not use inet_addr, but it is easier for this particular case. */ server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); server_addr.sin_port = htons(8035); /* * Connect */ result = connect(server, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in)); assert_int_not_equal(-1, result); /* * Create a SSL instance */ ssl = SSL_new(SSLCLIENTCONTEXT); assert_true(ssl != NULL); SSL_set_fd(ssl, server); /* * Establish the TLS connection over the socket. */ result = SSL_connect(ssl); assert_int_not_equal(-1, result); /* * Start testing. The first obvious thing to test is to send data. */ result = TLSSend(ssl, output_buffer, output_buffer_length); assert_int_equal(result, output_buffer_length); /* * Good we sent data and the data was sent. Let's check what we get back * by using TLSRecv. */ result = TLSRecv(ssl, input_buffer, output_buffer_length); assert_int_equal(output_buffer_length, result); input_buffer[output_buffer_length] = '\0'; assert_string_equal(output_buffer, input_buffer); /* * Brilliant! We transmitted and received data using simple communication. * Let's try the line sending. */ char output_line_buffer[] = "hello\ngoodbye\n"; int output_line_buffer_length = strlen(output_line_buffer); char output_just_hello[] = "hello"; int output_just_hello_length = strlen(output_just_hello); result = TLSSend(ssl, output_line_buffer, output_line_buffer_length); assert_int_equal(result, output_line_buffer_length); result = TLSRecvLine(ssl, input_buffer, output_line_buffer_length); /* The reply should be up to the first hello */ assert_int_equal(result, output_just_hello_length); assert_string_equal(input_buffer, output_just_hello); /* * Basic check */ USE_MOCK(SSL_write); USE_MOCK(SSL_read); assert_int_equal(0, TLSSend(ssl, output_buffer, 0)); assert_int_equal(-1, TLSSend(ssl, output_buffer, output_buffer_length)); assert_int_equal(-1, TLSRecv(ssl, input_buffer, output_buffer_length)); RESET_STATUS; /* * Start replacing the functions inside to check that the logic works * We start by testing TLSSend, then TLSRead and at last TLSRecvLine. */ USE_MOCK(SSL_write); SSL_WRITE_RETURN(0); assert_int_equal(0, TLSSend(ssl, output_buffer, output_buffer_length)); USE_MOCK(SSL_get_shutdown); SSL_GET_SHUTDOWN_RETURN(1); assert_int_equal(0, TLSSend(ssl, output_buffer, output_buffer_length)); SSL_WRITE_RETURN(-1); assert_int_equal(-1, TLSSend(ssl, output_buffer, output_buffer_length)); USE_MOCK(SSL_read); SSL_READ_RETURN(0); SSL_GET_SHUTDOWN_RETURN(0); assert_int_equal(0, TLSRecv(ssl, input_buffer, output_buffer_length)); SSL_GET_SHUTDOWN_RETURN(1); assert_int_equal(0, TLSRecv(ssl, input_buffer, output_buffer_length)); SSL_READ_RETURN(-1); assert_int_equal(-1, TLSRecv(ssl, input_buffer, output_buffer_length)); USE_ORIGINAL(SSL_write); SSL_READ_RETURN(0); assert_int_equal(-1, TLSRecvLine(ssl, input_buffer, output_buffer_length)); SSL_READ_RETURN(-1); assert_int_equal(-1, TLSRecvLine(ssl, input_buffer, output_buffer_length)); SSL_READ_RETURN(5); assert_int_equal(-1, TLSRecvLine(ssl, input_buffer, 10)); SSL_READ_USE_BUFFER(output_line_buffer); assert_int_equal(5, TLSRecvLine(ssl, input_buffer, output_line_buffer_length)); assert_string_equal(output_just_hello, input_buffer); result = SSL_shutdown(ssl); if (ssl) { SSL_free(ssl); } RESET_STATUS; }