Bug Summary

File:s/cmd/httpserv/httpserv.c
Warning:line 1333, column 9
Value stored to 'tmp' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

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