int mock_client(int writefd, int readfd, const char **protocols, int count, const char *expected) { char buffer[0xffff]; struct s2n_connection *conn; struct s2n_config *config; s2n_blocked_status blocked; int result = 0; /* Give the server a chance to listen */ sleep(1); conn = s2n_connection_new(S2N_CLIENT); config = s2n_config_new(); s2n_config_set_protocol_preferences(config, protocols, count); s2n_connection_set_config(conn, config); s2n_connection_set_read_fd(conn, readfd); s2n_connection_set_write_fd(conn, writefd); result = s2n_negotiate(conn, &blocked); if (result < 0) { result = 1; } const char *got = s2n_get_application_protocol(conn); if ((got != NULL && expected == NULL) || (got == NULL && expected != NULL) || (got != NULL && expected != NULL && strcmp(expected, got) != 0)) { result = 2; } for (int i = 1; i < 0xffff; i += 100) { for (int j = 0; j < i; j++) { buffer[j] = 33; } s2n_send(conn, buffer, i, &blocked); } s2n_connection_free(conn); /* Give the server a chance to a void a sigpipe */ sleep(1); _exit(result); }
int main(int argc, char * const *argv) { struct addrinfo hints, *ai_list, *ai; int r, sockfd = 0; /* Optional args */ const char *alpn_protocols = NULL; const char *server_name = NULL; s2n_status_request_type type = S2N_STATUS_REQUEST_NONE; /* required args */ const char *host = NULL; const char *port = "443"; static struct option long_options[] = { { "alpn", required_argument, 0, 'a' }, { "help", no_argument, 0, 'h' }, { "name", required_argument, 0, 'n' }, { "status", no_argument, 0, 's' }, }; while (1) { int option_index = 0; int c = getopt_long (argc, argv, "a:hn:s", long_options, &option_index); if (c == -1) { break; } switch (c) { case 'a': alpn_protocols = optarg; break; case 'h': usage(); break; case 'n': server_name = optarg; break; case 's': type = S2N_STATUS_REQUEST_OCSP; break; case '?': default: usage(); break; } } if (optind < argc) { host = argv[optind++]; } if (optind < argc) { port = argv[optind++]; } if (!host) { usage(); } if (!server_name) { server_name = host; } if (memset(&hints, 0, sizeof(hints)) != &hints) { fprintf(stderr, "memset error: %s\n", strerror(errno)); exit(1); } hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { fprintf(stderr, "Error disabling SIGPIPE\n"); exit(1); } if ((r = getaddrinfo(host, port, &hints, &ai_list)) != 0) { fprintf(stderr, "error: %s\n", gai_strerror(r)); exit(1); } int connected = 0; for (ai = ai_list; ai != NULL; ai = ai->ai_next) { if ((sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) { continue; } if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) == -1) { close(sockfd); continue; } connected = 1; /* connect() succeeded */ break; } freeaddrinfo(ai_list); if (connected == 0) { fprintf(stderr, "Failed to connect to %s:%s\n", argv[1], port); close(sockfd); exit(1); } const char *error; if (s2n_init(&error) < 0) { fprintf(stderr, "Error running s2n_init(): '%s'\n", s2n_strerror(s2n_errno, "EN")); } struct s2n_config *config = s2n_config_new(); if (config == NULL) { fprintf(stderr, "Error getting new config: '%s'\n", s2n_strerror(s2n_errno, "EN")); exit(1); } if (s2n_config_set_status_request_type(config, type) < 0) { fprintf(stderr, "Error setting status request type: '%s'\n", s2n_strerror(s2n_errno, "EN")); exit(1); } if (alpn_protocols) { /* Count the number of commas, this tells us how many protocols there are in the list */ const char *ptr = alpn_protocols; int protocol_count = 1; while (*ptr) { if (*ptr == ',') { protocol_count++; } ptr++; } char **protocols = malloc(sizeof(char *) * protocol_count); if (!protocols) { fprintf(stderr, "Error allocating memory\n"); exit(1); } const char *next = alpn_protocols; int index = 0; int length = 0; ptr = alpn_protocols; while (*ptr) { if (*ptr == ',') { protocols[index] = malloc(length + 1); if (!protocols[index]) { fprintf(stderr, "Error allocating memory\n"); exit(1); } memcpy(protocols[index], next, length); protocols[index][length] = '\0'; length = 0; index++; ptr++; next = ptr; } else { length++; ptr++; } } if (ptr != next) { protocols[index] = malloc(length + 1); if (!protocols[index]) { fprintf(stderr, "Error allocating memory\n"); exit(1); } memcpy(protocols[index], next, length); protocols[index][length] = '\0'; } if (s2n_config_set_protocol_preferences(config, (const char * const *)protocols, protocol_count) < 0) { fprintf(stderr, "Failed to set protocol preferences: '%s'\n", s2n_strerror(s2n_errno, "EN")); exit(1); } while(protocol_count) { protocol_count--; free(protocols[protocol_count]); } free(protocols); } struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); if (conn == NULL) { fprintf(stderr, "Error getting new connection: '%s'\n", s2n_strerror(s2n_errno, "EN")); exit(1); } printf("Connected to %s:%s\n", host, port); if (s2n_connection_set_config(conn, config) < 0) { fprintf(stderr, "Error setting configuration: '%s'\n", s2n_strerror(s2n_errno, "EN")); exit(1); } if (s2n_set_server_name(conn, server_name) < 0) { fprintf(stderr, "Error setting server name: '%s'\n", s2n_strerror(s2n_errno, "EN")); exit(1); } if (s2n_connection_set_fd(conn, sockfd) < 0) { fprintf(stderr, "Error setting file descriptor: '%s'\n", s2n_strerror(s2n_errno, "EN")); exit(1); } /* See echo.c */ echo(conn, sockfd); if (s2n_connection_free(conn) < 0) { fprintf(stderr, "Error freeing connection: '%s'\n", s2n_strerror(s2n_errno, "EN")); exit(1); } if (s2n_config_free(config) < 0) { fprintf(stderr, "Error freeing configuration: '%s'\n", s2n_strerror(s2n_errno, "EN")); exit(1); } if (s2n_cleanup(&error) < 0) { fprintf(stderr, "Error running s2n_cleanup(): '%s'\n", s2n_strerror(s2n_errno, "EN")); } return 0; }
static void setup_s2n_config(struct s2n_config *config, const char *cipher_prefs, s2n_status_request_type type, struct verify_data *unsafe_verify_data, const char *host, const char *alpn_protocols, uint16_t mfl_value) { if (config == NULL) { print_s2n_error("Error getting new config"); exit(1); } GUARD_EXIT(s2n_config_set_cipher_preferences(config, cipher_prefs), "Error setting cipher prefs"); GUARD_EXIT(s2n_config_set_status_request_type(config, type), "OCSP validation is not supported by the linked libCrypto implementation. It cannot be set."); if (s2n_config_set_verify_host_callback(config, unsafe_verify_host, unsafe_verify_data) < 0) { print_s2n_error("Error setting host name verification function."); } if (type == S2N_STATUS_REQUEST_OCSP) { if(s2n_config_set_check_stapled_ocsp_response(config, 1)) { print_s2n_error("OCSP validation is not supported by the linked libCrypto implementation. It cannot be set."); } } unsafe_verify_data->trusted_host = host; if (alpn_protocols) { /* Count the number of commas, this tells us how many protocols there are in the list */ const char *ptr = alpn_protocols; int protocol_count = 1; while (*ptr) { if (*ptr == ',') { protocol_count++; } ptr++; } char **protocols = malloc(sizeof(char *) * protocol_count); if (!protocols) { fprintf(stderr, "Error allocating memory\n"); exit(1); } const char *next = alpn_protocols; int idx = 0; int length = 0; ptr = alpn_protocols; while (*ptr) { if (*ptr == ',') { protocols[idx] = malloc(length + 1); if (!protocols[idx]) { fprintf(stderr, "Error allocating memory\n"); exit(1); } memcpy(protocols[idx], next, length); protocols[idx][length] = '\0'; length = 0; idx++; ptr++; next = ptr; } else { length++; ptr++; } } if (ptr != next) { protocols[idx] = malloc(length + 1); if (!protocols[idx]) { fprintf(stderr, "Error allocating memory\n"); exit(1); } memcpy(protocols[idx], next, length); protocols[idx][length] = '\0'; } GUARD_EXIT(s2n_config_set_protocol_preferences(config, (const char *const *)protocols, protocol_count), "Failed to set protocol preferences"); while (protocol_count) { protocol_count--; free(protocols[protocol_count]); } free(protocols); } uint8_t mfl_code = 0; if (mfl_value > 0) { switch(mfl_value) { case 512: mfl_code = S2N_TLS_MAX_FRAG_LEN_512; break; case 1024: mfl_code = S2N_TLS_MAX_FRAG_LEN_1024; break; case 2048: mfl_code = S2N_TLS_MAX_FRAG_LEN_2048; break; case 4096: mfl_code = S2N_TLS_MAX_FRAG_LEN_4096; break; default: fprintf(stderr, "Invalid maximum fragment length value\n"); exit(1); } } GUARD_EXIT(s2n_config_send_max_fragment_length(config, mfl_code), "Error setting maximum fragment length"); }
int main(int argc, char **argv) { char buffer[0xffff]; struct s2n_connection *conn; struct s2n_config *config; s2n_blocked_status blocked; int status; pid_t pid; int server_to_client[2]; int client_to_server[2]; const char *protocols[] = { "http/1.1", "spdy/3.1" }; const char *mismatch_protocols[] = { "spdy/2" }; BEGIN_TEST(); EXPECT_SUCCESS(setenv("S2N_ENABLE_CLIENT_MODE", "1", 0)); EXPECT_NOT_NULL(config = s2n_config_new()); EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, protocols, 2)); EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, certificate, private_key)); EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams)); /** Test no client ALPN request */ /* Create a pipe */ EXPECT_SUCCESS(pipe(server_to_client)); EXPECT_SUCCESS(pipe(client_to_server)); /* Create a child process */ pid = fork(); if (pid == 0) { /* This is the child process, close the read end of the pipe */ EXPECT_SUCCESS(close(client_to_server[0])); EXPECT_SUCCESS(close(server_to_client[1])); /* Send the client hello with no ALPN extensions, and validate we didn't * negotiate an application protocol */ mock_client(client_to_server[1], server_to_client[0], NULL, 0, NULL); } /* This is the parent */ EXPECT_SUCCESS(close(client_to_server[1])); EXPECT_SUCCESS(close(server_to_client[0])); EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); /* Set up the connection to read from the fd */ EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, client_to_server[0])); EXPECT_SUCCESS(s2n_connection_set_write_fd(conn, server_to_client[1])); /* Negotiate the handshake. */ EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); /* Expect NULL negotiated protocol */ EXPECT_EQUAL(s2n_get_application_protocol(conn), NULL); for (int i = 1; i < 0xffff; i += 100) { char * ptr = buffer; int bytes_read = 0; int size = i; do { EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); size -= bytes_read; ptr += bytes_read; } while(size); for (int j = 0; j < i; j++) { EXPECT_EQUAL(buffer[j], 33); } } EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); EXPECT_SUCCESS(s2n_connection_free(conn)); /* Clean up */ EXPECT_EQUAL(waitpid(-1, &status, 0), pid); EXPECT_EQUAL(status, 0); /* Test a matching ALPN request */ /* Create a pipe */ EXPECT_SUCCESS(pipe(server_to_client)); EXPECT_SUCCESS(pipe(client_to_server)); /* Create a child process */ pid = fork(); if (pid == 0) { /* This is the child process, close the read end of the pipe */ EXPECT_SUCCESS(close(client_to_server[0])); EXPECT_SUCCESS(close(server_to_client[1])); /* Clients ALPN preferences match our preferences, so we pick the * most preffered server one */ mock_client(client_to_server[1], server_to_client[0], protocols, 2, protocols[0]); } /* This is the parent */ EXPECT_SUCCESS(close(client_to_server[1])); EXPECT_SUCCESS(close(server_to_client[0])); EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); /* Set up the connection to read from the fd */ EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, client_to_server[0])); EXPECT_SUCCESS(s2n_connection_set_write_fd(conn, server_to_client[1])); /* Negotiate the handshake. */ EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); /* Expect our most prefered negotiated protocol */ EXPECT_STRING_EQUAL(s2n_get_application_protocol(conn), protocols[0]); for (int i = 1; i < 0xffff; i += 100) { char * ptr = buffer; int bytes_read = 0; int size = i; do { EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); size -= bytes_read; ptr += bytes_read; } while(size); for (int j = 0; j < i; j++) { EXPECT_EQUAL(buffer[j], 33); } } EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); EXPECT_SUCCESS(s2n_connection_free(conn)); /* Clean up */ EXPECT_EQUAL(waitpid(-1, &status, 0), pid); EXPECT_EQUAL(status, 0); /* Test a lower prefered matching ALPN request */ /* Create a pipe */ EXPECT_SUCCESS(pipe(server_to_client)); EXPECT_SUCCESS(pipe(client_to_server)); /* Create a child process */ pid = fork(); if (pid == 0) { /* This is the child process, close the read end of the pipe */ EXPECT_SUCCESS(close(client_to_server[0])); EXPECT_SUCCESS(close(server_to_client[1])); /* Client only advertises our second choice, so we should negotiate it */ mock_client(client_to_server[1], server_to_client[0], &protocols[1], 1, protocols[1]); } /* This is the parent */ EXPECT_SUCCESS(close(client_to_server[1])); EXPECT_SUCCESS(close(server_to_client[0])); EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); /* Set up the connection to read from the fd */ EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, client_to_server[0])); EXPECT_SUCCESS(s2n_connection_set_write_fd(conn, server_to_client[1])); /* Negotiate the handshake. */ EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); for (int i = 1; i < 0xffff; i += 100) { char * ptr = buffer; int bytes_read = 0; int size = i; do { EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); size -= bytes_read; ptr += bytes_read; } while(size); for (int j = 0; j < i; j++) { EXPECT_EQUAL(buffer[j], 33); } } /* Expect our least prefered negotiated protocol */ EXPECT_STRING_EQUAL(s2n_get_application_protocol(conn), protocols[1]); EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); EXPECT_SUCCESS(s2n_connection_free(conn)); /* Clean up */ EXPECT_EQUAL(waitpid(-1, &status, 0), pid); EXPECT_EQUAL(status, 0); /* Test a non-matching ALPN request */ /* Create a pipe */ EXPECT_SUCCESS(pipe(server_to_client)); EXPECT_SUCCESS(pipe(client_to_server)); /* Create a child process */ pid = fork(); if (pid == 0) { /* This is the child process, close the read end of the pipe */ EXPECT_SUCCESS(close(client_to_server[0])); EXPECT_SUCCESS(close(server_to_client[1])); /* Client doesn't support any of our protocols, so we shouldn't complete * the handshake */ mock_client(client_to_server[1], server_to_client[0], mismatch_protocols, 1, NULL); } /* This is the parent */ EXPECT_SUCCESS(close(client_to_server[1])); EXPECT_SUCCESS(close(server_to_client[0])); EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); /* Set up the connection to read from the fd */ EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, client_to_server[0])); EXPECT_SUCCESS(s2n_connection_set_write_fd(conn, server_to_client[1])); /* s2n_negotiate will fail, which ordinarily would delay with a sleep. * Remove the sleep and fake the delay with a mock time routine */ EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); EXPECT_SUCCESS(s2n_config_set_nanoseconds_since_epoch_callback(config, mock_nanoseconds_since_epoch, NULL)); /* Negotiate the handshake. */ EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); /* Expect NULL negotiated protocol */ EXPECT_EQUAL(s2n_get_application_protocol(conn), NULL); EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); EXPECT_SUCCESS(s2n_connection_free(conn)); /* Close the pipes */ EXPECT_SUCCESS(close(client_to_server[0])); EXPECT_SUCCESS(close(server_to_client[1])); /* Clean up */ EXPECT_EQUAL(waitpid(-1, &status, 0), pid); EXPECT_NOT_EQUAL(status, 0); EXPECT_SUCCESS(s2n_config_free(config)); END_TEST(); return 0; }