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  *  Read in a cert chain from one or more files, and verify the chain for
7  *  some usage.
8  *                                                                          *
9  *  This code was modified from other code also kept in the NSS directory.
10  ****************************************************************************/
11 
12 #include <stdio.h>
13 #include <string.h>
14 
15 #if defined(XP_UNIX)
16 #include <unistd.h>
17 #endif
18 
19 #include "prerror.h"
20 
21 #include "pk11func.h"
22 #include "seccomon.h"
23 #include "secutil.h"
24 #include "secmod.h"
25 #include "secitem.h"
26 #include "cert.h"
27 #include "ocsp.h"
28 
29 /* #include <stdlib.h> */
30 /* #include <errno.h> */
31 /* #include <fcntl.h> */
32 /* #include <stdarg.h> */
33 
34 #include "nspr.h"
35 #include "plgetopt.h"
36 #include "prio.h"
37 #include "nss.h"
38 
39 /* #include "vfyutil.h" */
40 
41 #define RD_BUF_SIZE (60 * 1024)
42 
43 int verbose;
44 
45 secuPWData pwdata = { PW_NONE, 0 };
46 
47 static void
Usage(const char * progName)48 Usage(const char *progName)
49 {
50     fprintf(stderr,
51             "Usage: %s [options] [revocation options] certfile "
52             "[[options] certfile] ...\n"
53             "\tWhere options are:\n"
54             "\t-a\t\t Following certfile is base64 encoded\n"
55             "\t-b YYMMDDHHMMZ\t Validate date (default: now)\n"
56             "\t-d directory\t Database directory\n"
57             "\t-i number of consecutive verifications\n"
58             "\t-f \t\t Enable cert fetching from AIA URL\n"
59             "\t-o oid\t\t Set policy OID for cert validation(Format OID.1.2.3)\n"
60             "\t-p \t\t Use PKIX Library to validate certificate by calling:\n"
61             "\t\t\t   * CERT_VerifyCertificate if specified once,\n"
62             "\t\t\t   * CERT_PKIXVerifyCert if specified twice and more.\n"
63             "\t-r\t\t Following certfile is raw binary DER (default)\n"
64             "\t-t\t\t Following cert is explicitly trusted (overrides db trust).\n"
65             "\t-u usage \t 0=SSL client, 1=SSL server, 2=SSL StepUp, 3=SSL CA,\n"
66             "\t\t\t 4=Email signer, 5=Email recipient, 6=Object signer,\n"
67             "\t\t\t 9=ProtectedObjectSigner, 10=OCSP responder, 11=Any CA,\n"
68             "\t\t\t 12=IPsec\n"
69             "\t-T\t\t Trust both explicit trust anchors (-t) and the database.\n"
70             "\t\t\t (Default is to only trust certificates marked -t, if there are any,\n"
71             "\t\t\t or to trust the database if there are certificates marked -t.)\n"
72             "\t-v\t\t Verbose mode. Prints root cert subject(double the\n"
73             "\t\t\t argument for whole root cert info)\n"
74             "\t-w password\t Database password.\n"
75             "\t-W pwfile\t Password file.\n\n"
76             "\tRevocation options for PKIX API(invoked with -pp options) is a\n"
77             "\tcollection of the following flags:\n"
78             "\t\t[-g type [-h flags] [-m type [-s flags]] ...] ...\n"
79             "\tWhere:\n"
80             "\t-g test type\t Sets status checking test type. Possible values\n"
81             "\t\t\tare \"leaf\" or \"chain\"\n"
82             "\t-h test flags\t Sets revocation flags for the test type it\n"
83             "\t\t\tfollows. Possible flags: \"testLocalInfoFirst\" and\n"
84             "\t\t\t\"requireFreshInfo\".\n"
85             "\t-m method type\t Sets method type for the test type it follows.\n"
86             "\t\t\tPossible types are \"crl\" and \"ocsp\".\n"
87             "\t-s method flags\t Sets revocation flags for the method it follows.\n"
88             "\t\t\tPossible types are \"doNotUse\", \"forbidFetching\",\n"
89             "\t\t\t\"ignoreDefaultSrc\", \"requireInfo\" and \"failIfNoInfo\".\n",
90             progName);
91     exit(1);
92 }
93 
94 /**************************************************************************
95 **
96 ** Error and information routines.
97 **
98 **************************************************************************/
99 
100 void
errWarn(char * function)101 errWarn(char *function)
102 {
103     fprintf(stderr, "Error in function %s: %s\n",
104             function, SECU_Strerror(PR_GetError()));
105 }
106 
107 void
exitErr(char * function)108 exitErr(char *function)
109 {
110     errWarn(function);
111     /* Exit gracefully. */
112     /* ignoring return value of NSS_Shutdown as code exits with 1 anyway*/
113     (void)NSS_Shutdown();
114     PR_Cleanup();
115     exit(1);
116 }
117 
118 typedef struct certMemStr {
119     struct certMemStr *next;
120     CERTCertificate *cert;
121 } certMem;
122 
123 certMem *theCerts;
124 CERTCertList *trustedCertList;
125 
126 void
rememberCert(CERTCertificate * cert,PRBool trusted)127 rememberCert(CERTCertificate *cert, PRBool trusted)
128 {
129     if (trusted) {
130         if (!trustedCertList) {
131             trustedCertList = CERT_NewCertList();
132         }
133         CERT_AddCertToListTail(trustedCertList, cert);
134     } else {
135         certMem *newCertMem = PORT_ZNew(certMem);
136         if (newCertMem) {
137             newCertMem->next = theCerts;
138             newCertMem->cert = cert;
139             theCerts = newCertMem;
140         }
141     }
142 }
143 
144 void
forgetCerts(void)145 forgetCerts(void)
146 {
147     certMem *oldCertMem;
148     while (theCerts) {
149         oldCertMem = theCerts;
150         theCerts = theCerts->next;
151         CERT_DestroyCertificate(oldCertMem->cert);
152         PORT_Free(oldCertMem);
153     }
154     if (trustedCertList) {
155         CERT_DestroyCertList(trustedCertList);
156     }
157 }
158 
159 CERTCertificate *
getCert(const char * name,PRBool isAscii,const char * progName)160 getCert(const char *name, PRBool isAscii, const char *progName)
161 {
162     CERTCertificate *cert;
163     CERTCertDBHandle *defaultDB;
164     PRFileDesc *fd;
165     SECStatus rv;
166     SECItem item = { 0, NULL, 0 };
167 
168     defaultDB = CERT_GetDefaultCertDB();
169 
170     /* First, let's try to find the cert in existing DB. */
171     cert = CERT_FindCertByNicknameOrEmailAddr(defaultDB, name);
172     if (cert) {
173         return cert;
174     }
175 
176     /* Don't have a cert with name "name" in the DB. Try to
177      * open a file with such name and get the cert from there.*/
178     fd = PR_Open(name, PR_RDONLY, 0777);
179     if (!fd) {
180         PRErrorCode err = PR_GetError();
181         fprintf(stderr, "open of %s failed, %d = %s\n",
182                 name, err, SECU_Strerror(err));
183         return cert;
184     }
185 
186     rv = SECU_ReadDERFromFile(&item, fd, isAscii, PR_FALSE);
187     PR_Close(fd);
188     if (rv != SECSuccess) {
189         fprintf(stderr, "%s: SECU_ReadDERFromFile failed\n", progName);
190         return cert;
191     }
192 
193     if (!item.len) { /* file was empty */
194         fprintf(stderr, "cert file %s was empty.\n", name);
195         return cert;
196     }
197 
198     cert = CERT_NewTempCertificate(defaultDB, &item,
199                                    NULL /* nickname */,
200                                    PR_FALSE /* isPerm */,
201                                    PR_TRUE /* copyDER */);
202     if (!cert) {
203         PRErrorCode err = PR_GetError();
204         fprintf(stderr, "couldn't import %s, %d = %s\n",
205                 name, err, SECU_Strerror(err));
206     }
207     PORT_Free(item.data);
208     return cert;
209 }
210 
211 #define REVCONFIG_TEST_UNDEFINED 0
212 #define REVCONFIG_TEST_LEAF 1
213 #define REVCONFIG_TEST_CHAIN 2
214 #define REVCONFIG_METHOD_CRL 1
215 #define REVCONFIG_METHOD_OCSP 2
216 
217 #define REVCONFIG_TEST_LEAF_STR "leaf"
218 #define REVCONFIG_TEST_CHAIN_STR "chain"
219 #define REVCONFIG_METHOD_CRL_STR "crl"
220 #define REVCONFIG_METHOD_OCSP_STR "ocsp"
221 
222 #define REVCONFIG_TEST_TESTLOCALINFOFIRST_STR "testLocalInfoFirst"
223 #define REVCONFIG_TEST_REQUIREFRESHINFO_STR "requireFreshInfo"
224 #define REVCONFIG_METHOD_DONOTUSEMETHOD_STR "doNotUse"
225 #define REVCONFIG_METHOD_FORBIDNETWORKFETCHIN_STR "forbidFetching"
226 #define REVCONFIG_METHOD_IGNOREDEFAULTSRC_STR "ignoreDefaultSrc"
227 #define REVCONFIG_METHOD_REQUIREINFO_STR "requireInfo"
228 #define REVCONFIG_METHOD_FAILIFNOINFO_STR "failIfNoInfo"
229 
230 #define REV_METHOD_INDEX_MAX 4
231 
232 typedef struct RevMethodsStruct {
233     unsigned int testType;
234     char *testTypeStr;
235     unsigned int testFlags;
236     char *testFlagsStr;
237     unsigned int methodType;
238     char *methodTypeStr;
239     unsigned int methodFlags;
240     char *methodFlagsStr;
241 } RevMethods;
242 
243 RevMethods revMethodsData[REV_METHOD_INDEX_MAX];
244 
245 SECStatus
parseRevMethodsAndFlags()246 parseRevMethodsAndFlags()
247 {
248     int i;
249     unsigned int testType = 0;
250 
251     for (i = 0; i < REV_METHOD_INDEX_MAX; i++) {
252         /* testType */
253         if (revMethodsData[i].testTypeStr) {
254             char *typeStr = revMethodsData[i].testTypeStr;
255 
256             testType = 0;
257             if (!PORT_Strcmp(typeStr, REVCONFIG_TEST_LEAF_STR)) {
258                 testType = REVCONFIG_TEST_LEAF;
259             } else if (!PORT_Strcmp(typeStr, REVCONFIG_TEST_CHAIN_STR)) {
260                 testType = REVCONFIG_TEST_CHAIN;
261             }
262         }
263         if (!testType) {
264             return SECFailure;
265         }
266         revMethodsData[i].testType = testType;
267         /* testFlags */
268         if (revMethodsData[i].testFlagsStr) {
269             char *flagStr = revMethodsData[i].testFlagsStr;
270             unsigned int testFlags = 0;
271 
272             if (PORT_Strstr(flagStr, REVCONFIG_TEST_TESTLOCALINFOFIRST_STR)) {
273                 testFlags |= CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST;
274             }
275             if (PORT_Strstr(flagStr, REVCONFIG_TEST_REQUIREFRESHINFO_STR)) {
276                 testFlags |= CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE;
277             }
278             revMethodsData[i].testFlags = testFlags;
279         }
280         /* method type */
281         if (revMethodsData[i].methodTypeStr) {
282             char *methodStr = revMethodsData[i].methodTypeStr;
283             unsigned int methodType = 0;
284 
285             if (!PORT_Strcmp(methodStr, REVCONFIG_METHOD_CRL_STR)) {
286                 methodType = REVCONFIG_METHOD_CRL;
287             } else if (!PORT_Strcmp(methodStr, REVCONFIG_METHOD_OCSP_STR)) {
288                 methodType = REVCONFIG_METHOD_OCSP;
289             }
290             if (!methodType) {
291                 return SECFailure;
292             }
293             revMethodsData[i].methodType = methodType;
294         }
295         if (!revMethodsData[i].methodType) {
296             revMethodsData[i].testType = REVCONFIG_TEST_UNDEFINED;
297             continue;
298         }
299         /* method flags */
300         if (revMethodsData[i].methodFlagsStr) {
301             char *flagStr = revMethodsData[i].methodFlagsStr;
302             unsigned int methodFlags = 0;
303 
304             if (!PORT_Strstr(flagStr, REVCONFIG_METHOD_DONOTUSEMETHOD_STR)) {
305                 methodFlags |= CERT_REV_M_TEST_USING_THIS_METHOD;
306             }
307             if (PORT_Strstr(flagStr,
308                             REVCONFIG_METHOD_FORBIDNETWORKFETCHIN_STR)) {
309                 methodFlags |= CERT_REV_M_FORBID_NETWORK_FETCHING;
310             }
311             if (PORT_Strstr(flagStr, REVCONFIG_METHOD_IGNOREDEFAULTSRC_STR)) {
312                 methodFlags |= CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE;
313             }
314             if (PORT_Strstr(flagStr, REVCONFIG_METHOD_REQUIREINFO_STR)) {
315                 methodFlags |= CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE;
316             }
317             if (PORT_Strstr(flagStr, REVCONFIG_METHOD_FAILIFNOINFO_STR)) {
318                 methodFlags |= CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO;
319             }
320             revMethodsData[i].methodFlags = methodFlags;
321         } else {
322             revMethodsData[i].methodFlags |= CERT_REV_M_TEST_USING_THIS_METHOD;
323         }
324     }
325     return SECSuccess;
326 }
327 
328 SECStatus
configureRevocationParams(CERTRevocationFlags * flags)329 configureRevocationParams(CERTRevocationFlags *flags)
330 {
331     int i;
332     unsigned int testType = REVCONFIG_TEST_UNDEFINED;
333     static CERTRevocationTests *revTests = NULL;
334     PRUint64 *revFlags = NULL;
335 
336     for (i = 0; i < REV_METHOD_INDEX_MAX; i++) {
337         if (revMethodsData[i].testType == REVCONFIG_TEST_UNDEFINED) {
338             continue;
339         }
340         if (revMethodsData[i].testType != testType) {
341             testType = revMethodsData[i].testType;
342             if (testType == REVCONFIG_TEST_CHAIN) {
343                 revTests = &flags->chainTests;
344             } else {
345                 revTests = &flags->leafTests;
346             }
347             revTests->number_of_preferred_methods = 0;
348             revTests->preferred_methods = 0;
349             revFlags = revTests->cert_rev_flags_per_method;
350         }
351         /* Set the number of the methods independently to the max number of
352         * methods. If method flags are not set it will be ignored due to
353         * default DO_NOT_USE flag. */
354         revTests->number_of_defined_methods = cert_revocation_method_count;
355         revTests->cert_rev_method_independent_flags |=
356             revMethodsData[i].testFlags;
357         if (revMethodsData[i].methodType == REVCONFIG_METHOD_CRL) {
358             revFlags[cert_revocation_method_crl] =
359                 revMethodsData[i].methodFlags;
360         } else if (revMethodsData[i].methodType == REVCONFIG_METHOD_OCSP) {
361             revFlags[cert_revocation_method_ocsp] =
362                 revMethodsData[i].methodFlags;
363         }
364     }
365     return SECSuccess;
366 }
367 
368 void
freeRevocationMethodData()369 freeRevocationMethodData()
370 {
371     int i = 0;
372     for (; i < REV_METHOD_INDEX_MAX; i++) {
373         if (revMethodsData[i].testTypeStr) {
374             PORT_Free(revMethodsData[i].testTypeStr);
375         }
376         if (revMethodsData[i].testFlagsStr) {
377             PORT_Free(revMethodsData[i].testFlagsStr);
378         }
379         if (revMethodsData[i].methodTypeStr) {
380             PORT_Free(revMethodsData[i].methodTypeStr);
381         }
382         if (revMethodsData[i].methodFlagsStr) {
383             PORT_Free(revMethodsData[i].methodFlagsStr);
384         }
385     }
386 }
387 
388 PRBool
isOCSPEnabled()389 isOCSPEnabled()
390 {
391     int i;
392 
393     for (i = 0; i < REV_METHOD_INDEX_MAX; i++) {
394         if (revMethodsData[i].methodType == REVCONFIG_METHOD_OCSP) {
395             return PR_TRUE;
396         }
397     }
398     return PR_FALSE;
399 }
400 
401 int
main(int argc,char * argv[],char * envp[])402 main(int argc, char *argv[], char *envp[])
403 {
404     char *certDir = NULL;
405     char *progName = NULL;
406     char *oidStr = NULL;
407     CERTCertificate *cert;
408     CERTCertificate *firstCert = NULL;
409     CERTCertificate *issuerCert = NULL;
410     CERTCertDBHandle *defaultDB = NULL;
411     PRBool isAscii = PR_FALSE;
412     PRBool trusted = PR_FALSE;
413     SECStatus secStatus;
414     SECCertificateUsage certUsage = certificateUsageSSLServer;
415     PLOptState *optstate;
416     PRTime time = 0;
417     PLOptStatus status;
418     int usePkix = 0;
419     int rv = 1;
420     int usage;
421     CERTVerifyLog log;
422     CERTCertList *builtChain = NULL;
423     PRBool certFetching = PR_FALSE;
424     int revDataIndex = 0;
425     PRBool ocsp_fetchingFailureIsAFailure = PR_TRUE;
426     PRBool useDefaultRevFlags = PR_TRUE;
427     PRBool onlyTrustAnchors = PR_TRUE;
428     int vfyCounts = 1;
429 
430     PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
431 
432     progName = PL_strdup(argv[0]);
433 
434     optstate = PL_CreateOptState(argc, argv, "ab:c:d:efg:h:i:m:o:prs:tTu:vw:W:");
435     while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
436         switch (optstate->option) {
437             case 0: /* positional parameter */
438                 goto breakout;
439             case 'a':
440                 isAscii = PR_TRUE;
441                 break;
442             case 'b':
443                 secStatus = DER_AsciiToTime(&time, optstate->value);
444                 if (secStatus != SECSuccess)
445                     Usage(progName);
446                 break;
447             case 'd':
448                 certDir = PL_strdup(optstate->value);
449                 break;
450             case 'e':
451                 ocsp_fetchingFailureIsAFailure = PR_FALSE;
452                 break;
453             case 'f':
454                 certFetching = PR_TRUE;
455                 break;
456             case 'g':
457                 if (revMethodsData[revDataIndex].testTypeStr ||
458                     revMethodsData[revDataIndex].methodTypeStr) {
459                     revDataIndex += 1;
460                     if (revDataIndex == REV_METHOD_INDEX_MAX) {
461                         fprintf(stderr, "Invalid revocation configuration"
462                                         "specified.\n");
463                         secStatus = SECFailure;
464                         break;
465                     }
466                 }
467                 useDefaultRevFlags = PR_FALSE;
468                 revMethodsData[revDataIndex].testTypeStr =
469                     PL_strdup(optstate->value);
470                 break;
471             case 'h':
472                 revMethodsData[revDataIndex].testFlagsStr =
473                     PL_strdup(optstate->value);
474                 break;
475             case 'i':
476                 vfyCounts = PORT_Atoi(optstate->value);
477                 break;
478                 break;
479             case 'm':
480                 if (revMethodsData[revDataIndex].methodTypeStr) {
481                     revDataIndex += 1;
482                     if (revDataIndex == REV_METHOD_INDEX_MAX) {
483                         fprintf(stderr, "Invalid revocation configuration"
484                                         "specified.\n");
485                         secStatus = SECFailure;
486                         break;
487                     }
488                 }
489                 useDefaultRevFlags = PR_FALSE;
490                 revMethodsData[revDataIndex].methodTypeStr =
491                     PL_strdup(optstate->value);
492                 break;
493             case 'o':
494                 oidStr = PL_strdup(optstate->value);
495                 break;
496             case 'p':
497                 usePkix += 1;
498                 break;
499             case 'r':
500                 isAscii = PR_FALSE;
501                 break;
502             case 's':
503                 revMethodsData[revDataIndex].methodFlagsStr =
504                     PL_strdup(optstate->value);
505                 break;
506             case 't':
507                 trusted = PR_TRUE;
508                 break;
509             case 'T':
510                 onlyTrustAnchors = PR_FALSE;
511                 break;
512             case 'u':
513                 usage = PORT_Atoi(optstate->value);
514                 if (usage < 0 || usage > 62)
515                     Usage(progName);
516                 certUsage = ((SECCertificateUsage)1) << usage;
517                 if (certUsage > certificateUsageHighest)
518                     Usage(progName);
519                 break;
520             case 'w':
521                 pwdata.source = PW_PLAINTEXT;
522                 pwdata.data = PORT_Strdup(optstate->value);
523                 break;
524 
525             case 'W':
526                 pwdata.source = PW_FROMFILE;
527                 pwdata.data = PORT_Strdup(optstate->value);
528                 break;
529             case 'v':
530                 verbose++;
531                 break;
532             default:
533                 Usage(progName);
534                 break;
535         }
536     }
537 breakout:
538     if (status != PL_OPT_OK)
539         Usage(progName);
540 
541     if (usePkix < 2) {
542         if (oidStr) {
543             fprintf(stderr, "Policy oid(-o) can be used only with"
544                             " CERT_PKIXVerifyCert(-pp) function.\n");
545             Usage(progName);
546         }
547         if (trusted) {
548             fprintf(stderr, "Cert trust flag can be used only with"
549                             " CERT_PKIXVerifyCert(-pp) function.\n");
550             Usage(progName);
551         }
552         if (!onlyTrustAnchors) {
553             fprintf(stderr, "Cert trust anchor exclusiveness can be"
554                             " used only with CERT_PKIXVerifyCert(-pp)"
555                             " function.\n");
556         }
557     }
558 
559     if (!useDefaultRevFlags && parseRevMethodsAndFlags()) {
560         fprintf(stderr, "Invalid revocation configuration specified.\n");
561         goto punt;
562     }
563 
564     /* Set our password function callback. */
565     PK11_SetPasswordFunc(SECU_GetModulePassword);
566 
567     /* Initialize the NSS libraries. */
568     if (certDir) {
569         secStatus = NSS_Init(certDir);
570     } else {
571         secStatus = NSS_NoDB_Init(NULL);
572 
573         /* load the builtins */
574         SECMOD_AddNewModule("Builtins", DLL_PREFIX "nssckbi." DLL_SUFFIX, 0, 0);
575     }
576     if (secStatus != SECSuccess) {
577         exitErr("NSS_Init");
578     }
579     SECU_RegisterDynamicOids();
580     if (isOCSPEnabled()) {
581         CERT_EnableOCSPChecking(CERT_GetDefaultCertDB());
582         CERT_DisableOCSPDefaultResponder(CERT_GetDefaultCertDB());
583         if (!ocsp_fetchingFailureIsAFailure) {
584             CERT_SetOCSPFailureMode(ocspMode_FailureIsNotAVerificationFailure);
585         }
586     }
587 
588     while (status == PL_OPT_OK) {
589         switch (optstate->option) {
590             default:
591                 Usage(progName);
592                 break;
593             case 'a':
594                 isAscii = PR_TRUE;
595                 break;
596             case 'r':
597                 isAscii = PR_FALSE;
598                 break;
599             case 't':
600                 trusted = PR_TRUE;
601                 break;
602             case 0: /* positional parameter */
603                 if (usePkix < 2 && trusted) {
604                     fprintf(stderr, "Cert trust flag can be used only with"
605                                     " CERT_PKIXVerifyCert(-pp) function.\n");
606                     Usage(progName);
607                 }
608                 cert = getCert(optstate->value, isAscii, progName);
609                 if (!cert)
610                     goto punt;
611                 rememberCert(cert, trusted);
612                 if (!firstCert)
613                     firstCert = cert;
614                 trusted = PR_FALSE;
615         }
616         status = PL_GetNextOpt(optstate);
617     }
618     PL_DestroyOptState(optstate);
619     if (status == PL_OPT_BAD || !firstCert)
620         Usage(progName);
621 
622     /* Initialize log structure */
623     log.arena = PORT_NewArena(512);
624     log.head = log.tail = NULL;
625     log.count = 0;
626 
627     do {
628         if (usePkix < 2) {
629             /* NOW, verify the cert chain. */
630             if (usePkix) {
631                 /* Use old API with libpkix validation lib */
632                 CERT_SetUsePKIXForValidation(PR_TRUE);
633             }
634             if (!time)
635                 time = PR_Now();
636 
637             defaultDB = CERT_GetDefaultCertDB();
638             secStatus = CERT_VerifyCertificate(defaultDB, firstCert,
639                                                PR_TRUE /* check sig */,
640                                                certUsage,
641                                                time,
642                                                &pwdata, /* wincx  */
643                                                &log,    /* error log */
644                                                NULL);   /* returned usages */
645         } else
646             do {
647                 static CERTValOutParam cvout[4];
648                 static CERTValInParam cvin[7];
649                 SECOidTag oidTag;
650                 int inParamIndex = 0;
651                 static PRUint64 revFlagsLeaf[2];
652                 static PRUint64 revFlagsChain[2];
653                 static CERTRevocationFlags rev;
654 
655                 if (oidStr) {
656                     PLArenaPool *arena;
657                     SECOidData od;
658                     memset(&od, 0, sizeof od);
659                     od.offset = SEC_OID_UNKNOWN;
660                     od.desc = "User Defined Policy OID";
661                     od.mechanism = CKM_INVALID_MECHANISM;
662                     od.supportedExtension = INVALID_CERT_EXTENSION;
663 
664                     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
665                     if (!arena) {
666                         fprintf(stderr, "out of memory");
667                         goto punt;
668                     }
669 
670                     secStatus = SEC_StringToOID(arena, &od.oid, oidStr, 0);
671                     if (secStatus != SECSuccess) {
672                         PORT_FreeArena(arena, PR_FALSE);
673                         fprintf(stderr, "Can not encode oid: %s(%s)\n", oidStr,
674                                 SECU_Strerror(PORT_GetError()));
675                         break;
676                     }
677 
678                     oidTag = SECOID_AddEntry(&od);
679                     PORT_FreeArena(arena, PR_FALSE);
680                     if (oidTag == SEC_OID_UNKNOWN) {
681                         fprintf(stderr, "Can not add new oid to the dynamic "
682                                         "table: %s\n",
683                                 oidStr);
684                         secStatus = SECFailure;
685                         break;
686                     }
687 
688                     cvin[inParamIndex].type = cert_pi_policyOID;
689                     cvin[inParamIndex].value.arraySize = 1;
690                     cvin[inParamIndex].value.array.oids = &oidTag;
691 
692                     inParamIndex++;
693                 }
694 
695                 if (trustedCertList) {
696                     cvin[inParamIndex].type = cert_pi_trustAnchors;
697                     cvin[inParamIndex].value.pointer.chain = trustedCertList;
698 
699                     inParamIndex++;
700                 }
701 
702                 cvin[inParamIndex].type = cert_pi_useAIACertFetch;
703                 cvin[inParamIndex].value.scalar.b = certFetching;
704                 inParamIndex++;
705 
706                 rev.leafTests.cert_rev_flags_per_method = revFlagsLeaf;
707                 rev.chainTests.cert_rev_flags_per_method = revFlagsChain;
708                 secStatus = configureRevocationParams(&rev);
709                 if (secStatus) {
710                     fprintf(stderr, "Can not config revocation parameters ");
711                     break;
712                 }
713 
714                 cvin[inParamIndex].type = cert_pi_revocationFlags;
715                 cvin[inParamIndex].value.pointer.revocation = &rev;
716                 inParamIndex++;
717 
718                 if (time) {
719                     cvin[inParamIndex].type = cert_pi_date;
720                     cvin[inParamIndex].value.scalar.time = time;
721                     inParamIndex++;
722                 }
723 
724                 if (!onlyTrustAnchors) {
725                     cvin[inParamIndex].type = cert_pi_useOnlyTrustAnchors;
726                     cvin[inParamIndex].value.scalar.b = onlyTrustAnchors;
727                     inParamIndex++;
728                 }
729 
730                 cvin[inParamIndex].type = cert_pi_end;
731 
732                 cvout[0].type = cert_po_trustAnchor;
733                 cvout[0].value.pointer.cert = NULL;
734                 cvout[1].type = cert_po_certList;
735                 cvout[1].value.pointer.chain = NULL;
736 
737                 /* setting pointer to CERTVerifyLog. Initialized structure
738                  * will be used CERT_PKIXVerifyCert */
739                 cvout[2].type = cert_po_errorLog;
740                 cvout[2].value.pointer.log = &log;
741 
742                 cvout[3].type = cert_po_end;
743 
744                 secStatus = CERT_PKIXVerifyCert(firstCert, certUsage,
745                                                 cvin, cvout, &pwdata);
746                 if (secStatus != SECSuccess) {
747                     break;
748                 }
749                 issuerCert = cvout[0].value.pointer.cert;
750                 builtChain = cvout[1].value.pointer.chain;
751             } while (0);
752 
753         /* Display validation results */
754         if (secStatus != SECSuccess || log.count > 0) {
755             CERTVerifyLogNode *node = NULL;
756             fprintf(stderr, "Chain is bad!\n");
757 
758             SECU_displayVerifyLog(stderr, &log, verbose);
759             /* Have cert refs in the log only in case of failure.
760              * Destroy them. */
761             for (node = log.head; node; node = node->next) {
762                 if (node->cert)
763                     CERT_DestroyCertificate(node->cert);
764             }
765             log.head = log.tail = NULL;
766             log.count = 0;
767             rv = 1;
768         } else {
769             fprintf(stderr, "Chain is good!\n");
770             if (issuerCert) {
771                 if (verbose > 1) {
772                     rv = SEC_PrintCertificateAndTrust(issuerCert, "Root Certificate",
773                                                       NULL);
774                     if (rv != SECSuccess) {
775                         SECU_PrintError(progName, "problem printing certificate");
776                     }
777                 } else if (verbose > 0) {
778                     SECU_PrintName(stdout, &issuerCert->subject, "Root "
779                                                                  "Certificate Subject:",
780                                    0);
781                 }
782                 CERT_DestroyCertificate(issuerCert);
783             }
784             if (builtChain) {
785                 CERTCertListNode *node;
786                 int count = 0;
787                 char buff[256];
788 
789                 if (verbose) {
790                     for (node = CERT_LIST_HEAD(builtChain); !CERT_LIST_END(node, builtChain);
791                          node = CERT_LIST_NEXT(node), count++) {
792                         sprintf(buff, "Certificate %d Subject", count + 1);
793                         SECU_PrintName(stdout, &node->cert->subject, buff, 0);
794                     }
795                 }
796                 CERT_DestroyCertList(builtChain);
797             }
798             rv = 0;
799         }
800     } while (--vfyCounts > 0);
801 
802     /* Need to destroy CERTVerifyLog arena at the end */
803     PORT_FreeArena(log.arena, PR_FALSE);
804 
805 punt:
806     forgetCerts();
807     if (NSS_Shutdown() != SECSuccess) {
808         SECU_PrintError(progName, "NSS_Shutdown");
809         rv = 1;
810     }
811     PORT_Free(progName);
812     PORT_Free(certDir);
813     PORT_Free(oidStr);
814     freeRevocationMethodData();
815     if (pwdata.data) {
816         PORT_Free(pwdata.data);
817     }
818     PL_ArenaFinish();
819     PR_Cleanup();
820     return rv;
821 }
822