/* suites.c * * Copyright (C) 2006-2014 wolfSSL Inc. * * This file is part of CyaSSL. * * CyaSSL 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. * * CyaSSL 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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #define MAX_ARGS 40 #define MAX_COMMAND_SZ 240 #define MAX_SUITE_SZ 80 #define NOT_BUILT_IN -123 #ifdef NO_OLD_TLS #define VERSION_TOO_OLD -124 #endif #include "examples/client/client.h" #include "examples/server/server.h" static CYASSL_CTX* cipherSuiteCtx = NULL; static char nonblockFlag[] = "-N"; static char noVerifyFlag[] = "-d"; static char portFlag[] = "-p"; static char flagSep[] = " "; static char svrPort[] = "0"; #ifdef NO_OLD_TLS /* if the protocol version is less than tls 1.2 return 1, else 0 */ static int IsOldTlsVersion(const char* line) { const char* find = "-v "; char* begin = strstr(line, find); if (begin) { int version = -1; begin += 3; version = atoi(begin); if (version < 3) return 1; } return 0; } #endif /* NO_OLD_TLS */ /* if the cipher suite on line is valid store in suite and return 1, else 0 */ static int IsValidCipherSuite(const char* line, char* suite) { int found = 0; int valid = 0; const char* find = "-l "; const char* begin = strstr(line, find); const char* end; suite[0] = '\0'; if (begin) { begin += 3; end = strstr(begin, " "); if (end) { long len = end - begin; if (len > MAX_SUITE_SZ) { printf("suite too long!\n"); return 0; } memcpy(suite, begin, len); suite[len] = '\0'; } else strncpy(suite, begin, MAX_SUITE_SZ); suite[MAX_SUITE_SZ] = '\0'; found = 1; } if (found) { if (CyaSSL_CTX_set_cipher_list(cipherSuiteCtx, suite) == SSL_SUCCESS) valid = 1; } return valid; } static int execute_test_case(int svr_argc, char** svr_argv, int cli_argc, char** cli_argv, int addNoVerify, int addNonBlocking) { func_args cliArgs = {cli_argc, cli_argv, 0, NULL, NULL}; func_args svrArgs = {svr_argc, svr_argv, 0, NULL, NULL}; tcp_ready ready; THREAD_TYPE serverThread; char commandLine[MAX_COMMAND_SZ]; char cipherSuite[MAX_SUITE_SZ+1]; int i; size_t added = 0; static int tests = 1; commandLine[0] = '\0'; for (i = 0; i < svr_argc; i++) { added += strlen(svr_argv[i]) + 2; if (added >= MAX_COMMAND_SZ) { printf("server command line too long\n"); break; } strcat(commandLine, svr_argv[i]); strcat(commandLine, flagSep); } if (IsValidCipherSuite(commandLine, cipherSuite) == 0) { #ifdef DEBUG_SUITE_TESTS printf("cipher suite %s not supported in build\n", cipherSuite); #endif return NOT_BUILT_IN; } #ifdef NO_OLD_TLS if (IsOldTlsVersion(commandLine) == 1) { #ifdef DEBUG_SUITE_TESTS printf("protocol version on line %s is too old\n", commandLine); #endif return VERSION_TOO_OLD; } #endif if (addNoVerify) { printf("repeating test with client cert request off\n"); added += 4; /* -d plus space plus terminator */ if (added >= MAX_COMMAND_SZ || svr_argc >= MAX_ARGS) printf("server command line too long\n"); else { svr_argv[svr_argc++] = noVerifyFlag; svrArgs.argc = svr_argc; strcat(commandLine, noVerifyFlag); strcat(commandLine, flagSep); } } if (addNonBlocking) { printf("repeating test with non blocking on\n"); added += 4; /* -N plus terminator */ if (added >= MAX_COMMAND_SZ || svr_argc >= MAX_ARGS) printf("server command line too long\n"); else { svr_argv[svr_argc++] = nonblockFlag; svrArgs.argc = svr_argc; strcat(commandLine, nonblockFlag); strcat(commandLine, flagSep); } } #ifndef USE_WINDOWS_API /* add port 0 */ if (svr_argc + 2 > MAX_ARGS) printf("cannot add the magic port number flag to server\n"); else { svr_argv[svr_argc++] = portFlag; svr_argv[svr_argc++] = svrPort; svrArgs.argc = svr_argc; } #endif printf("trying server command line[%d]: %s\n", tests, commandLine); commandLine[0] = '\0'; added = 0; for (i = 0; i < cli_argc; i++) { added += strlen(cli_argv[i]) + 2; if (added >= MAX_COMMAND_SZ) { printf("client command line too long\n"); break; } strcat(commandLine, cli_argv[i]); strcat(commandLine, flagSep); } if (addNonBlocking) { added += 4; /* -N plus space plus terminator */ if (added >= MAX_COMMAND_SZ) printf("client command line too long\n"); else { cli_argv[cli_argc++] = nonblockFlag; strcat(commandLine, nonblockFlag); strcat(commandLine, flagSep); cliArgs.argc = cli_argc; } } printf("trying client command line[%d]: %s\n", tests++, commandLine); InitTcpReady(&ready); /* start server */ svrArgs.signal = &ready; start_thread(server_test, &svrArgs, &serverThread); wait_tcp_ready(&svrArgs); #ifndef USE_WINDOWS_API if (ready.port != 0) { if (cli_argc + 2 > MAX_ARGS) printf("cannot add the magic port number flag to client\n"); else { char portNumber[8]; snprintf(portNumber, sizeof(portNumber), "%d", ready.port); cli_argv[cli_argc++] = portFlag; cli_argv[cli_argc++] = portNumber; cliArgs.argc = cli_argc; } } #endif /* start client */ client_test(&cliArgs); /* verify results */ if (cliArgs.return_code != 0) { printf("client_test failed\n"); exit(EXIT_FAILURE); } join_thread(serverThread); if (svrArgs.return_code != 0) { printf("server_test failed\n"); exit(EXIT_FAILURE); } FreeTcpReady(&ready); return 0; } static void test_harness(void* vargs) { func_args* args = (func_args*)vargs; char* script; long sz, len; int cliMode = 0; /* server or client command flag, server first */ int ret; FILE* file; char* svrArgs[MAX_ARGS]; int svrArgsSz; char* cliArgs[MAX_ARGS]; int cliArgsSz; char* cursor; char* comment; const char* fname = "tests/test.conf"; if (args->argc == 1) { printf("notice: using default file %s\n", fname); } else if(args->argc != 2) { printf("usage: harness [FILE]\n"); args->return_code = 1; return; } else { fname = args->argv[1]; } file = fopen(fname, "r"); if (file == NULL) { fprintf(stderr, "unable to open %s\n", fname); args->return_code = 1; return; } fseek(file, 0, SEEK_END); sz = ftell(file); rewind(file); if (sz <= 0) { fprintf(stderr, "%s is empty\n", fname); fclose(file); args->return_code = 1; return; } script = (char*)malloc(sz+1); if (script == 0) { fprintf(stderr, "unable to allocte script buffer\n"); fclose(file); args->return_code = 1; return; } len = fread(script, 1, sz, file); if (len != sz) { fprintf(stderr, "read error\n"); fclose(file); free(script); args->return_code = 1; return; } fclose(file); script[sz] = 0; cursor = script; svrArgsSz = 1; svrArgs[0] = args->argv[0]; cliArgsSz = 1; cliArgs[0] = args->argv[0]; while (*cursor != 0) { int do_it = 0; switch (*cursor) { case '\n': /* A blank line triggers test case execution or switches to client mode if we don't have the client command yet */ if (cliMode == 0) cliMode = 1; /* switch to client mode processing */ else do_it = 1; /* Do It, we have server and client */ cursor++; break; case '#': /* Ignore lines that start with a #. */ comment = strsep(&cursor, "\n"); #ifdef DEBUG_SUITE_TESTS printf("%s\n", comment); #else (void)comment; #endif break; case '-': /* Parameters start with a -. They end in either a newline * or a space. Capture until either, save in Args list. */ if (cliMode) cliArgs[cliArgsSz++] = strsep(&cursor, " \n"); else svrArgs[svrArgsSz++] = strsep(&cursor, " \n"); break; default: /* Anything from cursor until end of line that isn't the above * is data for a paramter. Just up until the next newline in * the Args list. */ if (cliMode) cliArgs[cliArgsSz++] = strsep(&cursor, "\n"); else svrArgs[svrArgsSz++] = strsep(&cursor, "\n"); if (*cursor == 0) /* eof */ do_it = 1; } if (svrArgsSz == MAX_ARGS || cliArgsSz == MAX_ARGS) { fprintf(stderr, "too many arguments, forcing test run\n"); do_it = 1; } if (do_it) { ret = execute_test_case(svrArgsSz, svrArgs, cliArgsSz, cliArgs,0,0); /* don't repeat if not supported in build */ if (ret == 0) { execute_test_case(svrArgsSz, svrArgs, cliArgsSz, cliArgs, 0, 1); execute_test_case(svrArgsSz, svrArgs, cliArgsSz, cliArgs, 1, 0); execute_test_case(svrArgsSz, svrArgs, cliArgsSz, cliArgs, 1, 1); } svrArgsSz = 1; cliArgsSz = 1; cliMode = 0; } } free(script); args->return_code = 0; } int SuiteTest(void) { func_args args; char argv0[2][80]; char* myArgv[2]; printf(" Begin Cipher Suite Tests\n"); /* setup */ myArgv[0] = argv0[0]; myArgv[1] = argv0[1]; args.argv = myArgv; strcpy(argv0[0], "SuiteTest"); (void)test_harness; cipherSuiteCtx = CyaSSL_CTX_new(CyaTLSv1_2_client_method()); if (cipherSuiteCtx == NULL) { printf("can't get cipher suite ctx\n"); exit(EXIT_FAILURE); } /* default case */ args.argc = 1; printf("starting default cipher suite tests\n"); test_harness(&args); if (args.return_code != 0) { printf("error from script %d\n", args.return_code); exit(EXIT_FAILURE); } /* any extra cases will need another argument */ args.argc = 2; #ifdef CYASSL_DTLS /* add dtls extra suites */ strcpy(argv0[1], "tests/test-dtls.conf"); printf("starting dtls extra cipher suite tests\n"); test_harness(&args); if (args.return_code != 0) { printf("error from script %d\n", args.return_code); exit(EXIT_FAILURE); } #endif printf(" End Cipher Suite Tests\n"); CyaSSL_CTX_free(cipherSuiteCtx); return args.return_code; }