1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007 Christian Grothoff 4 5 libmicrohttpd is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 2, or (at your 8 option) any later version. 9 10 libmicrohttpd is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with libmicrohttpd; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 Boston, MA 02111-1307, USA. 19 */ 20 21 /** 22 * @file tls_test_common.c 23 * @brief Common tls test functions 24 * @author Sagie Amir 25 */ 26 #include "tls_test_common.h" 27 #include "tls_test_keys.h" 28 29 30 int curl_check_version (const char *req_version, ...); 31 32 FILE * 33 setup_ca_cert () 34 { 35 FILE *cert_fd; 36 37 if (NULL == (cert_fd = fopen (ca_cert_file_name, "wb+"))) 38 { 39 fprintf (stderr, "Error: failed to open `%s': %s\n", 40 ca_cert_file_name, strerror (errno)); 41 return NULL; 42 } 43 if (fwrite (ca_cert_pem, sizeof (char), strlen (ca_cert_pem) + 1, cert_fd) 44 != strlen (ca_cert_pem) + 1) 45 { 46 fprintf (stderr, "Error: failed to write `%s. %s'\n", 47 ca_cert_file_name, strerror (errno)); 48 fclose (cert_fd); 49 return NULL; 50 } 51 if (fflush (cert_fd)) 52 { 53 fprintf (stderr, "Error: failed to flush ca cert file stream. %s\n", 54 strerror (errno)); 55 fclose (cert_fd); 56 return NULL; 57 } 58 return cert_fd; 59 } 60 61 62 /* 63 * test HTTPS transfer 64 */ 65 int 66 test_daemon_get (void *cls, 67 const char *cipher_suite, int proto_version, 68 int port, 69 int ver_peer) 70 { 71 CURL *c; 72 struct CBC cbc; 73 CURLcode errornum; 74 char url[255]; 75 size_t len; 76 77 len = strlen (test_data); 78 if (NULL == (cbc.buf = malloc (sizeof (char) * len))) 79 { 80 fprintf (stderr, MHD_E_MEM); 81 return -1; 82 } 83 cbc.size = len; 84 cbc.pos = 0; 85 86 /* construct url - this might use doc_path */ 87 gen_test_file_url (url, port); 88 89 c = curl_easy_init (); 90 #if DEBUG_HTTPS_TEST 91 curl_easy_setopt (c, CURLOPT_VERBOSE, 1); 92 #endif 93 curl_easy_setopt (c, CURLOPT_URL, url); 94 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 95 curl_easy_setopt (c, CURLOPT_TIMEOUT, 10L); 96 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 10L); 97 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 98 curl_easy_setopt (c, CURLOPT_FILE, &cbc); 99 100 /* TLS options */ 101 curl_easy_setopt (c, CURLOPT_SSLVERSION, proto_version); 102 curl_easy_setopt (c, CURLOPT_SSL_CIPHER_LIST, cipher_suite); 103 104 /* perform peer authentication */ 105 /* TODO merge into send_curl_req */ 106 curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, ver_peer); 107 curl_easy_setopt (c, CURLOPT_CAINFO, ca_cert_file_name); 108 curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0); 109 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 110 111 /* NOTE: use of CONNECTTIMEOUT without also 112 setting NOSIGNAL results in really weird 113 crashes on my system! */ 114 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 115 if (CURLE_OK != (errornum = curl_easy_perform (c))) 116 { 117 fprintf (stderr, "curl_easy_perform failed: `%s'\n", 118 curl_easy_strerror (errornum)); 119 curl_easy_cleanup (c); 120 free (cbc.buf); 121 return errornum; 122 } 123 124 curl_easy_cleanup (c); 125 126 if (memcmp (cbc.buf, test_data, len) != 0) 127 { 128 fprintf (stderr, "Error: local file & received file differ.\n"); 129 free (cbc.buf); 130 return -1; 131 } 132 133 free (cbc.buf); 134 return 0; 135 } 136 137 138 void 139 print_test_result (int test_outcome, char *test_name) 140 { 141 #if 0 142 if (test_outcome != 0) 143 fprintf (stderr, "running test: %s [fail]\n", test_name); 144 else 145 fprintf (stdout, "running test: %s [pass]\n", test_name); 146 #endif 147 } 148 149 size_t 150 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 151 { 152 struct CBC *cbc = ctx; 153 154 if (cbc->pos + size * nmemb > cbc->size) 155 return 0; /* overflow */ 156 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 157 cbc->pos += size * nmemb; 158 return size * nmemb; 159 } 160 161 /** 162 * HTTP access handler call back 163 */ 164 int 165 http_ahc (void *cls, struct MHD_Connection *connection, 166 const char *url, const char *method, const char *upload_data, 167 const char *version, size_t *upload_data_size, void **ptr) 168 { 169 static int aptr; 170 struct MHD_Response *response; 171 int ret; 172 173 if (0 != strcmp (method, MHD_HTTP_METHOD_GET)) 174 return MHD_NO; /* unexpected method */ 175 if (&aptr != *ptr) 176 { 177 /* do never respond on first call */ 178 *ptr = &aptr; 179 return MHD_YES; 180 } 181 *ptr = NULL; /* reset when done */ 182 response = MHD_create_response_from_buffer (strlen (test_data), 183 (void *) test_data, 184 MHD_RESPMEM_PERSISTENT); 185 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 186 MHD_destroy_response (response); 187 return ret; 188 } 189 190 /* HTTP access handler call back */ 191 int 192 http_dummy_ahc (void *cls, struct MHD_Connection *connection, 193 const char *url, const char *method, const char *upload_data, 194 const char *version, size_t *upload_data_size, 195 void **ptr) 196 { 197 return 0; 198 } 199 200 /** 201 * send a test http request to the daemon 202 * @param url 203 * @param cbc - may be null 204 * @param cipher_suite 205 * @param proto_version 206 * @return 207 */ 208 /* TODO have test wrap consider a NULL cbc */ 209 int 210 send_curl_req (char *url, struct CBC * cbc, const char *cipher_suite, 211 int proto_version) 212 { 213 CURL *c; 214 CURLcode errornum; 215 c = curl_easy_init (); 216 #if DEBUG_HTTPS_TEST 217 curl_easy_setopt (c, CURLOPT_VERBOSE, CURL_VERBOS_LEVEL); 218 #endif 219 curl_easy_setopt (c, CURLOPT_URL, url); 220 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 221 curl_easy_setopt (c, CURLOPT_TIMEOUT, 60L); 222 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 60L); 223 224 if (cbc != NULL) 225 { 226 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 227 curl_easy_setopt (c, CURLOPT_FILE, cbc); 228 } 229 230 /* TLS options */ 231 curl_easy_setopt (c, CURLOPT_SSLVERSION, proto_version); 232 curl_easy_setopt (c, CURLOPT_SSL_CIPHER_LIST, cipher_suite); 233 234 /* currently skip any peer authentication */ 235 curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0); 236 curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0); 237 238 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 239 240 /* NOTE: use of CONNECTTIMEOUT without also 241 setting NOSIGNAL results in really weird 242 crashes on my system! */ 243 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 244 if (CURLE_OK != (errornum = curl_easy_perform (c))) 245 { 246 fprintf (stderr, "curl_easy_perform failed: `%s'\n", 247 curl_easy_strerror (errornum)); 248 curl_easy_cleanup (c); 249 return errornum; 250 } 251 curl_easy_cleanup (c); 252 253 return CURLE_OK; 254 } 255 256 257 /** 258 * compile test file url pointing to the current running directory path 259 * 260 * @param url - char buffer into which the url is compiled 261 * @param port port to use for the test 262 * @return -1 on error 263 */ 264 int 265 gen_test_file_url (char *url, int port) 266 { 267 int ret = 0; 268 char *doc_path; 269 size_t doc_path_len; 270 /* setup test file path, url */ 271 doc_path_len = PATH_MAX > 4096 ? 4096 : PATH_MAX; 272 if (NULL == (doc_path = malloc (doc_path_len))) 273 { 274 fprintf (stderr, MHD_E_MEM); 275 return -1; 276 } 277 if (getcwd (doc_path, doc_path_len) == NULL) 278 { 279 fprintf (stderr, "Error: failed to get working directory. %s\n", 280 strerror (errno)); 281 ret = -1; 282 } 283 #ifdef WINDOWS 284 { 285 int i; 286 for (i = 0; i < doc_path_len; i++) 287 { 288 if (doc_path[i] == 0) 289 break; 290 if (doc_path[i] == '\\') 291 { 292 doc_path[i] = '/'; 293 } 294 if (doc_path[i] != ':') 295 continue; 296 if (i == 0) 297 break; 298 doc_path[i] = doc_path[i - 1]; 299 doc_path[i - 1] = '/'; 300 } 301 } 302 #endif 303 /* construct url - this might use doc_path */ 304 if (sprintf (url, "%s:%d%s/%s", "https://127.0.0.1", port, 305 doc_path, "urlpath") < 0) 306 ret = -1; 307 308 free (doc_path); 309 return ret; 310 } 311 312 /** 313 * test HTTPS file transfer 314 */ 315 int 316 test_https_transfer (void *cls, const char *cipher_suite, int proto_version) 317 { 318 int len; 319 int ret = 0; 320 struct CBC cbc; 321 char url[255]; 322 323 len = strlen (test_data); 324 if (NULL == (cbc.buf = malloc (sizeof (char) * len))) 325 { 326 fprintf (stderr, MHD_E_MEM); 327 return -1; 328 } 329 cbc.size = len; 330 cbc.pos = 0; 331 332 if (gen_test_file_url (url, DEAMON_TEST_PORT)) 333 { 334 ret = -1; 335 goto cleanup; 336 } 337 338 if (CURLE_OK != send_curl_req (url, &cbc, cipher_suite, proto_version)) 339 { 340 ret = -1; 341 goto cleanup; 342 } 343 344 /* compare test file & daemon responce */ 345 if ( (len != strlen (test_data)) || 346 (memcmp (cbc.buf, 347 test_data, 348 len) != 0) ) 349 { 350 fprintf (stderr, "Error: local file & received file differ.\n"); 351 ret = -1; 352 } 353 cleanup: 354 free (cbc.buf); 355 return ret; 356 } 357 358 /** 359 * setup test case 360 * 361 * @param d 362 * @param daemon_flags 363 * @param arg_list 364 * @return 365 */ 366 int 367 setup_testcase (struct MHD_Daemon **d, int daemon_flags, va_list arg_list) 368 { 369 *d = MHD_start_daemon_va (daemon_flags, DEAMON_TEST_PORT, 370 NULL, NULL, &http_ahc, NULL, arg_list); 371 372 if (*d == NULL) 373 { 374 fprintf (stderr, MHD_E_SERVER_INIT); 375 return -1; 376 } 377 378 return 0; 379 } 380 381 void 382 teardown_testcase (struct MHD_Daemon *d) 383 { 384 MHD_stop_daemon (d); 385 } 386 387 int 388 setup_session (gnutls_session_t * session, 389 gnutls_datum_t * key, 390 gnutls_datum_t * cert, 391 gnutls_certificate_credentials_t * xcred) 392 { 393 int ret; 394 const char *err_pos; 395 396 gnutls_certificate_allocate_credentials (xcred); 397 key->size = strlen (srv_key_pem) + 1; 398 key->data = malloc (key->size); 399 if (NULL == key->data) 400 { 401 gnutls_certificate_free_credentials (*xcred); 402 return -1; 403 } 404 memcpy (key->data, srv_key_pem, key->size); 405 cert->size = strlen (srv_self_signed_cert_pem) + 1; 406 cert->data = malloc (cert->size); 407 if (NULL == cert->data) 408 { 409 gnutls_certificate_free_credentials (*xcred); 410 free (key->data); 411 return -1; 412 } 413 memcpy (cert->data, srv_self_signed_cert_pem, cert->size); 414 gnutls_certificate_set_x509_key_mem (*xcred, cert, key, 415 GNUTLS_X509_FMT_PEM); 416 gnutls_init (session, GNUTLS_CLIENT); 417 ret = gnutls_priority_set_direct (*session, 418 "NORMAL", &err_pos); 419 if (ret < 0) 420 { 421 gnutls_deinit (*session); 422 gnutls_certificate_free_credentials (*xcred); 423 free (key->data); 424 return -1; 425 } 426 gnutls_credentials_set (*session, 427 GNUTLS_CRD_CERTIFICATE, 428 *xcred); 429 return 0; 430 } 431 432 int 433 teardown_session (gnutls_session_t session, 434 gnutls_datum_t * key, 435 gnutls_datum_t * cert, 436 gnutls_certificate_credentials_t xcred) 437 { 438 free (key->data); 439 key->data = NULL; 440 key->size = 0; 441 free (cert->data); 442 cert->data = NULL; 443 cert->size = 0; 444 gnutls_deinit (session); 445 gnutls_certificate_free_credentials (xcred); 446 return 0; 447 } 448 449 /* TODO test_wrap: change sig to (setup_func, test, va_list test_arg) */ 450 int 451 test_wrap (const char *test_name, int 452 (*test_function) (void * cls, const char *cipher_suite, 453 int proto_version), void * cls, 454 int daemon_flags, const char *cipher_suite, int proto_version, ...) 455 { 456 int ret; 457 va_list arg_list; 458 struct MHD_Daemon *d; 459 460 va_start (arg_list, proto_version); 461 if (setup_testcase (&d, daemon_flags, arg_list) != 0) 462 { 463 va_end (arg_list); 464 fprintf (stderr, "Failed to setup testcase %s\n", test_name); 465 return -1; 466 } 467 #if 0 468 fprintf (stdout, "running test: %s ", test_name); 469 #endif 470 ret = test_function (NULL, cipher_suite, proto_version); 471 #if 0 472 if (ret == 0) 473 { 474 fprintf (stdout, "[pass]\n"); 475 } 476 else 477 { 478 fprintf (stdout, "[fail]\n"); 479 } 480 #endif 481 teardown_testcase (d); 482 va_end (arg_list); 483 return ret; 484 } 485