security/nss/cmd/httpserv/httpserv.c

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

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

mercurial