security/nss/cmd/httpserv/httpserv.c

changeset 0
6474c204b198
     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 +

mercurial