1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 /****************************************************************************
6  *  SSL client program that tests  a server for proper operation of SSL2,   *
7  *  SSL3, and TLS. Test propder certificate installation.                   *
8  *                                                                          *
9  *  This code was modified from the SSLSample code also kept in the NSS     *
10  *  directory.                                                              *
11  ****************************************************************************/
12 
13 #include <stdio.h>
14 #include <string.h>
15 
16 #if defined(XP_UNIX)
17 #include <unistd.h>
18 #endif
19 
20 #include "prerror.h"
21 
22 #include "pk11func.h"
23 #include "secmod.h"
24 #include "secitem.h"
25 
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stdarg.h>
30 
31 #include "nspr.h"
32 #include "plgetopt.h"
33 #include "prio.h"
34 #include "prnetdb.h"
35 #include "nss.h"
36 #include "secutil.h"
37 #include "ocsp.h"
38 
39 #include "vfyserv.h"
40 
41 #define RD_BUF_SIZE (60 * 1024)
42 
43 extern int ssl3CipherSuites[];
44 extern int numSSL3CipherSuites;
45 
46 GlobalThreadMgr threadMGR;
47 char *certNickname = NULL;
48 char *hostName = NULL;
49 secuPWData pwdata = { PW_NONE, 0 };
50 unsigned short port = 0;
51 PRBool dumpChain;
52 
53 static void
Usage(const char * progName)54 Usage(const char *progName)
55 {
56     PRFileDesc *pr_stderr;
57 
58     pr_stderr = PR_STDERR;
59 
60     PR_fprintf(pr_stderr, "Usage:\n"
61                           "   %s  [-c ] [-o] [-p port] [-d dbdir] [-w password] [-f pwfile]\n"
62                           "   \t\t[-C cipher(s)]  [-l <url> -t <nickname> ] hostname",
63                progName);
64     PR_fprintf(pr_stderr, "\nWhere:\n");
65     PR_fprintf(pr_stderr,
66                "  %-13s dump server cert chain into files\n",
67                "-c");
68     PR_fprintf(pr_stderr,
69                "  %-13s perform server cert OCSP check\n",
70                "-o");
71     PR_fprintf(pr_stderr,
72                "  %-13s server port to be used\n",
73                "-p");
74     PR_fprintf(pr_stderr,
75                "  %-13s use security databases in \"dbdir\"\n",
76                "-d dbdir");
77     PR_fprintf(pr_stderr,
78                "  %-13s key database password\n",
79                "-w password");
80     PR_fprintf(pr_stderr,
81                "  %-13s token password file\n",
82                "-f pwfile");
83     PR_fprintf(pr_stderr,
84                "  %-13s communication cipher list\n",
85                "-C cipher(s)");
86     PR_fprintf(pr_stderr,
87                "  %-13s OCSP responder location. This location is used to\n"
88                "  %-13s check  status  of a server  certificate.  If  not \n"
89                "  %-13s specified, location  will  be taken  from the AIA\n"
90                "  %-13s server certificate extension.\n",
91                "-l url", "", "", "");
92     PR_fprintf(pr_stderr,
93                "  %-13s OCSP Trusted Responder Cert nickname\n\n",
94                "-t nickname");
95 
96     exit(1);
97 }
98 
99 PRFileDesc *
setupSSLSocket(PRNetAddr * addr)100 setupSSLSocket(PRNetAddr *addr)
101 {
102     PRFileDesc *tcpSocket;
103     PRFileDesc *sslSocket;
104     PRSocketOptionData socketOption;
105     PRStatus prStatus;
106     SECStatus secStatus;
107 
108     tcpSocket = PR_NewTCPSocket();
109     if (tcpSocket == NULL) {
110         errWarn("PR_NewTCPSocket");
111     }
112 
113     /* Make the socket blocking. */
114     socketOption.option = PR_SockOpt_Nonblocking;
115     socketOption.value.non_blocking = PR_FALSE;
116 
117     prStatus = PR_SetSocketOption(tcpSocket, &socketOption);
118     if (prStatus != PR_SUCCESS) {
119         errWarn("PR_SetSocketOption");
120         goto loser;
121     }
122 
123     /* Import the socket into the SSL layer. */
124     sslSocket = SSL_ImportFD(NULL, tcpSocket);
125     if (!sslSocket) {
126         errWarn("SSL_ImportFD");
127         goto loser;
128     }
129 
130     /* Set configuration options. */
131     secStatus = SSL_OptionSet(sslSocket, SSL_SECURITY, PR_TRUE);
132     if (secStatus != SECSuccess) {
133         errWarn("SSL_OptionSet:SSL_SECURITY");
134         goto loser;
135     }
136 
137     secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
138     if (secStatus != SECSuccess) {
139         errWarn("SSL_OptionSet:SSL_HANDSHAKE_AS_CLIENT");
140         goto loser;
141     }
142 
143     /* Set SSL callback routines. */
144     secStatus = SSL_GetClientAuthDataHook(sslSocket,
145                                           (SSLGetClientAuthData)myGetClientAuthData,
146                                           (void *)certNickname);
147     if (secStatus != SECSuccess) {
148         errWarn("SSL_GetClientAuthDataHook");
149         goto loser;
150     }
151 
152     secStatus = SSL_AuthCertificateHook(sslSocket,
153                                         (SSLAuthCertificate)myAuthCertificate,
154                                         (void *)CERT_GetDefaultCertDB());
155     if (secStatus != SECSuccess) {
156         errWarn("SSL_AuthCertificateHook");
157         goto loser;
158     }
159 
160     secStatus = SSL_BadCertHook(sslSocket,
161                                 (SSLBadCertHandler)myBadCertHandler, NULL);
162     if (secStatus != SECSuccess) {
163         errWarn("SSL_BadCertHook");
164         goto loser;
165     }
166 
167     secStatus = SSL_HandshakeCallback(sslSocket,
168                                       myHandshakeCallback,
169                                       NULL);
170     if (secStatus != SECSuccess) {
171         errWarn("SSL_HandshakeCallback");
172         goto loser;
173     }
174 
175     return sslSocket;
176 
177 loser:
178 
179     PR_Close(tcpSocket);
180     return NULL;
181 }
182 
183 const char requestString[] = { "GET /testfile HTTP/1.0\r\n\r\n" };
184 
185 SECStatus
handle_connection(PRFileDesc * sslSocket,int connection)186 handle_connection(PRFileDesc *sslSocket, int connection)
187 {
188     int countRead = 0;
189     PRInt32 numBytes;
190     char *readBuffer;
191 
192     readBuffer = PORT_Alloc(RD_BUF_SIZE);
193     if (!readBuffer) {
194         exitErr("PORT_Alloc");
195     }
196 
197     /* compose the http request here. */
198 
199     numBytes = PR_Write(sslSocket, requestString, strlen(requestString));
200     if (numBytes <= 0) {
201         errWarn("PR_Write");
202         PR_Free(readBuffer);
203         readBuffer = NULL;
204         return SECFailure;
205     }
206 
207     /* read until EOF */
208     while (PR_TRUE) {
209         numBytes = PR_Read(sslSocket, readBuffer, RD_BUF_SIZE);
210         if (numBytes == 0) {
211             break; /* EOF */
212         }
213         if (numBytes < 0) {
214             errWarn("PR_Read");
215             break;
216         }
217         countRead += numBytes;
218     }
219 
220     printSecurityInfo(stderr, sslSocket);
221 
222     PR_Free(readBuffer);
223     readBuffer = NULL;
224 
225     /* Caller closes the socket. */
226 
227     fprintf(stderr,
228             "***** Connection %d read %d bytes total.\n",
229             connection, countRead);
230 
231     return SECSuccess; /* success */
232 }
233 
234 #define BYTE(n, i) (((i) >> ((n)*8)) & 0xff)
235 
236 /* one copy of this function is launched in a separate thread for each
237 ** connection to be made.
238 */
239 SECStatus
do_connects(void * a,int connection)240 do_connects(void *a, int connection)
241 {
242     PRNetAddr *addr = (PRNetAddr *)a;
243     PRFileDesc *sslSocket;
244     PRHostEnt hostEntry;
245     char buffer[PR_NETDB_BUF_SIZE];
246     PRStatus prStatus;
247     PRIntn hostenum;
248     PRInt32 ip;
249     SECStatus secStatus;
250 
251     /* Set up SSL secure socket. */
252     sslSocket = setupSSLSocket(addr);
253     if (sslSocket == NULL) {
254         errWarn("setupSSLSocket");
255         return SECFailure;
256     }
257 
258     secStatus = SSL_SetPKCS11PinArg(sslSocket, &pwdata);
259     if (secStatus != SECSuccess) {
260         errWarn("SSL_SetPKCS11PinArg");
261         return secStatus;
262     }
263 
264     secStatus = SSL_SetURL(sslSocket, hostName);
265     if (secStatus != SECSuccess) {
266         errWarn("SSL_SetURL");
267         return secStatus;
268     }
269 
270     /* Prepare and setup network connection. */
271     prStatus = PR_GetHostByName(hostName, buffer, sizeof(buffer), &hostEntry);
272     if (prStatus != PR_SUCCESS) {
273         errWarn("PR_GetHostByName");
274         return SECFailure;
275     }
276 
277     hostenum = PR_EnumerateHostEnt(0, &hostEntry, port, addr);
278     if (hostenum == -1) {
279         errWarn("PR_EnumerateHostEnt");
280         return SECFailure;
281     }
282 
283     ip = PR_ntohl(addr->inet.ip);
284     fprintf(stderr,
285             "Connecting to host %s (addr %d.%d.%d.%d) on port %d\n",
286             hostName, BYTE(3, ip), BYTE(2, ip), BYTE(1, ip),
287             BYTE(0, ip), PR_ntohs(addr->inet.port));
288 
289     prStatus = PR_Connect(sslSocket, addr, PR_INTERVAL_NO_TIMEOUT);
290     if (prStatus != PR_SUCCESS) {
291         errWarn("PR_Connect");
292         return SECFailure;
293     }
294 
295 /* Established SSL connection, ready to send data. */
296 #if 0
297     secStatus = SSL_ForceHandshake(sslSocket);
298     if (secStatus != SECSuccess) {
299         errWarn("SSL_ForceHandshake");
300         return secStatus;
301     }
302 #endif
303 
304     secStatus = SSL_ResetHandshake(sslSocket, /* asServer */ PR_FALSE);
305     if (secStatus != SECSuccess) {
306         errWarn("SSL_ResetHandshake");
307         prStatus = PR_Close(sslSocket);
308         if (prStatus != PR_SUCCESS) {
309             errWarn("PR_Close");
310         }
311         return secStatus;
312     }
313 
314     secStatus = handle_connection(sslSocket, connection);
315     if (secStatus != SECSuccess) {
316         /* error already printed out in handle_connection */
317         /* errWarn("handle_connection"); */
318         prStatus = PR_Close(sslSocket);
319         if (prStatus != PR_SUCCESS) {
320             errWarn("PR_Close");
321         }
322         return secStatus;
323     }
324 
325     PR_Close(sslSocket);
326     return SECSuccess;
327 }
328 
329 void
client_main(int connections)330 client_main(int connections)
331 {
332     int i;
333     SECStatus secStatus;
334     PRStatus prStatus;
335     PRInt32 rv;
336     PRNetAddr addr;
337     PRHostEnt hostEntry;
338     char buffer[PR_NETDB_BUF_SIZE];
339 
340     /* Setup network connection. */
341     prStatus = PR_GetHostByName(hostName, buffer, sizeof(buffer), &hostEntry);
342     if (prStatus != PR_SUCCESS) {
343         exitErr("PR_GetHostByName");
344     }
345 
346     rv = PR_EnumerateHostEnt(0, &hostEntry, port, &addr);
347     if (rv < 0) {
348         exitErr("PR_EnumerateHostEnt");
349     }
350 
351     secStatus = launch_thread(&threadMGR, do_connects, &addr, 1);
352     if (secStatus != SECSuccess) {
353         exitErr("launch_thread");
354     }
355 
356     if (connections > 1) {
357         /* wait for the first connection to terminate, then launch the rest. */
358         reap_threads(&threadMGR);
359         /* Start up the connections */
360         for (i = 2; i <= connections; ++i) {
361             secStatus = launch_thread(&threadMGR, do_connects, &addr, i);
362             if (secStatus != SECSuccess) {
363                 errWarn("launch_thread");
364             }
365         }
366     }
367 
368     reap_threads(&threadMGR);
369     destroy_thread_data(&threadMGR);
370 }
371 
372 #define HEXCHAR_TO_INT(c, i)                   \
373     if (((c) >= '0') && ((c) <= '9')) {        \
374         i = (c) - '0';                         \
375     } else if (((c) >= 'a') && ((c) <= 'f')) { \
376         i = (c) - 'a' + 10;                    \
377     } else if (((c) >= 'A') && ((c) <= 'F')) { \
378         i = (c) - 'A' + 10;                    \
379     } else {                                   \
380         Usage(progName);                       \
381     }
382 
383 int
main(int argc,char ** argv)384 main(int argc, char **argv)
385 {
386     char *certDir = NULL;
387     char *progName = NULL;
388     int connections = 1;
389     char *cipherString = NULL;
390     char *respUrl = NULL;
391     char *respCertName = NULL;
392     SECStatus secStatus;
393     PLOptState *optstate;
394     PLOptStatus status;
395     PRBool doOcspCheck = PR_FALSE;
396 
397     /* Call the NSPR initialization routines */
398     PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
399 
400     progName = PORT_Strdup(argv[0]);
401 
402     hostName = NULL;
403     optstate = PL_CreateOptState(argc, argv, "C:cd:f:l:n:p:ot:w:");
404     while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
405         switch (optstate->option) {
406             case 'C':
407                 cipherString = PL_strdup(optstate->value);
408                 break;
409             case 'c':
410                 dumpChain = PR_TRUE;
411                 break;
412             case 'd':
413                 certDir = PL_strdup(optstate->value);
414                 break;
415             case 'l':
416                 respUrl = PL_strdup(optstate->value);
417                 break;
418             case 'p':
419                 port = PORT_Atoi(optstate->value);
420                 break;
421             case 'o':
422                 doOcspCheck = PR_TRUE;
423                 break;
424             case 't':
425                 respCertName = PL_strdup(optstate->value);
426                 break;
427             case 'w':
428                 pwdata.source = PW_PLAINTEXT;
429                 pwdata.data = PORT_Strdup(optstate->value);
430                 break;
431 
432             case 'f':
433                 pwdata.source = PW_FROMFILE;
434                 pwdata.data = PORT_Strdup(optstate->value);
435                 break;
436             case '\0':
437                 hostName = PL_strdup(optstate->value);
438                 break;
439             default:
440                 Usage(progName);
441         }
442     }
443 
444     if (port == 0) {
445         port = 443;
446     }
447 
448     if (port == 0 || hostName == NULL)
449         Usage(progName);
450 
451     if (doOcspCheck &&
452         ((respCertName != NULL && respUrl == NULL) ||
453          (respUrl != NULL && respCertName == NULL))) {
454         SECU_PrintError(progName, "options -l <url> and -t "
455                                   "<responder> must be used together");
456         Usage(progName);
457     }
458 
459     PK11_SetPasswordFunc(SECU_GetModulePassword);
460 
461     /* Initialize the NSS libraries. */
462     if (certDir) {
463         secStatus = NSS_Init(certDir);
464     } else {
465         secStatus = NSS_NoDB_Init(NULL);
466 
467         /* load the builtins */
468         SECMOD_AddNewModule("Builtins",
469                             DLL_PREFIX "nssckbi." DLL_SUFFIX, 0, 0);
470     }
471     if (secStatus != SECSuccess) {
472         exitErr("NSS_Init");
473     }
474     SECU_RegisterDynamicOids();
475 
476     if (doOcspCheck == PR_TRUE) {
477         SECStatus rv;
478         CERTCertDBHandle *handle = CERT_GetDefaultCertDB();
479         if (handle == NULL) {
480             SECU_PrintError(progName, "problem getting certdb handle");
481             goto cleanup;
482         }
483 
484         rv = CERT_EnableOCSPChecking(handle);
485         if (rv != SECSuccess) {
486             SECU_PrintError(progName, "error enabling OCSP checking");
487             goto cleanup;
488         }
489 
490         if (respUrl != NULL) {
491             rv = CERT_SetOCSPDefaultResponder(handle, respUrl,
492                                               respCertName);
493             if (rv != SECSuccess) {
494                 SECU_PrintError(progName,
495                                 "error setting default responder");
496                 goto cleanup;
497             }
498 
499             rv = CERT_EnableOCSPDefaultResponder(handle);
500             if (rv != SECSuccess) {
501                 SECU_PrintError(progName,
502                                 "error enabling default responder");
503                 goto cleanup;
504             }
505         }
506     }
507 
508     /* All cipher suites except RSA_NULL_MD5 are enabled by
509      * Domestic Policy. */
510     NSS_SetDomesticPolicy();
511     SSL_CipherPrefSetDefault(TLS_RSA_WITH_NULL_MD5, PR_TRUE);
512 
513     /* all the SSL2 and SSL3 cipher suites are enabled by default. */
514     if (cipherString) {
515         int ndx;
516 
517         /* disable all the ciphers, then enable the ones we want. */
518         disableAllSSLCiphers();
519 
520         while (0 != (ndx = *cipherString++)) {
521             int cipher = 0;
522 
523             if (ndx == ':') {
524                 int ctmp = 0;
525 
526                 HEXCHAR_TO_INT(*cipherString, ctmp)
527                 cipher |= (ctmp << 12);
528                 cipherString++;
529                 HEXCHAR_TO_INT(*cipherString, ctmp)
530                 cipher |= (ctmp << 8);
531                 cipherString++;
532                 HEXCHAR_TO_INT(*cipherString, ctmp)
533                 cipher |= (ctmp << 4);
534                 cipherString++;
535                 HEXCHAR_TO_INT(*cipherString, ctmp)
536                 cipher |= ctmp;
537                 cipherString++;
538             } else {
539                 if (!isalpha(ndx))
540                     Usage(progName);
541                 ndx = tolower(ndx) - 'a';
542                 if (ndx < numSSL3CipherSuites) {
543                     cipher = ssl3CipherSuites[ndx];
544                 }
545             }
546             if (cipher > 0) {
547                 SECStatus rv = SSL_CipherPrefSetDefault(cipher, PR_TRUE);
548                 if (rv != SECSuccess) {
549                     SECU_PrintError(progName,
550                                     "error setting cipher default preference");
551                     goto cleanup;
552                 }
553             } else {
554                 Usage(progName);
555             }
556         }
557     }
558 
559     client_main(connections);
560 
561 cleanup:
562     if (doOcspCheck) {
563         CERTCertDBHandle *handle = CERT_GetDefaultCertDB();
564         CERT_DisableOCSPDefaultResponder(handle);
565         CERT_DisableOCSPChecking(handle);
566     }
567 
568     if (NSS_Shutdown() != SECSuccess) {
569         exit(1);
570     }
571 
572     PR_Cleanup();
573     PORT_Free(progName);
574     return 0;
575 }
576