nss: import at 3.0.1 beta 1
[mozilla-nss.git] / security / nss / cmd / SSLsample / sslsample.c
blobebe714981b3f963cbd5b97284ce784ba2e978792
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
37 #include "sslsample.h"
38 #include "sslerror.h"
40 /* Declare SSL cipher suites. */
42 int ssl2CipherSuites[] = {
43 SSL_EN_RC4_128_WITH_MD5, /* A */
44 SSL_EN_RC4_128_EXPORT40_WITH_MD5, /* B */
45 SSL_EN_RC2_128_CBC_WITH_MD5, /* C */
46 SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, /* D */
47 SSL_EN_DES_64_CBC_WITH_MD5, /* E */
48 SSL_EN_DES_192_EDE3_CBC_WITH_MD5, /* F */
52 int ssl3CipherSuites[] = {
53 -1, /* SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA a */
54 -1, /* SSL_FORTEZZA_DMS_WITH_RC4_128_SHA * b */
55 SSL_RSA_WITH_RC4_128_MD5, /* c */
56 SSL_RSA_WITH_3DES_EDE_CBC_SHA, /* d */
57 SSL_RSA_WITH_DES_CBC_SHA, /* e */
58 SSL_RSA_EXPORT_WITH_RC4_40_MD5, /* f */
59 SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, /* g */
60 -1, /* SSL_FORTEZZA_DMS_WITH_NULL_SHA, * h */
61 SSL_RSA_WITH_NULL_MD5, /* i */
62 SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, /* j */
63 SSL_RSA_FIPS_WITH_DES_CBC_SHA, /* k */
64 TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, /* l */
65 TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, /* m */
69 /**************************************************************************
70 **
71 ** SSL callback routines.
73 **************************************************************************/
75 /* Function: char * myPasswd()
77 * Purpose: This function is our custom password handler that is called by
78 * SSL when retreiving private certs and keys from the database. Returns a
79 * pointer to a string that with a password for the database. Password pointer
80 * should point to dynamically allocated memory that will be freed later.
82 char *
83 myPasswd(PK11SlotInfo *info, PRBool retry, void *arg)
85 char * passwd = NULL;
87 if ( (!retry) && arg ) {
88 passwd = PORT_Strdup((char *)arg);
91 return passwd;
94 /* Function: SECStatus myAuthCertificate()
96 * Purpose: This function is our custom certificate authentication handler.
98 * Note: This implementation is essentially the same as the default
99 * SSL_AuthCertificate().
101 SECStatus
102 myAuthCertificate(void *arg, PRFileDesc *socket,
103 PRBool checksig, PRBool isServer)
106 SECCertUsage certUsage;
107 CERTCertificate * cert;
108 void * pinArg;
109 char * hostName;
110 SECStatus secStatus;
112 if (!arg || !socket) {
113 errWarn("myAuthCertificate");
114 return SECFailure;
117 /* Define how the cert is being used based upon the isServer flag. */
119 certUsage = isServer ? certUsageSSLClient : certUsageSSLServer;
121 cert = SSL_PeerCertificate(socket);
123 pinArg = SSL_RevealPinArg(socket);
125 secStatus = CERT_VerifyCertNow((CERTCertDBHandle *)arg,
126 cert,
127 checksig,
128 certUsage,
129 pinArg);
131 /* If this is a server, we're finished. */
132 if (isServer || secStatus != SECSuccess) {
133 CERT_DestroyCertificate(cert);
134 return secStatus;
137 /* Certificate is OK. Since this is the client side of an SSL
138 * connection, we need to verify that the name field in the cert
139 * matches the desired hostname. This is our defense against
140 * man-in-the-middle attacks.
143 /* SSL_RevealURL returns a hostName, not an URL. */
144 hostName = SSL_RevealURL(socket);
146 if (hostName && hostName[0]) {
147 secStatus = CERT_VerifyCertName(cert, hostName);
148 } else {
149 PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0);
150 secStatus = SECFailure;
153 if (hostName)
154 PR_Free(hostName);
156 CERT_DestroyCertificate(cert);
157 return secStatus;
160 /* Function: SECStatus myBadCertHandler()
162 * Purpose: This callback is called when the incoming certificate is not
163 * valid. We define a certain set of parameters that still cause the
164 * certificate to be "valid" for this session, and return SECSuccess to cause
165 * the server to continue processing the request when any of these conditions
166 * are met. Otherwise, SECFailure is return and the server rejects the
167 * request.
169 SECStatus
170 myBadCertHandler(void *arg, PRFileDesc *socket)
173 SECStatus secStatus = SECFailure;
174 PRErrorCode err;
176 /* log invalid cert here */
178 if (!arg) {
179 return secStatus;
182 *(PRErrorCode *)arg = err = PORT_GetError();
184 /* If any of the cases in the switch are met, then we will proceed */
185 /* with the processing of the request anyway. Otherwise, the default */
186 /* case will be reached and we will reject the request. */
188 switch (err) {
189 case SEC_ERROR_INVALID_AVA:
190 case SEC_ERROR_INVALID_TIME:
191 case SEC_ERROR_BAD_SIGNATURE:
192 case SEC_ERROR_EXPIRED_CERTIFICATE:
193 case SEC_ERROR_UNKNOWN_ISSUER:
194 case SEC_ERROR_UNTRUSTED_CERT:
195 case SEC_ERROR_CERT_VALID:
196 case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
197 case SEC_ERROR_CRL_EXPIRED:
198 case SEC_ERROR_CRL_BAD_SIGNATURE:
199 case SEC_ERROR_EXTENSION_VALUE_INVALID:
200 case SEC_ERROR_CA_CERT_INVALID:
201 case SEC_ERROR_CERT_USAGES_INVALID:
202 case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION:
203 secStatus = SECSuccess;
204 break;
205 default:
206 secStatus = SECFailure;
207 break;
210 printf("Bad certificate: %d, %s\n", err, SSL_Strerror(err));
212 return secStatus;
215 /* Function: SECStatus ownGetClientAuthData()
217 * Purpose: This callback is used by SSL to pull client certificate
218 * information upon server request.
220 SECStatus
221 myGetClientAuthData(void *arg,
222 PRFileDesc *socket,
223 struct CERTDistNamesStr *caNames,
224 struct CERTCertificateStr **pRetCert,
225 struct SECKEYPrivateKeyStr **pRetKey)
228 CERTCertificate * cert;
229 SECKEYPrivateKey * privKey;
230 char * chosenNickName = (char *)arg;
231 void * proto_win = NULL;
232 SECStatus secStatus = SECFailure;
234 proto_win = SSL_RevealPinArg(socket);
236 if (chosenNickName) {
237 cert = PK11_FindCertFromNickname(chosenNickName, proto_win);
238 if (cert) {
239 privKey = PK11_FindKeyByAnyCert(cert, proto_win);
240 if (privKey) {
241 secStatus = SECSuccess;
242 } else {
243 CERT_DestroyCertificate(cert);
246 } else { /* no nickname given, automatically find the right cert */
247 CERTCertNicknames *names;
248 int i;
250 names = CERT_GetCertNicknames(CERT_GetDefaultCertDB(),
251 SEC_CERT_NICKNAMES_USER, proto_win);
253 if (names != NULL) {
254 for(i = 0; i < names->numnicknames; i++ ) {
256 cert = PK11_FindCertFromNickname(names->nicknames[i],
257 proto_win);
258 if (!cert) {
259 continue;
262 /* Only check unexpired certs */
263 if (CERT_CheckCertValidTimes(cert, PR_Now(), PR_FALSE)
264 != secCertTimeValid ) {
265 CERT_DestroyCertificate(cert);
266 continue;
269 secStatus = NSS_CmpCertChainWCANames(cert, caNames);
270 if (secStatus == SECSuccess) {
271 privKey = PK11_FindKeyByAnyCert(cert, proto_win);
272 if (privKey) {
273 break;
275 secStatus = SECFailure;
276 break;
278 } /* for loop */
279 CERT_FreeNicknames(names);
283 if (secStatus == SECSuccess) {
284 *pRetCert = cert;
285 *pRetKey = privKey;
288 return secStatus;
291 /* Function: SECStatus myHandshakeCallback()
293 * Purpose: Called by SSL to inform application that the handshake is
294 * complete. This function is mostly used on the server side of an SSL
295 * connection, although it is provided for a client as well.
296 * Useful when a non-blocking SSL_ReHandshake or SSL_ResetHandshake
297 * is used to initiate a handshake.
299 * A typical scenario would be:
301 * 1. Server accepts an SSL connection from the client without client auth.
302 * 2. Client sends a request.
303 * 3. Server determines that to service request it needs to authenticate the
304 * client and initiates another handshake requesting client auth.
305 * 4. While handshake is in progress, server can do other work or spin waiting
306 * for the handshake to complete.
307 * 5. Server is notified that handshake has been successfully completed by
308 * the custom handshake callback function and it can service the client's
309 * request.
311 * Note: This function is not implemented in this sample, as we are using
312 * blocking sockets.
314 void
315 myHandshakeCallback(PRFileDesc *socket, void *arg)
317 printf("Handshake has completed, ready to send data securely.\n");
321 /**************************************************************************
323 ** Routines for disabling SSL ciphers.
325 **************************************************************************/
327 void
328 disableAllSSLCiphers(void)
330 const PRUint16 *cipherSuites = SSL_ImplementedCiphers;
331 int i = SSL_NumImplementedCiphers;
332 SECStatus rv;
334 /* disable all the SSL3 cipher suites */
335 while (--i >= 0) {
336 PRUint16 suite = cipherSuites[i];
337 rv = SSL_CipherPrefSetDefault(suite, PR_FALSE);
338 if (rv != SECSuccess) {
339 printf("SSL_CipherPrefSetDefault didn't like value 0x%04x (i = %d)\n",
340 suite, i);
341 errWarn("SSL_CipherPrefSetDefault");
342 exit(2);
347 /**************************************************************************
349 ** Error and information routines.
351 **************************************************************************/
353 void
354 errWarn(char *function)
356 PRErrorCode errorNumber = PR_GetError();
357 const char * errorString = SSL_Strerror(errorNumber);
359 printf("Error in function %s: %d\n - %s\n",
360 function, errorNumber, errorString);
363 void
364 exitErr(char *function)
366 errWarn(function);
367 /* Exit gracefully. */
368 /* ignoring return value of NSS_Shutdown as code exits with 1*/
369 (void) NSS_Shutdown();
370 PR_Cleanup();
371 exit(1);
374 void
375 printSecurityInfo(PRFileDesc *fd)
377 char * cp; /* bulk cipher name */
378 char * ip; /* cert issuer DN */
379 char * sp; /* cert subject DN */
380 int op; /* High, Low, Off */
381 int kp0; /* total key bits */
382 int kp1; /* secret key bits */
383 int result;
384 SSL3Statistics * ssl3stats = SSL_GetStatistics();
386 result = SSL_SecurityStatus(fd, &op, &cp, &kp0, &kp1, &ip, &sp);
387 if (result != SECSuccess)
388 return;
389 printf("bulk cipher %s, %d secret key bits, %d key bits, status: %d\n"
390 "subject DN: %s\n"
391 "issuer DN: %s\n", cp, kp1, kp0, op, sp, ip);
392 PR_Free(cp);
393 PR_Free(ip);
394 PR_Free(sp);
396 printf("%ld cache hits; %ld cache misses, %ld cache not reusable\n",
397 ssl3stats->hch_sid_cache_hits, ssl3stats->hch_sid_cache_misses,
398 ssl3stats->hch_sid_cache_not_ok);
403 /**************************************************************************
404 ** Begin thread management routines and data.
405 **************************************************************************/
407 void
408 thread_wrapper(void * arg)
410 GlobalThreadMgr *threadMGR = (GlobalThreadMgr *)arg;
411 perThread *slot = &threadMGR->threads[threadMGR->index];
413 /* wait for parent to finish launching us before proceeding. */
414 PR_Lock(threadMGR->threadLock);
415 PR_Unlock(threadMGR->threadLock);
417 slot->rv = (* slot->startFunc)(slot->a, slot->b);
419 PR_Lock(threadMGR->threadLock);
420 slot->running = rs_zombie;
422 /* notify the thread exit handler. */
423 PR_NotifyCondVar(threadMGR->threadEndQ);
425 PR_Unlock(threadMGR->threadLock);
428 SECStatus
429 launch_thread(GlobalThreadMgr *threadMGR,
430 startFn *startFunc,
431 void *a,
432 int b)
434 perThread *slot;
435 int i;
437 if (!threadMGR->threadStartQ) {
438 threadMGR->threadLock = PR_NewLock();
439 threadMGR->threadStartQ = PR_NewCondVar(threadMGR->threadLock);
440 threadMGR->threadEndQ = PR_NewCondVar(threadMGR->threadLock);
442 PR_Lock(threadMGR->threadLock);
443 while (threadMGR->numRunning >= MAX_THREADS) {
444 PR_WaitCondVar(threadMGR->threadStartQ, PR_INTERVAL_NO_TIMEOUT);
446 for (i = 0; i < threadMGR->numUsed; ++i) {
447 slot = &threadMGR->threads[i];
448 if (slot->running == rs_idle)
449 break;
451 if (i >= threadMGR->numUsed) {
452 if (i >= MAX_THREADS) {
453 /* something's really wrong here. */
454 PORT_Assert(i < MAX_THREADS);
455 PR_Unlock(threadMGR->threadLock);
456 return SECFailure;
458 ++(threadMGR->numUsed);
459 PORT_Assert(threadMGR->numUsed == i + 1);
460 slot = &threadMGR->threads[i];
463 slot->a = a;
464 slot->b = b;
465 slot->startFunc = startFunc;
467 threadMGR->index = i;
469 slot->prThread = PR_CreateThread(PR_USER_THREAD,
470 thread_wrapper, threadMGR,
471 PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
472 PR_JOINABLE_THREAD, 0);
474 if (slot->prThread == NULL) {
475 PR_Unlock(threadMGR->threadLock);
476 printf("Failed to launch thread!\n");
477 return SECFailure;
480 slot->inUse = 1;
481 slot->running = 1;
482 ++(threadMGR->numRunning);
483 PR_Unlock(threadMGR->threadLock);
484 printf("Launched thread in slot %d \n", threadMGR->index);
486 return SECSuccess;
489 SECStatus
490 reap_threads(GlobalThreadMgr *threadMGR)
492 perThread * slot;
493 int i;
495 if (!threadMGR->threadLock)
496 return 0;
497 PR_Lock(threadMGR->threadLock);
498 while (threadMGR->numRunning > 0) {
499 PR_WaitCondVar(threadMGR->threadEndQ, PR_INTERVAL_NO_TIMEOUT);
500 for (i = 0; i < threadMGR->numUsed; ++i) {
501 slot = &threadMGR->threads[i];
502 if (slot->running == rs_zombie) {
503 /* Handle cleanup of thread here. */
504 printf("Thread in slot %d returned %d\n", i, slot->rv);
506 /* Now make sure the thread has ended OK. */
507 PR_JoinThread(slot->prThread);
508 slot->running = rs_idle;
509 --threadMGR->numRunning;
511 /* notify the thread launcher. */
512 PR_NotifyCondVar(threadMGR->threadStartQ);
517 /* Safety Sam sez: make sure count is right. */
518 for (i = 0; i < threadMGR->numUsed; ++i) {
519 slot = &threadMGR->threads[i];
520 if (slot->running != rs_idle) {
521 fprintf(stderr, "Thread in slot %d is in state %d!\n",
522 i, slot->running);
525 PR_Unlock(threadMGR->threadLock);
526 return 0;
529 void
530 destroy_thread_data(GlobalThreadMgr *threadMGR)
532 PORT_Memset(threadMGR->threads, 0, sizeof(threadMGR->threads));
534 if (threadMGR->threadEndQ) {
535 PR_DestroyCondVar(threadMGR->threadEndQ);
536 threadMGR->threadEndQ = NULL;
538 if (threadMGR->threadStartQ) {
539 PR_DestroyCondVar(threadMGR->threadStartQ);
540 threadMGR->threadStartQ = NULL;
542 if (threadMGR->threadLock) {
543 PR_DestroyLock(threadMGR->threadLock);
544 threadMGR->threadLock = NULL;
548 /**************************************************************************
549 ** End thread management routines.
550 **************************************************************************/
552 void
553 lockedVars_Init( lockedVars * lv)
555 lv->count = 0;
556 lv->waiters = 0;
557 lv->lock = PR_NewLock();
558 lv->condVar = PR_NewCondVar(lv->lock);
561 void
562 lockedVars_Destroy( lockedVars * lv)
564 PR_DestroyCondVar(lv->condVar);
565 lv->condVar = NULL;
567 PR_DestroyLock(lv->lock);
568 lv->lock = NULL;
571 void
572 lockedVars_WaitForDone(lockedVars * lv)
574 PR_Lock(lv->lock);
575 while (lv->count > 0) {
576 PR_WaitCondVar(lv->condVar, PR_INTERVAL_NO_TIMEOUT);
578 PR_Unlock(lv->lock);
581 int /* returns count */
582 lockedVars_AddToCount(lockedVars * lv, int addend)
584 int rv;
586 PR_Lock(lv->lock);
587 rv = lv->count += addend;
588 if (rv <= 0) {
589 PR_NotifyCondVar(lv->condVar);
591 PR_Unlock(lv->lock);
592 return rv;