Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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 "vfyserv.h" |
michael@0 | 6 | #include "secerr.h" |
michael@0 | 7 | #include "sslerr.h" |
michael@0 | 8 | #include "nspr.h" |
michael@0 | 9 | #include "secutil.h" |
michael@0 | 10 | |
michael@0 | 11 | |
michael@0 | 12 | extern PRBool dumpChain; |
michael@0 | 13 | extern void dumpCertChain(CERTCertificate *, SECCertUsage); |
michael@0 | 14 | |
michael@0 | 15 | /* Declare SSL cipher suites. */ |
michael@0 | 16 | |
michael@0 | 17 | int ssl2CipherSuites[] = { |
michael@0 | 18 | SSL_EN_RC4_128_WITH_MD5, /* A */ |
michael@0 | 19 | SSL_EN_RC4_128_EXPORT40_WITH_MD5, /* B */ |
michael@0 | 20 | SSL_EN_RC2_128_CBC_WITH_MD5, /* C */ |
michael@0 | 21 | SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, /* D */ |
michael@0 | 22 | SSL_EN_DES_64_CBC_WITH_MD5, /* E */ |
michael@0 | 23 | SSL_EN_DES_192_EDE3_CBC_WITH_MD5, /* F */ |
michael@0 | 24 | 0 |
michael@0 | 25 | }; |
michael@0 | 26 | |
michael@0 | 27 | int ssl3CipherSuites[] = { |
michael@0 | 28 | -1, /* SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA* a */ |
michael@0 | 29 | -1, /* SSL_FORTEZZA_DMS_WITH_RC4_128_SHA, * b */ |
michael@0 | 30 | TLS_RSA_WITH_RC4_128_MD5, /* c */ |
michael@0 | 31 | TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* d */ |
michael@0 | 32 | TLS_RSA_WITH_DES_CBC_SHA, /* e */ |
michael@0 | 33 | TLS_RSA_EXPORT_WITH_RC4_40_MD5, /* f */ |
michael@0 | 34 | TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, /* g */ |
michael@0 | 35 | -1, /* SSL_FORTEZZA_DMS_WITH_NULL_SHA, * h */ |
michael@0 | 36 | TLS_RSA_WITH_NULL_MD5, /* i */ |
michael@0 | 37 | SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, /* j */ |
michael@0 | 38 | SSL_RSA_FIPS_WITH_DES_CBC_SHA, /* k */ |
michael@0 | 39 | TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, /* l */ |
michael@0 | 40 | TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, /* m */ |
michael@0 | 41 | TLS_RSA_WITH_RC4_128_SHA, /* n */ |
michael@0 | 42 | TLS_DHE_DSS_WITH_RC4_128_SHA, /* o */ |
michael@0 | 43 | TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, /* p */ |
michael@0 | 44 | TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, /* q */ |
michael@0 | 45 | TLS_DHE_RSA_WITH_DES_CBC_SHA, /* r */ |
michael@0 | 46 | TLS_DHE_DSS_WITH_DES_CBC_SHA, /* s */ |
michael@0 | 47 | TLS_DHE_DSS_WITH_AES_128_CBC_SHA, /* t */ |
michael@0 | 48 | TLS_DHE_RSA_WITH_AES_128_CBC_SHA, /* u */ |
michael@0 | 49 | TLS_RSA_WITH_AES_128_CBC_SHA, /* v */ |
michael@0 | 50 | TLS_DHE_DSS_WITH_AES_256_CBC_SHA, /* w */ |
michael@0 | 51 | TLS_DHE_RSA_WITH_AES_256_CBC_SHA, /* x */ |
michael@0 | 52 | TLS_RSA_WITH_AES_256_CBC_SHA, /* y */ |
michael@0 | 53 | TLS_RSA_WITH_NULL_SHA, /* z */ |
michael@0 | 54 | 0 |
michael@0 | 55 | }; |
michael@0 | 56 | |
michael@0 | 57 | /************************************************************************** |
michael@0 | 58 | ** |
michael@0 | 59 | ** SSL callback routines. |
michael@0 | 60 | ** |
michael@0 | 61 | **************************************************************************/ |
michael@0 | 62 | |
michael@0 | 63 | /* Function: char * myPasswd() |
michael@0 | 64 | * |
michael@0 | 65 | * Purpose: This function is our custom password handler that is called by |
michael@0 | 66 | * SSL when retreiving private certs and keys from the database. Returns a |
michael@0 | 67 | * pointer to a string that with a password for the database. Password pointer |
michael@0 | 68 | * should point to dynamically allocated memory that will be freed later. |
michael@0 | 69 | */ |
michael@0 | 70 | char * |
michael@0 | 71 | myPasswd(PK11SlotInfo *info, PRBool retry, void *arg) |
michael@0 | 72 | { |
michael@0 | 73 | char * passwd = NULL; |
michael@0 | 74 | |
michael@0 | 75 | if ( (!retry) && arg ) { |
michael@0 | 76 | passwd = PORT_Strdup((char *)arg); |
michael@0 | 77 | } |
michael@0 | 78 | return passwd; |
michael@0 | 79 | } |
michael@0 | 80 | |
michael@0 | 81 | /* Function: SECStatus myAuthCertificate() |
michael@0 | 82 | * |
michael@0 | 83 | * Purpose: This function is our custom certificate authentication handler. |
michael@0 | 84 | * |
michael@0 | 85 | * Note: This implementation is essentially the same as the default |
michael@0 | 86 | * SSL_AuthCertificate(). |
michael@0 | 87 | */ |
michael@0 | 88 | SECStatus |
michael@0 | 89 | myAuthCertificate(void *arg, PRFileDesc *socket, |
michael@0 | 90 | PRBool checksig, PRBool isServer) |
michael@0 | 91 | { |
michael@0 | 92 | |
michael@0 | 93 | SECCertificateUsage certUsage; |
michael@0 | 94 | CERTCertificate * cert; |
michael@0 | 95 | void * pinArg; |
michael@0 | 96 | char * hostName; |
michael@0 | 97 | SECStatus secStatus; |
michael@0 | 98 | |
michael@0 | 99 | if (!arg || !socket) { |
michael@0 | 100 | errWarn("myAuthCertificate"); |
michael@0 | 101 | return SECFailure; |
michael@0 | 102 | } |
michael@0 | 103 | |
michael@0 | 104 | /* Define how the cert is being used based upon the isServer flag. */ |
michael@0 | 105 | |
michael@0 | 106 | certUsage = isServer ? certificateUsageSSLClient : certificateUsageSSLServer; |
michael@0 | 107 | |
michael@0 | 108 | cert = SSL_PeerCertificate(socket); |
michael@0 | 109 | |
michael@0 | 110 | pinArg = SSL_RevealPinArg(socket); |
michael@0 | 111 | |
michael@0 | 112 | if (dumpChain == PR_TRUE) { |
michael@0 | 113 | dumpCertChain(cert, certUsage); |
michael@0 | 114 | } |
michael@0 | 115 | |
michael@0 | 116 | secStatus = CERT_VerifyCertificateNow((CERTCertDBHandle *)arg, |
michael@0 | 117 | cert, |
michael@0 | 118 | checksig, |
michael@0 | 119 | certUsage, |
michael@0 | 120 | pinArg, |
michael@0 | 121 | NULL); |
michael@0 | 122 | |
michael@0 | 123 | /* If this is a server, we're finished. */ |
michael@0 | 124 | if (isServer || secStatus != SECSuccess) { |
michael@0 | 125 | SECU_printCertProblems(stderr, (CERTCertDBHandle *)arg, cert, |
michael@0 | 126 | checksig, certUsage, pinArg, PR_FALSE); |
michael@0 | 127 | CERT_DestroyCertificate(cert); |
michael@0 | 128 | return secStatus; |
michael@0 | 129 | } |
michael@0 | 130 | |
michael@0 | 131 | /* Certificate is OK. Since this is the client side of an SSL |
michael@0 | 132 | * connection, we need to verify that the name field in the cert |
michael@0 | 133 | * matches the desired hostname. This is our defense against |
michael@0 | 134 | * man-in-the-middle attacks. |
michael@0 | 135 | */ |
michael@0 | 136 | |
michael@0 | 137 | /* SSL_RevealURL returns a hostName, not an URL. */ |
michael@0 | 138 | hostName = SSL_RevealURL(socket); |
michael@0 | 139 | |
michael@0 | 140 | if (hostName && hostName[0]) { |
michael@0 | 141 | secStatus = CERT_VerifyCertName(cert, hostName); |
michael@0 | 142 | } else { |
michael@0 | 143 | PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0); |
michael@0 | 144 | secStatus = SECFailure; |
michael@0 | 145 | } |
michael@0 | 146 | |
michael@0 | 147 | if (hostName) |
michael@0 | 148 | PR_Free(hostName); |
michael@0 | 149 | |
michael@0 | 150 | CERT_DestroyCertificate(cert); |
michael@0 | 151 | return secStatus; |
michael@0 | 152 | } |
michael@0 | 153 | |
michael@0 | 154 | /* Function: SECStatus myBadCertHandler() |
michael@0 | 155 | * |
michael@0 | 156 | * Purpose: This callback is called when the incoming certificate is not |
michael@0 | 157 | * valid. We define a certain set of parameters that still cause the |
michael@0 | 158 | * certificate to be "valid" for this session, and return SECSuccess to cause |
michael@0 | 159 | * the server to continue processing the request when any of these conditions |
michael@0 | 160 | * are met. Otherwise, SECFailure is return and the server rejects the |
michael@0 | 161 | * request. |
michael@0 | 162 | */ |
michael@0 | 163 | SECStatus |
michael@0 | 164 | myBadCertHandler(void *arg, PRFileDesc *socket) |
michael@0 | 165 | { |
michael@0 | 166 | |
michael@0 | 167 | SECStatus secStatus = SECFailure; |
michael@0 | 168 | PRErrorCode err; |
michael@0 | 169 | |
michael@0 | 170 | /* log invalid cert here */ |
michael@0 | 171 | |
michael@0 | 172 | if (!arg) { |
michael@0 | 173 | return secStatus; |
michael@0 | 174 | } |
michael@0 | 175 | |
michael@0 | 176 | *(PRErrorCode *)arg = err = PORT_GetError(); |
michael@0 | 177 | |
michael@0 | 178 | /* If any of the cases in the switch are met, then we will proceed */ |
michael@0 | 179 | /* with the processing of the request anyway. Otherwise, the default */ |
michael@0 | 180 | /* case will be reached and we will reject the request. */ |
michael@0 | 181 | |
michael@0 | 182 | switch (err) { |
michael@0 | 183 | case SEC_ERROR_INVALID_AVA: |
michael@0 | 184 | case SEC_ERROR_INVALID_TIME: |
michael@0 | 185 | case SEC_ERROR_BAD_SIGNATURE: |
michael@0 | 186 | case SEC_ERROR_EXPIRED_CERTIFICATE: |
michael@0 | 187 | case SEC_ERROR_UNKNOWN_ISSUER: |
michael@0 | 188 | case SEC_ERROR_UNTRUSTED_CERT: |
michael@0 | 189 | case SEC_ERROR_CERT_VALID: |
michael@0 | 190 | case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: |
michael@0 | 191 | case SEC_ERROR_CRL_EXPIRED: |
michael@0 | 192 | case SEC_ERROR_CRL_BAD_SIGNATURE: |
michael@0 | 193 | case SEC_ERROR_EXTENSION_VALUE_INVALID: |
michael@0 | 194 | case SEC_ERROR_CA_CERT_INVALID: |
michael@0 | 195 | case SEC_ERROR_CERT_USAGES_INVALID: |
michael@0 | 196 | case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION: |
michael@0 | 197 | secStatus = SECSuccess; |
michael@0 | 198 | break; |
michael@0 | 199 | default: |
michael@0 | 200 | secStatus = SECFailure; |
michael@0 | 201 | break; |
michael@0 | 202 | } |
michael@0 | 203 | |
michael@0 | 204 | fprintf(stderr, "Bad certificate: %d, %s\n", err, SECU_Strerror(err)); |
michael@0 | 205 | |
michael@0 | 206 | return secStatus; |
michael@0 | 207 | } |
michael@0 | 208 | |
michael@0 | 209 | /* Function: SECStatus ownGetClientAuthData() |
michael@0 | 210 | * |
michael@0 | 211 | * Purpose: This callback is used by SSL to pull client certificate |
michael@0 | 212 | * information upon server request. |
michael@0 | 213 | */ |
michael@0 | 214 | SECStatus |
michael@0 | 215 | myGetClientAuthData(void *arg, |
michael@0 | 216 | PRFileDesc *socket, |
michael@0 | 217 | struct CERTDistNamesStr *caNames, |
michael@0 | 218 | struct CERTCertificateStr **pRetCert, |
michael@0 | 219 | struct SECKEYPrivateKeyStr **pRetKey) |
michael@0 | 220 | { |
michael@0 | 221 | |
michael@0 | 222 | CERTCertificate * cert; |
michael@0 | 223 | SECKEYPrivateKey * privKey; |
michael@0 | 224 | char * chosenNickName = (char *)arg; |
michael@0 | 225 | void * proto_win = NULL; |
michael@0 | 226 | SECStatus secStatus = SECFailure; |
michael@0 | 227 | |
michael@0 | 228 | proto_win = SSL_RevealPinArg(socket); |
michael@0 | 229 | |
michael@0 | 230 | if (chosenNickName) { |
michael@0 | 231 | cert = PK11_FindCertFromNickname(chosenNickName, proto_win); |
michael@0 | 232 | if (cert) { |
michael@0 | 233 | privKey = PK11_FindKeyByAnyCert(cert, proto_win); |
michael@0 | 234 | if (privKey) { |
michael@0 | 235 | secStatus = SECSuccess; |
michael@0 | 236 | } else { |
michael@0 | 237 | CERT_DestroyCertificate(cert); |
michael@0 | 238 | } |
michael@0 | 239 | } |
michael@0 | 240 | } else { /* no nickname given, automatically find the right cert */ |
michael@0 | 241 | CERTCertNicknames *names; |
michael@0 | 242 | int i; |
michael@0 | 243 | |
michael@0 | 244 | names = CERT_GetCertNicknames(CERT_GetDefaultCertDB(), |
michael@0 | 245 | SEC_CERT_NICKNAMES_USER, proto_win); |
michael@0 | 246 | |
michael@0 | 247 | if (names != NULL) { |
michael@0 | 248 | for(i = 0; i < names->numnicknames; i++ ) { |
michael@0 | 249 | |
michael@0 | 250 | cert = PK11_FindCertFromNickname(names->nicknames[i], |
michael@0 | 251 | proto_win); |
michael@0 | 252 | if (!cert) { |
michael@0 | 253 | continue; |
michael@0 | 254 | } |
michael@0 | 255 | |
michael@0 | 256 | /* Only check unexpired certs */ |
michael@0 | 257 | if (CERT_CheckCertValidTimes(cert, PR_Now(), PR_FALSE) |
michael@0 | 258 | != secCertTimeValid ) { |
michael@0 | 259 | CERT_DestroyCertificate(cert); |
michael@0 | 260 | continue; |
michael@0 | 261 | } |
michael@0 | 262 | |
michael@0 | 263 | secStatus = NSS_CmpCertChainWCANames(cert, caNames); |
michael@0 | 264 | if (secStatus == SECSuccess) { |
michael@0 | 265 | privKey = PK11_FindKeyByAnyCert(cert, proto_win); |
michael@0 | 266 | if (privKey) { |
michael@0 | 267 | break; |
michael@0 | 268 | } |
michael@0 | 269 | secStatus = SECFailure; |
michael@0 | 270 | } |
michael@0 | 271 | CERT_DestroyCertificate(cert); |
michael@0 | 272 | } /* for loop */ |
michael@0 | 273 | CERT_FreeNicknames(names); |
michael@0 | 274 | } |
michael@0 | 275 | } |
michael@0 | 276 | |
michael@0 | 277 | if (secStatus == SECSuccess) { |
michael@0 | 278 | *pRetCert = cert; |
michael@0 | 279 | *pRetKey = privKey; |
michael@0 | 280 | } |
michael@0 | 281 | |
michael@0 | 282 | return secStatus; |
michael@0 | 283 | } |
michael@0 | 284 | |
michael@0 | 285 | /* Function: void myHandshakeCallback() |
michael@0 | 286 | * |
michael@0 | 287 | * Purpose: Called by SSL to inform application that the handshake is |
michael@0 | 288 | * complete. This function is mostly used on the server side of an SSL |
michael@0 | 289 | * connection, although it is provided for a client as well. |
michael@0 | 290 | * Useful when a non-blocking SSL_ReHandshake or SSL_ResetHandshake |
michael@0 | 291 | * is used to initiate a handshake. |
michael@0 | 292 | * |
michael@0 | 293 | * A typical scenario would be: |
michael@0 | 294 | * |
michael@0 | 295 | * 1. Server accepts an SSL connection from the client without client auth. |
michael@0 | 296 | * 2. Client sends a request. |
michael@0 | 297 | * 3. Server determines that to service request it needs to authenticate the |
michael@0 | 298 | * client and initiates another handshake requesting client auth. |
michael@0 | 299 | * 4. While handshake is in progress, server can do other work or spin waiting |
michael@0 | 300 | * for the handshake to complete. |
michael@0 | 301 | * 5. Server is notified that handshake has been successfully completed by |
michael@0 | 302 | * the custom handshake callback function and it can service the client's |
michael@0 | 303 | * request. |
michael@0 | 304 | * |
michael@0 | 305 | * Note: This function is not implemented in this sample, as we are using |
michael@0 | 306 | * blocking sockets. |
michael@0 | 307 | */ |
michael@0 | 308 | void |
michael@0 | 309 | myHandshakeCallback(PRFileDesc *socket, void *arg) |
michael@0 | 310 | { |
michael@0 | 311 | fprintf(stderr,"Handshake Complete: SERVER CONFIGURED CORRECTLY\n"); |
michael@0 | 312 | } |
michael@0 | 313 | |
michael@0 | 314 | |
michael@0 | 315 | /************************************************************************** |
michael@0 | 316 | ** |
michael@0 | 317 | ** Routines for disabling SSL ciphers. |
michael@0 | 318 | ** |
michael@0 | 319 | **************************************************************************/ |
michael@0 | 320 | |
michael@0 | 321 | void |
michael@0 | 322 | disableAllSSLCiphers(void) |
michael@0 | 323 | { |
michael@0 | 324 | const PRUint16 *cipherSuites = SSL_ImplementedCiphers; |
michael@0 | 325 | int i = SSL_NumImplementedCiphers; |
michael@0 | 326 | SECStatus rv; |
michael@0 | 327 | |
michael@0 | 328 | /* disable all the SSL3 cipher suites */ |
michael@0 | 329 | while (--i >= 0) { |
michael@0 | 330 | PRUint16 suite = cipherSuites[i]; |
michael@0 | 331 | rv = SSL_CipherPrefSetDefault(suite, PR_FALSE); |
michael@0 | 332 | if (rv != SECSuccess) { |
michael@0 | 333 | fprintf(stderr, |
michael@0 | 334 | "SSL_CipherPrefSetDefault didn't like value 0x%04x (i = %d)\n", |
michael@0 | 335 | suite, i); |
michael@0 | 336 | errWarn("SSL_CipherPrefSetDefault"); |
michael@0 | 337 | exit(2); |
michael@0 | 338 | } |
michael@0 | 339 | } |
michael@0 | 340 | } |
michael@0 | 341 | |
michael@0 | 342 | /************************************************************************** |
michael@0 | 343 | ** |
michael@0 | 344 | ** Error and information routines. |
michael@0 | 345 | ** |
michael@0 | 346 | **************************************************************************/ |
michael@0 | 347 | |
michael@0 | 348 | void |
michael@0 | 349 | errWarn(char *function) |
michael@0 | 350 | { |
michael@0 | 351 | PRErrorCode errorNumber = PR_GetError(); |
michael@0 | 352 | const char * errorString = SECU_Strerror(errorNumber); |
michael@0 | 353 | |
michael@0 | 354 | fprintf(stderr, "Error in function %s: %d\n - %s\n", |
michael@0 | 355 | function, errorNumber, errorString); |
michael@0 | 356 | } |
michael@0 | 357 | |
michael@0 | 358 | void |
michael@0 | 359 | exitErr(char *function) |
michael@0 | 360 | { |
michael@0 | 361 | errWarn(function); |
michael@0 | 362 | /* Exit gracefully. */ |
michael@0 | 363 | /* ignoring return value of NSS_Shutdown as code exits with 1 anyway*/ |
michael@0 | 364 | (void) NSS_Shutdown(); |
michael@0 | 365 | PR_Cleanup(); |
michael@0 | 366 | exit(1); |
michael@0 | 367 | } |
michael@0 | 368 | |
michael@0 | 369 | void |
michael@0 | 370 | printSecurityInfo(FILE *outfile, PRFileDesc *fd) |
michael@0 | 371 | { |
michael@0 | 372 | char * cp; /* bulk cipher name */ |
michael@0 | 373 | char * ip; /* cert issuer DN */ |
michael@0 | 374 | char * sp; /* cert subject DN */ |
michael@0 | 375 | int op; /* High, Low, Off */ |
michael@0 | 376 | int kp0; /* total key bits */ |
michael@0 | 377 | int kp1; /* secret key bits */ |
michael@0 | 378 | int result; |
michael@0 | 379 | SSL3Statistics * ssl3stats = SSL_GetStatistics(); |
michael@0 | 380 | |
michael@0 | 381 | if (!outfile) { |
michael@0 | 382 | outfile = stdout; |
michael@0 | 383 | } |
michael@0 | 384 | |
michael@0 | 385 | result = SSL_SecurityStatus(fd, &op, &cp, &kp0, &kp1, &ip, &sp); |
michael@0 | 386 | if (result != SECSuccess) |
michael@0 | 387 | return; |
michael@0 | 388 | fprintf(outfile, |
michael@0 | 389 | " bulk cipher %s, %d secret key bits, %d key bits, status: %d\n" |
michael@0 | 390 | " subject DN:\n %s\n" |
michael@0 | 391 | " issuer DN:\n %s\n", cp, kp1, kp0, op, sp, ip); |
michael@0 | 392 | PR_Free(cp); |
michael@0 | 393 | PR_Free(ip); |
michael@0 | 394 | PR_Free(sp); |
michael@0 | 395 | |
michael@0 | 396 | fprintf(outfile, |
michael@0 | 397 | " %ld cache hits; %ld cache misses, %ld cache not reusable\n", |
michael@0 | 398 | ssl3stats->hch_sid_cache_hits, ssl3stats->hch_sid_cache_misses, |
michael@0 | 399 | ssl3stats->hch_sid_cache_not_ok); |
michael@0 | 400 | |
michael@0 | 401 | } |
michael@0 | 402 | |
michael@0 | 403 | |
michael@0 | 404 | /************************************************************************** |
michael@0 | 405 | ** Begin thread management routines and data. |
michael@0 | 406 | **************************************************************************/ |
michael@0 | 407 | |
michael@0 | 408 | void |
michael@0 | 409 | thread_wrapper(void * arg) |
michael@0 | 410 | { |
michael@0 | 411 | GlobalThreadMgr *threadMGR = (GlobalThreadMgr *)arg; |
michael@0 | 412 | perThread *slot = &threadMGR->threads[threadMGR->index]; |
michael@0 | 413 | |
michael@0 | 414 | /* wait for parent to finish launching us before proceeding. */ |
michael@0 | 415 | PR_Lock(threadMGR->threadLock); |
michael@0 | 416 | PR_Unlock(threadMGR->threadLock); |
michael@0 | 417 | |
michael@0 | 418 | slot->rv = (* slot->startFunc)(slot->a, slot->b); |
michael@0 | 419 | |
michael@0 | 420 | PR_Lock(threadMGR->threadLock); |
michael@0 | 421 | slot->running = rs_zombie; |
michael@0 | 422 | |
michael@0 | 423 | /* notify the thread exit handler. */ |
michael@0 | 424 | PR_NotifyCondVar(threadMGR->threadEndQ); |
michael@0 | 425 | |
michael@0 | 426 | PR_Unlock(threadMGR->threadLock); |
michael@0 | 427 | } |
michael@0 | 428 | |
michael@0 | 429 | SECStatus |
michael@0 | 430 | launch_thread(GlobalThreadMgr *threadMGR, |
michael@0 | 431 | startFn *startFunc, |
michael@0 | 432 | void *a, |
michael@0 | 433 | int b) |
michael@0 | 434 | { |
michael@0 | 435 | perThread *slot; |
michael@0 | 436 | int i; |
michael@0 | 437 | |
michael@0 | 438 | if (!threadMGR->threadStartQ) { |
michael@0 | 439 | threadMGR->threadLock = PR_NewLock(); |
michael@0 | 440 | threadMGR->threadStartQ = PR_NewCondVar(threadMGR->threadLock); |
michael@0 | 441 | threadMGR->threadEndQ = PR_NewCondVar(threadMGR->threadLock); |
michael@0 | 442 | } |
michael@0 | 443 | PR_Lock(threadMGR->threadLock); |
michael@0 | 444 | while (threadMGR->numRunning >= MAX_THREADS) { |
michael@0 | 445 | PR_WaitCondVar(threadMGR->threadStartQ, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 446 | } |
michael@0 | 447 | for (i = 0; i < threadMGR->numUsed; ++i) { |
michael@0 | 448 | slot = &threadMGR->threads[i]; |
michael@0 | 449 | if (slot->running == rs_idle) |
michael@0 | 450 | break; |
michael@0 | 451 | } |
michael@0 | 452 | if (i >= threadMGR->numUsed) { |
michael@0 | 453 | if (i >= MAX_THREADS) { |
michael@0 | 454 | /* something's really wrong here. */ |
michael@0 | 455 | PORT_Assert(i < MAX_THREADS); |
michael@0 | 456 | PR_Unlock(threadMGR->threadLock); |
michael@0 | 457 | return SECFailure; |
michael@0 | 458 | } |
michael@0 | 459 | ++(threadMGR->numUsed); |
michael@0 | 460 | PORT_Assert(threadMGR->numUsed == i + 1); |
michael@0 | 461 | slot = &threadMGR->threads[i]; |
michael@0 | 462 | } |
michael@0 | 463 | |
michael@0 | 464 | slot->a = a; |
michael@0 | 465 | slot->b = b; |
michael@0 | 466 | slot->startFunc = startFunc; |
michael@0 | 467 | |
michael@0 | 468 | threadMGR->index = i; |
michael@0 | 469 | |
michael@0 | 470 | slot->prThread = PR_CreateThread(PR_USER_THREAD, |
michael@0 | 471 | thread_wrapper, threadMGR, |
michael@0 | 472 | PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, |
michael@0 | 473 | PR_JOINABLE_THREAD, 0); |
michael@0 | 474 | |
michael@0 | 475 | if (slot->prThread == NULL) { |
michael@0 | 476 | PR_Unlock(threadMGR->threadLock); |
michael@0 | 477 | printf("Failed to launch thread!\n"); |
michael@0 | 478 | return SECFailure; |
michael@0 | 479 | } |
michael@0 | 480 | |
michael@0 | 481 | slot->inUse = 1; |
michael@0 | 482 | slot->running = 1; |
michael@0 | 483 | ++(threadMGR->numRunning); |
michael@0 | 484 | PR_Unlock(threadMGR->threadLock); |
michael@0 | 485 | |
michael@0 | 486 | return SECSuccess; |
michael@0 | 487 | } |
michael@0 | 488 | |
michael@0 | 489 | SECStatus |
michael@0 | 490 | reap_threads(GlobalThreadMgr *threadMGR) |
michael@0 | 491 | { |
michael@0 | 492 | perThread * slot; |
michael@0 | 493 | int i; |
michael@0 | 494 | |
michael@0 | 495 | if (!threadMGR->threadLock) |
michael@0 | 496 | return SECSuccess; |
michael@0 | 497 | PR_Lock(threadMGR->threadLock); |
michael@0 | 498 | while (threadMGR->numRunning > 0) { |
michael@0 | 499 | PR_WaitCondVar(threadMGR->threadEndQ, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 500 | for (i = 0; i < threadMGR->numUsed; ++i) { |
michael@0 | 501 | slot = &threadMGR->threads[i]; |
michael@0 | 502 | if (slot->running == rs_zombie) { |
michael@0 | 503 | /* Handle cleanup of thread here. */ |
michael@0 | 504 | |
michael@0 | 505 | /* Now make sure the thread has ended OK. */ |
michael@0 | 506 | PR_JoinThread(slot->prThread); |
michael@0 | 507 | slot->running = rs_idle; |
michael@0 | 508 | --threadMGR->numRunning; |
michael@0 | 509 | |
michael@0 | 510 | /* notify the thread launcher. */ |
michael@0 | 511 | PR_NotifyCondVar(threadMGR->threadStartQ); |
michael@0 | 512 | } |
michael@0 | 513 | } |
michael@0 | 514 | } |
michael@0 | 515 | |
michael@0 | 516 | /* Safety Sam sez: make sure count is right. */ |
michael@0 | 517 | for (i = 0; i < threadMGR->numUsed; ++i) { |
michael@0 | 518 | slot = &threadMGR->threads[i]; |
michael@0 | 519 | if (slot->running != rs_idle) { |
michael@0 | 520 | fprintf(stderr, "Thread in slot %d is in state %d!\n", |
michael@0 | 521 | i, slot->running); |
michael@0 | 522 | } |
michael@0 | 523 | } |
michael@0 | 524 | PR_Unlock(threadMGR->threadLock); |
michael@0 | 525 | return SECSuccess; |
michael@0 | 526 | } |
michael@0 | 527 | |
michael@0 | 528 | void |
michael@0 | 529 | destroy_thread_data(GlobalThreadMgr *threadMGR) |
michael@0 | 530 | { |
michael@0 | 531 | PORT_Memset(threadMGR->threads, 0, sizeof(threadMGR->threads)); |
michael@0 | 532 | |
michael@0 | 533 | if (threadMGR->threadEndQ) { |
michael@0 | 534 | PR_DestroyCondVar(threadMGR->threadEndQ); |
michael@0 | 535 | threadMGR->threadEndQ = NULL; |
michael@0 | 536 | } |
michael@0 | 537 | if (threadMGR->threadStartQ) { |
michael@0 | 538 | PR_DestroyCondVar(threadMGR->threadStartQ); |
michael@0 | 539 | threadMGR->threadStartQ = NULL; |
michael@0 | 540 | } |
michael@0 | 541 | if (threadMGR->threadLock) { |
michael@0 | 542 | PR_DestroyLock(threadMGR->threadLock); |
michael@0 | 543 | threadMGR->threadLock = NULL; |
michael@0 | 544 | } |
michael@0 | 545 | } |
michael@0 | 546 | |
michael@0 | 547 | /************************************************************************** |
michael@0 | 548 | ** End thread management routines. |
michael@0 | 549 | **************************************************************************/ |
michael@0 | 550 | |
michael@0 | 551 | void |
michael@0 | 552 | lockedVars_Init( lockedVars * lv) |
michael@0 | 553 | { |
michael@0 | 554 | lv->count = 0; |
michael@0 | 555 | lv->waiters = 0; |
michael@0 | 556 | lv->lock = PR_NewLock(); |
michael@0 | 557 | lv->condVar = PR_NewCondVar(lv->lock); |
michael@0 | 558 | } |
michael@0 | 559 | |
michael@0 | 560 | void |
michael@0 | 561 | lockedVars_Destroy( lockedVars * lv) |
michael@0 | 562 | { |
michael@0 | 563 | PR_DestroyCondVar(lv->condVar); |
michael@0 | 564 | lv->condVar = NULL; |
michael@0 | 565 | |
michael@0 | 566 | PR_DestroyLock(lv->lock); |
michael@0 | 567 | lv->lock = NULL; |
michael@0 | 568 | } |
michael@0 | 569 | |
michael@0 | 570 | void |
michael@0 | 571 | lockedVars_WaitForDone(lockedVars * lv) |
michael@0 | 572 | { |
michael@0 | 573 | PR_Lock(lv->lock); |
michael@0 | 574 | while (lv->count > 0) { |
michael@0 | 575 | PR_WaitCondVar(lv->condVar, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 576 | } |
michael@0 | 577 | PR_Unlock(lv->lock); |
michael@0 | 578 | } |
michael@0 | 579 | |
michael@0 | 580 | int /* returns count */ |
michael@0 | 581 | lockedVars_AddToCount(lockedVars * lv, int addend) |
michael@0 | 582 | { |
michael@0 | 583 | int rv; |
michael@0 | 584 | |
michael@0 | 585 | PR_Lock(lv->lock); |
michael@0 | 586 | rv = lv->count += addend; |
michael@0 | 587 | if (rv <= 0) { |
michael@0 | 588 | PR_NotifyCondVar(lv->condVar); |
michael@0 | 589 | } |
michael@0 | 590 | PR_Unlock(lv->lock); |
michael@0 | 591 | return rv; |
michael@0 | 592 | } |
michael@0 | 593 | |
michael@0 | 594 | |
michael@0 | 595 | /* |
michael@0 | 596 | * Dump cert chain in to cert.* files. This function is will |
michael@0 | 597 | * create collisions while dumping cert chains if called from |
michael@0 | 598 | * multiple treads. But it should not be a problem since we |
michael@0 | 599 | * consider vfyserv to be single threaded(see bug 353477). |
michael@0 | 600 | */ |
michael@0 | 601 | |
michael@0 | 602 | void |
michael@0 | 603 | dumpCertChain(CERTCertificate *cert, SECCertUsage usage) |
michael@0 | 604 | { |
michael@0 | 605 | CERTCertificateList *certList; |
michael@0 | 606 | int count = 0; |
michael@0 | 607 | |
michael@0 | 608 | certList = CERT_CertChainFromCert(cert, usage, PR_TRUE); |
michael@0 | 609 | if (certList == NULL) { |
michael@0 | 610 | errWarn("CERT_CertChainFromCert"); |
michael@0 | 611 | return; |
michael@0 | 612 | } |
michael@0 | 613 | |
michael@0 | 614 | for(count = 0; count < (unsigned int)certList->len; count++) { |
michael@0 | 615 | char certFileName[16]; |
michael@0 | 616 | PRFileDesc *cfd; |
michael@0 | 617 | |
michael@0 | 618 | PR_snprintf(certFileName, sizeof certFileName, "cert.%03d", |
michael@0 | 619 | count); |
michael@0 | 620 | cfd = PR_Open(certFileName, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, |
michael@0 | 621 | 0664); |
michael@0 | 622 | if (!cfd) { |
michael@0 | 623 | PR_fprintf(PR_STDOUT, |
michael@0 | 624 | "Error: couldn't save cert der in file '%s'\n", |
michael@0 | 625 | certFileName); |
michael@0 | 626 | } else { |
michael@0 | 627 | PR_Write(cfd, certList->certs[count].data, certList->certs[count].len); |
michael@0 | 628 | PR_Close(cfd); |
michael@0 | 629 | PR_fprintf(PR_STDOUT, "Cert file %s was created.\n", certFileName); |
michael@0 | 630 | } |
michael@0 | 631 | } |
michael@0 | 632 | CERT_DestroyCertificateList(certList); |
michael@0 | 633 | } |