Wed, 31 Dec 2014 07:16:47 +0100
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 |