michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include "secutil.h" michael@0: michael@0: #if defined(XP_UNIX) michael@0: #include michael@0: #endif michael@0: michael@0: #if defined(_WINDOWS) michael@0: #include /* for getpid() */ michael@0: #endif michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "nspr.h" michael@0: #include "prio.h" michael@0: #include "prerror.h" michael@0: #include "prnetdb.h" michael@0: #include "prclist.h" michael@0: #include "plgetopt.h" michael@0: #include "pk11func.h" michael@0: #include "nss.h" michael@0: #include "nssb64.h" michael@0: #include "sechash.h" michael@0: #include "cert.h" michael@0: #include "certdb.h" michael@0: #include "ocsp.h" michael@0: #include "ocspti.h" michael@0: #include "ocspi.h" michael@0: michael@0: #ifndef PORT_Sprintf michael@0: #define PORT_Sprintf sprintf michael@0: #endif michael@0: michael@0: #ifndef PORT_Strstr michael@0: #define PORT_Strstr strstr michael@0: #endif michael@0: michael@0: #ifndef PORT_Malloc michael@0: #define PORT_Malloc PR_Malloc michael@0: #endif michael@0: michael@0: static int handle_connection( PRFileDesc *, PRFileDesc *, int ); michael@0: michael@0: /* data and structures for shutdown */ michael@0: static int stopping; michael@0: michael@0: static PRBool noDelay; michael@0: static int verbose; michael@0: michael@0: static PRThread * acceptorThread; michael@0: michael@0: static PRLogModuleInfo *lm; michael@0: michael@0: #define PRINTF if (verbose) printf michael@0: #define FPRINTF if (verbose) fprintf michael@0: #define FLUSH if (verbose) { fflush(stdout); fflush(stderr); } michael@0: #define VLOG(arg) PR_LOG(lm,PR_LOG_DEBUG,arg) michael@0: michael@0: static void michael@0: Usage(const char *progName) michael@0: { michael@0: fprintf(stderr, michael@0: michael@0: "Usage: %s -p port [-Dbv]\n" michael@0: " [-t threads] [-i pid_file]\n" michael@0: " [-A nickname -C crl-filename]... [-O method]\n" michael@0: " [-d dbdir] [-f password_file] [-w password] [-P dbprefix]\n" michael@0: "-D means disable Nagle delays in TCP\n" michael@0: "-b means try binding to the port and exit\n" michael@0: "-v means verbose output\n" michael@0: "-t threads -- specify the number of threads to use for connections.\n" michael@0: "-i pid_file file to write the process id of httpserv\n" michael@0: "Parameters -A, -C and -O are used to provide an OCSP server at /ocsp?\n" michael@0: "-A a nickname of a CA certificate\n" michael@0: "-C a CRL filename corresponding to the preceding CA nickname\n" michael@0: "-O allowed HTTP methods for OCSP requests: get, post, all, random, get-unknown\n" michael@0: " random means: randomly fail if request method is GET, POST always works\n" michael@0: " get-unknown means: status unknown for GET, correct status for POST\n" michael@0: "Multiple pairs of parameters -A and -C are allowed.\n" michael@0: "If status for a cert from an unknown CA is requested, the cert from the\n" michael@0: "first -A parameter will be used to sign the unknown status response.\n" michael@0: "NSS database parameters are used only if OCSP parameters are used.\n" michael@0: ,progName); michael@0: } michael@0: michael@0: static const char * michael@0: errWarn(char * funcString) michael@0: { michael@0: PRErrorCode perr = PR_GetError(); michael@0: const char * errString = SECU_Strerror(perr); michael@0: michael@0: fprintf(stderr, "httpserv: %s returned error %d:\n%s\n", michael@0: funcString, perr, errString); michael@0: return errString; michael@0: } michael@0: michael@0: static void michael@0: errExit(char * funcString) michael@0: { michael@0: errWarn(funcString); michael@0: exit(3); michael@0: } michael@0: michael@0: #define MAX_VIRT_SERVER_NAME_ARRAY_INDEX 10 michael@0: michael@0: /************************************************************************** michael@0: ** Begin thread management routines and data. michael@0: **************************************************************************/ michael@0: #define MIN_THREADS 3 michael@0: #define DEFAULT_THREADS 8 michael@0: #define MAX_THREADS 4096 michael@0: #define MAX_PROCS 25 michael@0: static int maxThreads = DEFAULT_THREADS; michael@0: michael@0: michael@0: typedef struct jobStr { michael@0: PRCList link; michael@0: PRFileDesc *tcp_sock; michael@0: PRFileDesc *model_sock; michael@0: int requestCert; michael@0: } JOB; michael@0: michael@0: static PZLock * qLock; /* this lock protects all data immediately below */ michael@0: static PRLock * lastLoadedCrlLock; /* this lock protects lastLoadedCrl variable */ michael@0: static PZCondVar * jobQNotEmptyCv; michael@0: static PZCondVar * freeListNotEmptyCv; michael@0: static PZCondVar * threadCountChangeCv; michael@0: static int threadCount; michael@0: static PRCList jobQ; michael@0: static PRCList freeJobs; michael@0: static JOB *jobTable; michael@0: michael@0: SECStatus michael@0: setupJobs(int maxJobs) michael@0: { michael@0: int i; michael@0: michael@0: jobTable = (JOB *)PR_Calloc(maxJobs, sizeof(JOB)); michael@0: if (!jobTable) michael@0: return SECFailure; michael@0: michael@0: PR_INIT_CLIST(&jobQ); michael@0: PR_INIT_CLIST(&freeJobs); michael@0: michael@0: for (i = 0; i < maxJobs; ++i) { michael@0: JOB * pJob = jobTable + i; michael@0: PR_APPEND_LINK(&pJob->link, &freeJobs); michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: typedef int startFn(PRFileDesc *a, PRFileDesc *b, int c); michael@0: michael@0: typedef enum { rs_idle = 0, rs_running = 1, rs_zombie = 2 } runState; michael@0: michael@0: typedef struct perThreadStr { michael@0: PRFileDesc *a; michael@0: PRFileDesc *b; michael@0: int c; michael@0: int rv; michael@0: startFn * startFunc; michael@0: PRThread * prThread; michael@0: runState state; michael@0: } perThread; michael@0: michael@0: static perThread *threads; michael@0: michael@0: void michael@0: thread_wrapper(void * arg) michael@0: { michael@0: perThread * slot = (perThread *)arg; michael@0: michael@0: slot->rv = (* slot->startFunc)(slot->a, slot->b, slot->c); michael@0: michael@0: /* notify the thread exit handler. */ michael@0: PZ_Lock(qLock); michael@0: slot->state = rs_zombie; michael@0: --threadCount; michael@0: PZ_NotifyAllCondVar(threadCountChangeCv); michael@0: PZ_Unlock(qLock); michael@0: } michael@0: michael@0: int michael@0: jobLoop(PRFileDesc *a, PRFileDesc *b, int c) michael@0: { michael@0: PRCList * myLink = 0; michael@0: JOB * myJob; michael@0: michael@0: PZ_Lock(qLock); michael@0: do { michael@0: myLink = 0; michael@0: while (PR_CLIST_IS_EMPTY(&jobQ) && !stopping) { michael@0: PZ_WaitCondVar(jobQNotEmptyCv, PR_INTERVAL_NO_TIMEOUT); michael@0: } michael@0: if (!PR_CLIST_IS_EMPTY(&jobQ)) { michael@0: myLink = PR_LIST_HEAD(&jobQ); michael@0: PR_REMOVE_AND_INIT_LINK(myLink); michael@0: } michael@0: PZ_Unlock(qLock); michael@0: myJob = (JOB *)myLink; michael@0: /* myJob will be null when stopping is true and jobQ is empty */ michael@0: if (!myJob) michael@0: break; michael@0: handle_connection( myJob->tcp_sock, myJob->model_sock, michael@0: myJob->requestCert); michael@0: PZ_Lock(qLock); michael@0: PR_APPEND_LINK(myLink, &freeJobs); michael@0: PZ_NotifyCondVar(freeListNotEmptyCv); michael@0: } while (PR_TRUE); michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: SECStatus michael@0: launch_threads( michael@0: startFn *startFunc, michael@0: PRFileDesc *a, michael@0: PRFileDesc *b, michael@0: int c, michael@0: PRBool local) michael@0: { michael@0: int i; michael@0: SECStatus rv = SECSuccess; michael@0: michael@0: /* create the thread management serialization structs */ michael@0: qLock = PZ_NewLock(nssILockSelfServ); michael@0: jobQNotEmptyCv = PZ_NewCondVar(qLock); michael@0: freeListNotEmptyCv = PZ_NewCondVar(qLock); michael@0: threadCountChangeCv = PZ_NewCondVar(qLock); michael@0: michael@0: /* create monitor for crl reload procedure */ michael@0: lastLoadedCrlLock = PR_NewLock(); michael@0: michael@0: /* allocate the array of thread slots */ michael@0: threads = PR_Calloc(maxThreads, sizeof(perThread)); michael@0: if ( NULL == threads ) { michael@0: fprintf(stderr, "Oh Drat! Can't allocate the perThread array\n"); michael@0: return SECFailure; michael@0: } michael@0: /* 5 is a little extra, intended to keep the jobQ from underflowing. michael@0: ** That is, from going empty while not stopping and clients are still michael@0: ** trying to contact us. michael@0: */ michael@0: rv = setupJobs(maxThreads + 5); michael@0: if (rv != SECSuccess) michael@0: return rv; michael@0: michael@0: PZ_Lock(qLock); michael@0: for (i = 0; i < maxThreads; ++i) { michael@0: perThread * slot = threads + i; michael@0: michael@0: slot->state = rs_running; michael@0: slot->a = a; michael@0: slot->b = b; michael@0: slot->c = c; michael@0: slot->startFunc = startFunc; michael@0: slot->prThread = PR_CreateThread(PR_USER_THREAD, michael@0: thread_wrapper, slot, PR_PRIORITY_NORMAL, michael@0: (PR_TRUE==local)?PR_LOCAL_THREAD:PR_GLOBAL_THREAD, michael@0: PR_UNJOINABLE_THREAD, 0); michael@0: if (slot->prThread == NULL) { michael@0: printf("httpserv: Failed to launch thread!\n"); michael@0: slot->state = rs_idle; michael@0: rv = SECFailure; michael@0: break; michael@0: } michael@0: michael@0: ++threadCount; michael@0: } michael@0: PZ_Unlock(qLock); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: #define DESTROY_CONDVAR(name) if (name) { \ michael@0: PZ_DestroyCondVar(name); name = NULL; } michael@0: #define DESTROY_LOCK(name) if (name) { \ michael@0: PZ_DestroyLock(name); name = NULL; } michael@0: michael@0: michael@0: void michael@0: terminateWorkerThreads(void) michael@0: { michael@0: VLOG(("httpserv: server_thead: waiting on stopping")); michael@0: PZ_Lock(qLock); michael@0: PZ_NotifyAllCondVar(jobQNotEmptyCv); michael@0: while (threadCount > 0) { michael@0: PZ_WaitCondVar(threadCountChangeCv, PR_INTERVAL_NO_TIMEOUT); michael@0: } michael@0: /* The worker threads empty the jobQ before they terminate. */ michael@0: PORT_Assert(PR_CLIST_IS_EMPTY(&jobQ)); michael@0: PZ_Unlock(qLock); michael@0: michael@0: DESTROY_CONDVAR(jobQNotEmptyCv); michael@0: DESTROY_CONDVAR(freeListNotEmptyCv); michael@0: DESTROY_CONDVAR(threadCountChangeCv); michael@0: michael@0: PR_DestroyLock(lastLoadedCrlLock); michael@0: DESTROY_LOCK(qLock); michael@0: PR_Free(jobTable); michael@0: PR_Free(threads); michael@0: } michael@0: michael@0: /************************************************************************** michael@0: ** End thread management routines. michael@0: **************************************************************************/ michael@0: michael@0: PRBool NoReuse = PR_FALSE; michael@0: PRBool disableLocking = PR_FALSE; michael@0: static secuPWData pwdata = { PW_NONE, 0 }; michael@0: michael@0: struct caRevoInfoStr michael@0: { michael@0: PRCList link; michael@0: char *nickname; michael@0: char *crlFilename; michael@0: CERTCertificate *cert; michael@0: CERTOCSPCertID *id; michael@0: CERTSignedCrl *crl; michael@0: }; michael@0: typedef struct caRevoInfoStr caRevoInfo; michael@0: /* Created during app init. No locks necessary, michael@0: * because later on, only read access will occur. */ michael@0: static caRevoInfo *caRevoInfos = NULL; michael@0: michael@0: static enum { michael@0: ocspGetOnly, ocspPostOnly, ocspGetAndPost, ocspRandomGetFailure, ocspGetUnknown michael@0: } ocspMethodsAllowed = ocspGetAndPost; michael@0: michael@0: static const char stopCmd[] = { "GET /stop " }; michael@0: static const char getCmd[] = { "GET " }; michael@0: static const char EOFmsg[] = { "EOF\r\n\r\n\r\n" }; michael@0: static const char outHeader[] = { michael@0: "HTTP/1.0 200 OK\r\n" michael@0: "Server: Generic Web Server\r\n" michael@0: "Date: Tue, 26 Aug 1997 22:10:05 GMT\r\n" michael@0: "Content-type: text/plain\r\n" michael@0: "\r\n" michael@0: }; michael@0: static const char outOcspHeader[] = { michael@0: "HTTP/1.0 200 OK\r\n" michael@0: "Server: Generic OCSP Server\r\n" michael@0: "Content-type: application/ocsp-response\r\n" michael@0: "\r\n" michael@0: }; michael@0: static const char outBadRequestHeader[] = { michael@0: "HTTP/1.0 400 Bad Request\r\n" michael@0: "Server: Generic OCSP Server\r\n" michael@0: "\r\n" michael@0: }; michael@0: michael@0: void stop_server() michael@0: { michael@0: stopping = 1; michael@0: PR_Interrupt(acceptorThread); michael@0: PZ_TraceFlush(); michael@0: } michael@0: michael@0: /* Will only work if the original input to url encoding was michael@0: * a base64 encoded buffer. Will only decode the sequences used michael@0: * for encoding the special base64 characters, and fail if any michael@0: * other encoded chars are found. michael@0: * Will return SECSuccess if input could be processed. michael@0: * Coversion is done in place. michael@0: */ michael@0: static SECStatus michael@0: urldecode_base64chars_inplace(char *buf) michael@0: { michael@0: char *walk; michael@0: size_t remaining_bytes; michael@0: michael@0: if (!buf || !*buf) michael@0: return SECFailure; michael@0: michael@0: walk = buf; michael@0: remaining_bytes = strlen(buf) + 1; /* include terminator */ michael@0: michael@0: while (*walk) { michael@0: if (*walk == '%') { michael@0: if (!PL_strncasecmp(walk, "%2B", 3)) { michael@0: *walk = '+'; michael@0: } else if (!PL_strncasecmp(walk, "%2F", 3)) { michael@0: *walk = '/'; michael@0: } else if (!PL_strncasecmp(walk, "%3D", 3)) { michael@0: *walk = '='; michael@0: } else { michael@0: return SECFailure; michael@0: } michael@0: remaining_bytes -= 3; michael@0: ++walk; michael@0: memmove(walk, walk+2, remaining_bytes); michael@0: } else { michael@0: ++walk; michael@0: --remaining_bytes; michael@0: } michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: int michael@0: handle_connection( michael@0: PRFileDesc *tcp_sock, michael@0: PRFileDesc *model_sock, michael@0: int requestCert michael@0: ) michael@0: { michael@0: PRFileDesc * ssl_sock = NULL; michael@0: PRFileDesc * local_file_fd = NULL; michael@0: char * pBuf; /* unused space at end of buf */ michael@0: const char * errString; michael@0: PRStatus status; michael@0: int bufRem; /* unused bytes at end of buf */ michael@0: int bufDat; /* characters received in buf */ michael@0: int newln = 0; /* # of consecutive newlns */ michael@0: int firstTime = 1; michael@0: int reqLen; michael@0: int rv; michael@0: int numIOVs; michael@0: PRSocketOptionData opt; michael@0: PRIOVec iovs[16]; michael@0: char msgBuf[160]; michael@0: char buf[10240]; michael@0: char fileName[513]; michael@0: char *getData = NULL; /* inplace conversion */ michael@0: SECItem postData; michael@0: PRBool isOcspRequest = PR_FALSE; michael@0: PRBool isPost; michael@0: michael@0: postData.data = NULL; michael@0: postData.len = 0; michael@0: michael@0: pBuf = buf; michael@0: bufRem = sizeof buf; michael@0: michael@0: VLOG(("httpserv: handle_connection: starting")); michael@0: opt.option = PR_SockOpt_Nonblocking; michael@0: opt.value.non_blocking = PR_FALSE; michael@0: PR_SetSocketOption(tcp_sock, &opt); michael@0: michael@0: VLOG(("httpserv: handle_connection: starting\n")); michael@0: ssl_sock = tcp_sock; michael@0: michael@0: if (noDelay) { michael@0: opt.option = PR_SockOpt_NoDelay; michael@0: opt.value.no_delay = PR_TRUE; michael@0: status = PR_SetSocketOption(ssl_sock, &opt); michael@0: if (status != PR_SUCCESS) { michael@0: errWarn("PR_SetSocketOption(PR_SockOpt_NoDelay, PR_TRUE)"); michael@0: if (ssl_sock) { michael@0: PR_Close(ssl_sock); michael@0: } michael@0: return SECFailure; michael@0: } michael@0: } michael@0: michael@0: while (1) { michael@0: const char *post; michael@0: const char *foundStr = NULL; michael@0: const char *tmp = NULL; michael@0: michael@0: newln = 0; michael@0: reqLen = 0; michael@0: michael@0: rv = PR_Read(ssl_sock, pBuf, bufRem - 1); michael@0: if (rv == 0 || michael@0: (rv < 0 && PR_END_OF_FILE_ERROR == PR_GetError())) { michael@0: if (verbose) michael@0: errWarn("HDX PR_Read hit EOF"); michael@0: break; michael@0: } michael@0: if (rv < 0) { michael@0: errWarn("HDX PR_Read"); michael@0: goto cleanup; michael@0: } michael@0: /* NULL termination */ michael@0: pBuf[rv] = 0; michael@0: if (firstTime) { michael@0: firstTime = 0; michael@0: } michael@0: michael@0: pBuf += rv; michael@0: bufRem -= rv; michael@0: bufDat = pBuf - buf; michael@0: /* Parse the input, starting at the beginning of the buffer. michael@0: * Stop when we detect two consecutive \n's (or \r\n's) michael@0: * as this signifies the end of the GET or POST portion. michael@0: * The posted data follows. michael@0: */ michael@0: while (reqLen < bufDat && newln < 2) { michael@0: int octet = buf[reqLen++]; michael@0: if (octet == '\n') { michael@0: newln++; michael@0: } else if (octet != '\r') { michael@0: newln = 0; michael@0: } michael@0: } michael@0: michael@0: /* came to the end of the buffer, or second newln michael@0: * If we didn't get an empty line (CRLFCRLF) then keep on reading. michael@0: */ michael@0: if (newln < 2) michael@0: continue; michael@0: michael@0: /* we're at the end of the HTTP request. michael@0: * If the request is a POST, then there will be one more michael@0: * line of data. michael@0: * This parsing is a hack, but ok for SSL test purposes. michael@0: */ michael@0: post = PORT_Strstr(buf, "POST "); michael@0: if (!post || *post != 'P') michael@0: break; michael@0: michael@0: postData.data = (void*)(buf + reqLen); michael@0: michael@0: tmp = "content-length: "; michael@0: foundStr = PL_strcasestr(buf, tmp); michael@0: if (foundStr) { michael@0: int expectedPostLen; michael@0: int havePostLen; michael@0: michael@0: expectedPostLen = atoi(foundStr+strlen(tmp)); michael@0: havePostLen = bufDat - reqLen; michael@0: if (havePostLen >= expectedPostLen) { michael@0: postData.len = expectedPostLen; michael@0: break; michael@0: } michael@0: } else { michael@0: /* use legacy hack */ michael@0: /* It's a post, so look for the next and final CR/LF. */ michael@0: while (reqLen < bufDat && newln < 3) { michael@0: int octet = buf[reqLen++]; michael@0: if (octet == '\n') { michael@0: newln++; michael@0: } michael@0: } michael@0: if (newln == 3) michael@0: break; michael@0: } michael@0: } /* read loop */ michael@0: michael@0: bufDat = pBuf - buf; michael@0: if (bufDat) do { /* just close if no data */ michael@0: /* Have either (a) a complete get, (b) a complete post, (c) EOF */ michael@0: if (reqLen > 0) { michael@0: PRBool isGetOrPost = PR_FALSE; michael@0: unsigned skipChars = 0; michael@0: isPost = PR_FALSE; michael@0: michael@0: if (!strncmp(buf, getCmd, sizeof getCmd - 1)) { michael@0: isGetOrPost = PR_TRUE; michael@0: skipChars = 4; michael@0: } michael@0: else if (!strncmp(buf, "POST ", 5)) { michael@0: isGetOrPost = PR_TRUE; michael@0: isPost = PR_TRUE; michael@0: skipChars = 5; michael@0: } michael@0: michael@0: if (isGetOrPost) { michael@0: char * fnBegin = buf; michael@0: char * fnEnd; michael@0: char * fnstart = NULL; michael@0: PRFileInfo info; michael@0: michael@0: fnBegin += skipChars; michael@0: michael@0: fnEnd = strpbrk(fnBegin, " \r\n"); michael@0: if (fnEnd) { michael@0: int fnLen = fnEnd - fnBegin; michael@0: if (fnLen < sizeof fileName) { michael@0: strncpy(fileName, fnBegin, fnLen); michael@0: fileName[fnLen] = 0; /* null terminate */ michael@0: fnstart = fileName; michael@0: /* strip initial / because our root is the current directory*/ michael@0: while (*fnstart && *fnstart=='/') michael@0: ++fnstart; michael@0: } michael@0: } michael@0: if (fnstart) { michael@0: if (!strncmp(fnstart, "ocsp", 4)) { michael@0: if (isPost) { michael@0: if (postData.data) { michael@0: isOcspRequest = PR_TRUE; michael@0: } michael@0: } else { michael@0: if (!strncmp(fnstart, "ocsp/", 5)) { michael@0: isOcspRequest = PR_TRUE; michael@0: getData = fnstart + 5; michael@0: } michael@0: } michael@0: } else { michael@0: /* try to open the file named. michael@0: * If successful, then write it to the client. michael@0: */ michael@0: status = PR_GetFileInfo(fnstart, &info); michael@0: if (status == PR_SUCCESS && michael@0: info.type == PR_FILE_FILE && michael@0: info.size >= 0 ) { michael@0: local_file_fd = PR_Open(fnstart, PR_RDONLY, 0); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: numIOVs = 0; michael@0: michael@0: iovs[numIOVs].iov_base = (char *)outHeader; michael@0: iovs[numIOVs].iov_len = (sizeof(outHeader)) - 1; michael@0: numIOVs++; michael@0: michael@0: if (isOcspRequest && caRevoInfos) { michael@0: CERTOCSPRequest *request = NULL; michael@0: PRBool failThisRequest = PR_FALSE; michael@0: michael@0: if (ocspMethodsAllowed == ocspGetOnly && postData.len) { michael@0: failThisRequest = PR_TRUE; michael@0: } else if (ocspMethodsAllowed == ocspPostOnly && getData) { michael@0: failThisRequest = PR_TRUE; michael@0: } else if (ocspMethodsAllowed == ocspRandomGetFailure && getData) { michael@0: if (!(rand() % 2)) { michael@0: failThisRequest = PR_TRUE; michael@0: } michael@0: } michael@0: michael@0: if (failThisRequest) { michael@0: PR_Write(ssl_sock, outBadRequestHeader, strlen(outBadRequestHeader)); michael@0: break; michael@0: } michael@0: /* get is base64, post is binary. michael@0: * If we have base64, convert into the (empty) postData array. michael@0: */ michael@0: if (getData) { michael@0: if (urldecode_base64chars_inplace(getData) == SECSuccess) { michael@0: NSSBase64_DecodeBuffer(NULL, &postData, getData, strlen(getData)); michael@0: } michael@0: } michael@0: if (postData.len) { michael@0: request = CERT_DecodeOCSPRequest(&postData); michael@0: } michael@0: if (!request || !request->tbsRequest || michael@0: !request->tbsRequest->requestList || michael@0: !request->tbsRequest->requestList[0]) { michael@0: PORT_Sprintf(msgBuf, "Cannot decode OCSP request.\r\n"); michael@0: michael@0: iovs[numIOVs].iov_base = msgBuf; michael@0: iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); michael@0: numIOVs++; michael@0: } else { michael@0: /* TODO: support more than one request entry */ michael@0: CERTOCSPCertID *reqid = request->tbsRequest->requestList[0]->reqCert; michael@0: const caRevoInfo *revoInfo = NULL; michael@0: PRBool unknown = PR_FALSE; michael@0: PRBool revoked = PR_FALSE; michael@0: PRTime nextUpdate = 0; michael@0: PRTime revoDate = 0; michael@0: PRCList *caRevoIter; michael@0: michael@0: caRevoIter = &caRevoInfos->link; michael@0: do { michael@0: CERTOCSPCertID *caid; michael@0: michael@0: revoInfo = (caRevoInfo*)caRevoIter; michael@0: caid = revoInfo->id; michael@0: michael@0: if (SECOID_CompareAlgorithmID(&reqid->hashAlgorithm, michael@0: &caid->hashAlgorithm) == SECEqual michael@0: && michael@0: SECITEM_CompareItem(&reqid->issuerNameHash, michael@0: &caid->issuerNameHash) == SECEqual michael@0: && michael@0: SECITEM_CompareItem(&reqid->issuerKeyHash, michael@0: &caid->issuerKeyHash) == SECEqual) { michael@0: break; michael@0: } michael@0: revoInfo = NULL; michael@0: caRevoIter = PR_NEXT_LINK(caRevoIter); michael@0: } while (caRevoIter != &caRevoInfos->link); michael@0: michael@0: if (!revoInfo) { michael@0: unknown = PR_TRUE; michael@0: revoInfo = caRevoInfos; michael@0: } else { michael@0: CERTCrl *crl = &revoInfo->crl->crl; michael@0: CERTCrlEntry *entry = NULL; michael@0: DER_DecodeTimeChoice(&nextUpdate, &crl->nextUpdate); michael@0: if (crl->entries) { michael@0: int iv = 0; michael@0: /* assign, not compare */ michael@0: while ((entry = crl->entries[iv++])) { michael@0: if (SECITEM_CompareItem(&reqid->serialNumber, michael@0: &entry->serialNumber) == SECEqual) { michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: if (entry) { michael@0: /* revoked status response */ michael@0: revoked = PR_TRUE; michael@0: DER_DecodeTimeChoice(&revoDate, &entry->revocationDate); michael@0: } else { michael@0: /* else good status response */ michael@0: if (!isPost && ocspMethodsAllowed == ocspGetUnknown) { michael@0: unknown = PR_TRUE; michael@0: nextUpdate = PR_Now() + 60*60*24 * PR_USEC_PER_SEC; /*tomorrow*/ michael@0: revoDate = PR_Now() - 60*60*24 * PR_USEC_PER_SEC; /*yesterday*/ michael@0: } michael@0: } michael@0: } michael@0: michael@0: { michael@0: PRTime now = PR_Now(); michael@0: PLArenaPool *arena = NULL; michael@0: CERTOCSPSingleResponse *sr; michael@0: CERTOCSPSingleResponse **singleResponses; michael@0: SECItem *ocspResponse; michael@0: michael@0: arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); michael@0: michael@0: if (unknown) { michael@0: sr = CERT_CreateOCSPSingleResponseUnknown(arena, reqid, now, michael@0: &nextUpdate); michael@0: } else if (revoked) { michael@0: sr = CERT_CreateOCSPSingleResponseRevoked(arena, reqid, now, michael@0: &nextUpdate, revoDate, NULL); michael@0: } else { michael@0: sr = CERT_CreateOCSPSingleResponseGood(arena, reqid, now, michael@0: &nextUpdate); michael@0: } michael@0: michael@0: /* meaning of value 2: one entry + one end marker */ michael@0: singleResponses = PORT_ArenaNewArray(arena, CERTOCSPSingleResponse*, 2); michael@0: singleResponses[0] = sr; michael@0: singleResponses[1] = NULL; michael@0: ocspResponse = CERT_CreateEncodedOCSPSuccessResponse(arena, michael@0: revoInfo->cert, ocspResponderID_byName, now, michael@0: singleResponses, &pwdata); michael@0: michael@0: if (!ocspResponse) { michael@0: PORT_Sprintf(msgBuf, "Failed to encode response\r\n"); michael@0: iovs[numIOVs].iov_base = msgBuf; michael@0: iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); michael@0: numIOVs++; michael@0: } else { michael@0: PR_Write(ssl_sock, outOcspHeader, strlen(outOcspHeader)); michael@0: PR_Write(ssl_sock, ocspResponse->data, ocspResponse->len); michael@0: PORT_FreeArena(arena, PR_FALSE); michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: } else if (local_file_fd) { michael@0: PRInt32 bytes; michael@0: int errLen; michael@0: bytes = PR_TransmitFile(ssl_sock, local_file_fd, outHeader, michael@0: sizeof outHeader - 1, michael@0: PR_TRANSMITFILE_KEEP_OPEN, michael@0: PR_INTERVAL_NO_TIMEOUT); michael@0: if (bytes >= 0) { michael@0: bytes -= sizeof outHeader - 1; michael@0: FPRINTF(stderr, michael@0: "httpserv: PR_TransmitFile wrote %d bytes from %s\n", michael@0: bytes, fileName); michael@0: break; michael@0: } michael@0: errString = errWarn("PR_TransmitFile"); michael@0: errLen = PORT_Strlen(errString); michael@0: errLen = PR_MIN(errLen, sizeof msgBuf - 1); michael@0: PORT_Memcpy(msgBuf, errString, errLen); michael@0: msgBuf[errLen] = 0; michael@0: michael@0: iovs[numIOVs].iov_base = msgBuf; michael@0: iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); michael@0: numIOVs++; michael@0: } else if (reqLen <= 0) { /* hit eof */ michael@0: PORT_Sprintf(msgBuf, "Get or Post incomplete after %d bytes.\r\n", michael@0: bufDat); michael@0: michael@0: iovs[numIOVs].iov_base = msgBuf; michael@0: iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); michael@0: numIOVs++; michael@0: } else if (reqLen < bufDat) { michael@0: PORT_Sprintf(msgBuf, "Discarded %d characters.\r\n", michael@0: bufDat - reqLen); michael@0: michael@0: iovs[numIOVs].iov_base = msgBuf; michael@0: iovs[numIOVs].iov_len = PORT_Strlen(msgBuf); michael@0: numIOVs++; michael@0: } michael@0: michael@0: if (reqLen > 0) { michael@0: if (verbose > 1) michael@0: fwrite(buf, 1, reqLen, stdout); /* display it */ michael@0: michael@0: iovs[numIOVs].iov_base = buf; michael@0: iovs[numIOVs].iov_len = reqLen; michael@0: numIOVs++; michael@0: } michael@0: michael@0: rv = PR_Writev(ssl_sock, iovs, numIOVs, PR_INTERVAL_NO_TIMEOUT); michael@0: if (rv < 0) { michael@0: errWarn("PR_Writev"); michael@0: break; michael@0: } michael@0: michael@0: } while (0); michael@0: michael@0: cleanup: michael@0: if (ssl_sock) { michael@0: PR_Close(ssl_sock); michael@0: } else if (tcp_sock) { michael@0: PR_Close(tcp_sock); michael@0: } michael@0: if (local_file_fd) michael@0: PR_Close(local_file_fd); michael@0: VLOG(("httpserv: handle_connection: exiting\n")); michael@0: michael@0: /* do a nice shutdown if asked. */ michael@0: if (!strncmp(buf, stopCmd, sizeof stopCmd - 1)) { michael@0: VLOG(("httpserv: handle_connection: stop command")); michael@0: stop_server(); michael@0: } michael@0: VLOG(("httpserv: handle_connection: exiting")); michael@0: return SECSuccess; /* success */ michael@0: } michael@0: michael@0: #ifdef XP_UNIX michael@0: michael@0: void sigusr1_handler(int sig) michael@0: { michael@0: VLOG(("httpserv: sigusr1_handler: stop server")); michael@0: stop_server(); michael@0: } michael@0: michael@0: #endif michael@0: michael@0: SECStatus michael@0: do_accepts( michael@0: PRFileDesc *listen_sock, michael@0: PRFileDesc *model_sock, michael@0: int requestCert michael@0: ) michael@0: { michael@0: PRNetAddr addr; michael@0: PRErrorCode perr; michael@0: #ifdef XP_UNIX michael@0: struct sigaction act; michael@0: #endif michael@0: michael@0: VLOG(("httpserv: do_accepts: starting")); michael@0: PR_SetThreadPriority( PR_GetCurrentThread(), PR_PRIORITY_HIGH); michael@0: michael@0: acceptorThread = PR_GetCurrentThread(); michael@0: #ifdef XP_UNIX michael@0: /* set up the signal handler */ michael@0: act.sa_handler = sigusr1_handler; michael@0: sigemptyset(&act.sa_mask); michael@0: act.sa_flags = 0; michael@0: if (sigaction(SIGUSR1, &act, NULL)) { michael@0: fprintf(stderr, "Error installing signal handler.\n"); michael@0: exit(1); michael@0: } michael@0: #endif michael@0: while (!stopping) { michael@0: PRFileDesc *tcp_sock; michael@0: PRCList *myLink; michael@0: michael@0: FPRINTF(stderr, "\n\n\nhttpserv: About to call accept.\n"); michael@0: tcp_sock = PR_Accept(listen_sock, &addr, PR_INTERVAL_NO_TIMEOUT); michael@0: if (tcp_sock == NULL) { michael@0: perr = PR_GetError(); michael@0: if ((perr != PR_CONNECT_RESET_ERROR && michael@0: perr != PR_PENDING_INTERRUPT_ERROR) || verbose) { michael@0: errWarn("PR_Accept"); michael@0: } michael@0: if (perr == PR_CONNECT_RESET_ERROR) { michael@0: FPRINTF(stderr, michael@0: "Ignoring PR_CONNECT_RESET_ERROR error - continue\n"); michael@0: continue; michael@0: } michael@0: stopping = 1; michael@0: break; michael@0: } michael@0: michael@0: VLOG(("httpserv: do_accept: Got connection\n")); michael@0: michael@0: PZ_Lock(qLock); michael@0: while (PR_CLIST_IS_EMPTY(&freeJobs) && !stopping) { michael@0: PZ_WaitCondVar(freeListNotEmptyCv, PR_INTERVAL_NO_TIMEOUT); michael@0: } michael@0: if (stopping) { michael@0: PZ_Unlock(qLock); michael@0: if (tcp_sock) { michael@0: PR_Close(tcp_sock); michael@0: } michael@0: break; michael@0: } michael@0: myLink = PR_LIST_HEAD(&freeJobs); michael@0: PR_REMOVE_AND_INIT_LINK(myLink); michael@0: /* could release qLock here and reaquire it 7 lines below, but michael@0: ** why bother for 4 assignment statements? michael@0: */ michael@0: { michael@0: JOB * myJob = (JOB *)myLink; michael@0: myJob->tcp_sock = tcp_sock; michael@0: myJob->model_sock = model_sock; michael@0: myJob->requestCert = requestCert; michael@0: } michael@0: michael@0: PR_APPEND_LINK(myLink, &jobQ); michael@0: PZ_NotifyCondVar(jobQNotEmptyCv); michael@0: PZ_Unlock(qLock); michael@0: } michael@0: michael@0: FPRINTF(stderr, "httpserv: Closing listen socket.\n"); michael@0: VLOG(("httpserv: do_accepts: exiting")); michael@0: if (listen_sock) { michael@0: PR_Close(listen_sock); michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: PRFileDesc * michael@0: getBoundListenSocket(unsigned short port) michael@0: { michael@0: PRFileDesc * listen_sock; michael@0: int listenQueueDepth = 5 + (2 * maxThreads); michael@0: PRStatus prStatus; michael@0: PRNetAddr addr; michael@0: PRSocketOptionData opt; michael@0: michael@0: addr.inet.family = PR_AF_INET; michael@0: addr.inet.ip = PR_INADDR_ANY; michael@0: addr.inet.port = PR_htons(port); michael@0: michael@0: listen_sock = PR_NewTCPSocket(); michael@0: if (listen_sock == NULL) { michael@0: errExit("PR_NewTCPSocket"); michael@0: } michael@0: michael@0: opt.option = PR_SockOpt_Nonblocking; michael@0: opt.value.non_blocking = PR_FALSE; michael@0: prStatus = PR_SetSocketOption(listen_sock, &opt); michael@0: if (prStatus < 0) { michael@0: PR_Close(listen_sock); michael@0: errExit("PR_SetSocketOption(PR_SockOpt_Nonblocking)"); michael@0: } michael@0: michael@0: opt.option=PR_SockOpt_Reuseaddr; michael@0: opt.value.reuse_addr = PR_TRUE; michael@0: prStatus = PR_SetSocketOption(listen_sock, &opt); michael@0: if (prStatus < 0) { michael@0: PR_Close(listen_sock); michael@0: errExit("PR_SetSocketOption(PR_SockOpt_Reuseaddr)"); michael@0: } michael@0: michael@0: #ifndef WIN95 michael@0: /* Set PR_SockOpt_Linger because it helps prevent a server bind issue michael@0: * after clean shutdown . See bug 331413 . michael@0: * Don't do it in the WIN95 build configuration because clean shutdown is michael@0: * not implemented, and PR_SockOpt_Linger causes a hang in ssl.sh . michael@0: * See bug 332348 */ michael@0: opt.option=PR_SockOpt_Linger; michael@0: opt.value.linger.polarity = PR_TRUE; michael@0: opt.value.linger.linger = PR_SecondsToInterval(1); michael@0: prStatus = PR_SetSocketOption(listen_sock, &opt); michael@0: if (prStatus < 0) { michael@0: PR_Close(listen_sock); michael@0: errExit("PR_SetSocketOption(PR_SockOpt_Linger)"); michael@0: } michael@0: #endif michael@0: michael@0: prStatus = PR_Bind(listen_sock, &addr); michael@0: if (prStatus < 0) { michael@0: PR_Close(listen_sock); michael@0: errExit("PR_Bind"); michael@0: } michael@0: michael@0: prStatus = PR_Listen(listen_sock, listenQueueDepth); michael@0: if (prStatus < 0) { michael@0: PR_Close(listen_sock); michael@0: errExit("PR_Listen"); michael@0: } michael@0: return listen_sock; michael@0: } michael@0: michael@0: void michael@0: server_main( michael@0: PRFileDesc * listen_sock, michael@0: int requestCert, michael@0: SECKEYPrivateKey ** privKey, michael@0: CERTCertificate ** cert, michael@0: const char *expectedHostNameVal) michael@0: { michael@0: PRFileDesc *model_sock = NULL; michael@0: michael@0: /* Now, do the accepting, here in the main thread. */ michael@0: do_accepts(listen_sock, model_sock, requestCert); michael@0: michael@0: terminateWorkerThreads(); michael@0: michael@0: if (model_sock) { michael@0: PR_Close(model_sock); michael@0: } michael@0: michael@0: } michael@0: michael@0: int numChildren; michael@0: PRProcess * child[MAX_PROCS]; michael@0: michael@0: PRProcess * michael@0: haveAChild(int argc, char **argv, PRProcessAttr * attr) michael@0: { michael@0: PRProcess * newProcess; michael@0: michael@0: newProcess = PR_CreateProcess(argv[0], argv, NULL, attr); michael@0: if (!newProcess) { michael@0: errWarn("Can't create new process."); michael@0: } else { michael@0: child[numChildren++] = newProcess; michael@0: } michael@0: return newProcess; michael@0: } michael@0: michael@0: /* slightly adjusted version of ocsp_CreateCertID (not using issuer) */ michael@0: static CERTOCSPCertID * michael@0: ocsp_CreateSelfCAID(PLArenaPool *arena, CERTCertificate *cert, PRTime time) michael@0: { michael@0: CERTOCSPCertID *certID; michael@0: void *mark = PORT_ArenaMark(arena); michael@0: SECStatus rv; michael@0: michael@0: PORT_Assert(arena != NULL); michael@0: michael@0: certID = PORT_ArenaZNew(arena, CERTOCSPCertID); michael@0: if (certID == NULL) { michael@0: goto loser; michael@0: } michael@0: michael@0: rv = SECOID_SetAlgorithmID(arena, &certID->hashAlgorithm, SEC_OID_SHA1, michael@0: NULL); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: if (CERT_GetSubjectNameDigest(arena, cert, SEC_OID_SHA1, michael@0: &(certID->issuerNameHash)) == NULL) { michael@0: goto loser; michael@0: } michael@0: certID->issuerSHA1NameHash.data = certID->issuerNameHash.data; michael@0: certID->issuerSHA1NameHash.len = certID->issuerNameHash.len; michael@0: michael@0: if (CERT_GetSubjectNameDigest(arena, cert, SEC_OID_MD5, michael@0: &(certID->issuerMD5NameHash)) == NULL) { michael@0: goto loser; michael@0: } michael@0: michael@0: if (CERT_GetSubjectNameDigest(arena, cert, SEC_OID_MD2, michael@0: &(certID->issuerMD2NameHash)) == NULL) { michael@0: goto loser; michael@0: } michael@0: michael@0: if (CERT_GetSubjectPublicKeyDigest(arena, cert, SEC_OID_SHA1, michael@0: &certID->issuerKeyHash) == NULL) { michael@0: goto loser; michael@0: } michael@0: certID->issuerSHA1KeyHash.data = certID->issuerKeyHash.data; michael@0: certID->issuerSHA1KeyHash.len = certID->issuerKeyHash.len; michael@0: /* cache the other two hash algorithms as well */ michael@0: if (CERT_GetSubjectPublicKeyDigest(arena, cert, SEC_OID_MD5, michael@0: &certID->issuerMD5KeyHash) == NULL) { michael@0: goto loser; michael@0: } michael@0: if (CERT_GetSubjectPublicKeyDigest(arena, cert, SEC_OID_MD2, michael@0: &certID->issuerMD2KeyHash) == NULL) { michael@0: goto loser; michael@0: } michael@0: michael@0: PORT_ArenaUnmark(arena, mark); michael@0: return certID; michael@0: michael@0: loser: michael@0: PORT_ArenaRelease(arena, mark); michael@0: return NULL; michael@0: } michael@0: michael@0: /* slightly adjusted version of CERT_CreateOCSPCertID */ michael@0: CERTOCSPCertID* michael@0: cert_CreateSelfCAID(CERTCertificate *cert, PRTime time) michael@0: { michael@0: PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); michael@0: CERTOCSPCertID *certID; michael@0: PORT_Assert(arena != NULL); michael@0: if (!arena) michael@0: return NULL; michael@0: michael@0: certID = ocsp_CreateSelfCAID(arena, cert, time); michael@0: if (!certID) { michael@0: PORT_FreeArena(arena, PR_FALSE); michael@0: return NULL; michael@0: } michael@0: certID->poolp = arena; michael@0: return certID; michael@0: } michael@0: michael@0: int michael@0: main(int argc, char **argv) michael@0: { michael@0: char * progName = NULL; michael@0: const char * dir = "."; michael@0: char * passwd = NULL; michael@0: char * pwfile = NULL; michael@0: const char * pidFile = NULL; michael@0: char * tmp; michael@0: PRFileDesc * listen_sock; michael@0: int optionsFound = 0; michael@0: unsigned short port = 0; michael@0: SECStatus rv; michael@0: PRStatus prStatus; michael@0: PRBool bindOnly = PR_FALSE; michael@0: PRBool useLocalThreads = PR_FALSE; michael@0: PLOptState *optstate; michael@0: PLOptStatus status; michael@0: char emptyString[] = { "" }; michael@0: char* certPrefix = emptyString; michael@0: caRevoInfo *revoInfo = NULL; michael@0: PRCList *caRevoIter = NULL; michael@0: PRBool provideOcsp = PR_FALSE; michael@0: michael@0: tmp = strrchr(argv[0], '/'); michael@0: tmp = tmp ? tmp + 1 : argv[0]; michael@0: progName = strrchr(tmp, '\\'); michael@0: progName = progName ? progName + 1 : tmp; michael@0: michael@0: PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); michael@0: michael@0: /* please keep this list of options in ASCII collating sequence. michael@0: ** numbers, then capital letters, then lower case, alphabetical. michael@0: */ michael@0: optstate = PL_CreateOptState(argc, argv, michael@0: "A:C:DO:P:bd:f:hi:p:t:vw:"); michael@0: while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { michael@0: ++optionsFound; michael@0: switch(optstate->option) { michael@0: /* A first, must be followed by C. Any other order is an error. michael@0: * A creates the object. C completes and moves into list. michael@0: */ michael@0: case 'A': michael@0: provideOcsp = PR_TRUE; michael@0: if (revoInfo) { Usage(progName); exit(0); } michael@0: revoInfo = PORT_New(caRevoInfo); michael@0: revoInfo->nickname = PORT_Strdup(optstate->value); michael@0: break; michael@0: case 'C': michael@0: if (!revoInfo) { Usage(progName); exit(0); } michael@0: revoInfo->crlFilename = PORT_Strdup(optstate->value); michael@0: if (!caRevoInfos) { michael@0: PR_INIT_CLIST(&revoInfo->link); michael@0: caRevoInfos = revoInfo; michael@0: } else { michael@0: PR_APPEND_LINK(&revoInfo->link, &caRevoInfos->link); michael@0: } michael@0: revoInfo = NULL; michael@0: break; michael@0: michael@0: case 'O': michael@0: if (!PL_strcasecmp(optstate->value, "all")) { michael@0: ocspMethodsAllowed = ocspGetAndPost; michael@0: } else if (!PL_strcasecmp(optstate->value, "get")) { michael@0: ocspMethodsAllowed = ocspGetOnly; michael@0: } else if (!PL_strcasecmp(optstate->value, "post")) { michael@0: ocspMethodsAllowed = ocspPostOnly; michael@0: } else if (!PL_strcasecmp(optstate->value, "random")) { michael@0: ocspMethodsAllowed = ocspRandomGetFailure; michael@0: } else if (!PL_strcasecmp(optstate->value, "get-unknown")) { michael@0: ocspMethodsAllowed = ocspGetUnknown; michael@0: } else { michael@0: Usage(progName); exit(0); michael@0: } michael@0: break; michael@0: michael@0: case 'D': noDelay = PR_TRUE; break; michael@0: michael@0: case 'P': certPrefix = PORT_Strdup(optstate->value); break; michael@0: michael@0: case 'b': bindOnly = PR_TRUE; break; michael@0: michael@0: case 'd': dir = optstate->value; break; michael@0: michael@0: case 'f': michael@0: pwdata.source = PW_FROMFILE; michael@0: pwdata.data = pwfile = PORT_Strdup(optstate->value); michael@0: break; michael@0: michael@0: case 'h': Usage(progName); exit(0); break; michael@0: michael@0: case 'i': pidFile = optstate->value; break; michael@0: michael@0: case 'p': port = PORT_Atoi(optstate->value); break; michael@0: michael@0: case 't': michael@0: maxThreads = PORT_Atoi(optstate->value); michael@0: if ( maxThreads > MAX_THREADS ) maxThreads = MAX_THREADS; michael@0: if ( maxThreads < MIN_THREADS ) maxThreads = MIN_THREADS; michael@0: break; michael@0: michael@0: case 'v': verbose++; break; michael@0: michael@0: case 'w': michael@0: pwdata.source = PW_PLAINTEXT; michael@0: pwdata.data = passwd = PORT_Strdup(optstate->value); michael@0: break; michael@0: michael@0: default: michael@0: case '?': michael@0: fprintf(stderr, "Unrecognized or bad option specified.\n"); michael@0: fprintf(stderr, "Run '%s -h' for usage information.\n", progName); michael@0: exit(4); michael@0: break; michael@0: } michael@0: } michael@0: PL_DestroyOptState(optstate); michael@0: if (status == PL_OPT_BAD) { michael@0: fprintf(stderr, "Unrecognized or bad option specified.\n"); michael@0: fprintf(stderr, "Run '%s -h' for usage information.\n", progName); michael@0: exit(5); michael@0: } michael@0: if (!optionsFound) { michael@0: Usage(progName); michael@0: exit(51); michael@0: } michael@0: michael@0: /* The -b (bindOnly) option is only used by the ssl.sh test michael@0: * script on Linux to determine whether a previous httpserv michael@0: * process has fully died and freed the port. (Bug 129701) michael@0: */ michael@0: if (bindOnly) { michael@0: listen_sock = getBoundListenSocket(port); michael@0: if (!listen_sock) { michael@0: exit(1); michael@0: } michael@0: if (listen_sock) { michael@0: PR_Close(listen_sock); michael@0: } michael@0: exit(0); michael@0: } michael@0: michael@0: if (port == 0) { michael@0: fprintf(stderr, "Required argument 'port' must be non-zero value\n"); michael@0: exit(7); michael@0: } michael@0: michael@0: if (pidFile) { michael@0: FILE *tmpfile=fopen(pidFile,"w+"); michael@0: michael@0: if (tmpfile) { michael@0: fprintf(tmpfile,"%d",getpid()); michael@0: fclose(tmpfile); michael@0: } michael@0: } michael@0: michael@0: tmp = getenv("TMP"); michael@0: if (!tmp) michael@0: tmp = getenv("TMPDIR"); michael@0: if (!tmp) michael@0: tmp = getenv("TEMP"); michael@0: /* we're an ordinary single process server. */ michael@0: listen_sock = getBoundListenSocket(port); michael@0: prStatus = PR_SetFDInheritable(listen_sock, PR_FALSE); michael@0: if (prStatus != PR_SUCCESS) michael@0: errExit("PR_SetFDInheritable"); michael@0: michael@0: lm = PR_NewLogModule("TestCase"); michael@0: michael@0: /* set our password function */ michael@0: PK11_SetPasswordFunc(SECU_GetModulePassword); michael@0: michael@0: if (provideOcsp) { michael@0: /* Call the NSS initialization routines */ michael@0: rv = NSS_Initialize(dir, certPrefix, certPrefix, SECMOD_DB, NSS_INIT_READONLY); michael@0: if (rv != SECSuccess) { michael@0: fputs("NSS_Init failed.\n", stderr); michael@0: exit(8); michael@0: } michael@0: michael@0: if (caRevoInfos) { michael@0: caRevoIter = &caRevoInfos->link; michael@0: do { michael@0: PRFileDesc *inFile; michael@0: int rv = SECFailure; michael@0: SECItem crlDER; michael@0: crlDER.data = NULL; michael@0: michael@0: revoInfo = (caRevoInfo*)caRevoIter; michael@0: revoInfo->cert = CERT_FindCertByNickname( michael@0: CERT_GetDefaultCertDB(), revoInfo->nickname); michael@0: if (!revoInfo->cert) { michael@0: fprintf(stderr, "cannot find cert with nickname %s\n", michael@0: revoInfo->nickname); michael@0: exit(1); michael@0: } michael@0: inFile = PR_Open(revoInfo->crlFilename, PR_RDONLY, 0); michael@0: if (inFile) { michael@0: rv = SECU_ReadDERFromFile(&crlDER, inFile, PR_FALSE, PR_FALSE); michael@0: PR_Close(inFile); michael@0: inFile = NULL; michael@0: } michael@0: if (rv != SECSuccess) { michael@0: fprintf(stderr, "unable to read crl file %s\n", michael@0: revoInfo->crlFilename); michael@0: exit(1); michael@0: } michael@0: revoInfo->crl = michael@0: CERT_DecodeDERCrlWithFlags(NULL, &crlDER, SEC_CRL_TYPE, michael@0: CRL_DECODE_DEFAULT_OPTIONS); michael@0: if (!revoInfo->crl) { michael@0: fprintf(stderr, "unable to decode crl file %s\n", michael@0: revoInfo->crlFilename); michael@0: exit(1); michael@0: } michael@0: if (CERT_CompareName(&revoInfo->crl->crl.name, michael@0: &revoInfo->cert->subject) != SECEqual) { michael@0: fprintf(stderr, "CRL %s doesn't match cert identified by preceding nickname %s\n", michael@0: revoInfo->crlFilename, revoInfo->nickname); michael@0: exit(1); michael@0: } michael@0: revoInfo->id = cert_CreateSelfCAID(revoInfo->cert, PR_Now()); michael@0: caRevoIter = PR_NEXT_LINK(caRevoIter); michael@0: } while (caRevoIter != &caRevoInfos->link); michael@0: } michael@0: } michael@0: michael@0: /* allocate the array of thread slots, and launch the worker threads. */ michael@0: rv = launch_threads(&jobLoop, 0, 0, 0, useLocalThreads); michael@0: michael@0: if (rv == SECSuccess) { michael@0: server_main(listen_sock, 0, 0, 0, michael@0: 0); michael@0: } michael@0: michael@0: VLOG(("httpserv: server_thread: exiting")); michael@0: michael@0: if (provideOcsp) { michael@0: if (caRevoInfos) { michael@0: PRCList *caRevoIter; michael@0: michael@0: caRevoIter = &caRevoInfos->link; michael@0: do { michael@0: caRevoInfo *revoInfo = (caRevoInfo*)caRevoIter; michael@0: if (revoInfo->nickname) michael@0: PORT_Free(revoInfo->nickname); michael@0: if (revoInfo->crlFilename) michael@0: PORT_Free(revoInfo->crlFilename); michael@0: if (revoInfo->cert) michael@0: CERT_DestroyCertificate(revoInfo->cert); michael@0: if (revoInfo->id) michael@0: CERT_DestroyOCSPCertID(revoInfo->id); michael@0: if (revoInfo->crl) michael@0: SEC_DestroyCrl(revoInfo->crl); michael@0: michael@0: caRevoIter = PR_NEXT_LINK(caRevoIter); michael@0: } while (caRevoIter != &caRevoInfos->link); michael@0: michael@0: } michael@0: if (NSS_Shutdown() != SECSuccess) { michael@0: SECU_PrintError(progName, "NSS_Shutdown"); michael@0: PR_Cleanup(); michael@0: exit(1); michael@0: } michael@0: } michael@0: if (passwd) { michael@0: PORT_Free(passwd); michael@0: } michael@0: if (pwfile) { michael@0: PORT_Free(pwfile); michael@0: } michael@0: if (certPrefix && certPrefix != emptyString) { michael@0: PORT_Free(certPrefix); michael@0: } michael@0: PR_Cleanup(); michael@0: printf("httpserv: normal termination\n"); michael@0: return 0; michael@0: } michael@0: