File: | s/cmd/httpserv/httpserv.c |
Warning: | line 1333, column 9 Value stored to 'tmp' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | |
48 | static int handle_connection(PRFileDesc *, PRFileDesc *, int); |
49 | |
50 | /* data and structures for shutdown */ |
51 | static int stopping; |
52 | |
53 | static PRBool noDelay; |
54 | static int verbose; |
55 | |
56 | static PRThread *acceptorThread; |
57 | |
58 | static PRLogModuleInfo *lm; |
59 | |
60 | #define 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 | |
73 | static void |
74 | Usage(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 | |
100 | static const char * |
101 | errWarn(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 | |
111 | static void |
112 | errExit(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 |
127 | static int maxThreads = DEFAULT_THREADS8; |
128 | |
129 | typedef struct jobStr { |
130 | PRCList link; |
131 | PRFileDesc *tcp_sock; |
132 | PRFileDesc *model_sock; |
133 | int requestCert; |
134 | } JOB; |
135 | |
136 | static PZLockPRLock *qLock; /* this lock protects all data immediately below */ |
137 | static PRLock *lastLoadedCrlLock; /* this lock protects lastLoadedCrl variable */ |
138 | static PZCondVarPRCondVar *jobQNotEmptyCv; |
139 | static PZCondVarPRCondVar *freeListNotEmptyCv; |
140 | static PZCondVarPRCondVar *threadCountChangeCv; |
141 | static int threadCount; |
142 | static PRCList jobQ; |
143 | static PRCList freeJobs; |
144 | static JOB *jobTable; |
145 | |
146 | SECStatus |
147 | setupJobs(int maxJobs) |
148 | { |
149 | int i; |
150 | |
151 | jobTable = (JOB *)PR_Calloc(maxJobs, sizeof(JOB)); |
152 | if (!jobTable) |
153 | return SECFailure; |
154 | |
155 | PR_INIT_CLIST(&jobQ)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 | |
165 | typedef int startFn(PRFileDesc *a, PRFileDesc *b, int c); |
166 | |
167 | typedef enum { rs_idle = 0, |
168 | rs_running = 1, |
169 | rs_zombie = 2 } runState; |
170 | |
171 | typedef struct perThreadStr { |
172 | PRFileDesc *a; |
173 | PRFileDesc *b; |
174 | int c; |
175 | int rv; |
176 | startFn *startFunc; |
177 | PRThread *prThread; |
178 | runState state; |
179 | } perThread; |
180 | |
181 | static perThread *threads; |
182 | |
183 | void |
184 | thread_wrapper(void *arg) |
185 | { |
186 | perThread *slot = (perThread *)arg; |
187 | |
188 | slot->rv = (*slot->startFunc)(slot->a, slot->b, slot->c); |
189 | |
190 | /* notify the thread exit handler. */ |
191 | PZ_Lock(qLock)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 | |
198 | int |
199 | jobLoop(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 | |
228 | SECStatus |
229 | launch_threads( |
230 | startFn *startFunc, |
231 | PRFileDesc *a, |
232 | PRFileDesc *b, |
233 | int c, |
234 | PRBool local) |
235 | { |
236 | int i; |
237 | SECStatus rv = SECSuccess; |
238 | |
239 | /* create the thread management serialization structs */ |
240 | qLock = PZ_NewLock(nssILockSelfServ)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 | |
303 | void |
304 | terminateWorkerThreads(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 | |
341 | PRBool NoReuse = PR_FALSE0; |
342 | PRBool disableLocking = PR_FALSE0; |
343 | static secuPWData pwdata = { PW_NONE, 0 }; |
344 | |
345 | struct caRevoInfoStr { |
346 | PRCList link; |
347 | char *nickname; |
348 | char *crlFilename; |
349 | CERTCertificate *cert; |
350 | CERTOCSPCertID *id; |
351 | CERTSignedCrl *crl; |
352 | }; |
353 | typedef struct caRevoInfoStr caRevoInfo; |
354 | /* Created during app init. No locks necessary, |
355 | * because later on, only read access will occur. */ |
356 | static caRevoInfo *caRevoInfos = NULL((void*)0); |
357 | |
358 | static enum { |
359 | ocspGetOnly, |
360 | ocspPostOnly, |
361 | ocspGetAndPost, |
362 | ocspRandomGetFailure, |
363 | ocspGetUnknown |
364 | } ocspMethodsAllowed = ocspGetAndPost; |
365 | |
366 | static const char stopCmd[] = { "GET /stop " }; |
367 | static const char getCmd[] = { "GET " }; |
368 | static const char outHeader[] = { |
369 | "HTTP/1.0 200 OK\r\n" |
370 | "Server: Generic Web Server\r\n" |
371 | "Date: Tue, 26 Aug 1997 22:10:05 GMT\r\n" |
372 | "Content-type: text/plain\r\n" |
373 | "\r\n" |
374 | }; |
375 | static const char outOcspHeader[] = { |
376 | "HTTP/1.0 200 OK\r\n" |
377 | "Server: Generic OCSP Server\r\n" |
378 | "Content-type: application/ocsp-response\r\n" |
379 | "\r\n" |
380 | }; |
381 | static const char outBadRequestHeader[] = { |
382 | "HTTP/1.0 400 Bad Request\r\n" |
383 | "Server: Generic OCSP Server\r\n" |
384 | "\r\n" |
385 | }; |
386 | |
387 | void |
388 | stop_server() |
389 | { |
390 | stopping = 1; |
391 | PR_Interrupt(acceptorThread); |
392 | PZ_TraceFlush(); |
393 | } |
394 | |
395 | /* Will only work if the original input to url encoding was |
396 | * a base64 encoded buffer. Will only decode the sequences used |
397 | * for encoding the special base64 characters, and fail if any |
398 | * other encoded chars are found. |
399 | * Will return SECSuccess if input could be processed. |
400 | * Coversion is done in place. |
401 | */ |
402 | static SECStatus |
403 | urldecode_base64chars_inplace(char *buf) |
404 | { |
405 | char *walk; |
406 | size_t remaining_bytes; |
407 | |
408 | if (!buf || !*buf) |
409 | return SECFailure; |
410 | |
411 | walk = buf; |
412 | remaining_bytes = strlen(buf) + 1; /* include terminator */ |
413 | |
414 | while (*walk) { |
415 | if (*walk == '%') { |
416 | if (!PL_strncasecmp(walk, "%2B", 3)) { |
417 | *walk = '+'; |
418 | } else if (!PL_strncasecmp(walk, "%2F", 3)) { |
419 | *walk = '/'; |
420 | } else if (!PL_strncasecmp(walk, "%3D", 3)) { |
421 | *walk = '='; |
422 | } else { |
423 | return SECFailure; |
424 | } |
425 | remaining_bytes -= 3; |
426 | ++walk; |
427 | memmove(walk, walk + 2, remaining_bytes); |
428 | } else { |
429 | ++walk; |
430 | --remaining_bytes; |
431 | } |
432 | } |
433 | return SECSuccess; |
434 | } |
435 | |
436 | int |
437 | handle_connection( |
438 | PRFileDesc *tcp_sock, |
439 | PRFileDesc *model_sock, |
440 | int requestCert) |
441 | { |
442 | PRFileDesc *ssl_sock = NULL((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 | |
849 | cleanup: |
850 | if (ssl_sock) { |
851 | PR_Close(ssl_sock); |
852 | } else if (tcp_sock) { |
853 | PR_Close(tcp_sock); |
854 | } |
855 | if (local_file_fd) |
856 | PR_Close(local_file_fd); |
857 | VLOG(("httpserv: handle_connection: exiting\n"))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 | |
870 | void |
871 | sigusr1_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 | |
879 | SECStatus |
880 | do_accepts( |
881 | PRFileDesc *listen_sock, |
882 | PRFileDesc *model_sock, |
883 | int requestCert) |
884 | { |
885 | PRNetAddr addr; |
886 | PRErrorCode perr; |
887 | #ifdef XP_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 | |
965 | PRFileDesc * |
966 | getBoundListenSocket(unsigned short port) |
967 | { |
968 | PRFileDesc *listen_sock; |
969 | int listenQueueDepth = 5 + (2 * maxThreads); |
970 | PRStatus prStatus; |
971 | PRNetAddr addr; |
972 | PRSocketOptionData opt; |
973 | |
974 | addr.inet.family = PR_AF_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 | |
1029 | void |
1030 | server_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 | |
1049 | int numChildren; |
1050 | PRProcess *child[MAX_PROCS25]; |
1051 | |
1052 | PRProcess * |
1053 | haveAChild(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) */ |
1067 | static CERTOCSPCertID * |
1068 | ocsp_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 | |
1123 | loser: |
1124 | PORT_ArenaReleasePORT_ArenaRelease_Util(arena, mark); |
1125 | return NULL((void*)0); |
1126 | } |
1127 | |
1128 | /* slightly adjusted version of CERT_CreateOCSPCertID */ |
1129 | CERTOCSPCertID * |
1130 | cert_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 | |
1147 | int |
1148 | main(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 | } |