1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/cmd/httpserv/httpserv.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1394 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include <stdio.h> 1.9 +#include <string.h> 1.10 + 1.11 +#include "secutil.h" 1.12 + 1.13 +#if defined(XP_UNIX) 1.14 +#include <unistd.h> 1.15 +#endif 1.16 + 1.17 +#if defined(_WINDOWS) 1.18 +#include <process.h> /* for getpid() */ 1.19 +#endif 1.20 + 1.21 +#include <signal.h> 1.22 +#include <stdlib.h> 1.23 +#include <errno.h> 1.24 +#include <fcntl.h> 1.25 +#include <stdarg.h> 1.26 + 1.27 +#include "nspr.h" 1.28 +#include "prio.h" 1.29 +#include "prerror.h" 1.30 +#include "prnetdb.h" 1.31 +#include "prclist.h" 1.32 +#include "plgetopt.h" 1.33 +#include "pk11func.h" 1.34 +#include "nss.h" 1.35 +#include "nssb64.h" 1.36 +#include "sechash.h" 1.37 +#include "cert.h" 1.38 +#include "certdb.h" 1.39 +#include "ocsp.h" 1.40 +#include "ocspti.h" 1.41 +#include "ocspi.h" 1.42 + 1.43 +#ifndef PORT_Sprintf 1.44 +#define PORT_Sprintf sprintf 1.45 +#endif 1.46 + 1.47 +#ifndef PORT_Strstr 1.48 +#define PORT_Strstr strstr 1.49 +#endif 1.50 + 1.51 +#ifndef PORT_Malloc 1.52 +#define PORT_Malloc PR_Malloc 1.53 +#endif 1.54 + 1.55 +static int handle_connection( PRFileDesc *, PRFileDesc *, int ); 1.56 + 1.57 +/* data and structures for shutdown */ 1.58 +static int stopping; 1.59 + 1.60 +static PRBool noDelay; 1.61 +static int verbose; 1.62 + 1.63 +static PRThread * acceptorThread; 1.64 + 1.65 +static PRLogModuleInfo *lm; 1.66 + 1.67 +#define PRINTF if (verbose) printf 1.68 +#define FPRINTF if (verbose) fprintf 1.69 +#define FLUSH if (verbose) { fflush(stdout); fflush(stderr); } 1.70 +#define VLOG(arg) PR_LOG(lm,PR_LOG_DEBUG,arg) 1.71 + 1.72 +static void 1.73 +Usage(const char *progName) 1.74 +{ 1.75 + fprintf(stderr, 1.76 + 1.77 +"Usage: %s -p port [-Dbv]\n" 1.78 +" [-t threads] [-i pid_file]\n" 1.79 +" [-A nickname -C crl-filename]... [-O method]\n" 1.80 +" [-d dbdir] [-f password_file] [-w password] [-P dbprefix]\n" 1.81 +"-D means disable Nagle delays in TCP\n" 1.82 +"-b means try binding to the port and exit\n" 1.83 +"-v means verbose output\n" 1.84 +"-t threads -- specify the number of threads to use for connections.\n" 1.85 +"-i pid_file file to write the process id of httpserv\n" 1.86 +"Parameters -A, -C and -O are used to provide an OCSP server at /ocsp?\n" 1.87 +"-A a nickname of a CA certificate\n" 1.88 +"-C a CRL filename corresponding to the preceding CA nickname\n" 1.89 +"-O allowed HTTP methods for OCSP requests: get, post, all, random, get-unknown\n" 1.90 +" random means: randomly fail if request method is GET, POST always works\n" 1.91 +" get-unknown means: status unknown for GET, correct status for POST\n" 1.92 +"Multiple pairs of parameters -A and -C are allowed.\n" 1.93 +"If status for a cert from an unknown CA is requested, the cert from the\n" 1.94 +"first -A parameter will be used to sign the unknown status response.\n" 1.95 +"NSS database parameters are used only if OCSP parameters are used.\n" 1.96 + ,progName); 1.97 +} 1.98 + 1.99 +static const char * 1.100 +errWarn(char * funcString) 1.101 +{ 1.102 + PRErrorCode perr = PR_GetError(); 1.103 + const char * errString = SECU_Strerror(perr); 1.104 + 1.105 + fprintf(stderr, "httpserv: %s returned error %d:\n%s\n", 1.106 + funcString, perr, errString); 1.107 + return errString; 1.108 +} 1.109 + 1.110 +static void 1.111 +errExit(char * funcString) 1.112 +{ 1.113 + errWarn(funcString); 1.114 + exit(3); 1.115 +} 1.116 + 1.117 +#define MAX_VIRT_SERVER_NAME_ARRAY_INDEX 10 1.118 + 1.119 +/************************************************************************** 1.120 +** Begin thread management routines and data. 1.121 +**************************************************************************/ 1.122 +#define MIN_THREADS 3 1.123 +#define DEFAULT_THREADS 8 1.124 +#define MAX_THREADS 4096 1.125 +#define MAX_PROCS 25 1.126 +static int maxThreads = DEFAULT_THREADS; 1.127 + 1.128 + 1.129 +typedef struct jobStr { 1.130 + PRCList link; 1.131 + PRFileDesc *tcp_sock; 1.132 + PRFileDesc *model_sock; 1.133 + int requestCert; 1.134 +} JOB; 1.135 + 1.136 +static PZLock * qLock; /* this lock protects all data immediately below */ 1.137 +static PRLock * lastLoadedCrlLock; /* this lock protects lastLoadedCrl variable */ 1.138 +static PZCondVar * jobQNotEmptyCv; 1.139 +static PZCondVar * freeListNotEmptyCv; 1.140 +static PZCondVar * threadCountChangeCv; 1.141 +static int threadCount; 1.142 +static PRCList jobQ; 1.143 +static PRCList freeJobs; 1.144 +static JOB *jobTable; 1.145 + 1.146 +SECStatus 1.147 +setupJobs(int maxJobs) 1.148 +{ 1.149 + int i; 1.150 + 1.151 + jobTable = (JOB *)PR_Calloc(maxJobs, sizeof(JOB)); 1.152 + if (!jobTable) 1.153 + return SECFailure; 1.154 + 1.155 + PR_INIT_CLIST(&jobQ); 1.156 + PR_INIT_CLIST(&freeJobs); 1.157 + 1.158 + for (i = 0; i < maxJobs; ++i) { 1.159 + JOB * pJob = jobTable + i; 1.160 + PR_APPEND_LINK(&pJob->link, &freeJobs); 1.161 + } 1.162 + return SECSuccess; 1.163 +} 1.164 + 1.165 +typedef int startFn(PRFileDesc *a, PRFileDesc *b, int c); 1.166 + 1.167 +typedef enum { rs_idle = 0, rs_running = 1, rs_zombie = 2 } runState; 1.168 + 1.169 +typedef struct perThreadStr { 1.170 + PRFileDesc *a; 1.171 + PRFileDesc *b; 1.172 + int c; 1.173 + int rv; 1.174 + startFn * startFunc; 1.175 + PRThread * prThread; 1.176 + runState state; 1.177 +} perThread; 1.178 + 1.179 +static perThread *threads; 1.180 + 1.181 +void 1.182 +thread_wrapper(void * arg) 1.183 +{ 1.184 + perThread * slot = (perThread *)arg; 1.185 + 1.186 + slot->rv = (* slot->startFunc)(slot->a, slot->b, slot->c); 1.187 + 1.188 + /* notify the thread exit handler. */ 1.189 + PZ_Lock(qLock); 1.190 + slot->state = rs_zombie; 1.191 + --threadCount; 1.192 + PZ_NotifyAllCondVar(threadCountChangeCv); 1.193 + PZ_Unlock(qLock); 1.194 +} 1.195 + 1.196 +int 1.197 +jobLoop(PRFileDesc *a, PRFileDesc *b, int c) 1.198 +{ 1.199 + PRCList * myLink = 0; 1.200 + JOB * myJob; 1.201 + 1.202 + PZ_Lock(qLock); 1.203 + do { 1.204 + myLink = 0; 1.205 + while (PR_CLIST_IS_EMPTY(&jobQ) && !stopping) { 1.206 + PZ_WaitCondVar(jobQNotEmptyCv, PR_INTERVAL_NO_TIMEOUT); 1.207 + } 1.208 + if (!PR_CLIST_IS_EMPTY(&jobQ)) { 1.209 + myLink = PR_LIST_HEAD(&jobQ); 1.210 + PR_REMOVE_AND_INIT_LINK(myLink); 1.211 + } 1.212 + PZ_Unlock(qLock); 1.213 + myJob = (JOB *)myLink; 1.214 + /* myJob will be null when stopping is true and jobQ is empty */ 1.215 + if (!myJob) 1.216 + break; 1.217 + handle_connection( myJob->tcp_sock, myJob->model_sock, 1.218 + myJob->requestCert); 1.219 + PZ_Lock(qLock); 1.220 + PR_APPEND_LINK(myLink, &freeJobs); 1.221 + PZ_NotifyCondVar(freeListNotEmptyCv); 1.222 + } while (PR_TRUE); 1.223 + return 0; 1.224 +} 1.225 + 1.226 + 1.227 +SECStatus 1.228 +launch_threads( 1.229 + startFn *startFunc, 1.230 + PRFileDesc *a, 1.231 + PRFileDesc *b, 1.232 + int c, 1.233 + PRBool local) 1.234 +{ 1.235 + int i; 1.236 + SECStatus rv = SECSuccess; 1.237 + 1.238 + /* create the thread management serialization structs */ 1.239 + qLock = PZ_NewLock(nssILockSelfServ); 1.240 + jobQNotEmptyCv = PZ_NewCondVar(qLock); 1.241 + freeListNotEmptyCv = PZ_NewCondVar(qLock); 1.242 + threadCountChangeCv = PZ_NewCondVar(qLock); 1.243 + 1.244 + /* create monitor for crl reload procedure */ 1.245 + lastLoadedCrlLock = PR_NewLock(); 1.246 + 1.247 + /* allocate the array of thread slots */ 1.248 + threads = PR_Calloc(maxThreads, sizeof(perThread)); 1.249 + if ( NULL == threads ) { 1.250 + fprintf(stderr, "Oh Drat! Can't allocate the perThread array\n"); 1.251 + return SECFailure; 1.252 + } 1.253 + /* 5 is a little extra, intended to keep the jobQ from underflowing. 1.254 + ** That is, from going empty while not stopping and clients are still 1.255 + ** trying to contact us. 1.256 + */ 1.257 + rv = setupJobs(maxThreads + 5); 1.258 + if (rv != SECSuccess) 1.259 + return rv; 1.260 + 1.261 + PZ_Lock(qLock); 1.262 + for (i = 0; i < maxThreads; ++i) { 1.263 + perThread * slot = threads + i; 1.264 + 1.265 + slot->state = rs_running; 1.266 + slot->a = a; 1.267 + slot->b = b; 1.268 + slot->c = c; 1.269 + slot->startFunc = startFunc; 1.270 + slot->prThread = PR_CreateThread(PR_USER_THREAD, 1.271 + thread_wrapper, slot, PR_PRIORITY_NORMAL, 1.272 + (PR_TRUE==local)?PR_LOCAL_THREAD:PR_GLOBAL_THREAD, 1.273 + PR_UNJOINABLE_THREAD, 0); 1.274 + if (slot->prThread == NULL) { 1.275 + printf("httpserv: Failed to launch thread!\n"); 1.276 + slot->state = rs_idle; 1.277 + rv = SECFailure; 1.278 + break; 1.279 + } 1.280 + 1.281 + ++threadCount; 1.282 + } 1.283 + PZ_Unlock(qLock); 1.284 + 1.285 + return rv; 1.286 +} 1.287 + 1.288 +#define DESTROY_CONDVAR(name) if (name) { \ 1.289 + PZ_DestroyCondVar(name); name = NULL; } 1.290 +#define DESTROY_LOCK(name) if (name) { \ 1.291 + PZ_DestroyLock(name); name = NULL; } 1.292 + 1.293 + 1.294 +void 1.295 +terminateWorkerThreads(void) 1.296 +{ 1.297 + VLOG(("httpserv: server_thead: waiting on stopping")); 1.298 + PZ_Lock(qLock); 1.299 + PZ_NotifyAllCondVar(jobQNotEmptyCv); 1.300 + while (threadCount > 0) { 1.301 + PZ_WaitCondVar(threadCountChangeCv, PR_INTERVAL_NO_TIMEOUT); 1.302 + } 1.303 + /* The worker threads empty the jobQ before they terminate. */ 1.304 + PORT_Assert(PR_CLIST_IS_EMPTY(&jobQ)); 1.305 + PZ_Unlock(qLock); 1.306 + 1.307 + DESTROY_CONDVAR(jobQNotEmptyCv); 1.308 + DESTROY_CONDVAR(freeListNotEmptyCv); 1.309 + DESTROY_CONDVAR(threadCountChangeCv); 1.310 + 1.311 + PR_DestroyLock(lastLoadedCrlLock); 1.312 + DESTROY_LOCK(qLock); 1.313 + PR_Free(jobTable); 1.314 + PR_Free(threads); 1.315 +} 1.316 + 1.317 +/************************************************************************** 1.318 +** End thread management routines. 1.319 +**************************************************************************/ 1.320 + 1.321 +PRBool NoReuse = PR_FALSE; 1.322 +PRBool disableLocking = PR_FALSE; 1.323 +static secuPWData pwdata = { PW_NONE, 0 }; 1.324 + 1.325 +struct caRevoInfoStr 1.326 +{ 1.327 + PRCList link; 1.328 + char *nickname; 1.329 + char *crlFilename; 1.330 + CERTCertificate *cert; 1.331 + CERTOCSPCertID *id; 1.332 + CERTSignedCrl *crl; 1.333 +}; 1.334 +typedef struct caRevoInfoStr caRevoInfo; 1.335 +/* Created during app init. No locks necessary, 1.336 + * because later on, only read access will occur. */ 1.337 +static caRevoInfo *caRevoInfos = NULL; 1.338 + 1.339 +static enum { 1.340 + ocspGetOnly, ocspPostOnly, ocspGetAndPost, ocspRandomGetFailure, ocspGetUnknown 1.341 +} ocspMethodsAllowed = ocspGetAndPost; 1.342 + 1.343 +static const char stopCmd[] = { "GET /stop " }; 1.344 +static const char getCmd[] = { "GET " }; 1.345 +static const char EOFmsg[] = { "EOF\r\n\r\n\r\n" }; 1.346 +static const char outHeader[] = { 1.347 + "HTTP/1.0 200 OK\r\n" 1.348 + "Server: Generic Web Server\r\n" 1.349 + "Date: Tue, 26 Aug 1997 22:10:05 GMT\r\n" 1.350 + "Content-type: text/plain\r\n" 1.351 + "\r\n" 1.352 +}; 1.353 +static const char outOcspHeader[] = { 1.354 + "HTTP/1.0 200 OK\r\n" 1.355 + "Server: Generic OCSP Server\r\n" 1.356 + "Content-type: application/ocsp-response\r\n" 1.357 + "\r\n" 1.358 +}; 1.359 +static const char outBadRequestHeader[] = { 1.360 + "HTTP/1.0 400 Bad Request\r\n" 1.361 + "Server: Generic OCSP Server\r\n" 1.362 + "\r\n" 1.363 +}; 1.364 + 1.365 +void stop_server() 1.366 +{ 1.367 + stopping = 1; 1.368 + PR_Interrupt(acceptorThread); 1.369 + PZ_TraceFlush(); 1.370 +} 1.371 + 1.372 +/* Will only work if the original input to url encoding was 1.373 + * a base64 encoded buffer. Will only decode the sequences used 1.374 + * for encoding the special base64 characters, and fail if any 1.375 + * other encoded chars are found. 1.376 + * Will return SECSuccess if input could be processed. 1.377 + * Coversion is done in place. 1.378 + */ 1.379 +static SECStatus 1.380 +urldecode_base64chars_inplace(char *buf) 1.381 +{ 1.382 + char *walk; 1.383 + size_t remaining_bytes; 1.384 + 1.385 + if (!buf || !*buf) 1.386 + return SECFailure; 1.387 + 1.388 + walk = buf; 1.389 + remaining_bytes = strlen(buf) + 1; /* include terminator */ 1.390 + 1.391 + while (*walk) { 1.392 + if (*walk == '%') { 1.393 + if (!PL_strncasecmp(walk, "%2B", 3)) { 1.394 + *walk = '+'; 1.395 + } else if (!PL_strncasecmp(walk, "%2F", 3)) { 1.396 + *walk = '/'; 1.397 + } else if (!PL_strncasecmp(walk, "%3D", 3)) { 1.398 + *walk = '='; 1.399 + } else { 1.400 + return SECFailure; 1.401 + } 1.402 + remaining_bytes -= 3; 1.403 + ++walk; 1.404 + memmove(walk, walk+2, remaining_bytes); 1.405 + } else { 1.406 + ++walk; 1.407 + --remaining_bytes; 1.408 + } 1.409 + } 1.410 + return SECSuccess; 1.411 +} 1.412 + 1.413 +int 1.414 +handle_connection( 1.415 + PRFileDesc *tcp_sock, 1.416 + PRFileDesc *model_sock, 1.417 + int requestCert 1.418 + ) 1.419 +{ 1.420 + PRFileDesc * ssl_sock = NULL; 1.421 + PRFileDesc * local_file_fd = NULL; 1.422 + char * pBuf; /* unused space at end of buf */ 1.423 + const char * errString; 1.424 + PRStatus status; 1.425 + int bufRem; /* unused bytes at end of buf */ 1.426 + int bufDat; /* characters received in buf */ 1.427 + int newln = 0; /* # of consecutive newlns */ 1.428 + int firstTime = 1; 1.429 + int reqLen; 1.430 + int rv; 1.431 + int numIOVs; 1.432 + PRSocketOptionData opt; 1.433 + PRIOVec iovs[16]; 1.434 + char msgBuf[160]; 1.435 + char buf[10240]; 1.436 + char fileName[513]; 1.437 + char *getData = NULL; /* inplace conversion */ 1.438 + SECItem postData; 1.439 + PRBool isOcspRequest = PR_FALSE; 1.440 + PRBool isPost; 1.441 + 1.442 + postData.data = NULL; 1.443 + postData.len = 0; 1.444 + 1.445 + pBuf = buf; 1.446 + bufRem = sizeof buf; 1.447 + 1.448 + VLOG(("httpserv: handle_connection: starting")); 1.449 + opt.option = PR_SockOpt_Nonblocking; 1.450 + opt.value.non_blocking = PR_FALSE; 1.451 + PR_SetSocketOption(tcp_sock, &opt); 1.452 + 1.453 + VLOG(("httpserv: handle_connection: starting\n")); 1.454 + ssl_sock = tcp_sock; 1.455 + 1.456 + if (noDelay) { 1.457 + opt.option = PR_SockOpt_NoDelay; 1.458 + opt.value.no_delay = PR_TRUE; 1.459 + status = PR_SetSocketOption(ssl_sock, &opt); 1.460 + if (status != PR_SUCCESS) { 1.461 + errWarn("PR_SetSocketOption(PR_SockOpt_NoDelay, PR_TRUE)"); 1.462 + if (ssl_sock) { 1.463 + PR_Close(ssl_sock); 1.464 + } 1.465 + return SECFailure; 1.466 + } 1.467 + } 1.468 + 1.469 + while (1) { 1.470 + const char *post; 1.471 + const char *foundStr = NULL; 1.472 + const char *tmp = NULL; 1.473 + 1.474 + newln = 0; 1.475 + reqLen = 0; 1.476 + 1.477 + rv = PR_Read(ssl_sock, pBuf, bufRem - 1); 1.478 + if (rv == 0 || 1.479 + (rv < 0 && PR_END_OF_FILE_ERROR == PR_GetError())) { 1.480 + if (verbose) 1.481 + errWarn("HDX PR_Read hit EOF"); 1.482 + break; 1.483 + } 1.484 + if (rv < 0) { 1.485 + errWarn("HDX PR_Read"); 1.486 + goto cleanup; 1.487 + } 1.488 + /* NULL termination */ 1.489 + pBuf[rv] = 0; 1.490 + if (firstTime) { 1.491 + firstTime = 0; 1.492 + } 1.493 + 1.494 + pBuf += rv; 1.495 + bufRem -= rv; 1.496 + bufDat = pBuf - buf; 1.497 + /* Parse the input, starting at the beginning of the buffer. 1.498 + * Stop when we detect two consecutive \n's (or \r\n's) 1.499 + * as this signifies the end of the GET or POST portion. 1.500 + * The posted data follows. 1.501 + */ 1.502 + while (reqLen < bufDat && newln < 2) { 1.503 + int octet = buf[reqLen++]; 1.504 + if (octet == '\n') { 1.505 + newln++; 1.506 + } else if (octet != '\r') { 1.507 + newln = 0; 1.508 + } 1.509 + } 1.510 + 1.511 + /* came to the end of the buffer, or second newln 1.512 + * If we didn't get an empty line (CRLFCRLF) then keep on reading. 1.513 + */ 1.514 + if (newln < 2) 1.515 + continue; 1.516 + 1.517 + /* we're at the end of the HTTP request. 1.518 + * If the request is a POST, then there will be one more 1.519 + * line of data. 1.520 + * This parsing is a hack, but ok for SSL test purposes. 1.521 + */ 1.522 + post = PORT_Strstr(buf, "POST "); 1.523 + if (!post || *post != 'P') 1.524 + break; 1.525 + 1.526 + postData.data = (void*)(buf + reqLen); 1.527 + 1.528 + tmp = "content-length: "; 1.529 + foundStr = PL_strcasestr(buf, tmp); 1.530 + if (foundStr) { 1.531 + int expectedPostLen; 1.532 + int havePostLen; 1.533 + 1.534 + expectedPostLen = atoi(foundStr+strlen(tmp)); 1.535 + havePostLen = bufDat - reqLen; 1.536 + if (havePostLen >= expectedPostLen) { 1.537 + postData.len = expectedPostLen; 1.538 + break; 1.539 + } 1.540 + } else { 1.541 + /* use legacy hack */ 1.542 + /* It's a post, so look for the next and final CR/LF. */ 1.543 + while (reqLen < bufDat && newln < 3) { 1.544 + int octet = buf[reqLen++]; 1.545 + if (octet == '\n') { 1.546 + newln++; 1.547 + } 1.548 + } 1.549 + if (newln == 3) 1.550 + break; 1.551 + } 1.552 + } /* read loop */ 1.553 + 1.554 + bufDat = pBuf - buf; 1.555 + if (bufDat) do { /* just close if no data */ 1.556 + /* Have either (a) a complete get, (b) a complete post, (c) EOF */ 1.557 + if (reqLen > 0) { 1.558 + PRBool isGetOrPost = PR_FALSE; 1.559 + unsigned skipChars = 0; 1.560 + isPost = PR_FALSE; 1.561 + 1.562 + if (!strncmp(buf, getCmd, sizeof getCmd - 1)) { 1.563 + isGetOrPost = PR_TRUE; 1.564 + skipChars = 4; 1.565 + } 1.566 + else if (!strncmp(buf, "POST ", 5)) { 1.567 + isGetOrPost = PR_TRUE; 1.568 + isPost = PR_TRUE; 1.569 + skipChars = 5; 1.570 + } 1.571 + 1.572 + if (isGetOrPost) { 1.573 + char * fnBegin = buf; 1.574 + char * fnEnd; 1.575 + char * fnstart = NULL; 1.576 + PRFileInfo info; 1.577 + 1.578 + fnBegin += skipChars; 1.579 + 1.580 + fnEnd = strpbrk(fnBegin, " \r\n"); 1.581 + if (fnEnd) { 1.582 + int fnLen = fnEnd - fnBegin; 1.583 + if (fnLen < sizeof fileName) { 1.584 + strncpy(fileName, fnBegin, fnLen); 1.585 + fileName[fnLen] = 0; /* null terminate */ 1.586 + fnstart = fileName; 1.587 + /* strip initial / because our root is the current directory*/ 1.588 + while (*fnstart && *fnstart=='/') 1.589 + ++fnstart; 1.590 + } 1.591 + } 1.592 + if (fnstart) { 1.593 + if (!strncmp(fnstart, "ocsp", 4)) { 1.594 + if (isPost) { 1.595 + if (postData.data) { 1.596 + isOcspRequest = PR_TRUE; 1.597 + } 1.598 + } else { 1.599 + if (!strncmp(fnstart, "ocsp/", 5)) { 1.600 + isOcspRequest = PR_TRUE; 1.601 + getData = fnstart + 5; 1.602 + } 1.603 + } 1.604 + } else { 1.605 + /* try to open the file named. 1.606 + * If successful, then write it to the client. 1.607 + */ 1.608 + status = PR_GetFileInfo(fnstart, &info); 1.609 + if (status == PR_SUCCESS && 1.610 + info.type == PR_FILE_FILE && 1.611 + info.size >= 0 ) { 1.612 + local_file_fd = PR_Open(fnstart, PR_RDONLY, 0); 1.613 + } 1.614 + } 1.615 + } 1.616 + } 1.617 + } 1.618 + 1.619 + numIOVs = 0; 1.620 + 1.621 + iovs[numIOVs].iov_base = (char *)outHeader; 1.622 + iovs[numIOVs].iov_len = (sizeof(outHeader)) - 1; 1.623 + numIOVs++; 1.624 + 1.625 + if (isOcspRequest && caRevoInfos) { 1.626 + CERTOCSPRequest *request = NULL; 1.627 + PRBool failThisRequest = PR_FALSE; 1.628 + 1.629 + if (ocspMethodsAllowed == ocspGetOnly && postData.len) { 1.630 + failThisRequest = PR_TRUE; 1.631 + } else if (ocspMethodsAllowed == ocspPostOnly && getData) { 1.632 + failThisRequest = PR_TRUE; 1.633 + } else if (ocspMethodsAllowed == ocspRandomGetFailure && getData) { 1.634 + if (!(rand() % 2)) { 1.635 + failThisRequest = PR_TRUE; 1.636 + } 1.637 + } 1.638 + 1.639 + if (failThisRequest) { 1.640 + PR_Write(ssl_sock, outBadRequestHeader, strlen(outBadRequestHeader)); 1.641 + break; 1.642 + } 1.643 + /* get is base64, post is binary. 1.644 + * If we have base64, convert into the (empty) postData array. 1.645 + */ 1.646 + if (getData) { 1.647 + if (urldecode_base64chars_inplace(getData) == SECSuccess) { 1.648 + NSSBase64_DecodeBuffer(NULL, &postData, getData, strlen(getData)); 1.649 + } 1.650 + } 1.651 + if (postData.len) { 1.652 + request = CERT_DecodeOCSPRequest(&postData); 1.653 + } 1.654 + if (!request || !request->tbsRequest || 1.655 + !request->tbsRequest->requestList || 1.656 + !request->tbsRequest->requestList[0]) { 1.657 + PORT_Sprintf(msgBuf, "Cannot decode OCSP request.\r\n"); 1.658 + 1.659 + iovs[numIOVs].iov_base = msgBuf; 1.660 + iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); 1.661 + numIOVs++; 1.662 + } else { 1.663 + /* TODO: support more than one request entry */ 1.664 + CERTOCSPCertID *reqid = request->tbsRequest->requestList[0]->reqCert; 1.665 + const caRevoInfo *revoInfo = NULL; 1.666 + PRBool unknown = PR_FALSE; 1.667 + PRBool revoked = PR_FALSE; 1.668 + PRTime nextUpdate = 0; 1.669 + PRTime revoDate = 0; 1.670 + PRCList *caRevoIter; 1.671 + 1.672 + caRevoIter = &caRevoInfos->link; 1.673 + do { 1.674 + CERTOCSPCertID *caid; 1.675 + 1.676 + revoInfo = (caRevoInfo*)caRevoIter; 1.677 + caid = revoInfo->id; 1.678 + 1.679 + if (SECOID_CompareAlgorithmID(&reqid->hashAlgorithm, 1.680 + &caid->hashAlgorithm) == SECEqual 1.681 + && 1.682 + SECITEM_CompareItem(&reqid->issuerNameHash, 1.683 + &caid->issuerNameHash) == SECEqual 1.684 + && 1.685 + SECITEM_CompareItem(&reqid->issuerKeyHash, 1.686 + &caid->issuerKeyHash) == SECEqual) { 1.687 + break; 1.688 + } 1.689 + revoInfo = NULL; 1.690 + caRevoIter = PR_NEXT_LINK(caRevoIter); 1.691 + } while (caRevoIter != &caRevoInfos->link); 1.692 + 1.693 + if (!revoInfo) { 1.694 + unknown = PR_TRUE; 1.695 + revoInfo = caRevoInfos; 1.696 + } else { 1.697 + CERTCrl *crl = &revoInfo->crl->crl; 1.698 + CERTCrlEntry *entry = NULL; 1.699 + DER_DecodeTimeChoice(&nextUpdate, &crl->nextUpdate); 1.700 + if (crl->entries) { 1.701 + int iv = 0; 1.702 + /* assign, not compare */ 1.703 + while ((entry = crl->entries[iv++])) { 1.704 + if (SECITEM_CompareItem(&reqid->serialNumber, 1.705 + &entry->serialNumber) == SECEqual) { 1.706 + break; 1.707 + } 1.708 + } 1.709 + } 1.710 + if (entry) { 1.711 + /* revoked status response */ 1.712 + revoked = PR_TRUE; 1.713 + DER_DecodeTimeChoice(&revoDate, &entry->revocationDate); 1.714 + } else { 1.715 + /* else good status response */ 1.716 + if (!isPost && ocspMethodsAllowed == ocspGetUnknown) { 1.717 + unknown = PR_TRUE; 1.718 + nextUpdate = PR_Now() + 60*60*24 * PR_USEC_PER_SEC; /*tomorrow*/ 1.719 + revoDate = PR_Now() - 60*60*24 * PR_USEC_PER_SEC; /*yesterday*/ 1.720 + } 1.721 + } 1.722 + } 1.723 + 1.724 + { 1.725 + PRTime now = PR_Now(); 1.726 + PLArenaPool *arena = NULL; 1.727 + CERTOCSPSingleResponse *sr; 1.728 + CERTOCSPSingleResponse **singleResponses; 1.729 + SECItem *ocspResponse; 1.730 + 1.731 + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); 1.732 + 1.733 + if (unknown) { 1.734 + sr = CERT_CreateOCSPSingleResponseUnknown(arena, reqid, now, 1.735 + &nextUpdate); 1.736 + } else if (revoked) { 1.737 + sr = CERT_CreateOCSPSingleResponseRevoked(arena, reqid, now, 1.738 + &nextUpdate, revoDate, NULL); 1.739 + } else { 1.740 + sr = CERT_CreateOCSPSingleResponseGood(arena, reqid, now, 1.741 + &nextUpdate); 1.742 + } 1.743 + 1.744 + /* meaning of value 2: one entry + one end marker */ 1.745 + singleResponses = PORT_ArenaNewArray(arena, CERTOCSPSingleResponse*, 2); 1.746 + singleResponses[0] = sr; 1.747 + singleResponses[1] = NULL; 1.748 + ocspResponse = CERT_CreateEncodedOCSPSuccessResponse(arena, 1.749 + revoInfo->cert, ocspResponderID_byName, now, 1.750 + singleResponses, &pwdata); 1.751 + 1.752 + if (!ocspResponse) { 1.753 + PORT_Sprintf(msgBuf, "Failed to encode response\r\n"); 1.754 + iovs[numIOVs].iov_base = msgBuf; 1.755 + iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); 1.756 + numIOVs++; 1.757 + } else { 1.758 + PR_Write(ssl_sock, outOcspHeader, strlen(outOcspHeader)); 1.759 + PR_Write(ssl_sock, ocspResponse->data, ocspResponse->len); 1.760 + PORT_FreeArena(arena, PR_FALSE); 1.761 + } 1.762 + } 1.763 + break; 1.764 + } 1.765 + } else if (local_file_fd) { 1.766 + PRInt32 bytes; 1.767 + int errLen; 1.768 + bytes = PR_TransmitFile(ssl_sock, local_file_fd, outHeader, 1.769 + sizeof outHeader - 1, 1.770 + PR_TRANSMITFILE_KEEP_OPEN, 1.771 + PR_INTERVAL_NO_TIMEOUT); 1.772 + if (bytes >= 0) { 1.773 + bytes -= sizeof outHeader - 1; 1.774 + FPRINTF(stderr, 1.775 + "httpserv: PR_TransmitFile wrote %d bytes from %s\n", 1.776 + bytes, fileName); 1.777 + break; 1.778 + } 1.779 + errString = errWarn("PR_TransmitFile"); 1.780 + errLen = PORT_Strlen(errString); 1.781 + errLen = PR_MIN(errLen, sizeof msgBuf - 1); 1.782 + PORT_Memcpy(msgBuf, errString, errLen); 1.783 + msgBuf[errLen] = 0; 1.784 + 1.785 + iovs[numIOVs].iov_base = msgBuf; 1.786 + iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); 1.787 + numIOVs++; 1.788 + } else if (reqLen <= 0) { /* hit eof */ 1.789 + PORT_Sprintf(msgBuf, "Get or Post incomplete after %d bytes.\r\n", 1.790 + bufDat); 1.791 + 1.792 + iovs[numIOVs].iov_base = msgBuf; 1.793 + iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); 1.794 + numIOVs++; 1.795 + } else if (reqLen < bufDat) { 1.796 + PORT_Sprintf(msgBuf, "Discarded %d characters.\r\n", 1.797 + bufDat - reqLen); 1.798 + 1.799 + iovs[numIOVs].iov_base = msgBuf; 1.800 + iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); 1.801 + numIOVs++; 1.802 + } 1.803 + 1.804 + if (reqLen > 0) { 1.805 + if (verbose > 1) 1.806 + fwrite(buf, 1, reqLen, stdout); /* display it */ 1.807 + 1.808 + iovs[numIOVs].iov_base = buf; 1.809 + iovs[numIOVs].iov_len = reqLen; 1.810 + numIOVs++; 1.811 + } 1.812 + 1.813 + rv = PR_Writev(ssl_sock, iovs, numIOVs, PR_INTERVAL_NO_TIMEOUT); 1.814 + if (rv < 0) { 1.815 + errWarn("PR_Writev"); 1.816 + break; 1.817 + } 1.818 + 1.819 + } while (0); 1.820 + 1.821 +cleanup: 1.822 + if (ssl_sock) { 1.823 + PR_Close(ssl_sock); 1.824 + } else if (tcp_sock) { 1.825 + PR_Close(tcp_sock); 1.826 + } 1.827 + if (local_file_fd) 1.828 + PR_Close(local_file_fd); 1.829 + VLOG(("httpserv: handle_connection: exiting\n")); 1.830 + 1.831 + /* do a nice shutdown if asked. */ 1.832 + if (!strncmp(buf, stopCmd, sizeof stopCmd - 1)) { 1.833 + VLOG(("httpserv: handle_connection: stop command")); 1.834 + stop_server(); 1.835 + } 1.836 + VLOG(("httpserv: handle_connection: exiting")); 1.837 + return SECSuccess; /* success */ 1.838 +} 1.839 + 1.840 +#ifdef XP_UNIX 1.841 + 1.842 +void sigusr1_handler(int sig) 1.843 +{ 1.844 + VLOG(("httpserv: sigusr1_handler: stop server")); 1.845 + stop_server(); 1.846 +} 1.847 + 1.848 +#endif 1.849 + 1.850 +SECStatus 1.851 +do_accepts( 1.852 + PRFileDesc *listen_sock, 1.853 + PRFileDesc *model_sock, 1.854 + int requestCert 1.855 + ) 1.856 +{ 1.857 + PRNetAddr addr; 1.858 + PRErrorCode perr; 1.859 +#ifdef XP_UNIX 1.860 + struct sigaction act; 1.861 +#endif 1.862 + 1.863 + VLOG(("httpserv: do_accepts: starting")); 1.864 + PR_SetThreadPriority( PR_GetCurrentThread(), PR_PRIORITY_HIGH); 1.865 + 1.866 + acceptorThread = PR_GetCurrentThread(); 1.867 +#ifdef XP_UNIX 1.868 + /* set up the signal handler */ 1.869 + act.sa_handler = sigusr1_handler; 1.870 + sigemptyset(&act.sa_mask); 1.871 + act.sa_flags = 0; 1.872 + if (sigaction(SIGUSR1, &act, NULL)) { 1.873 + fprintf(stderr, "Error installing signal handler.\n"); 1.874 + exit(1); 1.875 + } 1.876 +#endif 1.877 + while (!stopping) { 1.878 + PRFileDesc *tcp_sock; 1.879 + PRCList *myLink; 1.880 + 1.881 + FPRINTF(stderr, "\n\n\nhttpserv: About to call accept.\n"); 1.882 + tcp_sock = PR_Accept(listen_sock, &addr, PR_INTERVAL_NO_TIMEOUT); 1.883 + if (tcp_sock == NULL) { 1.884 + perr = PR_GetError(); 1.885 + if ((perr != PR_CONNECT_RESET_ERROR && 1.886 + perr != PR_PENDING_INTERRUPT_ERROR) || verbose) { 1.887 + errWarn("PR_Accept"); 1.888 + } 1.889 + if (perr == PR_CONNECT_RESET_ERROR) { 1.890 + FPRINTF(stderr, 1.891 + "Ignoring PR_CONNECT_RESET_ERROR error - continue\n"); 1.892 + continue; 1.893 + } 1.894 + stopping = 1; 1.895 + break; 1.896 + } 1.897 + 1.898 + VLOG(("httpserv: do_accept: Got connection\n")); 1.899 + 1.900 + PZ_Lock(qLock); 1.901 + while (PR_CLIST_IS_EMPTY(&freeJobs) && !stopping) { 1.902 + PZ_WaitCondVar(freeListNotEmptyCv, PR_INTERVAL_NO_TIMEOUT); 1.903 + } 1.904 + if (stopping) { 1.905 + PZ_Unlock(qLock); 1.906 + if (tcp_sock) { 1.907 + PR_Close(tcp_sock); 1.908 + } 1.909 + break; 1.910 + } 1.911 + myLink = PR_LIST_HEAD(&freeJobs); 1.912 + PR_REMOVE_AND_INIT_LINK(myLink); 1.913 + /* could release qLock here and reaquire it 7 lines below, but 1.914 + ** why bother for 4 assignment statements? 1.915 + */ 1.916 + { 1.917 + JOB * myJob = (JOB *)myLink; 1.918 + myJob->tcp_sock = tcp_sock; 1.919 + myJob->model_sock = model_sock; 1.920 + myJob->requestCert = requestCert; 1.921 + } 1.922 + 1.923 + PR_APPEND_LINK(myLink, &jobQ); 1.924 + PZ_NotifyCondVar(jobQNotEmptyCv); 1.925 + PZ_Unlock(qLock); 1.926 + } 1.927 + 1.928 + FPRINTF(stderr, "httpserv: Closing listen socket.\n"); 1.929 + VLOG(("httpserv: do_accepts: exiting")); 1.930 + if (listen_sock) { 1.931 + PR_Close(listen_sock); 1.932 + } 1.933 + return SECSuccess; 1.934 +} 1.935 + 1.936 +PRFileDesc * 1.937 +getBoundListenSocket(unsigned short port) 1.938 +{ 1.939 + PRFileDesc * listen_sock; 1.940 + int listenQueueDepth = 5 + (2 * maxThreads); 1.941 + PRStatus prStatus; 1.942 + PRNetAddr addr; 1.943 + PRSocketOptionData opt; 1.944 + 1.945 + addr.inet.family = PR_AF_INET; 1.946 + addr.inet.ip = PR_INADDR_ANY; 1.947 + addr.inet.port = PR_htons(port); 1.948 + 1.949 + listen_sock = PR_NewTCPSocket(); 1.950 + if (listen_sock == NULL) { 1.951 + errExit("PR_NewTCPSocket"); 1.952 + } 1.953 + 1.954 + opt.option = PR_SockOpt_Nonblocking; 1.955 + opt.value.non_blocking = PR_FALSE; 1.956 + prStatus = PR_SetSocketOption(listen_sock, &opt); 1.957 + if (prStatus < 0) { 1.958 + PR_Close(listen_sock); 1.959 + errExit("PR_SetSocketOption(PR_SockOpt_Nonblocking)"); 1.960 + } 1.961 + 1.962 + opt.option=PR_SockOpt_Reuseaddr; 1.963 + opt.value.reuse_addr = PR_TRUE; 1.964 + prStatus = PR_SetSocketOption(listen_sock, &opt); 1.965 + if (prStatus < 0) { 1.966 + PR_Close(listen_sock); 1.967 + errExit("PR_SetSocketOption(PR_SockOpt_Reuseaddr)"); 1.968 + } 1.969 + 1.970 +#ifndef WIN95 1.971 + /* Set PR_SockOpt_Linger because it helps prevent a server bind issue 1.972 + * after clean shutdown . See bug 331413 . 1.973 + * Don't do it in the WIN95 build configuration because clean shutdown is 1.974 + * not implemented, and PR_SockOpt_Linger causes a hang in ssl.sh . 1.975 + * See bug 332348 */ 1.976 + opt.option=PR_SockOpt_Linger; 1.977 + opt.value.linger.polarity = PR_TRUE; 1.978 + opt.value.linger.linger = PR_SecondsToInterval(1); 1.979 + prStatus = PR_SetSocketOption(listen_sock, &opt); 1.980 + if (prStatus < 0) { 1.981 + PR_Close(listen_sock); 1.982 + errExit("PR_SetSocketOption(PR_SockOpt_Linger)"); 1.983 + } 1.984 +#endif 1.985 + 1.986 + prStatus = PR_Bind(listen_sock, &addr); 1.987 + if (prStatus < 0) { 1.988 + PR_Close(listen_sock); 1.989 + errExit("PR_Bind"); 1.990 + } 1.991 + 1.992 + prStatus = PR_Listen(listen_sock, listenQueueDepth); 1.993 + if (prStatus < 0) { 1.994 + PR_Close(listen_sock); 1.995 + errExit("PR_Listen"); 1.996 + } 1.997 + return listen_sock; 1.998 +} 1.999 + 1.1000 +void 1.1001 +server_main( 1.1002 + PRFileDesc * listen_sock, 1.1003 + int requestCert, 1.1004 + SECKEYPrivateKey ** privKey, 1.1005 + CERTCertificate ** cert, 1.1006 + const char *expectedHostNameVal) 1.1007 +{ 1.1008 + PRFileDesc *model_sock = NULL; 1.1009 + 1.1010 + /* Now, do the accepting, here in the main thread. */ 1.1011 + do_accepts(listen_sock, model_sock, requestCert); 1.1012 + 1.1013 + terminateWorkerThreads(); 1.1014 + 1.1015 + if (model_sock) { 1.1016 + PR_Close(model_sock); 1.1017 + } 1.1018 + 1.1019 +} 1.1020 + 1.1021 +int numChildren; 1.1022 +PRProcess * child[MAX_PROCS]; 1.1023 + 1.1024 +PRProcess * 1.1025 +haveAChild(int argc, char **argv, PRProcessAttr * attr) 1.1026 +{ 1.1027 + PRProcess * newProcess; 1.1028 + 1.1029 + newProcess = PR_CreateProcess(argv[0], argv, NULL, attr); 1.1030 + if (!newProcess) { 1.1031 + errWarn("Can't create new process."); 1.1032 + } else { 1.1033 + child[numChildren++] = newProcess; 1.1034 + } 1.1035 + return newProcess; 1.1036 +} 1.1037 + 1.1038 +/* slightly adjusted version of ocsp_CreateCertID (not using issuer) */ 1.1039 +static CERTOCSPCertID * 1.1040 +ocsp_CreateSelfCAID(PLArenaPool *arena, CERTCertificate *cert, PRTime time) 1.1041 +{ 1.1042 + CERTOCSPCertID *certID; 1.1043 + void *mark = PORT_ArenaMark(arena); 1.1044 + SECStatus rv; 1.1045 + 1.1046 + PORT_Assert(arena != NULL); 1.1047 + 1.1048 + certID = PORT_ArenaZNew(arena, CERTOCSPCertID); 1.1049 + if (certID == NULL) { 1.1050 + goto loser; 1.1051 + } 1.1052 + 1.1053 + rv = SECOID_SetAlgorithmID(arena, &certID->hashAlgorithm, SEC_OID_SHA1, 1.1054 + NULL); 1.1055 + if (rv != SECSuccess) { 1.1056 + goto loser; 1.1057 + } 1.1058 + 1.1059 + if (CERT_GetSubjectNameDigest(arena, cert, SEC_OID_SHA1, 1.1060 + &(certID->issuerNameHash)) == NULL) { 1.1061 + goto loser; 1.1062 + } 1.1063 + certID->issuerSHA1NameHash.data = certID->issuerNameHash.data; 1.1064 + certID->issuerSHA1NameHash.len = certID->issuerNameHash.len; 1.1065 + 1.1066 + if (CERT_GetSubjectNameDigest(arena, cert, SEC_OID_MD5, 1.1067 + &(certID->issuerMD5NameHash)) == NULL) { 1.1068 + goto loser; 1.1069 + } 1.1070 + 1.1071 + if (CERT_GetSubjectNameDigest(arena, cert, SEC_OID_MD2, 1.1072 + &(certID->issuerMD2NameHash)) == NULL) { 1.1073 + goto loser; 1.1074 + } 1.1075 + 1.1076 + if (CERT_GetSubjectPublicKeyDigest(arena, cert, SEC_OID_SHA1, 1.1077 + &certID->issuerKeyHash) == NULL) { 1.1078 + goto loser; 1.1079 + } 1.1080 + certID->issuerSHA1KeyHash.data = certID->issuerKeyHash.data; 1.1081 + certID->issuerSHA1KeyHash.len = certID->issuerKeyHash.len; 1.1082 + /* cache the other two hash algorithms as well */ 1.1083 + if (CERT_GetSubjectPublicKeyDigest(arena, cert, SEC_OID_MD5, 1.1084 + &certID->issuerMD5KeyHash) == NULL) { 1.1085 + goto loser; 1.1086 + } 1.1087 + if (CERT_GetSubjectPublicKeyDigest(arena, cert, SEC_OID_MD2, 1.1088 + &certID->issuerMD2KeyHash) == NULL) { 1.1089 + goto loser; 1.1090 + } 1.1091 + 1.1092 + PORT_ArenaUnmark(arena, mark); 1.1093 + return certID; 1.1094 + 1.1095 +loser: 1.1096 + PORT_ArenaRelease(arena, mark); 1.1097 + return NULL; 1.1098 +} 1.1099 + 1.1100 +/* slightly adjusted version of CERT_CreateOCSPCertID */ 1.1101 +CERTOCSPCertID* 1.1102 +cert_CreateSelfCAID(CERTCertificate *cert, PRTime time) 1.1103 +{ 1.1104 + PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); 1.1105 + CERTOCSPCertID *certID; 1.1106 + PORT_Assert(arena != NULL); 1.1107 + if (!arena) 1.1108 + return NULL; 1.1109 + 1.1110 + certID = ocsp_CreateSelfCAID(arena, cert, time); 1.1111 + if (!certID) { 1.1112 + PORT_FreeArena(arena, PR_FALSE); 1.1113 + return NULL; 1.1114 + } 1.1115 + certID->poolp = arena; 1.1116 + return certID; 1.1117 +} 1.1118 + 1.1119 +int 1.1120 +main(int argc, char **argv) 1.1121 +{ 1.1122 + char * progName = NULL; 1.1123 + const char * dir = "."; 1.1124 + char * passwd = NULL; 1.1125 + char * pwfile = NULL; 1.1126 + const char * pidFile = NULL; 1.1127 + char * tmp; 1.1128 + PRFileDesc * listen_sock; 1.1129 + int optionsFound = 0; 1.1130 + unsigned short port = 0; 1.1131 + SECStatus rv; 1.1132 + PRStatus prStatus; 1.1133 + PRBool bindOnly = PR_FALSE; 1.1134 + PRBool useLocalThreads = PR_FALSE; 1.1135 + PLOptState *optstate; 1.1136 + PLOptStatus status; 1.1137 + char emptyString[] = { "" }; 1.1138 + char* certPrefix = emptyString; 1.1139 + caRevoInfo *revoInfo = NULL; 1.1140 + PRCList *caRevoIter = NULL; 1.1141 + PRBool provideOcsp = PR_FALSE; 1.1142 + 1.1143 + tmp = strrchr(argv[0], '/'); 1.1144 + tmp = tmp ? tmp + 1 : argv[0]; 1.1145 + progName = strrchr(tmp, '\\'); 1.1146 + progName = progName ? progName + 1 : tmp; 1.1147 + 1.1148 + PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); 1.1149 + 1.1150 + /* please keep this list of options in ASCII collating sequence. 1.1151 + ** numbers, then capital letters, then lower case, alphabetical. 1.1152 + */ 1.1153 + optstate = PL_CreateOptState(argc, argv, 1.1154 + "A:C:DO:P:bd:f:hi:p:t:vw:"); 1.1155 + while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { 1.1156 + ++optionsFound; 1.1157 + switch(optstate->option) { 1.1158 + /* A first, must be followed by C. Any other order is an error. 1.1159 + * A creates the object. C completes and moves into list. 1.1160 + */ 1.1161 + case 'A': 1.1162 + provideOcsp = PR_TRUE; 1.1163 + if (revoInfo) { Usage(progName); exit(0); } 1.1164 + revoInfo = PORT_New(caRevoInfo); 1.1165 + revoInfo->nickname = PORT_Strdup(optstate->value); 1.1166 + break; 1.1167 + case 'C': 1.1168 + if (!revoInfo) { Usage(progName); exit(0); } 1.1169 + revoInfo->crlFilename = PORT_Strdup(optstate->value); 1.1170 + if (!caRevoInfos) { 1.1171 + PR_INIT_CLIST(&revoInfo->link); 1.1172 + caRevoInfos = revoInfo; 1.1173 + } else { 1.1174 + PR_APPEND_LINK(&revoInfo->link, &caRevoInfos->link); 1.1175 + } 1.1176 + revoInfo = NULL; 1.1177 + break; 1.1178 + 1.1179 + case 'O': 1.1180 + if (!PL_strcasecmp(optstate->value, "all")) { 1.1181 + ocspMethodsAllowed = ocspGetAndPost; 1.1182 + } else if (!PL_strcasecmp(optstate->value, "get")) { 1.1183 + ocspMethodsAllowed = ocspGetOnly; 1.1184 + } else if (!PL_strcasecmp(optstate->value, "post")) { 1.1185 + ocspMethodsAllowed = ocspPostOnly; 1.1186 + } else if (!PL_strcasecmp(optstate->value, "random")) { 1.1187 + ocspMethodsAllowed = ocspRandomGetFailure; 1.1188 + } else if (!PL_strcasecmp(optstate->value, "get-unknown")) { 1.1189 + ocspMethodsAllowed = ocspGetUnknown; 1.1190 + } else { 1.1191 + Usage(progName); exit(0); 1.1192 + } 1.1193 + break; 1.1194 + 1.1195 + case 'D': noDelay = PR_TRUE; break; 1.1196 + 1.1197 + case 'P': certPrefix = PORT_Strdup(optstate->value); break; 1.1198 + 1.1199 + case 'b': bindOnly = PR_TRUE; break; 1.1200 + 1.1201 + case 'd': dir = optstate->value; break; 1.1202 + 1.1203 + case 'f': 1.1204 + pwdata.source = PW_FROMFILE; 1.1205 + pwdata.data = pwfile = PORT_Strdup(optstate->value); 1.1206 + break; 1.1207 + 1.1208 + case 'h': Usage(progName); exit(0); break; 1.1209 + 1.1210 + case 'i': pidFile = optstate->value; break; 1.1211 + 1.1212 + case 'p': port = PORT_Atoi(optstate->value); break; 1.1213 + 1.1214 + case 't': 1.1215 + maxThreads = PORT_Atoi(optstate->value); 1.1216 + if ( maxThreads > MAX_THREADS ) maxThreads = MAX_THREADS; 1.1217 + if ( maxThreads < MIN_THREADS ) maxThreads = MIN_THREADS; 1.1218 + break; 1.1219 + 1.1220 + case 'v': verbose++; break; 1.1221 + 1.1222 + case 'w': 1.1223 + pwdata.source = PW_PLAINTEXT; 1.1224 + pwdata.data = passwd = PORT_Strdup(optstate->value); 1.1225 + break; 1.1226 + 1.1227 + default: 1.1228 + case '?': 1.1229 + fprintf(stderr, "Unrecognized or bad option specified.\n"); 1.1230 + fprintf(stderr, "Run '%s -h' for usage information.\n", progName); 1.1231 + exit(4); 1.1232 + break; 1.1233 + } 1.1234 + } 1.1235 + PL_DestroyOptState(optstate); 1.1236 + if (status == PL_OPT_BAD) { 1.1237 + fprintf(stderr, "Unrecognized or bad option specified.\n"); 1.1238 + fprintf(stderr, "Run '%s -h' for usage information.\n", progName); 1.1239 + exit(5); 1.1240 + } 1.1241 + if (!optionsFound) { 1.1242 + Usage(progName); 1.1243 + exit(51); 1.1244 + } 1.1245 + 1.1246 + /* The -b (bindOnly) option is only used by the ssl.sh test 1.1247 + * script on Linux to determine whether a previous httpserv 1.1248 + * process has fully died and freed the port. (Bug 129701) 1.1249 + */ 1.1250 + if (bindOnly) { 1.1251 + listen_sock = getBoundListenSocket(port); 1.1252 + if (!listen_sock) { 1.1253 + exit(1); 1.1254 + } 1.1255 + if (listen_sock) { 1.1256 + PR_Close(listen_sock); 1.1257 + } 1.1258 + exit(0); 1.1259 + } 1.1260 + 1.1261 + if (port == 0) { 1.1262 + fprintf(stderr, "Required argument 'port' must be non-zero value\n"); 1.1263 + exit(7); 1.1264 + } 1.1265 + 1.1266 + if (pidFile) { 1.1267 + FILE *tmpfile=fopen(pidFile,"w+"); 1.1268 + 1.1269 + if (tmpfile) { 1.1270 + fprintf(tmpfile,"%d",getpid()); 1.1271 + fclose(tmpfile); 1.1272 + } 1.1273 + } 1.1274 + 1.1275 + tmp = getenv("TMP"); 1.1276 + if (!tmp) 1.1277 + tmp = getenv("TMPDIR"); 1.1278 + if (!tmp) 1.1279 + tmp = getenv("TEMP"); 1.1280 + /* we're an ordinary single process server. */ 1.1281 + listen_sock = getBoundListenSocket(port); 1.1282 + prStatus = PR_SetFDInheritable(listen_sock, PR_FALSE); 1.1283 + if (prStatus != PR_SUCCESS) 1.1284 + errExit("PR_SetFDInheritable"); 1.1285 + 1.1286 + lm = PR_NewLogModule("TestCase"); 1.1287 + 1.1288 + /* set our password function */ 1.1289 + PK11_SetPasswordFunc(SECU_GetModulePassword); 1.1290 + 1.1291 + if (provideOcsp) { 1.1292 + /* Call the NSS initialization routines */ 1.1293 + rv = NSS_Initialize(dir, certPrefix, certPrefix, SECMOD_DB, NSS_INIT_READONLY); 1.1294 + if (rv != SECSuccess) { 1.1295 + fputs("NSS_Init failed.\n", stderr); 1.1296 + exit(8); 1.1297 + } 1.1298 + 1.1299 + if (caRevoInfos) { 1.1300 + caRevoIter = &caRevoInfos->link; 1.1301 + do { 1.1302 + PRFileDesc *inFile; 1.1303 + int rv = SECFailure; 1.1304 + SECItem crlDER; 1.1305 + crlDER.data = NULL; 1.1306 + 1.1307 + revoInfo = (caRevoInfo*)caRevoIter; 1.1308 + revoInfo->cert = CERT_FindCertByNickname( 1.1309 + CERT_GetDefaultCertDB(), revoInfo->nickname); 1.1310 + if (!revoInfo->cert) { 1.1311 + fprintf(stderr, "cannot find cert with nickname %s\n", 1.1312 + revoInfo->nickname); 1.1313 + exit(1); 1.1314 + } 1.1315 + inFile = PR_Open(revoInfo->crlFilename, PR_RDONLY, 0); 1.1316 + if (inFile) { 1.1317 + rv = SECU_ReadDERFromFile(&crlDER, inFile, PR_FALSE, PR_FALSE); 1.1318 + PR_Close(inFile); 1.1319 + inFile = NULL; 1.1320 + } 1.1321 + if (rv != SECSuccess) { 1.1322 + fprintf(stderr, "unable to read crl file %s\n", 1.1323 + revoInfo->crlFilename); 1.1324 + exit(1); 1.1325 + } 1.1326 + revoInfo->crl = 1.1327 + CERT_DecodeDERCrlWithFlags(NULL, &crlDER, SEC_CRL_TYPE, 1.1328 + CRL_DECODE_DEFAULT_OPTIONS); 1.1329 + if (!revoInfo->crl) { 1.1330 + fprintf(stderr, "unable to decode crl file %s\n", 1.1331 + revoInfo->crlFilename); 1.1332 + exit(1); 1.1333 + } 1.1334 + if (CERT_CompareName(&revoInfo->crl->crl.name, 1.1335 + &revoInfo->cert->subject) != SECEqual) { 1.1336 + fprintf(stderr, "CRL %s doesn't match cert identified by preceding nickname %s\n", 1.1337 + revoInfo->crlFilename, revoInfo->nickname); 1.1338 + exit(1); 1.1339 + } 1.1340 + revoInfo->id = cert_CreateSelfCAID(revoInfo->cert, PR_Now()); 1.1341 + caRevoIter = PR_NEXT_LINK(caRevoIter); 1.1342 + } while (caRevoIter != &caRevoInfos->link); 1.1343 + } 1.1344 + } 1.1345 + 1.1346 +/* allocate the array of thread slots, and launch the worker threads. */ 1.1347 + rv = launch_threads(&jobLoop, 0, 0, 0, useLocalThreads); 1.1348 + 1.1349 + if (rv == SECSuccess) { 1.1350 + server_main(listen_sock, 0, 0, 0, 1.1351 + 0); 1.1352 + } 1.1353 + 1.1354 + VLOG(("httpserv: server_thread: exiting")); 1.1355 + 1.1356 + if (provideOcsp) { 1.1357 + if (caRevoInfos) { 1.1358 + PRCList *caRevoIter; 1.1359 + 1.1360 + caRevoIter = &caRevoInfos->link; 1.1361 + do { 1.1362 + caRevoInfo *revoInfo = (caRevoInfo*)caRevoIter; 1.1363 + if (revoInfo->nickname) 1.1364 + PORT_Free(revoInfo->nickname); 1.1365 + if (revoInfo->crlFilename) 1.1366 + PORT_Free(revoInfo->crlFilename); 1.1367 + if (revoInfo->cert) 1.1368 + CERT_DestroyCertificate(revoInfo->cert); 1.1369 + if (revoInfo->id) 1.1370 + CERT_DestroyOCSPCertID(revoInfo->id); 1.1371 + if (revoInfo->crl) 1.1372 + SEC_DestroyCrl(revoInfo->crl); 1.1373 + 1.1374 + caRevoIter = PR_NEXT_LINK(caRevoIter); 1.1375 + } while (caRevoIter != &caRevoInfos->link); 1.1376 + 1.1377 + } 1.1378 + if (NSS_Shutdown() != SECSuccess) { 1.1379 + SECU_PrintError(progName, "NSS_Shutdown"); 1.1380 + PR_Cleanup(); 1.1381 + exit(1); 1.1382 + } 1.1383 + } 1.1384 + if (passwd) { 1.1385 + PORT_Free(passwd); 1.1386 + } 1.1387 + if (pwfile) { 1.1388 + PORT_Free(pwfile); 1.1389 + } 1.1390 + if (certPrefix && certPrefix != emptyString) { 1.1391 + PORT_Free(certPrefix); 1.1392 + } 1.1393 + PR_Cleanup(); 1.1394 + printf("httpserv: normal termination\n"); 1.1395 + return 0; 1.1396 +} 1.1397 +