httpserv.c (45886B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include <stdio.h> 6 #include <string.h> 7 8 #include "secutil.h" 9 10 #if defined(XP_UNIX) 11 #include <unistd.h> 12 #endif 13 14 #if defined(_WINDOWS) 15 #include <process.h> /* for getpid() */ 16 #endif 17 18 #include <signal.h> 19 #include <stdlib.h> 20 #include <errno.h> 21 #include <fcntl.h> 22 #include <stdarg.h> 23 24 #include "nspr.h" 25 #include "prio.h" 26 #include "prerror.h" 27 #include "prnetdb.h" 28 #include "prclist.h" 29 #include "plgetopt.h" 30 #include "pk11func.h" 31 #include "nss.h" 32 #include "nssb64.h" 33 #include "sechash.h" 34 #include "cert.h" 35 #include "certdb.h" 36 #include "ocsp.h" 37 #include "ocspti.h" 38 #include "ocspi.h" 39 40 #ifndef PORT_Strstr 41 #define PORT_Strstr strstr 42 #endif 43 44 #ifndef PORT_Malloc 45 #define PORT_Malloc PR_Malloc 46 #endif 47 48 static int handle_connection(PRFileDesc *, PRFileDesc *, int); 49 50 /* data and structures for shutdown */ 51 static int stopping; 52 53 static PRBool noDelay; 54 static int verbose; 55 56 static PRThread *acceptorThread; 57 58 static PRLogModuleInfo *lm; 59 60 #define PRINTF \ 61 if (verbose) \ 62 printf 63 #define FPRINTF \ 64 if (verbose) \ 65 fprintf 66 #define FLUSH \ 67 if (verbose) { \ 68 fflush(stdout); \ 69 fflush(stderr); \ 70 } 71 #define VLOG(arg) PR_LOG(lm, PR_LOG_DEBUG, arg) 72 73 static void 74 Usage(const char *progName) 75 { 76 fprintf(stderr, 77 78 "Usage: %s -p port [-Dbv]\n" 79 " [-t threads] [-i pid_file]\n" 80 " [-A nickname -C crl-filename]... [-O method]\n" 81 " [-d dbdir] [-f password_file] [-w password] [-P dbprefix]\n" 82 "-D means disable Nagle delays in TCP\n" 83 "-b means try binding to the port and exit\n" 84 "-v means verbose output\n" 85 "-t threads -- specify the number of threads to use for connections.\n" 86 "-i pid_file file to write the process id of httpserv\n" 87 "Parameters -A, -C and -O are used to provide an OCSP server at /ocsp?\n" 88 "-A a nickname of a CA certificate\n" 89 "-C a CRL filename corresponding to the preceding CA nickname\n" 90 "-O allowed HTTP methods for OCSP requests: get, post, all, random, get-unknown\n" 91 " random means: randomly fail if request method is GET, POST always works\n" 92 " get-unknown means: status unknown for GET, correct status for POST\n" 93 "Multiple pairs of parameters -A and -C are allowed.\n" 94 "If status for a cert from an unknown CA is requested, the cert from the\n" 95 "first -A parameter will be used to sign the unknown status response.\n" 96 "NSS database parameters are used only if OCSP parameters are used.\n", 97 progName); 98 } 99 100 static const char * 101 errWarn(char *funcString) 102 { 103 PRErrorCode perr = PR_GetError(); 104 const char *errString = SECU_Strerror(perr); 105 106 fprintf(stderr, "httpserv: %s returned error %d:\n%s\n", 107 funcString, perr, errString); 108 return errString; 109 } 110 111 static void 112 errExit(char *funcString) 113 { 114 errWarn(funcString); 115 exit(3); 116 } 117 118 #define MAX_VIRT_SERVER_NAME_ARRAY_INDEX 10 119 120 /************************************************************************** 121 ** Begin thread management routines and data. 122 **************************************************************************/ 123 #define MIN_THREADS 3 124 #define DEFAULT_THREADS 8 125 #define MAX_THREADS 4096 126 #define MAX_PROCS 25 127 static int maxThreads = DEFAULT_THREADS; 128 129 typedef struct jobStr { 130 PRCList link; 131 PRFileDesc *tcp_sock; 132 PRFileDesc *model_sock; 133 int requestCert; 134 } JOB; 135 136 static PZLock *qLock; /* this lock protects all data immediately below */ 137 static PRLock *lastLoadedCrlLock; /* this lock protects lastLoadedCrl variable */ 138 static PZCondVar *jobQNotEmptyCv; 139 static PZCondVar *freeListNotEmptyCv; 140 static PZCondVar *threadCountChangeCv; 141 static int threadCount; 142 static PRCList jobQ; 143 static PRCList freeJobs; 144 static JOB *jobTable; 145 146 SECStatus 147 setupJobs(int maxJobs) 148 { 149 int i; 150 151 jobTable = (JOB *)PR_Calloc(maxJobs, sizeof(JOB)); 152 if (!jobTable) 153 return SECFailure; 154 155 PR_INIT_CLIST(&jobQ); 156 PR_INIT_CLIST(&freeJobs); 157 158 for (i = 0; i < maxJobs; ++i) { 159 JOB *pJob = jobTable + i; 160 PR_APPEND_LINK(&pJob->link, &freeJobs); 161 } 162 return SECSuccess; 163 } 164 165 typedef int startFn(PRFileDesc *a, PRFileDesc *b, int c); 166 167 typedef enum { rs_idle = 0, 168 rs_running = 1, 169 rs_zombie = 2 } runState; 170 171 typedef struct perThreadStr { 172 PRFileDesc *a; 173 PRFileDesc *b; 174 int c; 175 int rv; 176 startFn *startFunc; 177 PRThread *prThread; 178 runState state; 179 } perThread; 180 181 static perThread *threads; 182 183 void 184 thread_wrapper(void *arg) 185 { 186 perThread *slot = (perThread *)arg; 187 188 slot->rv = (*slot->startFunc)(slot->a, slot->b, slot->c); 189 190 /* notify the thread exit handler. */ 191 PZ_Lock(qLock); 192 slot->state = rs_zombie; 193 --threadCount; 194 PZ_NotifyAllCondVar(threadCountChangeCv); 195 PZ_Unlock(qLock); 196 } 197 198 int 199 jobLoop(PRFileDesc *a, PRFileDesc *b, int c) 200 { 201 PRCList *myLink = 0; 202 JOB *myJob; 203 204 PZ_Lock(qLock); 205 do { 206 myLink = 0; 207 while (PR_CLIST_IS_EMPTY(&jobQ) && !stopping) { 208 PZ_WaitCondVar(jobQNotEmptyCv, PR_INTERVAL_NO_TIMEOUT); 209 } 210 if (!PR_CLIST_IS_EMPTY(&jobQ)) { 211 myLink = PR_LIST_HEAD(&jobQ); 212 PR_REMOVE_AND_INIT_LINK(myLink); 213 } 214 PZ_Unlock(qLock); 215 myJob = (JOB *)myLink; 216 /* myJob will be null when stopping is true and jobQ is empty */ 217 if (!myJob) 218 break; 219 handle_connection(myJob->tcp_sock, myJob->model_sock, 220 myJob->requestCert); 221 PZ_Lock(qLock); 222 PR_APPEND_LINK(myLink, &freeJobs); 223 PZ_NotifyCondVar(freeListNotEmptyCv); 224 } while (PR_TRUE); 225 return 0; 226 } 227 228 SECStatus 229 launch_threads( 230 startFn *startFunc, 231 PRFileDesc *a, 232 PRFileDesc *b, 233 int c, 234 PRBool local) 235 { 236 int i; 237 SECStatus rv = SECSuccess; 238 239 /* create the thread management serialization structs */ 240 qLock = PZ_NewLock(nssILockSelfServ); 241 jobQNotEmptyCv = PZ_NewCondVar(qLock); 242 freeListNotEmptyCv = PZ_NewCondVar(qLock); 243 threadCountChangeCv = PZ_NewCondVar(qLock); 244 245 /* create monitor for crl reload procedure */ 246 lastLoadedCrlLock = PR_NewLock(); 247 248 /* allocate the array of thread slots */ 249 threads = PR_Calloc(maxThreads, sizeof(perThread)); 250 if (NULL == threads) { 251 fprintf(stderr, "Oh Drat! Can't allocate the perThread array\n"); 252 return SECFailure; 253 } 254 /* 5 is a little extra, intended to keep the jobQ from underflowing. 255 ** That is, from going empty while not stopping and clients are still 256 ** trying to contact us. 257 */ 258 rv = setupJobs(maxThreads + 5); 259 if (rv != SECSuccess) 260 return rv; 261 262 PZ_Lock(qLock); 263 for (i = 0; i < maxThreads; ++i) { 264 perThread *slot = threads + i; 265 266 slot->state = rs_running; 267 slot->a = a; 268 slot->b = b; 269 slot->c = c; 270 slot->startFunc = startFunc; 271 slot->prThread = PR_CreateThread(PR_USER_THREAD, 272 thread_wrapper, slot, PR_PRIORITY_NORMAL, 273 (PR_TRUE == 274 local) 275 ? PR_LOCAL_THREAD 276 : PR_GLOBAL_THREAD, 277 PR_JOINABLE_THREAD, 0); 278 if (slot->prThread == NULL) { 279 printf("httpserv: Failed to launch thread!\n"); 280 slot->state = rs_idle; 281 rv = SECFailure; 282 break; 283 } 284 285 ++threadCount; 286 } 287 PZ_Unlock(qLock); 288 289 return rv; 290 } 291 292 #define DESTROY_CONDVAR(name) \ 293 if (name) { \ 294 PZ_DestroyCondVar(name); \ 295 name = NULL; \ 296 } 297 #define DESTROY_LOCK(name) \ 298 if (name) { \ 299 PZ_DestroyLock(name); \ 300 name = NULL; \ 301 } 302 303 void 304 terminateWorkerThreads(void) 305 { 306 int i; 307 308 VLOG(("httpserv: server_thread: waiting on stopping")); 309 PZ_Lock(qLock); 310 PZ_NotifyAllCondVar(jobQNotEmptyCv); 311 PZ_Unlock(qLock); 312 313 /* Wait for worker threads to terminate. */ 314 for (i = 0; i < maxThreads; ++i) { 315 perThread *slot = threads + i; 316 if (slot->prThread) { 317 PR_JoinThread(slot->prThread); 318 } 319 } 320 321 /* The worker threads empty the jobQ before they terminate. */ 322 PZ_Lock(qLock); 323 PORT_Assert(threadCount == 0); 324 PORT_Assert(PR_CLIST_IS_EMPTY(&jobQ)); 325 PZ_Unlock(qLock); 326 327 DESTROY_CONDVAR(jobQNotEmptyCv); 328 DESTROY_CONDVAR(freeListNotEmptyCv); 329 DESTROY_CONDVAR(threadCountChangeCv); 330 331 PR_DestroyLock(lastLoadedCrlLock); 332 DESTROY_LOCK(qLock); 333 PR_Free(jobTable); 334 PR_Free(threads); 335 } 336 337 /************************************************************************** 338 ** End thread management routines. 339 **************************************************************************/ 340 341 PRBool NoReuse = PR_FALSE; 342 PRBool disableLocking = PR_FALSE; 343 static secuPWData pwdata = { PW_NONE, 0 }; 344 345 struct caRevoInfoStr { 346 PRCList link; 347 char *nickname; 348 char *crlFilename; 349 CERTCertificate *cert; 350 CERTOCSPCertID *id; 351 CERTSignedCrl *crl; 352 }; 353 typedef struct caRevoInfoStr caRevoInfo; 354 /* Created during app init. No locks necessary, 355 * because later on, only read access will occur. */ 356 static caRevoInfo *caRevoInfos = NULL; 357 358 static enum { 359 ocspGetOnly, 360 ocspPostOnly, 361 ocspGetAndPost, 362 ocspRandomGetFailure, 363 ocspGetUnknown 364 } ocspMethodsAllowed = ocspGetAndPost; 365 366 static const char stopCmd[] = { "GET /stop " }; 367 static const char getCmd[] = { "GET " }; 368 static const char outHeader[] = { 369 "HTTP/1.0 200 OK\r\n" 370 "Server: Generic Web Server\r\n" 371 "Date: Tue, 26 Aug 1997 22:10:05 GMT\r\n" 372 "Content-type: text/plain\r\n" 373 "\r\n" 374 }; 375 static const char outOcspHeader[] = { 376 "HTTP/1.0 200 OK\r\n" 377 "Server: Generic OCSP Server\r\n" 378 "Content-type: application/ocsp-response\r\n" 379 "\r\n" 380 }; 381 static const char outBadRequestHeader[] = { 382 "HTTP/1.0 400 Bad Request\r\n" 383 "Server: Generic OCSP Server\r\n" 384 "\r\n" 385 }; 386 387 void 388 stop_server() 389 { 390 stopping = 1; 391 PR_Interrupt(acceptorThread); 392 PZ_TraceFlush(); 393 } 394 395 /* Will only work if the original input to url encoding was 396 * a base64 encoded buffer. Will only decode the sequences used 397 * for encoding the special base64 characters, and fail if any 398 * other encoded chars are found. 399 * Will return SECSuccess if input could be processed. 400 * Coversion is done in place. 401 */ 402 static SECStatus 403 urldecode_base64chars_inplace(char *buf) 404 { 405 char *walk; 406 size_t remaining_bytes; 407 408 if (!buf || !*buf) 409 return SECFailure; 410 411 walk = buf; 412 remaining_bytes = strlen(buf) + 1; /* include terminator */ 413 414 while (*walk) { 415 if (*walk == '%') { 416 if (!PL_strncasecmp(walk, "%2B", 3)) { 417 *walk = '+'; 418 } else if (!PL_strncasecmp(walk, "%2F", 3)) { 419 *walk = '/'; 420 } else if (!PL_strncasecmp(walk, "%3D", 3)) { 421 *walk = '='; 422 } else { 423 return SECFailure; 424 } 425 remaining_bytes -= 3; 426 ++walk; 427 memmove(walk, walk + 2, remaining_bytes); 428 } else { 429 ++walk; 430 --remaining_bytes; 431 } 432 } 433 return SECSuccess; 434 } 435 436 int 437 handle_connection( 438 PRFileDesc *tcp_sock, 439 PRFileDesc *model_sock, 440 int requestCert) 441 { 442 PRFileDesc *ssl_sock = NULL; 443 PRFileDesc *local_file_fd = NULL; 444 char *pBuf; /* unused space at end of buf */ 445 const char *errString; 446 PRStatus status; 447 int bufRem; /* unused bytes at end of buf */ 448 int bufDat; /* characters received in buf */ 449 int newln = 0; /* # of consecutive newlns */ 450 int firstTime = 1; 451 int reqLen; 452 int rv; 453 int numIOVs; 454 PRSocketOptionData opt; 455 PRIOVec iovs[16]; 456 char msgBuf[160]; 457 char buf[10240]; 458 char fileName[513]; 459 char *getData = NULL; /* inplace conversion */ 460 SECItem postData; 461 PRBool isOcspRequest = PR_FALSE; 462 PRBool isPost = PR_FALSE; 463 464 postData.data = NULL; 465 postData.len = 0; 466 467 pBuf = buf; 468 bufRem = sizeof buf; 469 470 VLOG(("httpserv: handle_connection: starting")); 471 opt.option = PR_SockOpt_Nonblocking; 472 opt.value.non_blocking = PR_FALSE; 473 PR_SetSocketOption(tcp_sock, &opt); 474 475 VLOG(("httpserv: handle_connection: starting\n")); 476 ssl_sock = tcp_sock; 477 478 if (noDelay) { 479 opt.option = PR_SockOpt_NoDelay; 480 opt.value.no_delay = PR_TRUE; 481 status = PR_SetSocketOption(ssl_sock, &opt); 482 if (status != PR_SUCCESS) { 483 errWarn("PR_SetSocketOption(PR_SockOpt_NoDelay, PR_TRUE)"); 484 if (ssl_sock) { 485 PR_Close(ssl_sock); 486 } 487 return SECFailure; 488 } 489 } 490 491 while (1) { 492 const char *post; 493 const char *foundStr = NULL; 494 const char *tmp = NULL; 495 496 newln = 0; 497 reqLen = 0; 498 499 rv = PR_Read(ssl_sock, pBuf, bufRem - 1); 500 if (rv == 0 || 501 (rv < 0 && PR_END_OF_FILE_ERROR == PR_GetError())) { 502 if (verbose) 503 errWarn("HDX PR_Read hit EOF"); 504 break; 505 } 506 if (rv < 0) { 507 errWarn("HDX PR_Read"); 508 goto cleanup; 509 } 510 /* NULL termination */ 511 pBuf[rv] = 0; 512 if (firstTime) { 513 firstTime = 0; 514 } 515 516 pBuf += rv; 517 bufRem -= rv; 518 bufDat = pBuf - buf; 519 /* Parse the input, starting at the beginning of the buffer. 520 * Stop when we detect two consecutive \n's (or \r\n's) 521 * as this signifies the end of the GET or POST portion. 522 * The posted data follows. 523 */ 524 while (reqLen < bufDat && newln < 2) { 525 int octet = buf[reqLen++]; 526 if (octet == '\n') { 527 newln++; 528 } else if (octet != '\r') { 529 newln = 0; 530 } 531 } 532 533 /* came to the end of the buffer, or second newln 534 * If we didn't get an empty line (CRLFCRLF) then keep on reading. 535 */ 536 if (newln < 2) 537 continue; 538 539 /* we're at the end of the HTTP request. 540 * If the request is a POST, then there will be one more 541 * line of data. 542 * This parsing is a hack, but ok for SSL test purposes. 543 */ 544 post = PORT_Strstr(buf, "POST "); 545 if (!post || *post != 'P') 546 break; 547 548 postData.data = (void *)(buf + reqLen); 549 550 tmp = "content-length: "; 551 foundStr = PL_strcasestr(buf, tmp); 552 if (foundStr) { 553 int expectedPostLen; 554 int havePostLen; 555 556 expectedPostLen = atoi(foundStr + strlen(tmp)); 557 havePostLen = bufDat - reqLen; 558 if (havePostLen >= expectedPostLen) { 559 postData.len = expectedPostLen; 560 break; 561 } 562 } else { 563 /* use legacy hack */ 564 /* It's a post, so look for the next and final CR/LF. */ 565 while (reqLen < bufDat && newln < 3) { 566 int octet = buf[reqLen++]; 567 if (octet == '\n') { 568 newln++; 569 } 570 } 571 if (newln == 3) 572 break; 573 } 574 } /* read loop */ 575 576 bufDat = pBuf - buf; 577 if (bufDat) 578 do { /* just close if no data */ 579 /* Have either (a) a complete get, (b) a complete post, (c) EOF */ 580 if (reqLen > 0) { 581 PRBool isGetOrPost = PR_FALSE; 582 unsigned skipChars = 0; 583 isPost = PR_FALSE; 584 585 if (!strncmp(buf, getCmd, sizeof getCmd - 1)) { 586 isGetOrPost = PR_TRUE; 587 skipChars = 4; 588 } else if (!strncmp(buf, "POST ", 5)) { 589 isGetOrPost = PR_TRUE; 590 isPost = PR_TRUE; 591 skipChars = 5; 592 } 593 594 if (isGetOrPost) { 595 char *fnBegin = buf; 596 char *fnEnd; 597 char *fnstart = NULL; 598 PRFileInfo info; 599 600 fnBegin += skipChars; 601 602 fnEnd = strpbrk(fnBegin, " \r\n"); 603 if (fnEnd) { 604 int fnLen = fnEnd - fnBegin; 605 if (fnLen < sizeof fileName) { 606 strncpy(fileName, fnBegin, fnLen); 607 fileName[fnLen] = 0; /* null terminate */ 608 fnstart = fileName; 609 /* strip initial / because our root is the current directory*/ 610 while (*fnstart && *fnstart == '/') 611 ++fnstart; 612 } 613 } 614 if (fnstart) { 615 if (!strncmp(fnstart, "ocsp", 4)) { 616 if (isPost) { 617 if (postData.data) { 618 isOcspRequest = PR_TRUE; 619 } 620 } else { 621 if (!strncmp(fnstart, "ocsp/", 5)) { 622 isOcspRequest = PR_TRUE; 623 getData = fnstart + 5; 624 } 625 } 626 } else { 627 /* try to open the file named. 628 * If successful, then write it to the client. 629 */ 630 status = PR_GetFileInfo(fnstart, &info); 631 if (status == PR_SUCCESS && 632 info.type == PR_FILE_FILE && 633 info.size >= 0) { 634 local_file_fd = PR_Open(fnstart, PR_RDONLY, 0); 635 } 636 } 637 } 638 } 639 } 640 641 numIOVs = 0; 642 643 iovs[numIOVs].iov_base = (char *)outHeader; 644 iovs[numIOVs].iov_len = (sizeof(outHeader)) - 1; 645 numIOVs++; 646 647 if (isOcspRequest && caRevoInfos) { 648 CERTOCSPRequest *request = NULL; 649 PRBool failThisRequest = PR_FALSE; 650 PLArenaPool *arena = NULL; 651 652 if (ocspMethodsAllowed == ocspGetOnly && postData.len) { 653 failThisRequest = PR_TRUE; 654 } else if (ocspMethodsAllowed == ocspPostOnly && getData) { 655 failThisRequest = PR_TRUE; 656 } else if (ocspMethodsAllowed == ocspRandomGetFailure && getData) { 657 if (!(rand() % 2)) { 658 failThisRequest = PR_TRUE; 659 } 660 } 661 662 if (failThisRequest) { 663 PR_Write(ssl_sock, outBadRequestHeader, strlen(outBadRequestHeader)); 664 break; 665 } 666 /* get is base64, post is binary. 667 * If we have base64, convert into the (empty) postData array. 668 */ 669 if (getData) { 670 if (urldecode_base64chars_inplace(getData) == SECSuccess) { 671 /* The code below can handle a NULL arena */ 672 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); 673 NSSBase64_DecodeBuffer(arena, &postData, getData, strlen(getData)); 674 } 675 } 676 if (postData.len) { 677 request = CERT_DecodeOCSPRequest(&postData); 678 } 679 if (arena) { 680 PORT_FreeArena(arena, PR_FALSE); 681 arena = NULL; 682 } 683 if (!request || !request->tbsRequest || 684 !request->tbsRequest->requestList || 685 !request->tbsRequest->requestList[0]) { 686 snprintf(msgBuf, sizeof(msgBuf), "Cannot decode OCSP request.\r\n"); 687 688 iovs[numIOVs].iov_base = msgBuf; 689 iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); 690 numIOVs++; 691 } else { 692 /* TODO: support more than one request entry */ 693 CERTOCSPCertID *reqid = request->tbsRequest->requestList[0]->reqCert; 694 const caRevoInfo *revoInfo = NULL; 695 PRBool unknown = PR_FALSE; 696 PRBool revoked = PR_FALSE; 697 PRTime nextUpdate = 0; 698 PRTime revoDate = 0; 699 PRCList *caRevoIter; 700 701 caRevoIter = &caRevoInfos->link; 702 do { 703 CERTOCSPCertID *caid; 704 705 revoInfo = (caRevoInfo *)caRevoIter; 706 caid = revoInfo->id; 707 708 if (SECOID_CompareAlgorithmID(&reqid->hashAlgorithm, 709 &caid->hashAlgorithm) == SECEqual && 710 SECITEM_CompareItem(&reqid->issuerNameHash, 711 &caid->issuerNameHash) == SECEqual && 712 SECITEM_CompareItem(&reqid->issuerKeyHash, 713 &caid->issuerKeyHash) == SECEqual) { 714 break; 715 } 716 revoInfo = NULL; 717 caRevoIter = PR_NEXT_LINK(caRevoIter); 718 } while (caRevoIter != &caRevoInfos->link); 719 720 if (!revoInfo) { 721 unknown = PR_TRUE; 722 revoInfo = caRevoInfos; 723 } else { 724 CERTCrl *crl = &revoInfo->crl->crl; 725 CERTCrlEntry *entry = NULL; 726 DER_DecodeTimeChoice(&nextUpdate, &crl->nextUpdate); 727 if (crl->entries) { 728 int iv = 0; 729 /* assign, not compare */ 730 while ((entry = crl->entries[iv++])) { 731 if (SECITEM_CompareItem(&reqid->serialNumber, 732 &entry->serialNumber) == SECEqual) { 733 break; 734 } 735 } 736 } 737 if (entry) { 738 /* revoked status response */ 739 revoked = PR_TRUE; 740 DER_DecodeTimeChoice(&revoDate, &entry->revocationDate); 741 } else { 742 /* else good status response */ 743 if (!isPost && ocspMethodsAllowed == ocspGetUnknown) { 744 unknown = PR_TRUE; 745 nextUpdate = PR_Now() + (PRTime)60 * 60 * 24 * PR_USEC_PER_SEC; /*tomorrow*/ 746 revoDate = PR_Now() - (PRTime)60 * 60 * 24 * PR_USEC_PER_SEC; /*yesterday*/ 747 } 748 } 749 } 750 751 { 752 PRTime now = PR_Now(); 753 CERTOCSPSingleResponse *sr; 754 CERTOCSPSingleResponse **singleResponses; 755 SECItem *ocspResponse; 756 757 PORT_Assert(!arena); 758 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); 759 760 if (unknown) { 761 sr = CERT_CreateOCSPSingleResponseUnknown(arena, reqid, now, 762 &nextUpdate); 763 } else if (revoked) { 764 sr = CERT_CreateOCSPSingleResponseRevoked(arena, reqid, now, 765 &nextUpdate, revoDate, NULL); 766 } else { 767 sr = CERT_CreateOCSPSingleResponseGood(arena, reqid, now, 768 &nextUpdate); 769 } 770 771 /* meaning of value 2: one entry + one end marker */ 772 singleResponses = PORT_ArenaNewArray(arena, CERTOCSPSingleResponse *, 2); 773 singleResponses[0] = sr; 774 singleResponses[1] = NULL; 775 ocspResponse = CERT_CreateEncodedOCSPSuccessResponse(arena, 776 revoInfo->cert, ocspResponderID_byName, now, 777 singleResponses, &pwdata); 778 779 if (!ocspResponse) { 780 snprintf(msgBuf, sizeof(msgBuf), "Failed to encode response\r\n"); 781 iovs[numIOVs].iov_base = msgBuf; 782 iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); 783 numIOVs++; 784 } else { 785 PR_Write(ssl_sock, outOcspHeader, strlen(outOcspHeader)); 786 PR_Write(ssl_sock, ocspResponse->data, ocspResponse->len); 787 } 788 PORT_FreeArena(arena, PR_FALSE); 789 } 790 CERT_DestroyOCSPRequest(request); 791 break; 792 } 793 } else if (local_file_fd) { 794 PRInt32 bytes; 795 int errLen; 796 bytes = PR_TransmitFile(ssl_sock, local_file_fd, outHeader, 797 sizeof outHeader - 1, 798 PR_TRANSMITFILE_KEEP_OPEN, 799 PR_INTERVAL_NO_TIMEOUT); 800 if (bytes >= 0) { 801 bytes -= sizeof outHeader - 1; 802 FPRINTF(stderr, 803 "httpserv: PR_TransmitFile wrote %d bytes from %s\n", 804 bytes, fileName); 805 break; 806 } 807 errString = errWarn("PR_TransmitFile"); 808 errLen = PORT_Strlen(errString); 809 errLen = PR_MIN(errLen, sizeof msgBuf - 1); 810 PORT_Memcpy(msgBuf, errString, errLen); 811 msgBuf[errLen] = 0; 812 813 iovs[numIOVs].iov_base = msgBuf; 814 iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); 815 numIOVs++; 816 } else if (reqLen <= 0) { /* hit eof */ 817 snprintf(msgBuf, sizeof(msgBuf), "Get or Post incomplete after %d bytes.\r\n", 818 bufDat); 819 820 iovs[numIOVs].iov_base = msgBuf; 821 iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); 822 numIOVs++; 823 } else if (reqLen < bufDat) { 824 snprintf(msgBuf, sizeof(msgBuf), "Discarded %d characters.\r\n", 825 bufDat - reqLen); 826 827 iovs[numIOVs].iov_base = msgBuf; 828 iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); 829 numIOVs++; 830 } 831 832 if (reqLen > 0) { 833 if (verbose > 1) 834 fwrite(buf, 1, reqLen, stdout); /* display it */ 835 836 iovs[numIOVs].iov_base = buf; 837 iovs[numIOVs].iov_len = reqLen; 838 numIOVs++; 839 } 840 841 rv = PR_Writev(ssl_sock, iovs, numIOVs, PR_INTERVAL_NO_TIMEOUT); 842 if (rv < 0) { 843 errWarn("PR_Writev"); 844 break; 845 } 846 847 } while (0); 848 849 cleanup: 850 if (ssl_sock) { 851 PR_Close(ssl_sock); 852 } else if (tcp_sock) { 853 PR_Close(tcp_sock); 854 } 855 if (local_file_fd) 856 PR_Close(local_file_fd); 857 VLOG(("httpserv: handle_connection: exiting\n")); 858 859 /* do a nice shutdown if asked. */ 860 if (!strncmp(buf, stopCmd, sizeof stopCmd - 1)) { 861 VLOG(("httpserv: handle_connection: stop command")); 862 stop_server(); 863 } 864 VLOG(("httpserv: handle_connection: exiting")); 865 return SECSuccess; /* success */ 866 } 867 868 #ifdef XP_UNIX 869 870 void 871 sigusr1_handler(int sig) 872 { 873 VLOG(("httpserv: sigusr1_handler: stop server")); 874 stop_server(); 875 } 876 877 #endif 878 879 SECStatus 880 do_accepts( 881 PRFileDesc *listen_sock, 882 PRFileDesc *model_sock, 883 int requestCert) 884 { 885 PRNetAddr addr; 886 PRErrorCode perr; 887 #ifdef XP_UNIX 888 struct sigaction act; 889 #endif 890 891 VLOG(("httpserv: do_accepts: starting")); 892 PR_SetThreadPriority(PR_GetCurrentThread(), PR_PRIORITY_HIGH); 893 894 acceptorThread = PR_GetCurrentThread(); 895 #ifdef XP_UNIX 896 /* set up the signal handler */ 897 act.sa_handler = sigusr1_handler; 898 sigemptyset(&act.sa_mask); 899 act.sa_flags = 0; 900 if (sigaction(SIGUSR1, &act, NULL)) { 901 fprintf(stderr, "Error installing signal handler.\n"); 902 exit(1); 903 } 904 #endif 905 while (!stopping) { 906 PRFileDesc *tcp_sock; 907 PRCList *myLink; 908 909 FPRINTF(stderr, "\n\n\nhttpserv: About to call accept.\n"); 910 tcp_sock = PR_Accept(listen_sock, &addr, PR_INTERVAL_NO_TIMEOUT); 911 if (tcp_sock == NULL) { 912 perr = PR_GetError(); 913 if ((perr != PR_CONNECT_RESET_ERROR && 914 perr != PR_PENDING_INTERRUPT_ERROR) || 915 verbose) { 916 errWarn("PR_Accept"); 917 } 918 if (perr == PR_CONNECT_RESET_ERROR) { 919 FPRINTF(stderr, 920 "Ignoring PR_CONNECT_RESET_ERROR error - continue\n"); 921 continue; 922 } 923 stopping = 1; 924 break; 925 } 926 927 VLOG(("httpserv: do_accept: Got connection\n")); 928 929 PZ_Lock(qLock); 930 while (PR_CLIST_IS_EMPTY(&freeJobs) && !stopping) { 931 PZ_WaitCondVar(freeListNotEmptyCv, PR_INTERVAL_NO_TIMEOUT); 932 } 933 if (stopping) { 934 PZ_Unlock(qLock); 935 if (tcp_sock) { 936 PR_Close(tcp_sock); 937 } 938 break; 939 } 940 myLink = PR_LIST_HEAD(&freeJobs); 941 PR_REMOVE_AND_INIT_LINK(myLink); 942 /* could release qLock here and reaquire it 7 lines below, but 943 ** why bother for 4 assignment statements? 944 */ 945 { 946 JOB *myJob = (JOB *)myLink; 947 myJob->tcp_sock = tcp_sock; 948 myJob->model_sock = model_sock; 949 myJob->requestCert = requestCert; 950 } 951 952 PR_APPEND_LINK(myLink, &jobQ); 953 PZ_NotifyCondVar(jobQNotEmptyCv); 954 PZ_Unlock(qLock); 955 } 956 957 FPRINTF(stderr, "httpserv: Closing listen socket.\n"); 958 VLOG(("httpserv: do_accepts: exiting")); 959 if (listen_sock) { 960 PR_Close(listen_sock); 961 } 962 return SECSuccess; 963 } 964 965 PRFileDesc * 966 getBoundListenSocket(unsigned short port) 967 { 968 PRFileDesc *listen_sock; 969 int listenQueueDepth = 5 + (2 * maxThreads); 970 PRStatus prStatus; 971 PRNetAddr addr; 972 PRSocketOptionData opt; 973 974 addr.inet.family = PR_AF_INET; 975 addr.inet.ip = PR_htonl(PR_INADDR_ANY); 976 addr.inet.port = PR_htons(port); 977 978 listen_sock = PR_NewTCPSocket(); 979 if (listen_sock == NULL) { 980 errExit("PR_NewTCPSocket"); 981 } 982 983 opt.option = PR_SockOpt_Nonblocking; 984 opt.value.non_blocking = PR_FALSE; 985 prStatus = PR_SetSocketOption(listen_sock, &opt); 986 if (prStatus < 0) { 987 PR_Close(listen_sock); 988 errExit("PR_SetSocketOption(PR_SockOpt_Nonblocking)"); 989 } 990 991 opt.option = PR_SockOpt_Reuseaddr; 992 opt.value.reuse_addr = PR_TRUE; 993 prStatus = PR_SetSocketOption(listen_sock, &opt); 994 if (prStatus < 0) { 995 PR_Close(listen_sock); 996 errExit("PR_SetSocketOption(PR_SockOpt_Reuseaddr)"); 997 } 998 999 /* Set PR_SockOpt_Linger because it helps prevent a server bind issue 1000 * after clean shutdown . See bug 331413 .*/ 1001 opt.option = PR_SockOpt_Linger; 1002 opt.value.linger.polarity = PR_TRUE; 1003 opt.value.linger.linger = PR_SecondsToInterval(1); 1004 prStatus = PR_SetSocketOption(listen_sock, &opt); 1005 if (prStatus < 0) { 1006 PR_Close(listen_sock); 1007 errExit("PR_SetSocketOption(PR_SockOpt_Linger)"); 1008 } 1009 1010 prStatus = PR_Bind(listen_sock, &addr); 1011 if (prStatus < 0) { 1012 PR_Close(listen_sock); 1013 errExit("PR_Bind"); 1014 } 1015 1016 prStatus = PR_Listen(listen_sock, listenQueueDepth); 1017 if (prStatus < 0) { 1018 PR_Close(listen_sock); 1019 errExit("PR_Listen"); 1020 } 1021 return listen_sock; 1022 } 1023 1024 void 1025 server_main( 1026 PRFileDesc *listen_sock, 1027 int requestCert, 1028 SECKEYPrivateKey **privKey, 1029 CERTCertificate **cert, 1030 const char *expectedHostNameVal) 1031 { 1032 PRFileDesc *model_sock = NULL; 1033 1034 /* Now, do the accepting, here in the main thread. */ 1035 do_accepts(listen_sock, model_sock, requestCert); 1036 1037 terminateWorkerThreads(); 1038 1039 if (model_sock) { 1040 PR_Close(model_sock); 1041 } 1042 } 1043 1044 int numChildren; 1045 PRProcess *child[MAX_PROCS]; 1046 1047 PRProcess * 1048 haveAChild(int argc, char **argv, PRProcessAttr *attr) 1049 { 1050 PRProcess *newProcess; 1051 1052 newProcess = PR_CreateProcess(argv[0], argv, NULL, attr); 1053 if (!newProcess) { 1054 errWarn("Can't create new process."); 1055 } else { 1056 child[numChildren++] = newProcess; 1057 } 1058 return newProcess; 1059 } 1060 1061 /* slightly adjusted version of ocsp_CreateCertID (not using issuer) */ 1062 static CERTOCSPCertID * 1063 ocsp_CreateSelfCAID(PLArenaPool *arena, CERTCertificate *cert, PRTime time) 1064 { 1065 CERTOCSPCertID *certID; 1066 void *mark = PORT_ArenaMark(arena); 1067 SECStatus rv; 1068 1069 PORT_Assert(arena != NULL); 1070 1071 certID = PORT_ArenaZNew(arena, CERTOCSPCertID); 1072 if (certID == NULL) { 1073 goto loser; 1074 } 1075 1076 rv = SECOID_SetAlgorithmID(arena, &certID->hashAlgorithm, SEC_OID_SHA1, 1077 NULL); 1078 if (rv != SECSuccess) { 1079 goto loser; 1080 } 1081 1082 if (CERT_GetSubjectNameDigest(arena, cert, SEC_OID_SHA1, 1083 &(certID->issuerNameHash)) == NULL) { 1084 goto loser; 1085 } 1086 certID->issuerSHA1NameHash.data = certID->issuerNameHash.data; 1087 certID->issuerSHA1NameHash.len = certID->issuerNameHash.len; 1088 1089 if (CERT_GetSubjectNameDigest(arena, cert, SEC_OID_MD5, 1090 &(certID->issuerMD5NameHash)) == NULL) { 1091 goto loser; 1092 } 1093 1094 if (CERT_GetSubjectNameDigest(arena, cert, SEC_OID_MD2, 1095 &(certID->issuerMD2NameHash)) == NULL) { 1096 goto loser; 1097 } 1098 1099 if (CERT_GetSubjectPublicKeyDigest(arena, cert, SEC_OID_SHA1, 1100 &certID->issuerKeyHash) == NULL) { 1101 goto loser; 1102 } 1103 certID->issuerSHA1KeyHash.data = certID->issuerKeyHash.data; 1104 certID->issuerSHA1KeyHash.len = certID->issuerKeyHash.len; 1105 /* cache the other two hash algorithms as well */ 1106 if (CERT_GetSubjectPublicKeyDigest(arena, cert, SEC_OID_MD5, 1107 &certID->issuerMD5KeyHash) == NULL) { 1108 goto loser; 1109 } 1110 if (CERT_GetSubjectPublicKeyDigest(arena, cert, SEC_OID_MD2, 1111 &certID->issuerMD2KeyHash) == NULL) { 1112 goto loser; 1113 } 1114 1115 PORT_ArenaUnmark(arena, mark); 1116 return certID; 1117 1118 loser: 1119 PORT_ArenaRelease(arena, mark); 1120 return NULL; 1121 } 1122 1123 /* slightly adjusted version of CERT_CreateOCSPCertID */ 1124 CERTOCSPCertID * 1125 cert_CreateSelfCAID(CERTCertificate *cert, PRTime time) 1126 { 1127 PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); 1128 CERTOCSPCertID *certID; 1129 PORT_Assert(arena != NULL); 1130 if (!arena) 1131 return NULL; 1132 1133 certID = ocsp_CreateSelfCAID(arena, cert, time); 1134 if (!certID) { 1135 PORT_FreeArena(arena, PR_FALSE); 1136 return NULL; 1137 } 1138 certID->poolp = arena; 1139 return certID; 1140 } 1141 1142 int 1143 main(int argc, char **argv) 1144 { 1145 char *progName = NULL; 1146 const char *dir = "."; 1147 char *passwd = NULL; 1148 char *pwfile = NULL; 1149 const char *pidFile = NULL; 1150 char *tmp; 1151 PRFileDesc *listen_sock; 1152 int optionsFound = 0; 1153 unsigned short port = 0; 1154 SECStatus rv; 1155 PRStatus prStatus; 1156 PRBool bindOnly = PR_FALSE; 1157 PRBool useLocalThreads = PR_FALSE; 1158 PLOptState *optstate; 1159 PLOptStatus status; 1160 char emptyString[] = { "" }; 1161 char *certPrefix = emptyString; 1162 caRevoInfo *revoInfo = NULL; 1163 PRCList *caRevoIter = NULL; 1164 PRBool provideOcsp = PR_FALSE; 1165 1166 tmp = strrchr(argv[0], '/'); 1167 tmp = tmp ? tmp + 1 : argv[0]; 1168 progName = strrchr(tmp, '\\'); 1169 progName = progName ? progName + 1 : tmp; 1170 1171 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); 1172 1173 /* please keep this list of options in ASCII collating sequence. 1174 ** numbers, then capital letters, then lower case, alphabetical. 1175 */ 1176 optstate = PL_CreateOptState(argc, argv, 1177 "A:C:DO:P:bd:f:hi:p:t:vw:"); 1178 while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { 1179 ++optionsFound; 1180 switch (optstate->option) { 1181 /* A first, must be followed by C. Any other order is an error. 1182 * A creates the object. C completes and moves into list. 1183 */ 1184 case 'A': 1185 provideOcsp = PR_TRUE; 1186 if (revoInfo) { 1187 Usage(progName); 1188 exit(0); 1189 } 1190 revoInfo = PORT_New(caRevoInfo); 1191 revoInfo->nickname = PORT_Strdup(optstate->value); 1192 break; 1193 case 'C': 1194 if (!revoInfo) { 1195 Usage(progName); 1196 exit(0); 1197 } 1198 revoInfo->crlFilename = PORT_Strdup(optstate->value); 1199 if (!caRevoInfos) { 1200 PR_INIT_CLIST(&revoInfo->link); 1201 caRevoInfos = revoInfo; 1202 } else { 1203 PR_APPEND_LINK(&revoInfo->link, &caRevoInfos->link); 1204 } 1205 revoInfo = NULL; 1206 break; 1207 1208 case 'O': 1209 if (!PL_strcasecmp(optstate->value, "all")) { 1210 ocspMethodsAllowed = ocspGetAndPost; 1211 } else if (!PL_strcasecmp(optstate->value, "get")) { 1212 ocspMethodsAllowed = ocspGetOnly; 1213 } else if (!PL_strcasecmp(optstate->value, "post")) { 1214 ocspMethodsAllowed = ocspPostOnly; 1215 } else if (!PL_strcasecmp(optstate->value, "random")) { 1216 ocspMethodsAllowed = ocspRandomGetFailure; 1217 } else if (!PL_strcasecmp(optstate->value, "get-unknown")) { 1218 ocspMethodsAllowed = ocspGetUnknown; 1219 } else { 1220 Usage(progName); 1221 exit(0); 1222 } 1223 break; 1224 1225 case 'D': 1226 noDelay = PR_TRUE; 1227 break; 1228 1229 case 'P': 1230 certPrefix = PORT_Strdup(optstate->value); 1231 break; 1232 1233 case 'b': 1234 bindOnly = PR_TRUE; 1235 break; 1236 1237 case 'd': 1238 dir = optstate->value; 1239 break; 1240 1241 case 'f': 1242 pwdata.source = PW_FROMFILE; 1243 pwdata.data = pwfile = PORT_Strdup(optstate->value); 1244 break; 1245 1246 case 'h': 1247 Usage(progName); 1248 exit(0); 1249 break; 1250 1251 case 'i': 1252 pidFile = optstate->value; 1253 break; 1254 1255 case 'p': 1256 port = PORT_Atoi(optstate->value); 1257 break; 1258 1259 case 't': 1260 maxThreads = PORT_Atoi(optstate->value); 1261 if (maxThreads > MAX_THREADS) 1262 maxThreads = MAX_THREADS; 1263 if (maxThreads < MIN_THREADS) 1264 maxThreads = MIN_THREADS; 1265 break; 1266 1267 case 'v': 1268 verbose++; 1269 break; 1270 1271 case 'w': 1272 pwdata.source = PW_PLAINTEXT; 1273 pwdata.data = passwd = PORT_Strdup(optstate->value); 1274 break; 1275 1276 default: 1277 case '?': 1278 fprintf(stderr, "Unrecognized or bad option specified.\n"); 1279 fprintf(stderr, "Run '%s -h' for usage information.\n", progName); 1280 exit(4); 1281 break; 1282 } 1283 } 1284 PL_DestroyOptState(optstate); 1285 if (status == PL_OPT_BAD) { 1286 fprintf(stderr, "Unrecognized or bad option specified.\n"); 1287 fprintf(stderr, "Run '%s -h' for usage information.\n", progName); 1288 exit(5); 1289 } 1290 if (!optionsFound) { 1291 Usage(progName); 1292 exit(51); 1293 } 1294 1295 /* The -b (bindOnly) option is only used by the ssl.sh test 1296 * script on Linux to determine whether a previous httpserv 1297 * process has fully died and freed the port. (Bug 129701) 1298 */ 1299 if (bindOnly) { 1300 listen_sock = getBoundListenSocket(port); 1301 if (!listen_sock) { 1302 exit(1); 1303 } 1304 if (listen_sock) { 1305 PR_Close(listen_sock); 1306 } 1307 exit(0); 1308 } 1309 1310 if (port == 0) { 1311 fprintf(stderr, "Required argument 'port' must be non-zero value\n"); 1312 exit(7); 1313 } 1314 1315 if (pidFile) { 1316 FILE *tmpfile = fopen(pidFile, "w+"); 1317 1318 if (tmpfile) { 1319 fprintf(tmpfile, "%d", getpid()); 1320 fclose(tmpfile); 1321 } 1322 } 1323 1324 tmp = PR_GetEnvSecure("TMP"); 1325 if (!tmp) 1326 tmp = PR_GetEnvSecure("TMPDIR"); 1327 if (!tmp) 1328 tmp = PR_GetEnvSecure("TEMP"); 1329 /* we're an ordinary single process server. */ 1330 listen_sock = getBoundListenSocket(port); 1331 prStatus = PR_SetFDInheritable(listen_sock, PR_FALSE); 1332 if (prStatus != PR_SUCCESS) 1333 errExit("PR_SetFDInheritable"); 1334 1335 lm = PR_NewLogModule("TestCase"); 1336 1337 /* set our password function */ 1338 PK11_SetPasswordFunc(SECU_GetModulePassword); 1339 1340 if (provideOcsp) { 1341 /* Call the NSS initialization routines */ 1342 rv = NSS_Initialize(dir, certPrefix, certPrefix, SECMOD_DB, NSS_INIT_READONLY); 1343 if (rv != SECSuccess) { 1344 fputs("NSS_Init failed.\n", stderr); 1345 exit(8); 1346 } 1347 1348 if (caRevoInfos) { 1349 caRevoIter = &caRevoInfos->link; 1350 do { 1351 PRFileDesc *inFile; 1352 SECItem crlDER; 1353 crlDER.data = NULL; 1354 1355 revoInfo = (caRevoInfo *)caRevoIter; 1356 revoInfo->cert = CERT_FindCertByNickname( 1357 CERT_GetDefaultCertDB(), revoInfo->nickname); 1358 if (!revoInfo->cert) { 1359 fprintf(stderr, "cannot find cert with nickname %s\n", 1360 revoInfo->nickname); 1361 exit(1); 1362 } 1363 inFile = PR_Open(revoInfo->crlFilename, PR_RDONLY, 0); 1364 if (inFile) { 1365 rv = SECU_ReadDERFromFile(&crlDER, inFile, PR_FALSE, PR_FALSE); 1366 PR_Close(inFile); 1367 inFile = NULL; 1368 } 1369 if (rv != SECSuccess) { 1370 fprintf(stderr, "unable to read crl file %s\n", 1371 revoInfo->crlFilename); 1372 exit(1); 1373 } 1374 revoInfo->crl = 1375 CERT_DecodeDERCrlWithFlags(NULL, &crlDER, SEC_CRL_TYPE, 1376 CRL_DECODE_DEFAULT_OPTIONS); 1377 SECITEM_FreeItem(&crlDER, PR_FALSE); 1378 if (!revoInfo->crl) { 1379 fprintf(stderr, "unable to decode crl file %s\n", 1380 revoInfo->crlFilename); 1381 exit(1); 1382 } 1383 if (CERT_CompareName(&revoInfo->crl->crl.name, 1384 &revoInfo->cert->subject) != SECEqual) { 1385 fprintf(stderr, "CRL %s doesn't match cert identified by preceding nickname %s\n", 1386 revoInfo->crlFilename, revoInfo->nickname); 1387 exit(1); 1388 } 1389 revoInfo->id = cert_CreateSelfCAID(revoInfo->cert, PR_Now()); 1390 caRevoIter = PR_NEXT_LINK(caRevoIter); 1391 } while (caRevoIter != &caRevoInfos->link); 1392 } 1393 } 1394 1395 /* allocate the array of thread slots, and launch the worker threads. */ 1396 rv = launch_threads(&jobLoop, 0, 0, 0, useLocalThreads); 1397 1398 if (rv == SECSuccess) { 1399 server_main(listen_sock, 0, 0, 0, 1400 0); 1401 } 1402 1403 VLOG(("httpserv: server_thread: exiting")); 1404 1405 if (provideOcsp) { 1406 if (caRevoInfos) { 1407 caRevoIter = &caRevoInfos->link; 1408 do { 1409 revoInfo = (caRevoInfo *)caRevoIter; 1410 if (revoInfo->nickname) 1411 PORT_Free(revoInfo->nickname); 1412 if (revoInfo->crlFilename) 1413 PORT_Free(revoInfo->crlFilename); 1414 if (revoInfo->cert) 1415 CERT_DestroyCertificate(revoInfo->cert); 1416 if (revoInfo->id) 1417 CERT_DestroyOCSPCertID(revoInfo->id); 1418 if (revoInfo->crl) 1419 SEC_DestroyCrl(revoInfo->crl); 1420 1421 caRevoIter = PR_NEXT_LINK(caRevoIter); 1422 } while (caRevoIter != &caRevoInfos->link); 1423 } 1424 if (NSS_Shutdown() != SECSuccess) { 1425 SECU_PrintError(progName, "NSS_Shutdown"); 1426 PR_Cleanup(); 1427 exit(1); 1428 } 1429 } 1430 if (passwd) { 1431 PORT_Free(passwd); 1432 } 1433 if (pwfile) { 1434 PORT_Free(pwfile); 1435 } 1436 if (certPrefix && certPrefix != emptyString) { 1437 PORT_Free(certPrefix); 1438 } 1439 PR_Cleanup(); 1440 printf("httpserv: normal termination\n"); 1441 return 0; 1442 }