Import from 1.9a8 tarball
[mozilla-nss.git] / security / nss / cmd / SSLsample / sslsample.c
blob422d453de59c07199bcef614892aed082b69009c
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 SECStatus
315 myHandshakeCallback(PRFileDesc *socket, void *arg)
317 printf("Handshake has completed, ready to send data securely.\n");
318 return SECSuccess;
322 /**************************************************************************
324 ** Routines for disabling SSL ciphers.
326 **************************************************************************/
328 void
329 disableAllSSLCiphers(void)
331 const PRUint16 *cipherSuites = SSL_ImplementedCiphers;
332 int i = SSL_NumImplementedCiphers;
333 SECStatus rv;
335 /* disable all the SSL3 cipher suites */
336 while (--i >= 0) {
337 PRUint16 suite = cipherSuites[i];
338 rv = SSL_CipherPrefSetDefault(suite, PR_FALSE);
339 if (rv != SECSuccess) {
340 printf("SSL_CipherPrefSetDefault didn't like value 0x%04x (i = %d)\n",
341 suite, i);
342 errWarn("SSL_CipherPrefSetDefault");
343 exit(2);
348 /**************************************************************************
350 ** Error and information routines.
352 **************************************************************************/
354 void
355 errWarn(char *function)
357 PRErrorCode errorNumber = PR_GetError();
358 const char * errorString = SSL_Strerror(errorNumber);
360 printf("Error in function %s: %d\n - %s\n",
361 function, errorNumber, errorString);
364 void
365 exitErr(char *function)
367 errWarn(function);
368 /* Exit gracefully. */
369 /* ignoring return value of NSS_Shutdown as code exits with 1*/
370 (void) NSS_Shutdown();
371 PR_Cleanup();
372 exit(1);
375 void
376 printSecurityInfo(PRFileDesc *fd)
378 char * cp; /* bulk cipher name */
379 char * ip; /* cert issuer DN */
380 char * sp; /* cert subject DN */
381 int op; /* High, Low, Off */
382 int kp0; /* total key bits */
383 int kp1; /* secret key bits */
384 int result;
385 SSL3Statistics * ssl3stats = SSL_GetStatistics();
387 result = SSL_SecurityStatus(fd, &op, &cp, &kp0, &kp1, &ip, &sp);
388 if (result != SECSuccess)
389 return;
390 printf("bulk cipher %s, %d secret key bits, %d key bits, status: %d\n"
391 "subject DN: %s\n"
392 "issuer DN: %s\n", cp, kp1, kp0, op, sp, ip);
393 PR_Free(cp);
394 PR_Free(ip);
395 PR_Free(sp);
397 printf("%ld cache hits; %ld cache misses, %ld cache not reusable\n",
398 ssl3stats->hch_sid_cache_hits, ssl3stats->hch_sid_cache_misses,
399 ssl3stats->hch_sid_cache_not_ok);
404 /**************************************************************************
405 ** Begin thread management routines and data.
406 **************************************************************************/
408 void
409 thread_wrapper(void * arg)
411 GlobalThreadMgr *threadMGR = (GlobalThreadMgr *)arg;
412 perThread *slot = &threadMGR->threads[threadMGR->index];
414 /* wait for parent to finish launching us before proceeding. */
415 PR_Lock(threadMGR->threadLock);
416 PR_Unlock(threadMGR->threadLock);
418 slot->rv = (* slot->startFunc)(slot->a, slot->b);
420 PR_Lock(threadMGR->threadLock);
421 slot->running = rs_zombie;
423 /* notify the thread exit handler. */
424 PR_NotifyCondVar(threadMGR->threadEndQ);
426 PR_Unlock(threadMGR->threadLock);
429 SECStatus
430 launch_thread(GlobalThreadMgr *threadMGR,
431 startFn *startFunc,
432 void *a,
433 int b)
435 perThread *slot;
436 int i;
438 if (!threadMGR->threadStartQ) {
439 threadMGR->threadLock = PR_NewLock();
440 threadMGR->threadStartQ = PR_NewCondVar(threadMGR->threadLock);
441 threadMGR->threadEndQ = PR_NewCondVar(threadMGR->threadLock);
443 PR_Lock(threadMGR->threadLock);
444 while (threadMGR->numRunning >= MAX_THREADS) {
445 PR_WaitCondVar(threadMGR->threadStartQ, PR_INTERVAL_NO_TIMEOUT);
447 for (i = 0; i < threadMGR->numUsed; ++i) {
448 slot = &threadMGR->threads[i];
449 if (slot->running == rs_idle)
450 break;
452 if (i >= threadMGR->numUsed) {
453 if (i >= MAX_THREADS) {
454 /* something's really wrong here. */
455 PORT_Assert(i < MAX_THREADS);
456 PR_Unlock(threadMGR->threadLock);
457 return SECFailure;
459 ++(threadMGR->numUsed);
460 PORT_Assert(threadMGR->numUsed == i + 1);
461 slot = &threadMGR->threads[i];
464 slot->a = a;
465 slot->b = b;
466 slot->startFunc = startFunc;
468 threadMGR->index = i;
470 slot->prThread = PR_CreateThread(PR_USER_THREAD,
471 thread_wrapper, threadMGR,
472 PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
473 PR_JOINABLE_THREAD, 0);
475 if (slot->prThread == NULL) {
476 PR_Unlock(threadMGR->threadLock);
477 printf("Failed to launch thread!\n");
478 return SECFailure;
481 slot->inUse = 1;
482 slot->running = 1;
483 ++(threadMGR->numRunning);
484 PR_Unlock(threadMGR->threadLock);
485 printf("Launched thread in slot %d \n", threadMGR->index);
487 return SECSuccess;
490 SECStatus
491 reap_threads(GlobalThreadMgr *threadMGR)
493 perThread * slot;
494 int i;
496 if (!threadMGR->threadLock)
497 return 0;
498 PR_Lock(threadMGR->threadLock);
499 while (threadMGR->numRunning > 0) {
500 PR_WaitCondVar(threadMGR->threadEndQ, PR_INTERVAL_NO_TIMEOUT);
501 for (i = 0; i < threadMGR->numUsed; ++i) {
502 slot = &threadMGR->threads[i];
503 if (slot->running == rs_zombie) {
504 /* Handle cleanup of thread here. */
505 printf("Thread in slot %d returned %d\n", i, slot->rv);
507 /* Now make sure the thread has ended OK. */
508 PR_JoinThread(slot->prThread);
509 slot->running = rs_idle;
510 --threadMGR->numRunning;
512 /* notify the thread launcher. */
513 PR_NotifyCondVar(threadMGR->threadStartQ);
518 /* Safety Sam sez: make sure count is right. */
519 for (i = 0; i < threadMGR->numUsed; ++i) {
520 slot = &threadMGR->threads[i];
521 if (slot->running != rs_idle) {
522 fprintf(stderr, "Thread in slot %d is in state %d!\n",
523 i, slot->running);
526 PR_Unlock(threadMGR->threadLock);
527 return 0;
530 void
531 destroy_thread_data(GlobalThreadMgr *threadMGR)
533 PORT_Memset(threadMGR->threads, 0, sizeof(threadMGR->threads));
535 if (threadMGR->threadEndQ) {
536 PR_DestroyCondVar(threadMGR->threadEndQ);
537 threadMGR->threadEndQ = NULL;
539 if (threadMGR->threadStartQ) {
540 PR_DestroyCondVar(threadMGR->threadStartQ);
541 threadMGR->threadStartQ = NULL;
543 if (threadMGR->threadLock) {
544 PR_DestroyLock(threadMGR->threadLock);
545 threadMGR->threadLock = NULL;
549 /**************************************************************************
550 ** End thread management routines.
551 **************************************************************************/
553 void
554 lockedVars_Init( lockedVars * lv)
556 lv->count = 0;
557 lv->waiters = 0;
558 lv->lock = PR_NewLock();
559 lv->condVar = PR_NewCondVar(lv->lock);
562 void
563 lockedVars_Destroy( lockedVars * lv)
565 PR_DestroyCondVar(lv->condVar);
566 lv->condVar = NULL;
568 PR_DestroyLock(lv->lock);
569 lv->lock = NULL;
572 void
573 lockedVars_WaitForDone(lockedVars * lv)
575 PR_Lock(lv->lock);
576 while (lv->count > 0) {
577 PR_WaitCondVar(lv->condVar, PR_INTERVAL_NO_TIMEOUT);
579 PR_Unlock(lv->lock);
582 int /* returns count */
583 lockedVars_AddToCount(lockedVars * lv, int addend)
585 int rv;
587 PR_Lock(lv->lock);
588 rv = lv->count += addend;
589 if (rv <= 0) {
590 PR_NotifyCondVar(lv->condVar);
592 PR_Unlock(lv->lock);
593 return rv;