From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- security/nss/lib/Makefile | 109 + security/nss/lib/base/Makefile | 14 + security/nss/lib/base/arena.c | 1143 + security/nss/lib/base/base.gyp | 32 + security/nss/lib/base/base.h | 1106 + security/nss/lib/base/baset.h | 124 + security/nss/lib/base/error.c | 301 + security/nss/lib/base/errorval.c | 65 + security/nss/lib/base/exports.gyp | 33 + security/nss/lib/base/hash.c | 314 + security/nss/lib/base/hashops.c | 64 + security/nss/lib/base/item.c | 186 + security/nss/lib/base/libc.c | 143 + security/nss/lib/base/list.c | 405 + security/nss/lib/base/manifest.mn | 39 + security/nss/lib/base/nssbase.h | 233 + security/nss/lib/base/nssbaset.h | 118 + security/nss/lib/base/tracker.c | 378 + security/nss/lib/base/utf8.c | 702 + security/nss/lib/certdb/Makefile | 45 + security/nss/lib/certdb/alg1485.c | 1590 + security/nss/lib/certdb/cert.h | 1606 + security/nss/lib/certdb/certdb.c | 3411 + security/nss/lib/certdb/certdb.gyp | 34 + security/nss/lib/certdb/certdb.h | 89 + security/nss/lib/certdb/certi.h | 407 + security/nss/lib/certdb/certt.h | 1359 + security/nss/lib/certdb/certv3.c | 222 + security/nss/lib/certdb/certxutl.c | 496 + security/nss/lib/certdb/certxutl.h | 44 + security/nss/lib/certdb/crl.c | 3046 + security/nss/lib/certdb/exports.gyp | 36 + security/nss/lib/certdb/genname.c | 2011 + security/nss/lib/certdb/genname.h | 93 + security/nss/lib/certdb/manifest.mn | 41 + security/nss/lib/certdb/polcyxtn.c | 806 + security/nss/lib/certdb/secname.c | 701 + security/nss/lib/certdb/stanpcertdb.c | 1137 + security/nss/lib/certdb/xauthkid.c | 128 + security/nss/lib/certdb/xbsconst.c | 146 + security/nss/lib/certdb/xconst.c | 269 + security/nss/lib/certdb/xconst.h | 30 + security/nss/lib/certhigh/Makefile | 45 + security/nss/lib/certhigh/certhigh.c | 1210 + security/nss/lib/certhigh/certhigh.gyp | 31 + security/nss/lib/certhigh/certhtml.c | 321 + security/nss/lib/certhigh/certreq.c | 331 + security/nss/lib/certhigh/certvfy.c | 2158 + security/nss/lib/certhigh/certvfypkix.c | 2302 + security/nss/lib/certhigh/crlv2.c | 160 + security/nss/lib/certhigh/exports.gyp | 33 + security/nss/lib/certhigh/manifest.mn | 35 + security/nss/lib/certhigh/ocsp.c | 6119 + security/nss/lib/certhigh/ocsp.h | 723 + security/nss/lib/certhigh/ocspi.h | 166 + security/nss/lib/certhigh/ocspsig.c | 597 + security/nss/lib/certhigh/ocspt.h | 301 + security/nss/lib/certhigh/ocspti.h | 356 + security/nss/lib/certhigh/xcrldist.c | 212 + security/nss/lib/ckfw/Makefile | 39 + security/nss/lib/ckfw/builtins/Makefile | 70 + security/nss/lib/ckfw/builtins/README | 106 + security/nss/lib/ckfw/builtins/anchor.c | 17 + security/nss/lib/ckfw/builtins/bfind.c | 261 + security/nss/lib/ckfw/builtins/binst.c | 87 + security/nss/lib/ckfw/builtins/bobject.c | 206 + security/nss/lib/ckfw/builtins/bsession.c | 71 + security/nss/lib/ckfw/builtins/bslot.c | 81 + security/nss/lib/ckfw/builtins/btoken.c | 135 + security/nss/lib/ckfw/builtins/builtins.gyp | 63 + security/nss/lib/ckfw/builtins/builtins.h | 66 + security/nss/lib/ckfw/builtins/certdata.perl | 192 + security/nss/lib/ckfw/builtins/certdata.py | 18 + security/nss/lib/ckfw/builtins/certdata.txt | 25361 ++ security/nss/lib/ckfw/builtins/ckbiver.c | 18 + security/nss/lib/ckfw/builtins/constants.c | 64 + security/nss/lib/ckfw/builtins/exports.gyp | 25 + security/nss/lib/ckfw/builtins/manifest.mn | 36 + security/nss/lib/ckfw/builtins/nssckbi.def | 26 + security/nss/lib/ckfw/builtins/nssckbi.h | 61 + security/nss/lib/ckfw/builtins/nssckbi.rc | 64 + security/nss/lib/ckfw/builtins/testlib/Makefile | 75 + .../lib/ckfw/builtins/testlib/builtins-testlib.gyp | 64 + .../lib/ckfw/builtins/testlib/certdata-testlib.txt | 479 + security/nss/lib/ckfw/builtins/testlib/manifest.mn | 26 + .../lib/ckfw/builtins/testlib/nssckbi-testlib.def | 26 + .../lib/ckfw/builtins/testlib/nssckbi-testlib.rc | 52 + .../builtins/testlib/testcert_err_distrust.txt | 50 + .../ckfw/builtins/testlib/testcert_no_distrust.txt | 50 + .../ckfw/builtins/testlib/testcert_ok_distrust.txt | 50 + security/nss/lib/ckfw/capi/Makefile | 82 + security/nss/lib/ckfw/capi/README | 7 + security/nss/lib/ckfw/capi/anchor.c | 17 + security/nss/lib/ckfw/capi/cfind.c | 561 + security/nss/lib/ckfw/capi/cinst.c | 97 + security/nss/lib/ckfw/capi/ckcapi.h | 242 + security/nss/lib/ckfw/capi/ckcapiver.c | 17 + security/nss/lib/ckfw/capi/cobject.c | 2226 + security/nss/lib/ckfw/capi/constants.c | 63 + security/nss/lib/ckfw/capi/crsa.c | 687 + security/nss/lib/ckfw/capi/csession.c | 87 + security/nss/lib/ckfw/capi/cslot.c | 81 + security/nss/lib/ckfw/capi/ctoken.c | 184 + security/nss/lib/ckfw/capi/manifest.mn | 35 + security/nss/lib/ckfw/capi/nsscapi.def | 26 + security/nss/lib/ckfw/capi/nsscapi.h | 41 + security/nss/lib/ckfw/capi/nsscapi.rc | 64 + security/nss/lib/ckfw/capi/staticobj.c | 40 + security/nss/lib/ckfw/ck.api | 541 + security/nss/lib/ckfw/ck.h | 88 + security/nss/lib/ckfw/ckapi.perl | 434 + security/nss/lib/ckfw/ckfw.gyp | 34 + security/nss/lib/ckfw/ckfw.h | 2049 + security/nss/lib/ckfw/ckfwm.h | 112 + security/nss/lib/ckfw/ckfwtm.h | 23 + security/nss/lib/ckfw/ckmd.h | 28 + security/nss/lib/ckfw/ckt.h | 8 + security/nss/lib/ckfw/crypto.c | 318 + security/nss/lib/ckfw/dbm/Makefile | 13 + security/nss/lib/ckfw/dbm/anchor.c | 17 + security/nss/lib/ckfw/dbm/ckdbm.h | 210 + security/nss/lib/ckfw/dbm/db.c | 1069 + security/nss/lib/ckfw/dbm/find.c | 126 + security/nss/lib/ckfw/dbm/instance.c | 147 + security/nss/lib/ckfw/dbm/manifest.mn | 26 + security/nss/lib/ckfw/dbm/object.c | 155 + security/nss/lib/ckfw/dbm/session.c | 255 + security/nss/lib/ckfw/dbm/slot.c | 165 + security/nss/lib/ckfw/dbm/token.c | 260 + security/nss/lib/ckfw/exports.gyp | 44 + security/nss/lib/ckfw/find.c | 362 + security/nss/lib/ckfw/hash.c | 280 + security/nss/lib/ckfw/instance.c | 1318 + security/nss/lib/ckfw/manifest.mn | 56 + security/nss/lib/ckfw/mechanism.c | 1102 + security/nss/lib/ckfw/mutex.c | 248 + security/nss/lib/ckfw/nssck.api | 1854 + security/nss/lib/ckfw/nssckepv.h | 10 + security/nss/lib/ckfw/nssckft.h | 11 + security/nss/lib/ckfw/nssckfw.h | 462 + security/nss/lib/ckfw/nssckfwc.h | 879 + security/nss/lib/ckfw/nssckfwt.h | 109 + security/nss/lib/ckfw/nssckg.h | 10 + security/nss/lib/ckfw/nssckmdt.h | 1904 + security/nss/lib/ckfw/nssckt.h | 12 + security/nss/lib/ckfw/object.c | 998 + security/nss/lib/ckfw/session.c | 2389 + security/nss/lib/ckfw/sessobj.c | 1012 + security/nss/lib/ckfw/slot.c | 694 + security/nss/lib/ckfw/token.c | 1790 + security/nss/lib/ckfw/wrap.c | 5557 + security/nss/lib/crmf/Makefile | 46 + security/nss/lib/crmf/asn1cmn.c | 216 + security/nss/lib/crmf/challcli.c | 238 + security/nss/lib/crmf/cmmf.h | 1082 + security/nss/lib/crmf/cmmfasn1.c | 131 + security/nss/lib/crmf/cmmfchal.c | 289 + security/nss/lib/crmf/cmmfi.h | 92 + security/nss/lib/crmf/cmmfit.h | 115 + security/nss/lib/crmf/cmmfrec.c | 316 + security/nss/lib/crmf/cmmfresp.c | 280 + security/nss/lib/crmf/cmmft.h | 73 + security/nss/lib/crmf/crmf.gyp | 43 + security/nss/lib/crmf/crmf.h | 1741 + security/nss/lib/crmf/crmfcont.c | 1161 + security/nss/lib/crmf/crmfdec.c | 360 + security/nss/lib/crmf/crmfenc.c | 48 + security/nss/lib/crmf/crmffut.h | 357 + security/nss/lib/crmf/crmfget.c | 443 + security/nss/lib/crmf/crmfi.h | 185 + security/nss/lib/crmf/crmfit.h | 185 + security/nss/lib/crmf/crmfpop.c | 598 + security/nss/lib/crmf/crmfreq.c | 663 + security/nss/lib/crmf/crmft.h | 186 + security/nss/lib/crmf/crmftmpl.c | 240 + security/nss/lib/crmf/encutil.c | 33 + security/nss/lib/crmf/exports.gyp | 37 + security/nss/lib/crmf/manifest.mn | 47 + security/nss/lib/crmf/respcli.c | 136 + security/nss/lib/crmf/respcmn.c | 399 + security/nss/lib/crmf/servget.c | 974 + security/nss/lib/cryptohi/Makefile | 46 + security/nss/lib/cryptohi/cryptohi.gyp | 27 + security/nss/lib/cryptohi/cryptohi.h | 425 + security/nss/lib/cryptohi/cryptoht.h | 14 + security/nss/lib/cryptohi/dsautil.c | 271 + security/nss/lib/cryptohi/exports.gyp | 37 + security/nss/lib/cryptohi/key.h | 14 + security/nss/lib/cryptohi/keyhi.h | 276 + security/nss/lib/cryptohi/keyi.h | 45 + security/nss/lib/cryptohi/keyt.h | 14 + security/nss/lib/cryptohi/keythi.h | 260 + security/nss/lib/cryptohi/manifest.mn | 37 + security/nss/lib/cryptohi/sechash.c | 583 + security/nss/lib/cryptohi/sechash.h | 60 + security/nss/lib/cryptohi/seckey.c | 2325 + security/nss/lib/cryptohi/secsign.c | 884 + security/nss/lib/cryptohi/secvfy.c | 952 + security/nss/lib/dbm/Makefile | 52 + security/nss/lib/dbm/config/config.mk | 27 + security/nss/lib/dbm/include/Makefile | 46 + security/nss/lib/dbm/include/exports.gyp | 38 + security/nss/lib/dbm/include/extern.h | 63 + security/nss/lib/dbm/include/hash.h | 341 + security/nss/lib/dbm/include/hsearch.h | 50 + security/nss/lib/dbm/include/include.gyp | 12 + security/nss/lib/dbm/include/manifest.mn | 23 + security/nss/lib/dbm/include/mcom_db.h | 425 + security/nss/lib/dbm/include/ncompat.h | 224 + security/nss/lib/dbm/include/page.h | 93 + security/nss/lib/dbm/include/queue.h | 258 + security/nss/lib/dbm/include/search.h | 50 + security/nss/lib/dbm/include/winfile.h | 103 + security/nss/lib/dbm/manifest.mn | 17 + security/nss/lib/dbm/src/Makefile | 48 + security/nss/lib/dbm/src/config.mk | 27 + security/nss/lib/dbm/src/db.c | 134 + security/nss/lib/dbm/src/dirent.c | 344 + security/nss/lib/dbm/src/dirent.h | 97 + security/nss/lib/dbm/src/h_bigkey.c | 707 + security/nss/lib/dbm/src/h_func.c | 207 + security/nss/lib/dbm/src/h_log2.c | 54 + security/nss/lib/dbm/src/h_page.c | 1267 + security/nss/lib/dbm/src/hash.c | 1171 + security/nss/lib/dbm/src/hash_buf.c | 407 + security/nss/lib/dbm/src/manifest.mn | 29 + security/nss/lib/dbm/src/memmove.c | 146 + security/nss/lib/dbm/src/mktemp.c | 149 + security/nss/lib/dbm/src/snprintf.c | 50 + security/nss/lib/dbm/src/src.gyp | 40 + security/nss/lib/dbm/src/strerror.c | 73 + security/nss/lib/dbm/tests/Makefile | 41 + security/nss/lib/dbm/tests/dbmtest.pkg | 2 + security/nss/lib/dbm/tests/lots.c | 553 + security/nss/lib/dev/Makefile | 27 + security/nss/lib/dev/ckhelper.c | 595 + security/nss/lib/dev/ckhelper.h | 148 + security/nss/lib/dev/dev.gyp | 26 + security/nss/lib/dev/dev.h | 751 + security/nss/lib/dev/devm.h | 157 + security/nss/lib/dev/devslot.c | 322 + security/nss/lib/dev/devt.h | 161 + security/nss/lib/dev/devtm.h | 25 + security/nss/lib/dev/devtoken.c | 1553 + security/nss/lib/dev/devutil.c | 998 + security/nss/lib/dev/exports.gyp | 31 + security/nss/lib/dev/manifest.mn | 33 + security/nss/lib/dev/nssdev.h | 36 + security/nss/lib/dev/nssdevt.h | 36 + security/nss/lib/freebl/Makefile | 806 + security/nss/lib/freebl/aes-armv8.c | 1169 + security/nss/lib/freebl/aes-armv8.h | 103 + security/nss/lib/freebl/aes-x86.c | 184 + security/nss/lib/freebl/aeskeywrap.c | 642 + security/nss/lib/freebl/alghmac.c | 209 + security/nss/lib/freebl/alghmac.h | 70 + security/nss/lib/freebl/altivec-types.h | 25 + security/nss/lib/freebl/arcfive.c | 87 + security/nss/lib/freebl/arcfour-amd64-gas.s | 88 + security/nss/lib/freebl/arcfour-amd64-masm.asm | 107 + security/nss/lib/freebl/arcfour-amd64-sun.s | 84 + security/nss/lib/freebl/arcfour.c | 594 + security/nss/lib/freebl/blake2b.c | 428 + security/nss/lib/freebl/blake2b.h | 23 + security/nss/lib/freebl/blapi.h | 1926 + security/nss/lib/freebl/blapii.h | 123 + security/nss/lib/freebl/blapit.h | 474 + security/nss/lib/freebl/blinit.c | 584 + security/nss/lib/freebl/blname.c | 100 + security/nss/lib/freebl/camellia.c | 1896 + security/nss/lib/freebl/camellia.h | 42 + security/nss/lib/freebl/chacha20-ppc64le.S | 668 + security/nss/lib/freebl/chacha20poly1305-ppc.c | 588 + security/nss/lib/freebl/chacha20poly1305.c | 549 + security/nss/lib/freebl/chacha20poly1305.h | 21 + security/nss/lib/freebl/cmac.c | 323 + security/nss/lib/freebl/cmac.h | 47 + security/nss/lib/freebl/config.mk | 93 + security/nss/lib/freebl/crypto_primitives.c | 36 + security/nss/lib/freebl/crypto_primitives.h | 66 + security/nss/lib/freebl/ctr.c | 276 + security/nss/lib/freebl/ctr.h | 52 + security/nss/lib/freebl/cts.c | 303 + security/nss/lib/freebl/cts.h | 33 + security/nss/lib/freebl/deprecated/alg2268.c | 509 + security/nss/lib/freebl/deprecated/seed.c | 671 + security/nss/lib/freebl/deprecated/seed.h | 125 + security/nss/lib/freebl/des.c | 676 + security/nss/lib/freebl/des.h | 43 + security/nss/lib/freebl/desblapi.c | 256 + security/nss/lib/freebl/det_rng.c | 163 + security/nss/lib/freebl/det_rng.h | 12 + security/nss/lib/freebl/dh.c | 480 + security/nss/lib/freebl/drbg.c | 1024 + security/nss/lib/freebl/dsa.c | 691 + security/nss/lib/freebl/ec.c | 1319 + security/nss/lib/freebl/ec.h | 24 + security/nss/lib/freebl/ecdecode.c | 269 + security/nss/lib/freebl/ecl/README | 163 + security/nss/lib/freebl/ecl/curve25519_32.c | 1213 + security/nss/lib/freebl/ecl/curve25519_64.c | 24 + security/nss/lib/freebl/ecl/ec_naf.c | 68 + security/nss/lib/freebl/ecl/ecl-curve.h | 245 + security/nss/lib/freebl/ecl/ecl-exp.h | 167 + security/nss/lib/freebl/ecl/ecl-priv.h | 252 + security/nss/lib/freebl/ecl/ecl.c | 329 + security/nss/lib/freebl/ecl/ecl.h | 64 + security/nss/lib/freebl/ecl/ecl_gf.c | 958 + security/nss/lib/freebl/ecl/ecl_mult.c | 305 + security/nss/lib/freebl/ecl/eclt.h | 30 + security/nss/lib/freebl/ecl/ecp.h | 106 + security/nss/lib/freebl/ecl/ecp_25519.c | 145 + security/nss/lib/freebl/ecl/ecp_256.c | 401 + security/nss/lib/freebl/ecl/ecp_256_32.c | 1535 + security/nss/lib/freebl/ecl/ecp_384.c | 258 + security/nss/lib/freebl/ecl/ecp_521.c | 137 + security/nss/lib/freebl/ecl/ecp_aff.c | 308 + security/nss/lib/freebl/ecl/ecp_jac.c | 513 + security/nss/lib/freebl/ecl/ecp_jm.c | 297 + security/nss/lib/freebl/ecl/ecp_mont.c | 154 + security/nss/lib/freebl/ecl/ecp_secp256r1.c | 258 + security/nss/lib/freebl/ecl/ecp_secp384r1.c | 20411 ++ security/nss/lib/freebl/ecl/ecp_secp384r1.h | 41 + security/nss/lib/freebl/ecl/ecp_secp384r1_wrap.c | 228 + security/nss/lib/freebl/ecl/ecp_secp521r1.c | 11622 + security/nss/lib/freebl/ecl/ecp_secp521r1.h | 33 + security/nss/lib/freebl/ecl/ecp_secp521r1_wrap.c | 255 + security/nss/lib/freebl/exports.gyp | 51 + security/nss/lib/freebl/fipsfreebl.c | 2059 + security/nss/lib/freebl/freebl.def | 26 + security/nss/lib/freebl/freebl.gyp | 952 + security/nss/lib/freebl/freebl.rc | 68 + security/nss/lib/freebl/freebl_base.gypi | 249 + security/nss/lib/freebl/freebl_hash.def | 39 + security/nss/lib/freebl/freebl_hash_vector.def | 34 + security/nss/lib/freebl/freeblver.c | 18 + security/nss/lib/freebl/gcm-aarch64.c | 96 + security/nss/lib/freebl/gcm-arm32-neon.c | 202 + security/nss/lib/freebl/gcm-ppc.c | 109 + security/nss/lib/freebl/gcm-x86.c | 127 + security/nss/lib/freebl/gcm.c | 1171 + security/nss/lib/freebl/gcm.h | 125 + security/nss/lib/freebl/genload.c | 167 + security/nss/lib/freebl/hmacct.c | 325 + security/nss/lib/freebl/hmacct.h | 38 + security/nss/lib/freebl/intel-aes-x64-masm.asm | 964 + security/nss/lib/freebl/intel-aes-x86-masm.asm | 942 + security/nss/lib/freebl/intel-aes.h | 143 + security/nss/lib/freebl/intel-aes.s | 2485 + security/nss/lib/freebl/intel-gcm-wrap.c | 475 + security/nss/lib/freebl/intel-gcm-x64-masm.asm | 1294 + security/nss/lib/freebl/intel-gcm-x86-masm.asm | 1207 + security/nss/lib/freebl/intel-gcm.h | 97 + security/nss/lib/freebl/intel-gcm.s | 1340 + security/nss/lib/freebl/jpake.c | 495 + security/nss/lib/freebl/kyber-pqcrystals-ref.c | 2693 + security/nss/lib/freebl/kyber-pqcrystals-ref.h | 144 + security/nss/lib/freebl/kyber.c | 205 + security/nss/lib/freebl/ldvector.c | 493 + security/nss/lib/freebl/loader.c | 2856 + security/nss/lib/freebl/loader.h | 1028 + security/nss/lib/freebl/lowhash_vector.c | 224 + security/nss/lib/freebl/manifest.mn | 212 + security/nss/lib/freebl/md2.c | 269 + security/nss/lib/freebl/md5.c | 598 + security/nss/lib/freebl/mknewpc2.c | 208 + security/nss/lib/freebl/mksp.c | 119 + security/nss/lib/freebl/mpi/README | 646 + security/nss/lib/freebl/mpi/doc/LICENSE | 11 + security/nss/lib/freebl/mpi/doc/LICENSE-MPL | 3 + security/nss/lib/freebl/mpi/doc/basecvt.pod | 65 + security/nss/lib/freebl/mpi/doc/build | 30 + security/nss/lib/freebl/mpi/doc/div.txt | 64 + security/nss/lib/freebl/mpi/doc/expt.txt | 94 + security/nss/lib/freebl/mpi/doc/gcd.pod | 28 + security/nss/lib/freebl/mpi/doc/invmod.pod | 34 + security/nss/lib/freebl/mpi/doc/isprime.pod | 63 + security/nss/lib/freebl/mpi/doc/lap.pod | 36 + security/nss/lib/freebl/mpi/doc/mpi-test.pod | 51 + security/nss/lib/freebl/mpi/doc/mul.txt | 77 + security/nss/lib/freebl/mpi/doc/pi.txt | 53 + security/nss/lib/freebl/mpi/doc/prime.txt | 6542 + security/nss/lib/freebl/mpi/doc/prng.pod | 38 + security/nss/lib/freebl/mpi/doc/redux.txt | 86 + security/nss/lib/freebl/mpi/doc/sqrt.txt | 50 + security/nss/lib/freebl/mpi/doc/square.txt | 72 + security/nss/lib/freebl/mpi/doc/timing.txt | 213 + security/nss/lib/freebl/mpi/hpma512.s | 615 + security/nss/lib/freebl/mpi/hppa20.s | 904 + security/nss/lib/freebl/mpi/logtab.h | 28 + security/nss/lib/freebl/mpi/montmulf.c | 286 + security/nss/lib/freebl/mpi/montmulf.h | 65 + security/nss/lib/freebl/mpi/montmulf.il | 108 + security/nss/lib/freebl/mpi/montmulf.s | 1938 + security/nss/lib/freebl/mpi/montmulfv8.il | 108 + security/nss/lib/freebl/mpi/montmulfv8.s | 1818 + security/nss/lib/freebl/mpi/montmulfv9.il | 93 + security/nss/lib/freebl/mpi/montmulfv9.s | 2346 + security/nss/lib/freebl/mpi/mp_comba.c | 3235 + .../nss/lib/freebl/mpi/mp_comba_amd64_masm.asm | 13066 + security/nss/lib/freebl/mpi/mp_comba_amd64_sun.s | 16097 ++ security/nss/lib/freebl/mpi/mp_gf2m-priv.h | 73 + security/nss/lib/freebl/mpi/mp_gf2m.c | 677 + security/nss/lib/freebl/mpi/mp_gf2m.h | 28 + security/nss/lib/freebl/mpi/mpcpucache.c | 788 + security/nss/lib/freebl/mpi/mpcpucache_amd64.s | 861 + security/nss/lib/freebl/mpi/mpcpucache_x86.s | 902 + security/nss/lib/freebl/mpi/mpi-config.h | 56 + security/nss/lib/freebl/mpi/mpi-priv.h | 246 + security/nss/lib/freebl/mpi/mpi.c | 5241 + security/nss/lib/freebl/mpi/mpi.h | 363 + security/nss/lib/freebl/mpi/mpi_amd64.c | 32 + security/nss/lib/freebl/mpi/mpi_amd64_common.S | 409 + security/nss/lib/freebl/mpi/mpi_amd64_masm.asm | 388 + security/nss/lib/freebl/mpi/mpi_amd64_sun.s | 385 + security/nss/lib/freebl/mpi/mpi_arm.c | 175 + security/nss/lib/freebl/mpi/mpi_hp.c | 81 + security/nss/lib/freebl/mpi/mpi_i86pc.s | 313 + security/nss/lib/freebl/mpi/mpi_mips.s | 472 + security/nss/lib/freebl/mpi/mpi_sparc.c | 226 + security/nss/lib/freebl/mpi/mpi_sse2.s | 294 + security/nss/lib/freebl/mpi/mpi_x86.s | 541 + security/nss/lib/freebl/mpi/mpi_x86_asm.c | 531 + security/nss/lib/freebl/mpi/mpi_x86_os2.s | 538 + security/nss/lib/freebl/mpi/mplogic.c | 460 + security/nss/lib/freebl/mpi/mplogic.h | 55 + security/nss/lib/freebl/mpi/mpmontg.c | 1160 + security/nss/lib/freebl/mpi/mpprime.c | 610 + security/nss/lib/freebl/mpi/mpprime.h | 48 + security/nss/lib/freebl/mpi/mpv_sparc.c | 221 + security/nss/lib/freebl/mpi/mpv_sparcv8.s | 1607 + security/nss/lib/freebl/mpi/mpv_sparcv9.s | 1645 + security/nss/lib/freebl/mpi/mpvalpha.c | 183 + security/nss/lib/freebl/mpi/mulsqr.c | 84 + security/nss/lib/freebl/mpi/primes.c | 841 + security/nss/lib/freebl/mpi/vis_32.il | 1291 + security/nss/lib/freebl/mpi/vis_64.il | 997 + security/nss/lib/freebl/mpi/vis_proto.h | 234 + security/nss/lib/freebl/nsslowhash.c | 131 + security/nss/lib/freebl/nsslowhash.h | 33 + security/nss/lib/freebl/ppc-crypto.h | 31 + security/nss/lib/freebl/ppc-gcm-wrap.c | 458 + security/nss/lib/freebl/ppc-gcm.h | 76 + security/nss/lib/freebl/ppc-gcm.s | 1051 + security/nss/lib/freebl/pqg.c | 1926 + security/nss/lib/freebl/pqg.h | 28 + security/nss/lib/freebl/rawhash.c | 198 + security/nss/lib/freebl/ret_cr16.s | 27 + security/nss/lib/freebl/rijndael.c | 1265 + security/nss/lib/freebl/rijndael.h | 80 + security/nss/lib/freebl/rijndael32.tab | 1219 + security/nss/lib/freebl/rijndael_tables.c | 213 + security/nss/lib/freebl/rsa.c | 1725 + security/nss/lib/freebl/rsa_blind.c | 471 + security/nss/lib/freebl/rsapkcs.c | 1706 + security/nss/lib/freebl/scripts/LICENSE | 36 + security/nss/lib/freebl/scripts/gen.sh | 19 + security/nss/lib/freebl/scripts/ppc-xlate.pl | 352 + security/nss/lib/freebl/scripts/sha512p8-ppc.pl | 413 + security/nss/lib/freebl/secmpi.c | 28 + security/nss/lib/freebl/secmpi.h | 63 + security/nss/lib/freebl/secrng.h | 65 + security/nss/lib/freebl/sha-fast-amd64-sun.s | 2151 + security/nss/lib/freebl/sha1-armv8.c | 264 + security/nss/lib/freebl/sha256-armv8.c | 203 + security/nss/lib/freebl/sha256-x86.c | 236 + security/nss/lib/freebl/sha256.h | 27 + security/nss/lib/freebl/sha3.c | 288 + security/nss/lib/freebl/sha512-p8.s | 851 + security/nss/lib/freebl/sha512.c | 1776 + security/nss/lib/freebl/sha_fast.c | 592 + security/nss/lib/freebl/sha_fast.h | 186 + security/nss/lib/freebl/shake.c | 128 + security/nss/lib/freebl/shsign.h | 26 + security/nss/lib/freebl/shvfy.c | 664 + security/nss/lib/freebl/stubs.c | 879 + security/nss/lib/freebl/stubs.h | 72 + security/nss/lib/freebl/sysrand.c | 18 + security/nss/lib/freebl/tlsprfalg.c | 134 + security/nss/lib/freebl/unix_rand.c | 811 + security/nss/lib/freebl/unix_urandom.c | 84 + security/nss/lib/freebl/verified/Hacl_Chacha20.c | 227 + security/nss/lib/freebl/verified/Hacl_Chacha20.h | 60 + .../freebl/verified/Hacl_Chacha20Poly1305_128.c | 1216 + .../freebl/verified/Hacl_Chacha20Poly1305_128.h | 104 + .../freebl/verified/Hacl_Chacha20Poly1305_256.c | 1218 + .../freebl/verified/Hacl_Chacha20Poly1305_256.h | 104 + .../lib/freebl/verified/Hacl_Chacha20Poly1305_32.c | 630 + .../lib/freebl/verified/Hacl_Chacha20Poly1305_32.h | 104 + .../nss/lib/freebl/verified/Hacl_Chacha20_Vec128.c | 821 + .../nss/lib/freebl/verified/Hacl_Chacha20_Vec128.h | 60 + .../nss/lib/freebl/verified/Hacl_Chacha20_Vec256.c | 1209 + .../nss/lib/freebl/verified/Hacl_Chacha20_Vec256.h | 60 + .../nss/lib/freebl/verified/Hacl_Curve25519_51.c | 320 + .../nss/lib/freebl/verified/Hacl_Curve25519_51.h | 72 + .../nss/lib/freebl/verified/Hacl_Curve25519_64.c | 424 + .../nss/lib/freebl/verified/Hacl_Curve25519_64.h | 72 + security/nss/lib/freebl/verified/Hacl_Hash_SHA3.c | 713 + security/nss/lib/freebl/verified/Hacl_Hash_SHA3.h | 121 + .../lib/freebl/verified/Hacl_IntTypes_Intrinsics.h | 83 + .../freebl/verified/Hacl_IntTypes_Intrinsics_128.h | 72 + security/nss/lib/freebl/verified/Hacl_Krmllib.h | 59 + security/nss/lib/freebl/verified/Hacl_P256.c | 1829 + security/nss/lib/freebl/verified/Hacl_P256.h | 237 + security/nss/lib/freebl/verified/Hacl_P384.c | 126 + security/nss/lib/freebl/verified/Hacl_P384.h | 68 + security/nss/lib/freebl/verified/Hacl_P521.c | 131 + security/nss/lib/freebl/verified/Hacl_P521.h | 59 + .../nss/lib/freebl/verified/Hacl_Poly1305_128.c | 1617 + .../nss/lib/freebl/verified/Hacl_Poly1305_128.h | 64 + .../nss/lib/freebl/verified/Hacl_Poly1305_256.c | 2088 + .../nss/lib/freebl/verified/Hacl_Poly1305_256.h | 64 + .../nss/lib/freebl/verified/Hacl_Poly1305_32.c | 573 + .../nss/lib/freebl/verified/Hacl_Poly1305_32.h | 56 + .../nss/lib/freebl/verified/Hacl_Streaming_Types.h | 78 + security/nss/lib/freebl/verified/config.h | 0 .../nss/lib/freebl/verified/curve25519-inline.h | 942 + .../nss/lib/freebl/verified/internal/Hacl_Bignum.h | 315 + .../freebl/verified/internal/Hacl_Bignum25519_51.h | 680 + .../freebl/verified/internal/Hacl_Bignum_Base.h | 444 + .../lib/freebl/verified/internal/Hacl_Chacha20.h | 51 + .../freebl/verified/internal/Hacl_Curve25519_51.h | 55 + .../lib/freebl/verified/internal/Hacl_Hash_SHA3.h | 62 + .../lib/freebl/verified/internal/Hacl_Krmllib.h | 67 + .../nss/lib/freebl/verified/internal/Hacl_P256.h | 56 + .../verified/internal/Hacl_P256_PrecompTable.h | 508 + .../freebl/verified/internal/Hacl_Poly1305_128.h | 53 + .../freebl/verified/internal/Hacl_Poly1305_256.h | 53 + .../nss/lib/freebl/verified/internal/Hacl_Spec.h | 55 + security/nss/lib/freebl/verified/internal/Vale.h | 185 + .../verified/karamel/include/krml/c_endianness.h | 13 + .../verified/karamel/include/krml/fstar_int.h | 89 + .../karamel/include/krml/internal/builtin.h | 16 + .../karamel/include/krml/internal/callconv.h | 27 + .../karamel/include/krml/internal/compat.h | 32 + .../verified/karamel/include/krml/internal/debug.h | 57 + .../karamel/include/krml/internal/target.h | 375 + .../verified/karamel/include/krml/internal/types.h | 105 + .../karamel/include/krml/internal/wasmsupport.h | 5 + .../karamel/include/krml/lowstar_endianness.h | 243 + .../lib/freebl/verified/karamel/include/krmllib.h | 28 + .../karamel/krmllib/dist/minimal/FStar_UInt128.h | 76 + .../krmllib/dist/minimal/FStar_UInt128_Verified.h | 328 + .../krmllib/dist/minimal/FStar_UInt_8_16_32_64.h | 219 + .../krmllib/dist/minimal/LowStar_Endianness.h | 25 + .../karamel/krmllib/dist/minimal/Makefile.basic | 56 + .../karamel/krmllib/dist/minimal/Makefile.include | 5 + .../krmllib/dist/minimal/fstar_uint128_gcc64.h | 225 + .../krmllib/dist/minimal/fstar_uint128_msvc.h | 571 + .../dist/minimal/fstar_uint128_struct_endianness.h | 84 + .../karamel/krmllib/dist/minimal/libkrmllib.def | 11 + security/nss/lib/freebl/verified/lib_intrinsics.h | 93 + security/nss/lib/freebl/verified/libintvector.h | 914 + security/nss/lib/freebl/win_rand.c | 161 + security/nss/lib/jar/Makefile | 23 + security/nss/lib/jar/exports.gyp | 27 + security/nss/lib/jar/jar-ds.c | 36 + security/nss/lib/jar/jar-ds.h | 77 + security/nss/lib/jar/jar.c | 694 + security/nss/lib/jar/jar.gyp | 33 + security/nss/lib/jar/jar.h | 372 + security/nss/lib/jar/jarfile.c | 976 + security/nss/lib/jar/jarfile.h | 76 + security/nss/lib/jar/jarint.c | 52 + security/nss/lib/jar/jarint.h | 54 + security/nss/lib/jar/jarnav.c | 63 + security/nss/lib/jar/jarsign.c | 250 + security/nss/lib/jar/jarver.c | 1167 + security/nss/lib/jar/jzconf.h | 189 + security/nss/lib/jar/jzlib.h | 916 + security/nss/lib/jar/manifest.mn | 26 + security/nss/lib/libpkix/Makefile | 45 + security/nss/lib/libpkix/include/Makefile | 46 + security/nss/lib/libpkix/include/exports.gyp | 38 + security/nss/lib/libpkix/include/include.gyp | 12 + security/nss/lib/libpkix/include/manifest.mn | 29 + security/nss/lib/libpkix/include/pkix.h | 301 + security/nss/lib/libpkix/include/pkix_certsel.h | 1826 + security/nss/lib/libpkix/include/pkix_certstore.h | 714 + security/nss/lib/libpkix/include/pkix_checker.h | 394 + security/nss/lib/libpkix/include/pkix_crlsel.h | 759 + .../nss/lib/libpkix/include/pkix_errorstrings.h | 1099 + security/nss/lib/libpkix/include/pkix_params.h | 1793 + security/nss/lib/libpkix/include/pkix_pl_pki.h | 2735 + security/nss/lib/libpkix/include/pkix_pl_system.h | 1545 + security/nss/lib/libpkix/include/pkix_results.h | 425 + security/nss/lib/libpkix/include/pkix_revchecker.h | 215 + .../nss/lib/libpkix/include/pkix_sample_modules.h | 420 + security/nss/lib/libpkix/include/pkix_util.h | 941 + security/nss/lib/libpkix/include/pkixt.h | 485 + security/nss/lib/libpkix/libpkix.gyp | 31 + security/nss/lib/libpkix/manifest.mn | 13 + security/nss/lib/libpkix/pkix/Makefile | 46 + security/nss/lib/libpkix/pkix/certsel/Makefile | 46 + security/nss/lib/libpkix/pkix/certsel/certsel.gyp | 24 + security/nss/lib/libpkix/pkix/certsel/exports.gyp | 26 + security/nss/lib/libpkix/pkix/certsel/manifest.mn | 20 + .../lib/libpkix/pkix/certsel/pkix_certselector.c | 1637 + .../lib/libpkix/pkix/certsel/pkix_certselector.h | 41 + .../libpkix/pkix/certsel/pkix_comcertselparams.c | 1188 + .../libpkix/pkix/certsel/pkix_comcertselparams.h | 57 + security/nss/lib/libpkix/pkix/checker/Makefile | 46 + security/nss/lib/libpkix/pkix/checker/checker.gyp | 35 + security/nss/lib/libpkix/pkix/checker/exports.gyp | 37 + security/nss/lib/libpkix/pkix/checker/manifest.mn | 42 + .../pkix/checker/pkix_basicconstraintschecker.c | 306 + .../pkix/checker/pkix_basicconstraintschecker.h | 42 + .../libpkix/pkix/checker/pkix_certchainchecker.c | 322 + .../libpkix/pkix/checker/pkix_certchainchecker.h | 36 + .../nss/lib/libpkix/pkix/checker/pkix_crlchecker.c | 438 + .../nss/lib/libpkix/pkix/checker/pkix_crlchecker.h | 68 + .../nss/lib/libpkix/pkix/checker/pkix_ekuchecker.c | 328 + .../nss/lib/libpkix/pkix/checker/pkix_ekuchecker.h | 92 + .../libpkix/pkix/checker/pkix_expirationchecker.c | 113 + .../libpkix/pkix/checker/pkix_expirationchecker.h | 30 + .../pkix/checker/pkix_namechainingchecker.c | 121 + .../pkix/checker/pkix_namechainingchecker.h | 30 + .../pkix/checker/pkix_nameconstraintschecker.c | 340 + .../pkix/checker/pkix_nameconstraintschecker.h | 43 + .../lib/libpkix/pkix/checker/pkix_ocspchecker.c | 419 + .../lib/libpkix/pkix/checker/pkix_ocspchecker.h | 67 + .../lib/libpkix/pkix/checker/pkix_policychecker.c | 2783 + .../lib/libpkix/pkix/checker/pkix_policychecker.h | 73 + .../libpkix/pkix/checker/pkix_revocationchecker.c | 475 + .../libpkix/pkix/checker/pkix_revocationchecker.h | 151 + .../libpkix/pkix/checker/pkix_revocationmethod.c | 66 + .../libpkix/pkix/checker/pkix_revocationmethod.h | 81 + .../libpkix/pkix/checker/pkix_signaturechecker.c | 443 + .../libpkix/pkix/checker/pkix_signaturechecker.h | 44 + .../libpkix/pkix/checker/pkix_targetcertchecker.c | 516 + .../libpkix/pkix/checker/pkix_targetcertchecker.h | 47 + security/nss/lib/libpkix/pkix/crlsel/Makefile | 46 + security/nss/lib/libpkix/pkix/crlsel/crlsel.gyp | 24 + security/nss/lib/libpkix/pkix/crlsel/exports.gyp | 26 + security/nss/lib/libpkix/pkix/crlsel/manifest.mn | 20 + .../lib/libpkix/pkix/crlsel/pkix_comcrlselparams.c | 826 + .../lib/libpkix/pkix/crlsel/pkix_comcrlselparams.h | 38 + .../nss/lib/libpkix/pkix/crlsel/pkix_crlselector.c | 870 + .../nss/lib/libpkix/pkix/crlsel/pkix_crlselector.h | 40 + security/nss/lib/libpkix/pkix/manifest.mn | 11 + security/nss/lib/libpkix/pkix/params/Makefile | 46 + security/nss/lib/libpkix/pkix/params/exports.gyp | 28 + security/nss/lib/libpkix/pkix/params/manifest.mn | 24 + security/nss/lib/libpkix/pkix/params/params.gyp | 26 + .../nss/lib/libpkix/pkix/params/pkix_procparams.c | 1417 + .../nss/lib/libpkix/pkix/params/pkix_procparams.h | 50 + .../lib/libpkix/pkix/params/pkix_resourcelimits.c | 433 + .../lib/libpkix/pkix/params/pkix_resourcelimits.h | 36 + .../nss/lib/libpkix/pkix/params/pkix_trustanchor.c | 525 + .../nss/lib/libpkix/pkix/params/pkix_trustanchor.h | 35 + .../nss/lib/libpkix/pkix/params/pkix_valparams.c | 335 + .../nss/lib/libpkix/pkix/params/pkix_valparams.h | 33 + security/nss/lib/libpkix/pkix/results/Makefile | 46 + security/nss/lib/libpkix/pkix/results/exports.gyp | 28 + security/nss/lib/libpkix/pkix/results/manifest.mn | 24 + .../lib/libpkix/pkix/results/pkix_buildresult.c | 362 + .../lib/libpkix/pkix/results/pkix_buildresult.h | 40 + .../nss/lib/libpkix/pkix/results/pkix_policynode.c | 1377 + .../nss/lib/libpkix/pkix/results/pkix_policynode.h | 74 + .../nss/lib/libpkix/pkix/results/pkix_valresult.c | 442 + .../nss/lib/libpkix/pkix/results/pkix_valresult.h | 43 + .../nss/lib/libpkix/pkix/results/pkix_verifynode.c | 1182 + .../nss/lib/libpkix/pkix/results/pkix_verifynode.h | 75 + security/nss/lib/libpkix/pkix/results/results.gyp | 26 + security/nss/lib/libpkix/pkix/store/Makefile | 46 + security/nss/lib/libpkix/pkix/store/exports.gyp | 25 + security/nss/lib/libpkix/pkix/store/manifest.mn | 18 + security/nss/lib/libpkix/pkix/store/pkix_store.c | 415 + security/nss/lib/libpkix/pkix/store/pkix_store.h | 41 + security/nss/lib/libpkix/pkix/store/store.gyp | 23 + security/nss/lib/libpkix/pkix/top/Makefile | 46 + security/nss/lib/libpkix/pkix/top/exports.gyp | 27 + security/nss/lib/libpkix/pkix/top/manifest.mn | 22 + security/nss/lib/libpkix/pkix/top/pkix_build.c | 3763 + security/nss/lib/libpkix/pkix/top/pkix_build.h | 120 + security/nss/lib/libpkix/pkix/top/pkix_lifecycle.c | 210 + security/nss/lib/libpkix/pkix/top/pkix_lifecycle.h | 23 + security/nss/lib/libpkix/pkix/top/pkix_validate.c | 1451 + security/nss/lib/libpkix/pkix/top/pkix_validate.h | 42 + security/nss/lib/libpkix/pkix/top/top.gyp | 25 + security/nss/lib/libpkix/pkix/util/Makefile | 44 + security/nss/lib/libpkix/pkix/util/exports.gyp | 28 + security/nss/lib/libpkix/pkix/util/manifest.mn | 25 + security/nss/lib/libpkix/pkix/util/pkix_error.c | 565 + security/nss/lib/libpkix/pkix/util/pkix_error.h | 36 + security/nss/lib/libpkix/pkix/util/pkix_errpaths.c | 103 + security/nss/lib/libpkix/pkix/util/pkix_list.c | 1701 + security/nss/lib/libpkix/pkix/util/pkix_list.h | 95 + security/nss/lib/libpkix/pkix/util/pkix_logger.c | 1088 + security/nss/lib/libpkix/pkix/util/pkix_logger.h | 57 + security/nss/lib/libpkix/pkix/util/pkix_tools.c | 1525 + security/nss/lib/libpkix/pkix/util/pkix_tools.h | 1588 + security/nss/lib/libpkix/pkix/util/util.gyp | 27 + security/nss/lib/libpkix/pkix_pl_nss/Makefile | 44 + security/nss/lib/libpkix/pkix_pl_nss/manifest.mn | 11 + .../nss/lib/libpkix/pkix_pl_nss/module/Makefile | 66 + .../nss/lib/libpkix/pkix_pl_nss/module/exports.gyp | 36 + .../nss/lib/libpkix/pkix_pl_nss/module/manifest.mn | 34 + .../nss/lib/libpkix/pkix_pl_nss/module/module.gyp | 41 + .../libpkix/pkix_pl_nss/module/pkix_pl_aiamgr.c | 699 + .../libpkix/pkix_pl_nss/module/pkix_pl_aiamgr.h | 65 + .../pkix_pl_nss/module/pkix_pl_colcertstore.c | 1292 + .../pkix_pl_nss/module/pkix_pl_colcertstore.h | 34 + .../pkix_pl_nss/module/pkix_pl_httpcertstore.c | 1147 + .../pkix_pl_nss/module/pkix_pl_httpcertstore.h | 62 + .../pkix_pl_nss/module/pkix_pl_httpdefaultclient.c | 1654 + .../pkix_pl_nss/module/pkix_pl_httpdefaultclient.h | 139 + .../pkix_pl_nss/module/pkix_pl_ldapcertstore.c | 1113 + .../pkix_pl_nss/module/pkix_pl_ldapcertstore.h | 75 + .../pkix_pl_nss/module/pkix_pl_ldapdefaultclient.c | 2494 + .../pkix_pl_nss/module/pkix_pl_ldapdefaultclient.h | 82 + .../pkix_pl_nss/module/pkix_pl_ldaprequest.c | 757 + .../pkix_pl_nss/module/pkix_pl_ldaprequest.h | 86 + .../pkix_pl_nss/module/pkix_pl_ldapresponse.c | 786 + .../pkix_pl_nss/module/pkix_pl_ldapresponse.h | 96 + .../lib/libpkix/pkix_pl_nss/module/pkix_pl_ldapt.h | 314 + .../pkix_pl_nss/module/pkix_pl_ldaptemplates.c | 417 + .../pkix_pl_nss/module/pkix_pl_nsscontext.c | 389 + .../pkix_pl_nss/module/pkix_pl_nsscontext.h | 61 + .../pkix_pl_nss/module/pkix_pl_pk11certstore.c | 1039 + .../pkix_pl_nss/module/pkix_pl_pk11certstore.h | 31 + .../libpkix/pkix_pl_nss/module/pkix_pl_socket.c | 1695 + .../libpkix/pkix_pl_nss/module/pkix_pl_socket.h | 209 + security/nss/lib/libpkix/pkix_pl_nss/pki/Makefile | 47 + .../nss/lib/libpkix/pkix_pl_nss/pki/exports.gyp | 41 + .../nss/lib/libpkix/pkix_pl_nss/pki/manifest.mn | 50 + security/nss/lib/libpkix/pkix_pl_nss/pki/pki.gyp | 39 + .../pkix_pl_nss/pki/pkix_pl_basicconstraints.c | 407 + .../pkix_pl_nss/pki/pkix_pl_basicconstraints.h | 47 + .../nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_cert.c | 3734 + .../nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_cert.h | 107 + .../pkix_pl_nss/pki/pkix_pl_certpolicyinfo.c | 371 + .../pkix_pl_nss/pki/pkix_pl_certpolicyinfo.h | 50 + .../pkix_pl_nss/pki/pkix_pl_certpolicymap.c | 386 + .../pkix_pl_nss/pki/pkix_pl_certpolicymap.h | 49 + .../pkix_pl_nss/pki/pkix_pl_certpolicyqualifier.c | 365 + .../pkix_pl_nss/pki/pkix_pl_certpolicyqualifier.h | 52 + .../nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crl.c | 1068 + .../nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crl.h | 48 + .../lib/libpkix/pkix_pl_nss/pki/pkix_pl_crldp.c | 151 + .../lib/libpkix/pkix_pl_nss/pki/pkix_pl_crldp.h | 53 + .../lib/libpkix/pkix_pl_nss/pki/pkix_pl_crlentry.c | 880 + .../lib/libpkix/pkix_pl_nss/pki/pkix_pl_crlentry.h | 46 + .../nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_date.c | 466 + .../nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_date.h | 55 + .../libpkix/pkix_pl_nss/pki/pkix_pl_generalname.c | 873 + .../libpkix/pkix_pl_nss/pki/pkix_pl_generalname.h | 49 + .../libpkix/pkix_pl_nss/pki/pkix_pl_infoaccess.c | 874 + .../libpkix/pkix_pl_nss/pki/pkix_pl_infoaccess.h | 49 + .../pkix_pl_nss/pki/pkix_pl_nameconstraints.c | 1279 + .../pkix_pl_nss/pki/pkix_pl_nameconstraints.h | 68 + .../libpkix/pkix_pl_nss/pki/pkix_pl_ocspcertid.c | 251 + .../libpkix/pkix_pl_nss/pki/pkix_pl_ocspcertid.h | 53 + .../libpkix/pkix_pl_nss/pki/pkix_pl_ocsprequest.c | 441 + .../libpkix/pkix_pl_nss/pki/pkix_pl_ocsprequest.h | 61 + .../libpkix/pkix_pl_nss/pki/pkix_pl_ocspresponse.c | 1069 + .../libpkix/pkix_pl_nss/pki/pkix_pl_ocspresponse.h | 106 + .../libpkix/pkix_pl_nss/pki/pkix_pl_publickey.c | 489 + .../libpkix/pkix_pl_nss/pki/pkix_pl_publickey.h | 38 + .../lib/libpkix/pkix_pl_nss/pki/pkix_pl_x500name.c | 667 + .../lib/libpkix/pkix_pl_nss/pki/pkix_pl_x500name.h | 74 + .../nss/lib/libpkix/pkix_pl_nss/system/Makefile | 47 + .../nss/lib/libpkix/pkix_pl_nss/system/exports.gyp | 37 + .../nss/lib/libpkix/pkix_pl_nss/system/manifest.mn | 43 + .../libpkix/pkix_pl_nss/system/pkix_pl_bigint.c | 398 + .../libpkix/pkix_pl_nss/system/pkix_pl_bigint.h | 40 + .../libpkix/pkix_pl_nss/system/pkix_pl_bytearray.c | 504 + .../libpkix/pkix_pl_nss/system/pkix_pl_bytearray.h | 40 + .../libpkix/pkix_pl_nss/system/pkix_pl_common.c | 1073 + .../libpkix/pkix_pl_nss/system/pkix_pl_common.h | 159 + .../lib/libpkix/pkix_pl_nss/system/pkix_pl_error.c | 26 + .../libpkix/pkix_pl_nss/system/pkix_pl_hashtable.c | 383 + .../libpkix/pkix_pl_nss/system/pkix_pl_hashtable.h | 29 + .../libpkix/pkix_pl_nss/system/pkix_pl_lifecycle.c | 279 + .../libpkix/pkix_pl_nss/system/pkix_pl_lifecycle.h | 91 + .../lib/libpkix/pkix_pl_nss/system/pkix_pl_mem.c | 168 + .../lib/libpkix/pkix_pl_nss/system/pkix_pl_mem.h | 24 + .../pkix_pl_nss/system/pkix_pl_monitorlock.c | 136 + .../pkix_pl_nss/system/pkix_pl_monitorlock.h | 33 + .../lib/libpkix/pkix_pl_nss/system/pkix_pl_mutex.c | 163 + .../lib/libpkix/pkix_pl_nss/system/pkix_pl_mutex.h | 33 + .../libpkix/pkix_pl_nss/system/pkix_pl_object.c | 1440 + .../libpkix/pkix_pl_nss/system/pkix_pl_object.h | 76 + .../lib/libpkix/pkix_pl_nss/system/pkix_pl_oid.c | 316 + .../lib/libpkix/pkix_pl_nss/system/pkix_pl_oid.h | 39 + .../libpkix/pkix_pl_nss/system/pkix_pl_primhash.c | 584 + .../libpkix/pkix_pl_nss/system/pkix_pl_primhash.h | 102 + .../libpkix/pkix_pl_nss/system/pkix_pl_rwlock.c | 217 + .../libpkix/pkix_pl_nss/system/pkix_pl_rwlock.h | 35 + .../libpkix/pkix_pl_nss/system/pkix_pl_string.c | 628 + .../libpkix/pkix_pl_nss/system/pkix_pl_string.h | 37 + .../nss/lib/libpkix/pkix_pl_nss/system/system.gyp | 36 + security/nss/lib/mozpkix/.clang-format | 4 + security/nss/lib/mozpkix/exports.gyp | 48 + .../lib/mozpkix/include/pkix-test/pkixtestnss.h | 48 + .../lib/mozpkix/include/pkix-test/pkixtestutil.h | 439 + security/nss/lib/mozpkix/include/pkix/Input.h | 310 + security/nss/lib/mozpkix/include/pkix/Result.h | 219 + security/nss/lib/mozpkix/include/pkix/Time.h | 142 + security/nss/lib/mozpkix/include/pkix/pkix.h | 162 + security/nss/lib/mozpkix/include/pkix/pkixc.h | 47 + security/nss/lib/mozpkix/include/pkix/pkixcheck.h | 65 + security/nss/lib/mozpkix/include/pkix/pkixder.h | 534 + security/nss/lib/mozpkix/include/pkix/pkixnss.h | 112 + security/nss/lib/mozpkix/include/pkix/pkixtypes.h | 421 + security/nss/lib/mozpkix/include/pkix/pkixutil.h | 267 + security/nss/lib/mozpkix/lib/pkixbuild.cpp | 399 + security/nss/lib/mozpkix/lib/pkixc.cpp | 232 + security/nss/lib/mozpkix/lib/pkixcert.cpp | 318 + security/nss/lib/mozpkix/lib/pkixcheck.cpp | 1096 + security/nss/lib/mozpkix/lib/pkixder.cpp | 740 + security/nss/lib/mozpkix/lib/pkixnames.cpp | 2086 + security/nss/lib/mozpkix/lib/pkixnss.cpp | 406 + security/nss/lib/mozpkix/lib/pkixocsp.cpp | 1030 + security/nss/lib/mozpkix/lib/pkixresult.cpp | 46 + security/nss/lib/mozpkix/lib/pkixtime.cpp | 92 + security/nss/lib/mozpkix/lib/pkixverify.cpp | 60 + security/nss/lib/mozpkix/mozpkix.gyp | 61 + security/nss/lib/mozpkix/test-lib/pkixtestalg.cpp | 211 + security/nss/lib/mozpkix/test-lib/pkixtestnss.cpp | 374 + security/nss/lib/mozpkix/test-lib/pkixtestutil.cpp | 1188 + security/nss/lib/mozpkix/tools/DottedOIDToCode.py | 216 + security/nss/lib/nss/Makefile | 45 + security/nss/lib/nss/config.mk | 107 + security/nss/lib/nss/exports.gyp | 32 + security/nss/lib/nss/manifest.mn | 30 + security/nss/lib/nss/nss.def | 1258 + security/nss/lib/nss/nss.gyp | 67 + security/nss/lib/nss/nss.h | 355 + security/nss/lib/nss/nss.rc | 68 + security/nss/lib/nss/nssinit.c | 1372 + security/nss/lib/nss/nssoptions.c | 153 + security/nss/lib/nss/nssoptions.h | 22 + security/nss/lib/nss/nssrenam.h | 15 + security/nss/lib/nss/nssver.c | 18 + security/nss/lib/nss/pkixpriv.def | 322 + security/nss/lib/nss/utilwrap.c | 884 + security/nss/lib/pk11wrap/Makefile | 58 + security/nss/lib/pk11wrap/debug_module.c | 3454 + security/nss/lib/pk11wrap/dev3hack.c | 264 + security/nss/lib/pk11wrap/dev3hack.h | 30 + security/nss/lib/pk11wrap/exports.gyp | 41 + security/nss/lib/pk11wrap/manifest.mn | 67 + security/nss/lib/pk11wrap/pk11akey.c | 2765 + security/nss/lib/pk11wrap/pk11auth.c | 814 + security/nss/lib/pk11wrap/pk11cert.c | 2983 + security/nss/lib/pk11wrap/pk11cxt.c | 1795 + security/nss/lib/pk11wrap/pk11err.c | 141 + security/nss/lib/pk11wrap/pk11func.h | 15 + security/nss/lib/pk11wrap/pk11hpke.c | 1276 + security/nss/lib/pk11wrap/pk11hpke.h | 82 + security/nss/lib/pk11wrap/pk11kea.c | 141 + security/nss/lib/pk11wrap/pk11list.c | 99 + security/nss/lib/pk11wrap/pk11load.c | 715 + security/nss/lib/pk11wrap/pk11mech.c | 1954 + security/nss/lib/pk11wrap/pk11merge.c | 1437 + security/nss/lib/pk11wrap/pk11nobj.c | 807 + security/nss/lib/pk11wrap/pk11obj.c | 2285 + security/nss/lib/pk11wrap/pk11pars.c | 2159 + security/nss/lib/pk11wrap/pk11pbe.c | 1486 + security/nss/lib/pk11wrap/pk11pk12.c | 829 + security/nss/lib/pk11wrap/pk11pqg.c | 522 + security/nss/lib/pk11wrap/pk11pqg.h | 135 + security/nss/lib/pk11wrap/pk11priv.h | 210 + security/nss/lib/pk11wrap/pk11pub.h | 1095 + security/nss/lib/pk11wrap/pk11sdr.c | 437 + security/nss/lib/pk11wrap/pk11sdr.h | 28 + security/nss/lib/pk11wrap/pk11skey.c | 3297 + security/nss/lib/pk11wrap/pk11slot.c | 2798 + security/nss/lib/pk11wrap/pk11util.c | 1725 + security/nss/lib/pk11wrap/pk11wrap.gyp | 71 + security/nss/lib/pk11wrap/secmod.h | 194 + security/nss/lib/pk11wrap/secmodi.h | 174 + security/nss/lib/pk11wrap/secmodt.h | 448 + security/nss/lib/pk11wrap/secmodti.h | 205 + security/nss/lib/pk11wrap/secpkcs5.h | 61 + security/nss/lib/pkcs12/Makefile | 47 + security/nss/lib/pkcs12/exports.gyp | 29 + security/nss/lib/pkcs12/manifest.mn | 32 + security/nss/lib/pkcs12/p12.h | 236 + security/nss/lib/pkcs12/p12creat.c | 218 + security/nss/lib/pkcs12/p12d.c | 3626 + security/nss/lib/pkcs12/p12dec.c | 670 + security/nss/lib/pkcs12/p12e.c | 2087 + security/nss/lib/pkcs12/p12exp.c | 1374 + security/nss/lib/pkcs12/p12local.c | 1417 + security/nss/lib/pkcs12/p12local.h | 70 + security/nss/lib/pkcs12/p12plcy.c | 106 + security/nss/lib/pkcs12/p12plcy.h | 25 + security/nss/lib/pkcs12/p12t.h | 156 + security/nss/lib/pkcs12/p12tmpl.c | 290 + security/nss/lib/pkcs12/pkcs12.gyp | 29 + security/nss/lib/pkcs12/pkcs12.h | 41 + security/nss/lib/pkcs12/pkcs12t.h | 341 + security/nss/lib/pkcs7/Makefile | 46 + security/nss/lib/pkcs7/certread.c | 535 + security/nss/lib/pkcs7/exports.gyp | 33 + security/nss/lib/pkcs7/manifest.mn | 34 + security/nss/lib/pkcs7/p7common.c | 663 + security/nss/lib/pkcs7/p7create.c | 1306 + security/nss/lib/pkcs7/p7decode.c | 1919 + security/nss/lib/pkcs7/p7encode.c | 1079 + security/nss/lib/pkcs7/p7local.c | 1309 + security/nss/lib/pkcs7/p7local.h | 137 + security/nss/lib/pkcs7/pkcs7.gyp | 29 + security/nss/lib/pkcs7/pkcs7t.h | 233 + security/nss/lib/pkcs7/secmime.c | 800 + security/nss/lib/pkcs7/secmime.h | 160 + security/nss/lib/pkcs7/secpkcs7.h | 626 + security/nss/lib/pki/Makefile | 14 + security/nss/lib/pki/asymmkey.c | 361 + security/nss/lib/pki/certdecode.c | 60 + security/nss/lib/pki/certificate.c | 1101 + security/nss/lib/pki/cryptocontext.c | 914 + security/nss/lib/pki/doc/standiag.png | Bin 0 -> 20475 bytes security/nss/lib/pki/doc/standoc.html | 442 + security/nss/lib/pki/exports.gyp | 32 + security/nss/lib/pki/manifest.mn | 42 + security/nss/lib/pki/nsspki.h | 2791 + security/nss/lib/pki/nsspkit.h | 247 + security/nss/lib/pki/pki.gyp | 32 + security/nss/lib/pki/pki.h | 141 + security/nss/lib/pki/pki3hack.c | 1510 + security/nss/lib/pki/pki3hack.h | 149 + security/nss/lib/pki/pkibase.c | 1224 + security/nss/lib/pki/pkim.h | 547 + security/nss/lib/pki/pkistore.c | 675 + security/nss/lib/pki/pkistore.h | 138 + security/nss/lib/pki/pkit.h | 183 + security/nss/lib/pki/pkitm.h | 88 + security/nss/lib/pki/symmkey.c | 238 + security/nss/lib/pki/tdcache.c | 1113 + security/nss/lib/pki/trustdomain.c | 1230 + security/nss/lib/smime/Makefile | 48 + security/nss/lib/smime/cms.h | 1153 + security/nss/lib/smime/cmsarray.c | 186 + security/nss/lib/smime/cmsasn1.c | 491 + security/nss/lib/smime/cmsattr.c | 425 + security/nss/lib/smime/cmscinfo.c | 443 + security/nss/lib/smime/cmscipher.c | 722 + security/nss/lib/smime/cmsdecode.c | 736 + security/nss/lib/smime/cmsdigdata.c | 212 + security/nss/lib/smime/cmsdigest.c | 259 + security/nss/lib/smime/cmsencdata.c | 262 + security/nss/lib/smime/cmsencode.c | 762 + security/nss/lib/smime/cmsenvdata.c | 403 + security/nss/lib/smime/cmslocal.h | 351 + security/nss/lib/smime/cmsmessage.c | 345 + security/nss/lib/smime/cmspubkey.c | 285 + security/nss/lib/smime/cmsrecinfo.c | 669 + security/nss/lib/smime/cmsreclist.c | 170 + security/nss/lib/smime/cmsreclist.h | 27 + security/nss/lib/smime/cmssigdata.c | 1141 + security/nss/lib/smime/cmssiginfo.c | 1081 + security/nss/lib/smime/cmst.h | 491 + security/nss/lib/smime/cmsudf.c | 440 + security/nss/lib/smime/cmsutil.c | 371 + security/nss/lib/smime/config.mk | 56 + security/nss/lib/smime/exports.gyp | 34 + security/nss/lib/smime/manifest.mn | 51 + security/nss/lib/smime/smime.def | 293 + security/nss/lib/smime/smime.gyp | 80 + security/nss/lib/smime/smime.h | 141 + security/nss/lib/smime/smime.rc | 68 + security/nss/lib/smime/smimemessage.c | 183 + security/nss/lib/smime/smimesym.c | 12 + security/nss/lib/smime/smimeutil.c | 795 + security/nss/lib/smime/smimever.c | 18 + security/nss/lib/softoken/Makefile | 76 + security/nss/lib/softoken/config.mk | 65 + security/nss/lib/softoken/exports.gyp | 44 + security/nss/lib/softoken/fips_algorithms.h | 182 + security/nss/lib/softoken/fipsaudt.c | 321 + security/nss/lib/softoken/fipstest.c | 797 + security/nss/lib/softoken/fipstokn.c | 2080 + security/nss/lib/softoken/jpakesftk.c | 359 + security/nss/lib/softoken/kbkdf.c | 1520 + security/nss/lib/softoken/kem.c | 366 + security/nss/lib/softoken/kem.h | 34 + security/nss/lib/softoken/legacydb/Makefile | 60 + security/nss/lib/softoken/legacydb/cdbhdl.h | 51 + security/nss/lib/softoken/legacydb/config.mk | 50 + security/nss/lib/softoken/legacydb/dbmshim.c | 539 + security/nss/lib/softoken/legacydb/keydb.c | 2274 + security/nss/lib/softoken/legacydb/keydbi.h | 52 + security/nss/lib/softoken/legacydb/legacydb.gyp | 66 + security/nss/lib/softoken/legacydb/lgattr.c | 1782 + security/nss/lib/softoken/legacydb/lgcreate.c | 1020 + security/nss/lib/softoken/legacydb/lgdb.h | 177 + security/nss/lib/softoken/legacydb/lgdestroy.c | 110 + security/nss/lib/softoken/legacydb/lgfind.c | 912 + security/nss/lib/softoken/legacydb/lgfips.c | 120 + security/nss/lib/softoken/legacydb/lginit.c | 661 + security/nss/lib/softoken/legacydb/lgutil.c | 399 + security/nss/lib/softoken/legacydb/lowcert.c | 854 + security/nss/lib/softoken/legacydb/lowkey.c | 388 + security/nss/lib/softoken/legacydb/lowkeyi.h | 148 + security/nss/lib/softoken/legacydb/lowkeyti.h | 130 + security/nss/lib/softoken/legacydb/manifest.mn | 32 + security/nss/lib/softoken/legacydb/nssdbm.def | 31 + security/nss/lib/softoken/legacydb/nssdbm.rc | 68 + security/nss/lib/softoken/legacydb/pcert.h | 228 + security/nss/lib/softoken/legacydb/pcertdb.c | 5350 + security/nss/lib/softoken/legacydb/pcertt.h | 418 + security/nss/lib/softoken/legacydb/pk11db.c | 731 + security/nss/lib/softoken/lgglue.c | 422 + security/nss/lib/softoken/lgglue.h | 59 + security/nss/lib/softoken/lowkey.c | 544 + security/nss/lib/softoken/lowkeyi.h | 70 + security/nss/lib/softoken/lowkeyti.h | 100 + security/nss/lib/softoken/lowpbe.c | 1820 + security/nss/lib/softoken/lowpbe.h | 112 + security/nss/lib/softoken/manifest.mn | 71 + security/nss/lib/softoken/padbuf.c | 49 + security/nss/lib/softoken/pkcs11.c | 5581 + security/nss/lib/softoken/pkcs11c.c | 9085 + security/nss/lib/softoken/pkcs11i.h | 977 + security/nss/lib/softoken/pkcs11ni.h | 20 + security/nss/lib/softoken/pkcs11u.c | 2563 + security/nss/lib/softoken/sdb.c | 2469 + security/nss/lib/softoken/sdb.h | 102 + security/nss/lib/softoken/sftkdb.c | 3047 + security/nss/lib/softoken/sftkdb.h | 75 + security/nss/lib/softoken/sftkdbt.h | 12 + security/nss/lib/softoken/sftkdbti.h | 82 + security/nss/lib/softoken/sftkdhverify.c | 6845 + security/nss/lib/softoken/sftkhmac.c | 501 + security/nss/lib/softoken/sftkike.c | 1419 + security/nss/lib/softoken/sftkmessage.c | 377 + security/nss/lib/softoken/sftkpars.c | 268 + security/nss/lib/softoken/sftkpwd.c | 1485 + security/nss/lib/softoken/softkver.c | 18 + security/nss/lib/softoken/softkver.h | 27 + security/nss/lib/softoken/softoken.gyp | 112 + security/nss/lib/softoken/softoken.h | 280 + security/nss/lib/softoken/softokn.def | 39 + security/nss/lib/softoken/softokn.rc | 68 + security/nss/lib/softoken/softoknt.h | 46 + security/nss/lib/softoken/tlsprf.c | 198 + security/nss/lib/sqlite/Makefile | 54 + security/nss/lib/sqlite/README | 3 + security/nss/lib/sqlite/config.mk | 31 + security/nss/lib/sqlite/exports.gyp | 25 + security/nss/lib/sqlite/manifest.mn | 30 + security/nss/lib/sqlite/sqlite.def | 147 + security/nss/lib/sqlite/sqlite.gyp | 58 + security/nss/lib/sqlite/sqlite3.c | 223787 ++++++++++++++++++ security/nss/lib/sqlite/sqlite3.h | 11792 + security/nss/lib/ssl/Makefile | 63 + security/nss/lib/ssl/SSLerrs.h | 605 + security/nss/lib/ssl/authcert.c | 250 + security/nss/lib/ssl/cmpcert.c | 64 + security/nss/lib/ssl/config.mk | 51 + security/nss/lib/ssl/dhe-param.c | 418 + security/nss/lib/ssl/dtls13con.c | 868 + security/nss/lib/ssl/dtls13con.h | 45 + security/nss/lib/ssl/dtlscon.c | 1453 + security/nss/lib/ssl/dtlscon.h | 52 + security/nss/lib/ssl/exports.gyp | 30 + security/nss/lib/ssl/manifest.mn | 73 + security/nss/lib/ssl/notes.txt | 104 + security/nss/lib/ssl/os2_err.c | 330 + security/nss/lib/ssl/os2_err.h | 53 + security/nss/lib/ssl/preenc.h | 113 + security/nss/lib/ssl/prelib.c | 34 + security/nss/lib/ssl/selfencrypt.c | 321 + security/nss/lib/ssl/selfencrypt.h | 32 + security/nss/lib/ssl/ssl.def | 255 + security/nss/lib/ssl/ssl.gyp | 102 + security/nss/lib/ssl/ssl.h | 1608 + security/nss/lib/ssl/ssl.rc | 68 + security/nss/lib/ssl/ssl3con.c | 14398 ++ security/nss/lib/ssl/ssl3ecc.c | 969 + security/nss/lib/ssl/ssl3ext.c | 1186 + security/nss/lib/ssl/ssl3ext.h | 226 + security/nss/lib/ssl/ssl3exthandle.c | 2004 + security/nss/lib/ssl/ssl3exthandle.h | 133 + security/nss/lib/ssl/ssl3gthr.c | 823 + security/nss/lib/ssl/ssl3prot.h | 196 + security/nss/lib/ssl/sslauth.c | 292 + security/nss/lib/ssl/sslbloom.c | 94 + security/nss/lib/ssl/sslbloom.h | 32 + security/nss/lib/ssl/sslcert.c | 1016 + security/nss/lib/ssl/sslcert.h | 67 + security/nss/lib/ssl/sslcon.c | 248 + security/nss/lib/ssl/ssldef.c | 226 + security/nss/lib/ssl/sslencode.c | 440 + security/nss/lib/ssl/sslencode.h | 103 + security/nss/lib/ssl/sslenum.c | 157 + security/nss/lib/ssl/sslerr.c | 41 + security/nss/lib/ssl/sslerr.h | 300 + security/nss/lib/ssl/sslerrstrs.c | 36 + security/nss/lib/ssl/sslexp.h | 1099 + security/nss/lib/ssl/sslgrp.c | 164 + security/nss/lib/ssl/sslimpl.h | 2048 + security/nss/lib/ssl/sslinfo.c | 578 + security/nss/lib/ssl/sslinit.c | 60 + security/nss/lib/ssl/sslmutex.c | 658 + security/nss/lib/ssl/sslmutex.h | 129 + security/nss/lib/ssl/sslnonce.c | 1228 + security/nss/lib/ssl/sslprimitive.c | 482 + security/nss/lib/ssl/sslproto.h | 294 + security/nss/lib/ssl/sslreveal.c | 108 + security/nss/lib/ssl/sslsecur.c | 1389 + security/nss/lib/ssl/sslsnce.c | 2251 + security/nss/lib/ssl/sslsock.c | 4650 + security/nss/lib/ssl/sslspec.c | 276 + security/nss/lib/ssl/sslspec.h | 200 + security/nss/lib/ssl/sslt.h | 588 + security/nss/lib/ssl/ssltrace.c | 114 + security/nss/lib/ssl/sslver.c | 18 + security/nss/lib/ssl/tls13con.c | 7210 + security/nss/lib/ssl/tls13con.h | 180 + security/nss/lib/ssl/tls13ech.c | 2828 + security/nss/lib/ssl/tls13ech.h | 125 + security/nss/lib/ssl/tls13echv.c | 167 + security/nss/lib/ssl/tls13err.h | 28 + security/nss/lib/ssl/tls13exthandle.c | 1961 + security/nss/lib/ssl/tls13exthandle.h | 135 + security/nss/lib/ssl/tls13hashstate.c | 332 + security/nss/lib/ssl/tls13hashstate.h | 27 + security/nss/lib/ssl/tls13hkdf.c | 305 + security/nss/lib/ssl/tls13hkdf.h | 39 + security/nss/lib/ssl/tls13psk.c | 219 + security/nss/lib/ssl/tls13psk.h | 58 + security/nss/lib/ssl/tls13replay.c | 285 + security/nss/lib/ssl/tls13subcerts.c | 801 + security/nss/lib/ssl/tls13subcerts.h | 56 + security/nss/lib/ssl/unix_err.c | 837 + security/nss/lib/ssl/unix_err.h | 57 + security/nss/lib/ssl/win32err.c | 550 + security/nss/lib/ssl/win32err.h | 51 + security/nss/lib/sysinit/Makefile | 44 + security/nss/lib/sysinit/config.mk | 73 + security/nss/lib/sysinit/manifest.mn | 17 + security/nss/lib/sysinit/nsssysinit.c | 418 + security/nss/lib/sysinit/nsssysinit.def | 26 + security/nss/lib/sysinit/sysinit.gyp | 34 + security/nss/lib/util/Makefile | 47 + security/nss/lib/util/SECerrs.h | 569 + security/nss/lib/util/base64.h | 41 + security/nss/lib/util/ciferfam.h | 62 + security/nss/lib/util/config.mk | 38 + security/nss/lib/util/derdec.c | 189 + security/nss/lib/util/derenc.c | 460 + security/nss/lib/util/dersubr.c | 249 + security/nss/lib/util/dertime.c | 310 + security/nss/lib/util/eccutil.h | 17 + security/nss/lib/util/errstrs.c | 41 + security/nss/lib/util/exports.gyp | 69 + security/nss/lib/util/hasht.h | 71 + security/nss/lib/util/kyber.h | 32 + security/nss/lib/util/manifest.mn | 91 + security/nss/lib/util/nssb64.h | 94 + security/nss/lib/util/nssb64d.c | 852 + security/nss/lib/util/nssb64e.c | 734 + security/nss/lib/util/nssb64t.h | 15 + security/nss/lib/util/nssilckt.h | 191 + security/nss/lib/util/nssilock.c | 479 + security/nss/lib/util/nssilock.h | 267 + security/nss/lib/util/nsslocks.h | 10 + security/nss/lib/util/nssrwlk.c | 450 + security/nss/lib/util/nssrwlk.h | 133 + security/nss/lib/util/nssrwlkt.h | 19 + security/nss/lib/util/nssutil.def | 362 + security/nss/lib/util/nssutil.h | 41 + security/nss/lib/util/nssutil.rc | 68 + security/nss/lib/util/oidstring.c | 114 + security/nss/lib/util/pkcs11.h | 274 + security/nss/lib/util/pkcs11f.h | 1044 + security/nss/lib/util/pkcs11n.h | 745 + security/nss/lib/util/pkcs11p.h | 24 + security/nss/lib/util/pkcs11t.h | 2591 + security/nss/lib/util/pkcs11u.h | 22 + security/nss/lib/util/pkcs11uri.c | 867 + security/nss/lib/util/pkcs11uri.h | 80 + security/nss/lib/util/pkcs1sig.c | 158 + security/nss/lib/util/pkcs1sig.h | 30 + security/nss/lib/util/portreg.c | 375 + security/nss/lib/util/portreg.h | 81 + security/nss/lib/util/quickder.c | 837 + security/nss/lib/util/secalgid.c | 129 + security/nss/lib/util/secasn1.h | 303 + security/nss/lib/util/secasn1d.c | 3443 + security/nss/lib/util/secasn1e.c | 1572 + security/nss/lib/util/secasn1t.h | 267 + security/nss/lib/util/secasn1u.c | 94 + security/nss/lib/util/seccomon.h | 91 + security/nss/lib/util/secder.h | 175 + security/nss/lib/util/secdert.h | 129 + security/nss/lib/util/secdig.c | 182 + security/nss/lib/util/secdig.h | 100 + security/nss/lib/util/secdigt.h | 26 + security/nss/lib/util/secerr.h | 226 + security/nss/lib/util/secitem.c | 496 + security/nss/lib/util/secitem.h | 126 + security/nss/lib/util/secload.c | 182 + security/nss/lib/util/secoid.c | 2402 + security/nss/lib/util/secoid.h | 149 + security/nss/lib/util/secoidt.h | 578 + security/nss/lib/util/secplcy.c | 83 + security/nss/lib/util/secplcy.h | 104 + security/nss/lib/util/secport.c | 925 + security/nss/lib/util/secport.h | 378 + security/nss/lib/util/sectime.c | 163 + security/nss/lib/util/templates.c | 136 + security/nss/lib/util/utf8.c | 445 + security/nss/lib/util/util.gyp | 60 + security/nss/lib/util/utilmod.c | 946 + security/nss/lib/util/utilmodt.h | 43 + security/nss/lib/util/utilpars.c | 1314 + security/nss/lib/util/utilpars.h | 70 + security/nss/lib/util/utilparst.h | 78 + security/nss/lib/util/utilrename.h | 164 + security/nss/lib/util/verref.h | 43 + security/nss/lib/zlib/LICENSE | 22 + security/nss/lib/zlib/Makefile | 59 + security/nss/lib/zlib/README | 117 + security/nss/lib/zlib/README.nss | 10 + security/nss/lib/zlib/adler32.c | 164 + security/nss/lib/zlib/compress.c | 75 + security/nss/lib/zlib/crc32.c | 1049 + security/nss/lib/zlib/crc32.h | 9446 + security/nss/lib/zlib/deflate.c | 2139 + security/nss/lib/zlib/deflate.h | 377 + security/nss/lib/zlib/exports.gyp | 33 + security/nss/lib/zlib/gzclose.c | 23 + security/nss/lib/zlib/gzguts.h | 214 + security/nss/lib/zlib/gzlib.c | 582 + security/nss/lib/zlib/gzread.c | 602 + security/nss/lib/zlib/gzwrite.c | 631 + security/nss/lib/zlib/infback.c | 628 + security/nss/lib/zlib/inffast.c | 320 + security/nss/lib/zlib/inffast.h | 11 + security/nss/lib/zlib/inffixed.h | 94 + security/nss/lib/zlib/inflate.c | 1526 + security/nss/lib/zlib/inflate.h | 126 + security/nss/lib/zlib/inftrees.c | 299 + security/nss/lib/zlib/inftrees.h | 62 + security/nss/lib/zlib/manifest.mn | 36 + security/nss/lib/zlib/patches/msvc-vsnprintf.patch | 22 + security/nss/lib/zlib/patches/prune-zlib.sh | 34 + security/nss/lib/zlib/trees.c | 1117 + security/nss/lib/zlib/trees.h | 128 + security/nss/lib/zlib/uncompr.c | 85 + security/nss/lib/zlib/vendor.sh | 59 + security/nss/lib/zlib/zconf.h | 543 + security/nss/lib/zlib/zlib.gyp | 57 + security/nss/lib/zlib/zlib.h | 1938 + security/nss/lib/zlib/zutil.c | 299 + security/nss/lib/zlib/zutil.h | 254 + 1251 files changed, 926800 insertions(+) create mode 100644 security/nss/lib/Makefile create mode 100644 security/nss/lib/base/Makefile create mode 100644 security/nss/lib/base/arena.c create mode 100644 security/nss/lib/base/base.gyp create mode 100644 security/nss/lib/base/base.h create mode 100644 security/nss/lib/base/baset.h create mode 100644 security/nss/lib/base/error.c create mode 100644 security/nss/lib/base/errorval.c create mode 100644 security/nss/lib/base/exports.gyp create mode 100644 security/nss/lib/base/hash.c create mode 100644 security/nss/lib/base/hashops.c create mode 100644 security/nss/lib/base/item.c create mode 100644 security/nss/lib/base/libc.c create mode 100644 security/nss/lib/base/list.c create mode 100644 security/nss/lib/base/manifest.mn create mode 100644 security/nss/lib/base/nssbase.h create mode 100644 security/nss/lib/base/nssbaset.h create mode 100644 security/nss/lib/base/tracker.c create mode 100644 security/nss/lib/base/utf8.c create mode 100644 security/nss/lib/certdb/Makefile create mode 100644 security/nss/lib/certdb/alg1485.c create mode 100644 security/nss/lib/certdb/cert.h create mode 100644 security/nss/lib/certdb/certdb.c create mode 100644 security/nss/lib/certdb/certdb.gyp create mode 100644 security/nss/lib/certdb/certdb.h create mode 100644 security/nss/lib/certdb/certi.h create mode 100644 security/nss/lib/certdb/certt.h create mode 100644 security/nss/lib/certdb/certv3.c create mode 100644 security/nss/lib/certdb/certxutl.c create mode 100644 security/nss/lib/certdb/certxutl.h create mode 100644 security/nss/lib/certdb/crl.c create mode 100644 security/nss/lib/certdb/exports.gyp create mode 100644 security/nss/lib/certdb/genname.c create mode 100644 security/nss/lib/certdb/genname.h create mode 100644 security/nss/lib/certdb/manifest.mn create mode 100644 security/nss/lib/certdb/polcyxtn.c create mode 100644 security/nss/lib/certdb/secname.c create mode 100644 security/nss/lib/certdb/stanpcertdb.c create mode 100644 security/nss/lib/certdb/xauthkid.c create mode 100644 security/nss/lib/certdb/xbsconst.c create mode 100644 security/nss/lib/certdb/xconst.c create mode 100644 security/nss/lib/certdb/xconst.h create mode 100644 security/nss/lib/certhigh/Makefile create mode 100644 security/nss/lib/certhigh/certhigh.c create mode 100644 security/nss/lib/certhigh/certhigh.gyp create mode 100644 security/nss/lib/certhigh/certhtml.c create mode 100644 security/nss/lib/certhigh/certreq.c create mode 100644 security/nss/lib/certhigh/certvfy.c create mode 100644 security/nss/lib/certhigh/certvfypkix.c create mode 100644 security/nss/lib/certhigh/crlv2.c create mode 100644 security/nss/lib/certhigh/exports.gyp create mode 100644 security/nss/lib/certhigh/manifest.mn create mode 100644 security/nss/lib/certhigh/ocsp.c create mode 100644 security/nss/lib/certhigh/ocsp.h create mode 100644 security/nss/lib/certhigh/ocspi.h create mode 100644 security/nss/lib/certhigh/ocspsig.c create mode 100644 security/nss/lib/certhigh/ocspt.h create mode 100644 security/nss/lib/certhigh/ocspti.h create mode 100644 security/nss/lib/certhigh/xcrldist.c create mode 100644 security/nss/lib/ckfw/Makefile create mode 100644 security/nss/lib/ckfw/builtins/Makefile create mode 100644 security/nss/lib/ckfw/builtins/README create mode 100644 security/nss/lib/ckfw/builtins/anchor.c create mode 100644 security/nss/lib/ckfw/builtins/bfind.c create mode 100644 security/nss/lib/ckfw/builtins/binst.c create mode 100644 security/nss/lib/ckfw/builtins/bobject.c create mode 100644 security/nss/lib/ckfw/builtins/bsession.c create mode 100644 security/nss/lib/ckfw/builtins/bslot.c create mode 100644 security/nss/lib/ckfw/builtins/btoken.c create mode 100644 security/nss/lib/ckfw/builtins/builtins.gyp create mode 100644 security/nss/lib/ckfw/builtins/builtins.h create mode 100644 security/nss/lib/ckfw/builtins/certdata.perl create mode 100755 security/nss/lib/ckfw/builtins/certdata.py create mode 100644 security/nss/lib/ckfw/builtins/certdata.txt create mode 100644 security/nss/lib/ckfw/builtins/ckbiver.c create mode 100644 security/nss/lib/ckfw/builtins/constants.c create mode 100644 security/nss/lib/ckfw/builtins/exports.gyp create mode 100644 security/nss/lib/ckfw/builtins/manifest.mn create mode 100644 security/nss/lib/ckfw/builtins/nssckbi.def create mode 100644 security/nss/lib/ckfw/builtins/nssckbi.h create mode 100644 security/nss/lib/ckfw/builtins/nssckbi.rc create mode 100644 security/nss/lib/ckfw/builtins/testlib/Makefile create mode 100644 security/nss/lib/ckfw/builtins/testlib/builtins-testlib.gyp create mode 100644 security/nss/lib/ckfw/builtins/testlib/certdata-testlib.txt create mode 100644 security/nss/lib/ckfw/builtins/testlib/manifest.mn create mode 100644 security/nss/lib/ckfw/builtins/testlib/nssckbi-testlib.def create mode 100644 security/nss/lib/ckfw/builtins/testlib/nssckbi-testlib.rc create mode 100644 security/nss/lib/ckfw/builtins/testlib/testcert_err_distrust.txt create mode 100644 security/nss/lib/ckfw/builtins/testlib/testcert_no_distrust.txt create mode 100644 security/nss/lib/ckfw/builtins/testlib/testcert_ok_distrust.txt create mode 100644 security/nss/lib/ckfw/capi/Makefile create mode 100644 security/nss/lib/ckfw/capi/README create mode 100644 security/nss/lib/ckfw/capi/anchor.c create mode 100644 security/nss/lib/ckfw/capi/cfind.c create mode 100644 security/nss/lib/ckfw/capi/cinst.c create mode 100644 security/nss/lib/ckfw/capi/ckcapi.h create mode 100644 security/nss/lib/ckfw/capi/ckcapiver.c create mode 100644 security/nss/lib/ckfw/capi/cobject.c create mode 100644 security/nss/lib/ckfw/capi/constants.c create mode 100644 security/nss/lib/ckfw/capi/crsa.c create mode 100644 security/nss/lib/ckfw/capi/csession.c create mode 100644 security/nss/lib/ckfw/capi/cslot.c create mode 100644 security/nss/lib/ckfw/capi/ctoken.c create mode 100644 security/nss/lib/ckfw/capi/manifest.mn create mode 100644 security/nss/lib/ckfw/capi/nsscapi.def create mode 100644 security/nss/lib/ckfw/capi/nsscapi.h create mode 100644 security/nss/lib/ckfw/capi/nsscapi.rc create mode 100644 security/nss/lib/ckfw/capi/staticobj.c create mode 100644 security/nss/lib/ckfw/ck.api create mode 100644 security/nss/lib/ckfw/ck.h create mode 100644 security/nss/lib/ckfw/ckapi.perl create mode 100644 security/nss/lib/ckfw/ckfw.gyp create mode 100644 security/nss/lib/ckfw/ckfw.h create mode 100644 security/nss/lib/ckfw/ckfwm.h create mode 100644 security/nss/lib/ckfw/ckfwtm.h create mode 100644 security/nss/lib/ckfw/ckmd.h create mode 100644 security/nss/lib/ckfw/ckt.h create mode 100644 security/nss/lib/ckfw/crypto.c create mode 100644 security/nss/lib/ckfw/dbm/Makefile create mode 100644 security/nss/lib/ckfw/dbm/anchor.c create mode 100644 security/nss/lib/ckfw/dbm/ckdbm.h create mode 100644 security/nss/lib/ckfw/dbm/db.c create mode 100644 security/nss/lib/ckfw/dbm/find.c create mode 100644 security/nss/lib/ckfw/dbm/instance.c create mode 100644 security/nss/lib/ckfw/dbm/manifest.mn create mode 100644 security/nss/lib/ckfw/dbm/object.c create mode 100644 security/nss/lib/ckfw/dbm/session.c create mode 100644 security/nss/lib/ckfw/dbm/slot.c create mode 100644 security/nss/lib/ckfw/dbm/token.c create mode 100644 security/nss/lib/ckfw/exports.gyp create mode 100644 security/nss/lib/ckfw/find.c create mode 100644 security/nss/lib/ckfw/hash.c create mode 100644 security/nss/lib/ckfw/instance.c create mode 100644 security/nss/lib/ckfw/manifest.mn create mode 100644 security/nss/lib/ckfw/mechanism.c create mode 100644 security/nss/lib/ckfw/mutex.c create mode 100644 security/nss/lib/ckfw/nssck.api create mode 100644 security/nss/lib/ckfw/nssckepv.h create mode 100644 security/nss/lib/ckfw/nssckft.h create mode 100644 security/nss/lib/ckfw/nssckfw.h create mode 100644 security/nss/lib/ckfw/nssckfwc.h create mode 100644 security/nss/lib/ckfw/nssckfwt.h create mode 100644 security/nss/lib/ckfw/nssckg.h create mode 100644 security/nss/lib/ckfw/nssckmdt.h create mode 100644 security/nss/lib/ckfw/nssckt.h create mode 100644 security/nss/lib/ckfw/object.c create mode 100644 security/nss/lib/ckfw/session.c create mode 100644 security/nss/lib/ckfw/sessobj.c create mode 100644 security/nss/lib/ckfw/slot.c create mode 100644 security/nss/lib/ckfw/token.c create mode 100644 security/nss/lib/ckfw/wrap.c create mode 100644 security/nss/lib/crmf/Makefile create mode 100644 security/nss/lib/crmf/asn1cmn.c create mode 100644 security/nss/lib/crmf/challcli.c create mode 100644 security/nss/lib/crmf/cmmf.h create mode 100644 security/nss/lib/crmf/cmmfasn1.c create mode 100644 security/nss/lib/crmf/cmmfchal.c create mode 100644 security/nss/lib/crmf/cmmfi.h create mode 100644 security/nss/lib/crmf/cmmfit.h create mode 100644 security/nss/lib/crmf/cmmfrec.c create mode 100644 security/nss/lib/crmf/cmmfresp.c create mode 100644 security/nss/lib/crmf/cmmft.h create mode 100644 security/nss/lib/crmf/crmf.gyp create mode 100644 security/nss/lib/crmf/crmf.h create mode 100644 security/nss/lib/crmf/crmfcont.c create mode 100644 security/nss/lib/crmf/crmfdec.c create mode 100644 security/nss/lib/crmf/crmfenc.c create mode 100644 security/nss/lib/crmf/crmffut.h create mode 100644 security/nss/lib/crmf/crmfget.c create mode 100644 security/nss/lib/crmf/crmfi.h create mode 100644 security/nss/lib/crmf/crmfit.h create mode 100644 security/nss/lib/crmf/crmfpop.c create mode 100644 security/nss/lib/crmf/crmfreq.c create mode 100644 security/nss/lib/crmf/crmft.h create mode 100644 security/nss/lib/crmf/crmftmpl.c create mode 100644 security/nss/lib/crmf/encutil.c create mode 100644 security/nss/lib/crmf/exports.gyp create mode 100644 security/nss/lib/crmf/manifest.mn create mode 100644 security/nss/lib/crmf/respcli.c create mode 100644 security/nss/lib/crmf/respcmn.c create mode 100644 security/nss/lib/crmf/servget.c create mode 100644 security/nss/lib/cryptohi/Makefile create mode 100644 security/nss/lib/cryptohi/cryptohi.gyp create mode 100644 security/nss/lib/cryptohi/cryptohi.h create mode 100644 security/nss/lib/cryptohi/cryptoht.h create mode 100644 security/nss/lib/cryptohi/dsautil.c create mode 100644 security/nss/lib/cryptohi/exports.gyp create mode 100644 security/nss/lib/cryptohi/key.h create mode 100644 security/nss/lib/cryptohi/keyhi.h create mode 100644 security/nss/lib/cryptohi/keyi.h create mode 100644 security/nss/lib/cryptohi/keyt.h create mode 100644 security/nss/lib/cryptohi/keythi.h create mode 100644 security/nss/lib/cryptohi/manifest.mn create mode 100644 security/nss/lib/cryptohi/sechash.c create mode 100644 security/nss/lib/cryptohi/sechash.h create mode 100644 security/nss/lib/cryptohi/seckey.c create mode 100644 security/nss/lib/cryptohi/secsign.c create mode 100644 security/nss/lib/cryptohi/secvfy.c create mode 100644 security/nss/lib/dbm/Makefile create mode 100644 security/nss/lib/dbm/config/config.mk create mode 100644 security/nss/lib/dbm/include/Makefile create mode 100644 security/nss/lib/dbm/include/exports.gyp create mode 100644 security/nss/lib/dbm/include/extern.h create mode 100644 security/nss/lib/dbm/include/hash.h create mode 100644 security/nss/lib/dbm/include/hsearch.h create mode 100644 security/nss/lib/dbm/include/include.gyp create mode 100644 security/nss/lib/dbm/include/manifest.mn create mode 100644 security/nss/lib/dbm/include/mcom_db.h create mode 100644 security/nss/lib/dbm/include/ncompat.h create mode 100644 security/nss/lib/dbm/include/page.h create mode 100644 security/nss/lib/dbm/include/queue.h create mode 100644 security/nss/lib/dbm/include/search.h create mode 100644 security/nss/lib/dbm/include/winfile.h create mode 100644 security/nss/lib/dbm/manifest.mn create mode 100644 security/nss/lib/dbm/src/Makefile create mode 100644 security/nss/lib/dbm/src/config.mk create mode 100644 security/nss/lib/dbm/src/db.c create mode 100644 security/nss/lib/dbm/src/dirent.c create mode 100644 security/nss/lib/dbm/src/dirent.h create mode 100644 security/nss/lib/dbm/src/h_bigkey.c create mode 100644 security/nss/lib/dbm/src/h_func.c create mode 100644 security/nss/lib/dbm/src/h_log2.c create mode 100644 security/nss/lib/dbm/src/h_page.c create mode 100644 security/nss/lib/dbm/src/hash.c create mode 100644 security/nss/lib/dbm/src/hash_buf.c create mode 100644 security/nss/lib/dbm/src/manifest.mn create mode 100644 security/nss/lib/dbm/src/memmove.c create mode 100644 security/nss/lib/dbm/src/mktemp.c create mode 100644 security/nss/lib/dbm/src/snprintf.c create mode 100644 security/nss/lib/dbm/src/src.gyp create mode 100644 security/nss/lib/dbm/src/strerror.c create mode 100644 security/nss/lib/dbm/tests/Makefile create mode 100644 security/nss/lib/dbm/tests/dbmtest.pkg create mode 100644 security/nss/lib/dbm/tests/lots.c create mode 100644 security/nss/lib/dev/Makefile create mode 100644 security/nss/lib/dev/ckhelper.c create mode 100644 security/nss/lib/dev/ckhelper.h create mode 100644 security/nss/lib/dev/dev.gyp create mode 100644 security/nss/lib/dev/dev.h create mode 100644 security/nss/lib/dev/devm.h create mode 100644 security/nss/lib/dev/devslot.c create mode 100644 security/nss/lib/dev/devt.h create mode 100644 security/nss/lib/dev/devtm.h create mode 100644 security/nss/lib/dev/devtoken.c create mode 100644 security/nss/lib/dev/devutil.c create mode 100644 security/nss/lib/dev/exports.gyp create mode 100644 security/nss/lib/dev/manifest.mn create mode 100644 security/nss/lib/dev/nssdev.h create mode 100644 security/nss/lib/dev/nssdevt.h create mode 100644 security/nss/lib/freebl/Makefile create mode 100644 security/nss/lib/freebl/aes-armv8.c create mode 100644 security/nss/lib/freebl/aes-armv8.h create mode 100644 security/nss/lib/freebl/aes-x86.c create mode 100644 security/nss/lib/freebl/aeskeywrap.c create mode 100644 security/nss/lib/freebl/alghmac.c create mode 100644 security/nss/lib/freebl/alghmac.h create mode 100644 security/nss/lib/freebl/altivec-types.h create mode 100644 security/nss/lib/freebl/arcfive.c create mode 100644 security/nss/lib/freebl/arcfour-amd64-gas.s create mode 100644 security/nss/lib/freebl/arcfour-amd64-masm.asm create mode 100644 security/nss/lib/freebl/arcfour-amd64-sun.s create mode 100644 security/nss/lib/freebl/arcfour.c create mode 100644 security/nss/lib/freebl/blake2b.c create mode 100644 security/nss/lib/freebl/blake2b.h create mode 100644 security/nss/lib/freebl/blapi.h create mode 100644 security/nss/lib/freebl/blapii.h create mode 100644 security/nss/lib/freebl/blapit.h create mode 100644 security/nss/lib/freebl/blinit.c create mode 100644 security/nss/lib/freebl/blname.c create mode 100644 security/nss/lib/freebl/camellia.c create mode 100644 security/nss/lib/freebl/camellia.h create mode 100644 security/nss/lib/freebl/chacha20-ppc64le.S create mode 100644 security/nss/lib/freebl/chacha20poly1305-ppc.c create mode 100644 security/nss/lib/freebl/chacha20poly1305.c create mode 100644 security/nss/lib/freebl/chacha20poly1305.h create mode 100644 security/nss/lib/freebl/cmac.c create mode 100644 security/nss/lib/freebl/cmac.h create mode 100644 security/nss/lib/freebl/config.mk create mode 100644 security/nss/lib/freebl/crypto_primitives.c create mode 100644 security/nss/lib/freebl/crypto_primitives.h create mode 100644 security/nss/lib/freebl/ctr.c create mode 100644 security/nss/lib/freebl/ctr.h create mode 100644 security/nss/lib/freebl/cts.c create mode 100644 security/nss/lib/freebl/cts.h create mode 100644 security/nss/lib/freebl/deprecated/alg2268.c create mode 100644 security/nss/lib/freebl/deprecated/seed.c create mode 100644 security/nss/lib/freebl/deprecated/seed.h create mode 100644 security/nss/lib/freebl/des.c create mode 100644 security/nss/lib/freebl/des.h create mode 100644 security/nss/lib/freebl/desblapi.c create mode 100644 security/nss/lib/freebl/det_rng.c create mode 100644 security/nss/lib/freebl/det_rng.h create mode 100644 security/nss/lib/freebl/dh.c create mode 100644 security/nss/lib/freebl/drbg.c create mode 100644 security/nss/lib/freebl/dsa.c create mode 100644 security/nss/lib/freebl/ec.c create mode 100644 security/nss/lib/freebl/ec.h create mode 100644 security/nss/lib/freebl/ecdecode.c create mode 100644 security/nss/lib/freebl/ecl/README create mode 100644 security/nss/lib/freebl/ecl/curve25519_32.c create mode 100644 security/nss/lib/freebl/ecl/curve25519_64.c create mode 100644 security/nss/lib/freebl/ecl/ec_naf.c create mode 100644 security/nss/lib/freebl/ecl/ecl-curve.h create mode 100644 security/nss/lib/freebl/ecl/ecl-exp.h create mode 100644 security/nss/lib/freebl/ecl/ecl-priv.h create mode 100644 security/nss/lib/freebl/ecl/ecl.c create mode 100644 security/nss/lib/freebl/ecl/ecl.h create mode 100644 security/nss/lib/freebl/ecl/ecl_gf.c create mode 100644 security/nss/lib/freebl/ecl/ecl_mult.c create mode 100644 security/nss/lib/freebl/ecl/eclt.h create mode 100644 security/nss/lib/freebl/ecl/ecp.h create mode 100644 security/nss/lib/freebl/ecl/ecp_25519.c create mode 100644 security/nss/lib/freebl/ecl/ecp_256.c create mode 100644 security/nss/lib/freebl/ecl/ecp_256_32.c create mode 100644 security/nss/lib/freebl/ecl/ecp_384.c create mode 100644 security/nss/lib/freebl/ecl/ecp_521.c create mode 100644 security/nss/lib/freebl/ecl/ecp_aff.c create mode 100644 security/nss/lib/freebl/ecl/ecp_jac.c create mode 100644 security/nss/lib/freebl/ecl/ecp_jm.c create mode 100644 security/nss/lib/freebl/ecl/ecp_mont.c create mode 100644 security/nss/lib/freebl/ecl/ecp_secp256r1.c create mode 100644 security/nss/lib/freebl/ecl/ecp_secp384r1.c create mode 100644 security/nss/lib/freebl/ecl/ecp_secp384r1.h create mode 100644 security/nss/lib/freebl/ecl/ecp_secp384r1_wrap.c create mode 100644 security/nss/lib/freebl/ecl/ecp_secp521r1.c create mode 100644 security/nss/lib/freebl/ecl/ecp_secp521r1.h create mode 100644 security/nss/lib/freebl/ecl/ecp_secp521r1_wrap.c create mode 100644 security/nss/lib/freebl/exports.gyp create mode 100644 security/nss/lib/freebl/fipsfreebl.c create mode 100644 security/nss/lib/freebl/freebl.def create mode 100644 security/nss/lib/freebl/freebl.gyp create mode 100644 security/nss/lib/freebl/freebl.rc create mode 100644 security/nss/lib/freebl/freebl_base.gypi create mode 100644 security/nss/lib/freebl/freebl_hash.def create mode 100644 security/nss/lib/freebl/freebl_hash_vector.def create mode 100644 security/nss/lib/freebl/freeblver.c create mode 100644 security/nss/lib/freebl/gcm-aarch64.c create mode 100644 security/nss/lib/freebl/gcm-arm32-neon.c create mode 100644 security/nss/lib/freebl/gcm-ppc.c create mode 100644 security/nss/lib/freebl/gcm-x86.c create mode 100644 security/nss/lib/freebl/gcm.c create mode 100644 security/nss/lib/freebl/gcm.h create mode 100644 security/nss/lib/freebl/genload.c create mode 100644 security/nss/lib/freebl/hmacct.c create mode 100644 security/nss/lib/freebl/hmacct.h create mode 100644 security/nss/lib/freebl/intel-aes-x64-masm.asm create mode 100644 security/nss/lib/freebl/intel-aes-x86-masm.asm create mode 100644 security/nss/lib/freebl/intel-aes.h create mode 100644 security/nss/lib/freebl/intel-aes.s create mode 100644 security/nss/lib/freebl/intel-gcm-wrap.c create mode 100644 security/nss/lib/freebl/intel-gcm-x64-masm.asm create mode 100644 security/nss/lib/freebl/intel-gcm-x86-masm.asm create mode 100644 security/nss/lib/freebl/intel-gcm.h create mode 100644 security/nss/lib/freebl/intel-gcm.s create mode 100644 security/nss/lib/freebl/jpake.c create mode 100644 security/nss/lib/freebl/kyber-pqcrystals-ref.c create mode 100644 security/nss/lib/freebl/kyber-pqcrystals-ref.h create mode 100644 security/nss/lib/freebl/kyber.c create mode 100644 security/nss/lib/freebl/ldvector.c create mode 100644 security/nss/lib/freebl/loader.c create mode 100644 security/nss/lib/freebl/loader.h create mode 100644 security/nss/lib/freebl/lowhash_vector.c create mode 100644 security/nss/lib/freebl/manifest.mn create mode 100644 security/nss/lib/freebl/md2.c create mode 100644 security/nss/lib/freebl/md5.c create mode 100644 security/nss/lib/freebl/mknewpc2.c create mode 100644 security/nss/lib/freebl/mksp.c create mode 100644 security/nss/lib/freebl/mpi/README create mode 100644 security/nss/lib/freebl/mpi/doc/LICENSE create mode 100644 security/nss/lib/freebl/mpi/doc/LICENSE-MPL create mode 100644 security/nss/lib/freebl/mpi/doc/basecvt.pod create mode 100755 security/nss/lib/freebl/mpi/doc/build create mode 100644 security/nss/lib/freebl/mpi/doc/div.txt create mode 100644 security/nss/lib/freebl/mpi/doc/expt.txt create mode 100644 security/nss/lib/freebl/mpi/doc/gcd.pod create mode 100644 security/nss/lib/freebl/mpi/doc/invmod.pod create mode 100644 security/nss/lib/freebl/mpi/doc/isprime.pod create mode 100644 security/nss/lib/freebl/mpi/doc/lap.pod create mode 100644 security/nss/lib/freebl/mpi/doc/mpi-test.pod create mode 100644 security/nss/lib/freebl/mpi/doc/mul.txt create mode 100644 security/nss/lib/freebl/mpi/doc/pi.txt create mode 100644 security/nss/lib/freebl/mpi/doc/prime.txt create mode 100644 security/nss/lib/freebl/mpi/doc/prng.pod create mode 100644 security/nss/lib/freebl/mpi/doc/redux.txt create mode 100644 security/nss/lib/freebl/mpi/doc/sqrt.txt create mode 100644 security/nss/lib/freebl/mpi/doc/square.txt create mode 100644 security/nss/lib/freebl/mpi/doc/timing.txt create mode 100644 security/nss/lib/freebl/mpi/hpma512.s create mode 100644 security/nss/lib/freebl/mpi/hppa20.s create mode 100644 security/nss/lib/freebl/mpi/logtab.h create mode 100644 security/nss/lib/freebl/mpi/montmulf.c create mode 100644 security/nss/lib/freebl/mpi/montmulf.h create mode 100644 security/nss/lib/freebl/mpi/montmulf.il create mode 100644 security/nss/lib/freebl/mpi/montmulf.s create mode 100644 security/nss/lib/freebl/mpi/montmulfv8.il create mode 100644 security/nss/lib/freebl/mpi/montmulfv8.s create mode 100644 security/nss/lib/freebl/mpi/montmulfv9.il create mode 100644 security/nss/lib/freebl/mpi/montmulfv9.s create mode 100644 security/nss/lib/freebl/mpi/mp_comba.c create mode 100644 security/nss/lib/freebl/mpi/mp_comba_amd64_masm.asm create mode 100644 security/nss/lib/freebl/mpi/mp_comba_amd64_sun.s create mode 100644 security/nss/lib/freebl/mpi/mp_gf2m-priv.h create mode 100644 security/nss/lib/freebl/mpi/mp_gf2m.c create mode 100644 security/nss/lib/freebl/mpi/mp_gf2m.h create mode 100644 security/nss/lib/freebl/mpi/mpcpucache.c create mode 100644 security/nss/lib/freebl/mpi/mpcpucache_amd64.s create mode 100644 security/nss/lib/freebl/mpi/mpcpucache_x86.s create mode 100644 security/nss/lib/freebl/mpi/mpi-config.h create mode 100644 security/nss/lib/freebl/mpi/mpi-priv.h create mode 100644 security/nss/lib/freebl/mpi/mpi.c create mode 100644 security/nss/lib/freebl/mpi/mpi.h create mode 100644 security/nss/lib/freebl/mpi/mpi_amd64.c create mode 100644 security/nss/lib/freebl/mpi/mpi_amd64_common.S create mode 100644 security/nss/lib/freebl/mpi/mpi_amd64_masm.asm create mode 100644 security/nss/lib/freebl/mpi/mpi_amd64_sun.s create mode 100644 security/nss/lib/freebl/mpi/mpi_arm.c create mode 100644 security/nss/lib/freebl/mpi/mpi_hp.c create mode 100644 security/nss/lib/freebl/mpi/mpi_i86pc.s create mode 100644 security/nss/lib/freebl/mpi/mpi_mips.s create mode 100644 security/nss/lib/freebl/mpi/mpi_sparc.c create mode 100644 security/nss/lib/freebl/mpi/mpi_sse2.s create mode 100644 security/nss/lib/freebl/mpi/mpi_x86.s create mode 100644 security/nss/lib/freebl/mpi/mpi_x86_asm.c create mode 100644 security/nss/lib/freebl/mpi/mpi_x86_os2.s create mode 100644 security/nss/lib/freebl/mpi/mplogic.c create mode 100644 security/nss/lib/freebl/mpi/mplogic.h create mode 100644 security/nss/lib/freebl/mpi/mpmontg.c create mode 100644 security/nss/lib/freebl/mpi/mpprime.c create mode 100644 security/nss/lib/freebl/mpi/mpprime.h create mode 100644 security/nss/lib/freebl/mpi/mpv_sparc.c create mode 100644 security/nss/lib/freebl/mpi/mpv_sparcv8.s create mode 100644 security/nss/lib/freebl/mpi/mpv_sparcv9.s create mode 100644 security/nss/lib/freebl/mpi/mpvalpha.c create mode 100644 security/nss/lib/freebl/mpi/mulsqr.c create mode 100644 security/nss/lib/freebl/mpi/primes.c create mode 100644 security/nss/lib/freebl/mpi/vis_32.il create mode 100644 security/nss/lib/freebl/mpi/vis_64.il create mode 100644 security/nss/lib/freebl/mpi/vis_proto.h create mode 100644 security/nss/lib/freebl/nsslowhash.c create mode 100644 security/nss/lib/freebl/nsslowhash.h create mode 100644 security/nss/lib/freebl/ppc-crypto.h create mode 100644 security/nss/lib/freebl/ppc-gcm-wrap.c create mode 100644 security/nss/lib/freebl/ppc-gcm.h create mode 100644 security/nss/lib/freebl/ppc-gcm.s create mode 100644 security/nss/lib/freebl/pqg.c create mode 100644 security/nss/lib/freebl/pqg.h create mode 100644 security/nss/lib/freebl/rawhash.c create mode 100644 security/nss/lib/freebl/ret_cr16.s create mode 100644 security/nss/lib/freebl/rijndael.c create mode 100644 security/nss/lib/freebl/rijndael.h create mode 100644 security/nss/lib/freebl/rijndael32.tab create mode 100644 security/nss/lib/freebl/rijndael_tables.c create mode 100644 security/nss/lib/freebl/rsa.c create mode 100644 security/nss/lib/freebl/rsa_blind.c create mode 100644 security/nss/lib/freebl/rsapkcs.c create mode 100644 security/nss/lib/freebl/scripts/LICENSE create mode 100755 security/nss/lib/freebl/scripts/gen.sh create mode 100644 security/nss/lib/freebl/scripts/ppc-xlate.pl create mode 100644 security/nss/lib/freebl/scripts/sha512p8-ppc.pl create mode 100644 security/nss/lib/freebl/secmpi.c create mode 100644 security/nss/lib/freebl/secmpi.h create mode 100644 security/nss/lib/freebl/secrng.h create mode 100644 security/nss/lib/freebl/sha-fast-amd64-sun.s create mode 100644 security/nss/lib/freebl/sha1-armv8.c create mode 100644 security/nss/lib/freebl/sha256-armv8.c create mode 100644 security/nss/lib/freebl/sha256-x86.c create mode 100644 security/nss/lib/freebl/sha256.h create mode 100644 security/nss/lib/freebl/sha3.c create mode 100644 security/nss/lib/freebl/sha512-p8.s create mode 100644 security/nss/lib/freebl/sha512.c create mode 100644 security/nss/lib/freebl/sha_fast.c create mode 100644 security/nss/lib/freebl/sha_fast.h create mode 100644 security/nss/lib/freebl/shake.c create mode 100644 security/nss/lib/freebl/shsign.h create mode 100644 security/nss/lib/freebl/shvfy.c create mode 100644 security/nss/lib/freebl/stubs.c create mode 100644 security/nss/lib/freebl/stubs.h create mode 100644 security/nss/lib/freebl/sysrand.c create mode 100644 security/nss/lib/freebl/tlsprfalg.c create mode 100644 security/nss/lib/freebl/unix_rand.c create mode 100644 security/nss/lib/freebl/unix_urandom.c create mode 100644 security/nss/lib/freebl/verified/Hacl_Chacha20.c create mode 100644 security/nss/lib/freebl/verified/Hacl_Chacha20.h create mode 100644 security/nss/lib/freebl/verified/Hacl_Chacha20Poly1305_128.c create mode 100644 security/nss/lib/freebl/verified/Hacl_Chacha20Poly1305_128.h create mode 100644 security/nss/lib/freebl/verified/Hacl_Chacha20Poly1305_256.c create mode 100644 security/nss/lib/freebl/verified/Hacl_Chacha20Poly1305_256.h create mode 100644 security/nss/lib/freebl/verified/Hacl_Chacha20Poly1305_32.c create mode 100644 security/nss/lib/freebl/verified/Hacl_Chacha20Poly1305_32.h create mode 100644 security/nss/lib/freebl/verified/Hacl_Chacha20_Vec128.c create mode 100644 security/nss/lib/freebl/verified/Hacl_Chacha20_Vec128.h create mode 100644 security/nss/lib/freebl/verified/Hacl_Chacha20_Vec256.c create mode 100644 security/nss/lib/freebl/verified/Hacl_Chacha20_Vec256.h create mode 100644 security/nss/lib/freebl/verified/Hacl_Curve25519_51.c create mode 100644 security/nss/lib/freebl/verified/Hacl_Curve25519_51.h create mode 100644 security/nss/lib/freebl/verified/Hacl_Curve25519_64.c create mode 100644 security/nss/lib/freebl/verified/Hacl_Curve25519_64.h create mode 100644 security/nss/lib/freebl/verified/Hacl_Hash_SHA3.c create mode 100644 security/nss/lib/freebl/verified/Hacl_Hash_SHA3.h create mode 100644 security/nss/lib/freebl/verified/Hacl_IntTypes_Intrinsics.h create mode 100644 security/nss/lib/freebl/verified/Hacl_IntTypes_Intrinsics_128.h create mode 100644 security/nss/lib/freebl/verified/Hacl_Krmllib.h create mode 100644 security/nss/lib/freebl/verified/Hacl_P256.c create mode 100644 security/nss/lib/freebl/verified/Hacl_P256.h create mode 100644 security/nss/lib/freebl/verified/Hacl_P384.c create mode 100644 security/nss/lib/freebl/verified/Hacl_P384.h create mode 100644 security/nss/lib/freebl/verified/Hacl_P521.c create mode 100644 security/nss/lib/freebl/verified/Hacl_P521.h create mode 100644 security/nss/lib/freebl/verified/Hacl_Poly1305_128.c create mode 100644 security/nss/lib/freebl/verified/Hacl_Poly1305_128.h create mode 100644 security/nss/lib/freebl/verified/Hacl_Poly1305_256.c create mode 100644 security/nss/lib/freebl/verified/Hacl_Poly1305_256.h create mode 100644 security/nss/lib/freebl/verified/Hacl_Poly1305_32.c create mode 100644 security/nss/lib/freebl/verified/Hacl_Poly1305_32.h create mode 100644 security/nss/lib/freebl/verified/Hacl_Streaming_Types.h create mode 100644 security/nss/lib/freebl/verified/config.h create mode 100644 security/nss/lib/freebl/verified/curve25519-inline.h create mode 100644 security/nss/lib/freebl/verified/internal/Hacl_Bignum.h create mode 100644 security/nss/lib/freebl/verified/internal/Hacl_Bignum25519_51.h create mode 100644 security/nss/lib/freebl/verified/internal/Hacl_Bignum_Base.h create mode 100644 security/nss/lib/freebl/verified/internal/Hacl_Chacha20.h create mode 100644 security/nss/lib/freebl/verified/internal/Hacl_Curve25519_51.h create mode 100644 security/nss/lib/freebl/verified/internal/Hacl_Hash_SHA3.h create mode 100644 security/nss/lib/freebl/verified/internal/Hacl_Krmllib.h create mode 100644 security/nss/lib/freebl/verified/internal/Hacl_P256.h create mode 100644 security/nss/lib/freebl/verified/internal/Hacl_P256_PrecompTable.h create mode 100644 security/nss/lib/freebl/verified/internal/Hacl_Poly1305_128.h create mode 100644 security/nss/lib/freebl/verified/internal/Hacl_Poly1305_256.h create mode 100644 security/nss/lib/freebl/verified/internal/Hacl_Spec.h create mode 100644 security/nss/lib/freebl/verified/internal/Vale.h create mode 100644 security/nss/lib/freebl/verified/karamel/include/krml/c_endianness.h create mode 100644 security/nss/lib/freebl/verified/karamel/include/krml/fstar_int.h create mode 100644 security/nss/lib/freebl/verified/karamel/include/krml/internal/builtin.h create mode 100644 security/nss/lib/freebl/verified/karamel/include/krml/internal/callconv.h create mode 100644 security/nss/lib/freebl/verified/karamel/include/krml/internal/compat.h create mode 100644 security/nss/lib/freebl/verified/karamel/include/krml/internal/debug.h create mode 100644 security/nss/lib/freebl/verified/karamel/include/krml/internal/target.h create mode 100644 security/nss/lib/freebl/verified/karamel/include/krml/internal/types.h create mode 100644 security/nss/lib/freebl/verified/karamel/include/krml/internal/wasmsupport.h create mode 100644 security/nss/lib/freebl/verified/karamel/include/krml/lowstar_endianness.h create mode 100644 security/nss/lib/freebl/verified/karamel/include/krmllib.h create mode 100644 security/nss/lib/freebl/verified/karamel/krmllib/dist/minimal/FStar_UInt128.h create mode 100644 security/nss/lib/freebl/verified/karamel/krmllib/dist/minimal/FStar_UInt128_Verified.h create mode 100644 security/nss/lib/freebl/verified/karamel/krmllib/dist/minimal/FStar_UInt_8_16_32_64.h create mode 100644 security/nss/lib/freebl/verified/karamel/krmllib/dist/minimal/LowStar_Endianness.h create mode 100644 security/nss/lib/freebl/verified/karamel/krmllib/dist/minimal/Makefile.basic create mode 100644 security/nss/lib/freebl/verified/karamel/krmllib/dist/minimal/Makefile.include create mode 100644 security/nss/lib/freebl/verified/karamel/krmllib/dist/minimal/fstar_uint128_gcc64.h create mode 100644 security/nss/lib/freebl/verified/karamel/krmllib/dist/minimal/fstar_uint128_msvc.h create mode 100644 security/nss/lib/freebl/verified/karamel/krmllib/dist/minimal/fstar_uint128_struct_endianness.h create mode 100644 security/nss/lib/freebl/verified/karamel/krmllib/dist/minimal/libkrmllib.def create mode 100644 security/nss/lib/freebl/verified/lib_intrinsics.h create mode 100644 security/nss/lib/freebl/verified/libintvector.h create mode 100644 security/nss/lib/freebl/win_rand.c create mode 100644 security/nss/lib/jar/Makefile create mode 100644 security/nss/lib/jar/exports.gyp create mode 100644 security/nss/lib/jar/jar-ds.c create mode 100644 security/nss/lib/jar/jar-ds.h create mode 100644 security/nss/lib/jar/jar.c create mode 100644 security/nss/lib/jar/jar.gyp create mode 100644 security/nss/lib/jar/jar.h create mode 100644 security/nss/lib/jar/jarfile.c create mode 100644 security/nss/lib/jar/jarfile.h create mode 100644 security/nss/lib/jar/jarint.c create mode 100644 security/nss/lib/jar/jarint.h create mode 100644 security/nss/lib/jar/jarnav.c create mode 100644 security/nss/lib/jar/jarsign.c create mode 100644 security/nss/lib/jar/jarver.c create mode 100644 security/nss/lib/jar/jzconf.h create mode 100644 security/nss/lib/jar/jzlib.h create mode 100644 security/nss/lib/jar/manifest.mn create mode 100644 security/nss/lib/libpkix/Makefile create mode 100644 security/nss/lib/libpkix/include/Makefile create mode 100644 security/nss/lib/libpkix/include/exports.gyp create mode 100644 security/nss/lib/libpkix/include/include.gyp create mode 100644 security/nss/lib/libpkix/include/manifest.mn create mode 100644 security/nss/lib/libpkix/include/pkix.h create mode 100644 security/nss/lib/libpkix/include/pkix_certsel.h create mode 100644 security/nss/lib/libpkix/include/pkix_certstore.h create mode 100644 security/nss/lib/libpkix/include/pkix_checker.h create mode 100644 security/nss/lib/libpkix/include/pkix_crlsel.h create mode 100644 security/nss/lib/libpkix/include/pkix_errorstrings.h create mode 100644 security/nss/lib/libpkix/include/pkix_params.h create mode 100644 security/nss/lib/libpkix/include/pkix_pl_pki.h create mode 100644 security/nss/lib/libpkix/include/pkix_pl_system.h create mode 100644 security/nss/lib/libpkix/include/pkix_results.h create mode 100644 security/nss/lib/libpkix/include/pkix_revchecker.h create mode 100644 security/nss/lib/libpkix/include/pkix_sample_modules.h create mode 100644 security/nss/lib/libpkix/include/pkix_util.h create mode 100644 security/nss/lib/libpkix/include/pkixt.h create mode 100644 security/nss/lib/libpkix/libpkix.gyp create mode 100644 security/nss/lib/libpkix/manifest.mn create mode 100644 security/nss/lib/libpkix/pkix/Makefile create mode 100644 security/nss/lib/libpkix/pkix/certsel/Makefile create mode 100644 security/nss/lib/libpkix/pkix/certsel/certsel.gyp create mode 100644 security/nss/lib/libpkix/pkix/certsel/exports.gyp create mode 100644 security/nss/lib/libpkix/pkix/certsel/manifest.mn create mode 100644 security/nss/lib/libpkix/pkix/certsel/pkix_certselector.c create mode 100644 security/nss/lib/libpkix/pkix/certsel/pkix_certselector.h create mode 100644 security/nss/lib/libpkix/pkix/certsel/pkix_comcertselparams.c create mode 100644 security/nss/lib/libpkix/pkix/certsel/pkix_comcertselparams.h create mode 100644 security/nss/lib/libpkix/pkix/checker/Makefile create mode 100644 security/nss/lib/libpkix/pkix/checker/checker.gyp create mode 100644 security/nss/lib/libpkix/pkix/checker/exports.gyp create mode 100644 security/nss/lib/libpkix/pkix/checker/manifest.mn create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_basicconstraintschecker.c create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_basicconstraintschecker.h create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_certchainchecker.c create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_certchainchecker.h create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_crlchecker.c create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_crlchecker.h create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_ekuchecker.c create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_ekuchecker.h create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_expirationchecker.c create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_expirationchecker.h create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_namechainingchecker.c create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_namechainingchecker.h create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_nameconstraintschecker.c create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_nameconstraintschecker.h create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_ocspchecker.c create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_ocspchecker.h create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_policychecker.c create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_policychecker.h create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_revocationchecker.c create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_revocationchecker.h create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_revocationmethod.c create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_revocationmethod.h create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_signaturechecker.c create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_signaturechecker.h create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_targetcertchecker.c create mode 100644 security/nss/lib/libpkix/pkix/checker/pkix_targetcertchecker.h create mode 100644 security/nss/lib/libpkix/pkix/crlsel/Makefile create mode 100644 security/nss/lib/libpkix/pkix/crlsel/crlsel.gyp create mode 100644 security/nss/lib/libpkix/pkix/crlsel/exports.gyp create mode 100644 security/nss/lib/libpkix/pkix/crlsel/manifest.mn create mode 100644 security/nss/lib/libpkix/pkix/crlsel/pkix_comcrlselparams.c create mode 100644 security/nss/lib/libpkix/pkix/crlsel/pkix_comcrlselparams.h create mode 100644 security/nss/lib/libpkix/pkix/crlsel/pkix_crlselector.c create mode 100644 security/nss/lib/libpkix/pkix/crlsel/pkix_crlselector.h create mode 100644 security/nss/lib/libpkix/pkix/manifest.mn create mode 100644 security/nss/lib/libpkix/pkix/params/Makefile create mode 100644 security/nss/lib/libpkix/pkix/params/exports.gyp create mode 100644 security/nss/lib/libpkix/pkix/params/manifest.mn create mode 100644 security/nss/lib/libpkix/pkix/params/params.gyp create mode 100644 security/nss/lib/libpkix/pkix/params/pkix_procparams.c create mode 100644 security/nss/lib/libpkix/pkix/params/pkix_procparams.h create mode 100644 security/nss/lib/libpkix/pkix/params/pkix_resourcelimits.c create mode 100644 security/nss/lib/libpkix/pkix/params/pkix_resourcelimits.h create mode 100644 security/nss/lib/libpkix/pkix/params/pkix_trustanchor.c create mode 100644 security/nss/lib/libpkix/pkix/params/pkix_trustanchor.h create mode 100644 security/nss/lib/libpkix/pkix/params/pkix_valparams.c create mode 100644 security/nss/lib/libpkix/pkix/params/pkix_valparams.h create mode 100644 security/nss/lib/libpkix/pkix/results/Makefile create mode 100644 security/nss/lib/libpkix/pkix/results/exports.gyp create mode 100644 security/nss/lib/libpkix/pkix/results/manifest.mn create mode 100644 security/nss/lib/libpkix/pkix/results/pkix_buildresult.c create mode 100644 security/nss/lib/libpkix/pkix/results/pkix_buildresult.h create mode 100644 security/nss/lib/libpkix/pkix/results/pkix_policynode.c create mode 100644 security/nss/lib/libpkix/pkix/results/pkix_policynode.h create mode 100644 security/nss/lib/libpkix/pkix/results/pkix_valresult.c create mode 100644 security/nss/lib/libpkix/pkix/results/pkix_valresult.h create mode 100644 security/nss/lib/libpkix/pkix/results/pkix_verifynode.c create mode 100644 security/nss/lib/libpkix/pkix/results/pkix_verifynode.h create mode 100644 security/nss/lib/libpkix/pkix/results/results.gyp create mode 100644 security/nss/lib/libpkix/pkix/store/Makefile create mode 100644 security/nss/lib/libpkix/pkix/store/exports.gyp create mode 100644 security/nss/lib/libpkix/pkix/store/manifest.mn create mode 100644 security/nss/lib/libpkix/pkix/store/pkix_store.c create mode 100644 security/nss/lib/libpkix/pkix/store/pkix_store.h create mode 100644 security/nss/lib/libpkix/pkix/store/store.gyp create mode 100644 security/nss/lib/libpkix/pkix/top/Makefile create mode 100644 security/nss/lib/libpkix/pkix/top/exports.gyp create mode 100644 security/nss/lib/libpkix/pkix/top/manifest.mn create mode 100644 security/nss/lib/libpkix/pkix/top/pkix_build.c create mode 100644 security/nss/lib/libpkix/pkix/top/pkix_build.h create mode 100644 security/nss/lib/libpkix/pkix/top/pkix_lifecycle.c create mode 100644 security/nss/lib/libpkix/pkix/top/pkix_lifecycle.h create mode 100644 security/nss/lib/libpkix/pkix/top/pkix_validate.c create mode 100644 security/nss/lib/libpkix/pkix/top/pkix_validate.h create mode 100644 security/nss/lib/libpkix/pkix/top/top.gyp create mode 100644 security/nss/lib/libpkix/pkix/util/Makefile create mode 100644 security/nss/lib/libpkix/pkix/util/exports.gyp create mode 100644 security/nss/lib/libpkix/pkix/util/manifest.mn create mode 100644 security/nss/lib/libpkix/pkix/util/pkix_error.c create mode 100644 security/nss/lib/libpkix/pkix/util/pkix_error.h create mode 100644 security/nss/lib/libpkix/pkix/util/pkix_errpaths.c create mode 100644 security/nss/lib/libpkix/pkix/util/pkix_list.c create mode 100644 security/nss/lib/libpkix/pkix/util/pkix_list.h create mode 100644 security/nss/lib/libpkix/pkix/util/pkix_logger.c create mode 100644 security/nss/lib/libpkix/pkix/util/pkix_logger.h create mode 100644 security/nss/lib/libpkix/pkix/util/pkix_tools.c create mode 100644 security/nss/lib/libpkix/pkix/util/pkix_tools.h create mode 100644 security/nss/lib/libpkix/pkix/util/util.gyp create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/Makefile create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/manifest.mn create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/Makefile create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/exports.gyp create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/manifest.mn create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/module.gyp create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_aiamgr.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_aiamgr.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_colcertstore.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_colcertstore.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_httpcertstore.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_httpcertstore.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_httpdefaultclient.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_httpdefaultclient.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_ldapcertstore.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_ldapcertstore.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_ldapdefaultclient.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_ldapdefaultclient.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_ldaprequest.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_ldaprequest.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_ldapresponse.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_ldapresponse.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_ldapt.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_ldaptemplates.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_nsscontext.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_nsscontext.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_pk11certstore.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_pk11certstore.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_socket.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_socket.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/Makefile create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/exports.gyp create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/manifest.mn create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pki.gyp create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_basicconstraints.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_basicconstraints.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_cert.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_cert.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_certpolicyinfo.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_certpolicyinfo.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_certpolicymap.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_certpolicymap.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_certpolicyqualifier.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_certpolicyqualifier.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crl.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crl.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crldp.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crldp.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crlentry.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crlentry.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_date.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_date.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_generalname.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_generalname.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_infoaccess.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_infoaccess.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_nameconstraints.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_nameconstraints.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspcertid.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspcertid.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocsprequest.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocsprequest.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspresponse.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspresponse.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_publickey.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_publickey.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_x500name.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_x500name.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/Makefile create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/exports.gyp create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/manifest.mn create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_bigint.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_bigint.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_bytearray.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_bytearray.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_common.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_common.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_error.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_hashtable.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_hashtable.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_lifecycle.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_lifecycle.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_mem.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_mem.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_monitorlock.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_monitorlock.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_mutex.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_mutex.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_object.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_object.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_oid.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_oid.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_primhash.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_primhash.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_rwlock.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_rwlock.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_string.c create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_string.h create mode 100644 security/nss/lib/libpkix/pkix_pl_nss/system/system.gyp create mode 100644 security/nss/lib/mozpkix/.clang-format create mode 100644 security/nss/lib/mozpkix/exports.gyp create mode 100644 security/nss/lib/mozpkix/include/pkix-test/pkixtestnss.h create mode 100644 security/nss/lib/mozpkix/include/pkix-test/pkixtestutil.h create mode 100644 security/nss/lib/mozpkix/include/pkix/Input.h create mode 100644 security/nss/lib/mozpkix/include/pkix/Result.h create mode 100644 security/nss/lib/mozpkix/include/pkix/Time.h create mode 100644 security/nss/lib/mozpkix/include/pkix/pkix.h create mode 100644 security/nss/lib/mozpkix/include/pkix/pkixc.h create mode 100644 security/nss/lib/mozpkix/include/pkix/pkixcheck.h create mode 100644 security/nss/lib/mozpkix/include/pkix/pkixder.h create mode 100644 security/nss/lib/mozpkix/include/pkix/pkixnss.h create mode 100644 security/nss/lib/mozpkix/include/pkix/pkixtypes.h create mode 100644 security/nss/lib/mozpkix/include/pkix/pkixutil.h create mode 100644 security/nss/lib/mozpkix/lib/pkixbuild.cpp create mode 100644 security/nss/lib/mozpkix/lib/pkixc.cpp create mode 100644 security/nss/lib/mozpkix/lib/pkixcert.cpp create mode 100644 security/nss/lib/mozpkix/lib/pkixcheck.cpp create mode 100644 security/nss/lib/mozpkix/lib/pkixder.cpp create mode 100644 security/nss/lib/mozpkix/lib/pkixnames.cpp create mode 100644 security/nss/lib/mozpkix/lib/pkixnss.cpp create mode 100644 security/nss/lib/mozpkix/lib/pkixocsp.cpp create mode 100644 security/nss/lib/mozpkix/lib/pkixresult.cpp create mode 100644 security/nss/lib/mozpkix/lib/pkixtime.cpp create mode 100644 security/nss/lib/mozpkix/lib/pkixverify.cpp create mode 100644 security/nss/lib/mozpkix/mozpkix.gyp create mode 100644 security/nss/lib/mozpkix/test-lib/pkixtestalg.cpp create mode 100644 security/nss/lib/mozpkix/test-lib/pkixtestnss.cpp create mode 100644 security/nss/lib/mozpkix/test-lib/pkixtestutil.cpp create mode 100644 security/nss/lib/mozpkix/tools/DottedOIDToCode.py create mode 100644 security/nss/lib/nss/Makefile create mode 100644 security/nss/lib/nss/config.mk create mode 100644 security/nss/lib/nss/exports.gyp create mode 100644 security/nss/lib/nss/manifest.mn create mode 100644 security/nss/lib/nss/nss.def create mode 100644 security/nss/lib/nss/nss.gyp create mode 100644 security/nss/lib/nss/nss.h create mode 100644 security/nss/lib/nss/nss.rc create mode 100644 security/nss/lib/nss/nssinit.c create mode 100644 security/nss/lib/nss/nssoptions.c create mode 100644 security/nss/lib/nss/nssoptions.h create mode 100644 security/nss/lib/nss/nssrenam.h create mode 100644 security/nss/lib/nss/nssver.c create mode 100644 security/nss/lib/nss/pkixpriv.def create mode 100644 security/nss/lib/nss/utilwrap.c create mode 100644 security/nss/lib/pk11wrap/Makefile create mode 100644 security/nss/lib/pk11wrap/debug_module.c create mode 100644 security/nss/lib/pk11wrap/dev3hack.c create mode 100644 security/nss/lib/pk11wrap/dev3hack.h create mode 100644 security/nss/lib/pk11wrap/exports.gyp create mode 100644 security/nss/lib/pk11wrap/manifest.mn create mode 100644 security/nss/lib/pk11wrap/pk11akey.c create mode 100644 security/nss/lib/pk11wrap/pk11auth.c create mode 100644 security/nss/lib/pk11wrap/pk11cert.c create mode 100644 security/nss/lib/pk11wrap/pk11cxt.c create mode 100644 security/nss/lib/pk11wrap/pk11err.c create mode 100644 security/nss/lib/pk11wrap/pk11func.h create mode 100644 security/nss/lib/pk11wrap/pk11hpke.c create mode 100644 security/nss/lib/pk11wrap/pk11hpke.h create mode 100644 security/nss/lib/pk11wrap/pk11kea.c create mode 100644 security/nss/lib/pk11wrap/pk11list.c create mode 100644 security/nss/lib/pk11wrap/pk11load.c create mode 100644 security/nss/lib/pk11wrap/pk11mech.c create mode 100644 security/nss/lib/pk11wrap/pk11merge.c create mode 100644 security/nss/lib/pk11wrap/pk11nobj.c create mode 100644 security/nss/lib/pk11wrap/pk11obj.c create mode 100644 security/nss/lib/pk11wrap/pk11pars.c create mode 100644 security/nss/lib/pk11wrap/pk11pbe.c create mode 100644 security/nss/lib/pk11wrap/pk11pk12.c create mode 100644 security/nss/lib/pk11wrap/pk11pqg.c create mode 100644 security/nss/lib/pk11wrap/pk11pqg.h create mode 100644 security/nss/lib/pk11wrap/pk11priv.h create mode 100644 security/nss/lib/pk11wrap/pk11pub.h create mode 100644 security/nss/lib/pk11wrap/pk11sdr.c create mode 100644 security/nss/lib/pk11wrap/pk11sdr.h create mode 100644 security/nss/lib/pk11wrap/pk11skey.c create mode 100644 security/nss/lib/pk11wrap/pk11slot.c create mode 100644 security/nss/lib/pk11wrap/pk11util.c create mode 100644 security/nss/lib/pk11wrap/pk11wrap.gyp create mode 100644 security/nss/lib/pk11wrap/secmod.h create mode 100644 security/nss/lib/pk11wrap/secmodi.h create mode 100644 security/nss/lib/pk11wrap/secmodt.h create mode 100644 security/nss/lib/pk11wrap/secmodti.h create mode 100644 security/nss/lib/pk11wrap/secpkcs5.h create mode 100644 security/nss/lib/pkcs12/Makefile create mode 100644 security/nss/lib/pkcs12/exports.gyp create mode 100644 security/nss/lib/pkcs12/manifest.mn create mode 100644 security/nss/lib/pkcs12/p12.h create mode 100644 security/nss/lib/pkcs12/p12creat.c create mode 100644 security/nss/lib/pkcs12/p12d.c create mode 100644 security/nss/lib/pkcs12/p12dec.c create mode 100644 security/nss/lib/pkcs12/p12e.c create mode 100644 security/nss/lib/pkcs12/p12exp.c create mode 100644 security/nss/lib/pkcs12/p12local.c create mode 100644 security/nss/lib/pkcs12/p12local.h create mode 100644 security/nss/lib/pkcs12/p12plcy.c create mode 100644 security/nss/lib/pkcs12/p12plcy.h create mode 100644 security/nss/lib/pkcs12/p12t.h create mode 100644 security/nss/lib/pkcs12/p12tmpl.c create mode 100644 security/nss/lib/pkcs12/pkcs12.gyp create mode 100644 security/nss/lib/pkcs12/pkcs12.h create mode 100644 security/nss/lib/pkcs12/pkcs12t.h create mode 100644 security/nss/lib/pkcs7/Makefile create mode 100644 security/nss/lib/pkcs7/certread.c create mode 100644 security/nss/lib/pkcs7/exports.gyp create mode 100644 security/nss/lib/pkcs7/manifest.mn create mode 100644 security/nss/lib/pkcs7/p7common.c create mode 100644 security/nss/lib/pkcs7/p7create.c create mode 100644 security/nss/lib/pkcs7/p7decode.c create mode 100644 security/nss/lib/pkcs7/p7encode.c create mode 100644 security/nss/lib/pkcs7/p7local.c create mode 100644 security/nss/lib/pkcs7/p7local.h create mode 100644 security/nss/lib/pkcs7/pkcs7.gyp create mode 100644 security/nss/lib/pkcs7/pkcs7t.h create mode 100644 security/nss/lib/pkcs7/secmime.c create mode 100644 security/nss/lib/pkcs7/secmime.h create mode 100644 security/nss/lib/pkcs7/secpkcs7.h create mode 100644 security/nss/lib/pki/Makefile create mode 100644 security/nss/lib/pki/asymmkey.c create mode 100644 security/nss/lib/pki/certdecode.c create mode 100644 security/nss/lib/pki/certificate.c create mode 100644 security/nss/lib/pki/cryptocontext.c create mode 100644 security/nss/lib/pki/doc/standiag.png create mode 100644 security/nss/lib/pki/doc/standoc.html create mode 100644 security/nss/lib/pki/exports.gyp create mode 100644 security/nss/lib/pki/manifest.mn create mode 100644 security/nss/lib/pki/nsspki.h create mode 100644 security/nss/lib/pki/nsspkit.h create mode 100644 security/nss/lib/pki/pki.gyp create mode 100644 security/nss/lib/pki/pki.h create mode 100644 security/nss/lib/pki/pki3hack.c create mode 100644 security/nss/lib/pki/pki3hack.h create mode 100644 security/nss/lib/pki/pkibase.c create mode 100644 security/nss/lib/pki/pkim.h create mode 100644 security/nss/lib/pki/pkistore.c create mode 100644 security/nss/lib/pki/pkistore.h create mode 100644 security/nss/lib/pki/pkit.h create mode 100644 security/nss/lib/pki/pkitm.h create mode 100644 security/nss/lib/pki/symmkey.c create mode 100644 security/nss/lib/pki/tdcache.c create mode 100644 security/nss/lib/pki/trustdomain.c create mode 100644 security/nss/lib/smime/Makefile create mode 100644 security/nss/lib/smime/cms.h create mode 100644 security/nss/lib/smime/cmsarray.c create mode 100644 security/nss/lib/smime/cmsasn1.c create mode 100644 security/nss/lib/smime/cmsattr.c create mode 100644 security/nss/lib/smime/cmscinfo.c create mode 100644 security/nss/lib/smime/cmscipher.c create mode 100644 security/nss/lib/smime/cmsdecode.c create mode 100644 security/nss/lib/smime/cmsdigdata.c create mode 100644 security/nss/lib/smime/cmsdigest.c create mode 100644 security/nss/lib/smime/cmsencdata.c create mode 100644 security/nss/lib/smime/cmsencode.c create mode 100644 security/nss/lib/smime/cmsenvdata.c create mode 100644 security/nss/lib/smime/cmslocal.h create mode 100644 security/nss/lib/smime/cmsmessage.c create mode 100644 security/nss/lib/smime/cmspubkey.c create mode 100644 security/nss/lib/smime/cmsrecinfo.c create mode 100644 security/nss/lib/smime/cmsreclist.c create mode 100644 security/nss/lib/smime/cmsreclist.h create mode 100644 security/nss/lib/smime/cmssigdata.c create mode 100644 security/nss/lib/smime/cmssiginfo.c create mode 100644 security/nss/lib/smime/cmst.h create mode 100644 security/nss/lib/smime/cmsudf.c create mode 100644 security/nss/lib/smime/cmsutil.c create mode 100644 security/nss/lib/smime/config.mk create mode 100644 security/nss/lib/smime/exports.gyp create mode 100644 security/nss/lib/smime/manifest.mn create mode 100644 security/nss/lib/smime/smime.def create mode 100644 security/nss/lib/smime/smime.gyp create mode 100644 security/nss/lib/smime/smime.h create mode 100644 security/nss/lib/smime/smime.rc create mode 100644 security/nss/lib/smime/smimemessage.c create mode 100644 security/nss/lib/smime/smimesym.c create mode 100644 security/nss/lib/smime/smimeutil.c create mode 100644 security/nss/lib/smime/smimever.c create mode 100644 security/nss/lib/softoken/Makefile create mode 100644 security/nss/lib/softoken/config.mk create mode 100644 security/nss/lib/softoken/exports.gyp create mode 100644 security/nss/lib/softoken/fips_algorithms.h create mode 100644 security/nss/lib/softoken/fipsaudt.c create mode 100644 security/nss/lib/softoken/fipstest.c create mode 100644 security/nss/lib/softoken/fipstokn.c create mode 100644 security/nss/lib/softoken/jpakesftk.c create mode 100644 security/nss/lib/softoken/kbkdf.c create mode 100644 security/nss/lib/softoken/kem.c create mode 100644 security/nss/lib/softoken/kem.h create mode 100644 security/nss/lib/softoken/legacydb/Makefile create mode 100644 security/nss/lib/softoken/legacydb/cdbhdl.h create mode 100644 security/nss/lib/softoken/legacydb/config.mk create mode 100644 security/nss/lib/softoken/legacydb/dbmshim.c create mode 100644 security/nss/lib/softoken/legacydb/keydb.c create mode 100644 security/nss/lib/softoken/legacydb/keydbi.h create mode 100644 security/nss/lib/softoken/legacydb/legacydb.gyp create mode 100644 security/nss/lib/softoken/legacydb/lgattr.c create mode 100644 security/nss/lib/softoken/legacydb/lgcreate.c create mode 100644 security/nss/lib/softoken/legacydb/lgdb.h create mode 100644 security/nss/lib/softoken/legacydb/lgdestroy.c create mode 100644 security/nss/lib/softoken/legacydb/lgfind.c create mode 100644 security/nss/lib/softoken/legacydb/lgfips.c create mode 100644 security/nss/lib/softoken/legacydb/lginit.c create mode 100644 security/nss/lib/softoken/legacydb/lgutil.c create mode 100644 security/nss/lib/softoken/legacydb/lowcert.c create mode 100644 security/nss/lib/softoken/legacydb/lowkey.c create mode 100644 security/nss/lib/softoken/legacydb/lowkeyi.h create mode 100644 security/nss/lib/softoken/legacydb/lowkeyti.h create mode 100644 security/nss/lib/softoken/legacydb/manifest.mn create mode 100644 security/nss/lib/softoken/legacydb/nssdbm.def create mode 100644 security/nss/lib/softoken/legacydb/nssdbm.rc create mode 100644 security/nss/lib/softoken/legacydb/pcert.h create mode 100644 security/nss/lib/softoken/legacydb/pcertdb.c create mode 100644 security/nss/lib/softoken/legacydb/pcertt.h create mode 100644 security/nss/lib/softoken/legacydb/pk11db.c create mode 100644 security/nss/lib/softoken/lgglue.c create mode 100644 security/nss/lib/softoken/lgglue.h create mode 100644 security/nss/lib/softoken/lowkey.c create mode 100644 security/nss/lib/softoken/lowkeyi.h create mode 100644 security/nss/lib/softoken/lowkeyti.h create mode 100644 security/nss/lib/softoken/lowpbe.c create mode 100644 security/nss/lib/softoken/lowpbe.h create mode 100644 security/nss/lib/softoken/manifest.mn create mode 100644 security/nss/lib/softoken/padbuf.c create mode 100644 security/nss/lib/softoken/pkcs11.c create mode 100644 security/nss/lib/softoken/pkcs11c.c create mode 100644 security/nss/lib/softoken/pkcs11i.h create mode 100644 security/nss/lib/softoken/pkcs11ni.h create mode 100644 security/nss/lib/softoken/pkcs11u.c create mode 100644 security/nss/lib/softoken/sdb.c create mode 100644 security/nss/lib/softoken/sdb.h create mode 100644 security/nss/lib/softoken/sftkdb.c create mode 100644 security/nss/lib/softoken/sftkdb.h create mode 100644 security/nss/lib/softoken/sftkdbt.h create mode 100644 security/nss/lib/softoken/sftkdbti.h create mode 100644 security/nss/lib/softoken/sftkdhverify.c create mode 100644 security/nss/lib/softoken/sftkhmac.c create mode 100644 security/nss/lib/softoken/sftkike.c create mode 100644 security/nss/lib/softoken/sftkmessage.c create mode 100644 security/nss/lib/softoken/sftkpars.c create mode 100644 security/nss/lib/softoken/sftkpwd.c create mode 100644 security/nss/lib/softoken/softkver.c create mode 100644 security/nss/lib/softoken/softkver.h create mode 100644 security/nss/lib/softoken/softoken.gyp create mode 100644 security/nss/lib/softoken/softoken.h create mode 100644 security/nss/lib/softoken/softokn.def create mode 100644 security/nss/lib/softoken/softokn.rc create mode 100644 security/nss/lib/softoken/softoknt.h create mode 100644 security/nss/lib/softoken/tlsprf.c create mode 100644 security/nss/lib/sqlite/Makefile create mode 100644 security/nss/lib/sqlite/README create mode 100644 security/nss/lib/sqlite/config.mk create mode 100644 security/nss/lib/sqlite/exports.gyp create mode 100644 security/nss/lib/sqlite/manifest.mn create mode 100644 security/nss/lib/sqlite/sqlite.def create mode 100644 security/nss/lib/sqlite/sqlite.gyp create mode 100644 security/nss/lib/sqlite/sqlite3.c create mode 100644 security/nss/lib/sqlite/sqlite3.h create mode 100644 security/nss/lib/ssl/Makefile create mode 100644 security/nss/lib/ssl/SSLerrs.h create mode 100644 security/nss/lib/ssl/authcert.c create mode 100644 security/nss/lib/ssl/cmpcert.c create mode 100644 security/nss/lib/ssl/config.mk create mode 100644 security/nss/lib/ssl/dhe-param.c create mode 100644 security/nss/lib/ssl/dtls13con.c create mode 100644 security/nss/lib/ssl/dtls13con.h create mode 100644 security/nss/lib/ssl/dtlscon.c create mode 100644 security/nss/lib/ssl/dtlscon.h create mode 100644 security/nss/lib/ssl/exports.gyp create mode 100644 security/nss/lib/ssl/manifest.mn create mode 100644 security/nss/lib/ssl/notes.txt create mode 100644 security/nss/lib/ssl/os2_err.c create mode 100644 security/nss/lib/ssl/os2_err.h create mode 100644 security/nss/lib/ssl/preenc.h create mode 100644 security/nss/lib/ssl/prelib.c create mode 100644 security/nss/lib/ssl/selfencrypt.c create mode 100644 security/nss/lib/ssl/selfencrypt.h create mode 100644 security/nss/lib/ssl/ssl.def create mode 100644 security/nss/lib/ssl/ssl.gyp create mode 100644 security/nss/lib/ssl/ssl.h create mode 100644 security/nss/lib/ssl/ssl.rc create mode 100644 security/nss/lib/ssl/ssl3con.c create mode 100644 security/nss/lib/ssl/ssl3ecc.c create mode 100644 security/nss/lib/ssl/ssl3ext.c create mode 100644 security/nss/lib/ssl/ssl3ext.h create mode 100644 security/nss/lib/ssl/ssl3exthandle.c create mode 100644 security/nss/lib/ssl/ssl3exthandle.h create mode 100644 security/nss/lib/ssl/ssl3gthr.c create mode 100644 security/nss/lib/ssl/ssl3prot.h create mode 100644 security/nss/lib/ssl/sslauth.c create mode 100644 security/nss/lib/ssl/sslbloom.c create mode 100644 security/nss/lib/ssl/sslbloom.h create mode 100644 security/nss/lib/ssl/sslcert.c create mode 100644 security/nss/lib/ssl/sslcert.h create mode 100644 security/nss/lib/ssl/sslcon.c create mode 100644 security/nss/lib/ssl/ssldef.c create mode 100644 security/nss/lib/ssl/sslencode.c create mode 100644 security/nss/lib/ssl/sslencode.h create mode 100644 security/nss/lib/ssl/sslenum.c create mode 100644 security/nss/lib/ssl/sslerr.c create mode 100644 security/nss/lib/ssl/sslerr.h create mode 100644 security/nss/lib/ssl/sslerrstrs.c create mode 100644 security/nss/lib/ssl/sslexp.h create mode 100644 security/nss/lib/ssl/sslgrp.c create mode 100644 security/nss/lib/ssl/sslimpl.h create mode 100644 security/nss/lib/ssl/sslinfo.c create mode 100644 security/nss/lib/ssl/sslinit.c create mode 100644 security/nss/lib/ssl/sslmutex.c create mode 100644 security/nss/lib/ssl/sslmutex.h create mode 100644 security/nss/lib/ssl/sslnonce.c create mode 100644 security/nss/lib/ssl/sslprimitive.c create mode 100644 security/nss/lib/ssl/sslproto.h create mode 100644 security/nss/lib/ssl/sslreveal.c create mode 100644 security/nss/lib/ssl/sslsecur.c create mode 100644 security/nss/lib/ssl/sslsnce.c create mode 100644 security/nss/lib/ssl/sslsock.c create mode 100644 security/nss/lib/ssl/sslspec.c create mode 100644 security/nss/lib/ssl/sslspec.h create mode 100644 security/nss/lib/ssl/sslt.h create mode 100644 security/nss/lib/ssl/ssltrace.c create mode 100644 security/nss/lib/ssl/sslver.c create mode 100644 security/nss/lib/ssl/tls13con.c create mode 100644 security/nss/lib/ssl/tls13con.h create mode 100644 security/nss/lib/ssl/tls13ech.c create mode 100644 security/nss/lib/ssl/tls13ech.h create mode 100644 security/nss/lib/ssl/tls13echv.c create mode 100644 security/nss/lib/ssl/tls13err.h create mode 100644 security/nss/lib/ssl/tls13exthandle.c create mode 100644 security/nss/lib/ssl/tls13exthandle.h create mode 100644 security/nss/lib/ssl/tls13hashstate.c create mode 100644 security/nss/lib/ssl/tls13hashstate.h create mode 100644 security/nss/lib/ssl/tls13hkdf.c create mode 100644 security/nss/lib/ssl/tls13hkdf.h create mode 100644 security/nss/lib/ssl/tls13psk.c create mode 100644 security/nss/lib/ssl/tls13psk.h create mode 100644 security/nss/lib/ssl/tls13replay.c create mode 100644 security/nss/lib/ssl/tls13subcerts.c create mode 100644 security/nss/lib/ssl/tls13subcerts.h create mode 100644 security/nss/lib/ssl/unix_err.c create mode 100644 security/nss/lib/ssl/unix_err.h create mode 100644 security/nss/lib/ssl/win32err.c create mode 100644 security/nss/lib/ssl/win32err.h create mode 100644 security/nss/lib/sysinit/Makefile create mode 100644 security/nss/lib/sysinit/config.mk create mode 100644 security/nss/lib/sysinit/manifest.mn create mode 100644 security/nss/lib/sysinit/nsssysinit.c create mode 100644 security/nss/lib/sysinit/nsssysinit.def create mode 100644 security/nss/lib/sysinit/sysinit.gyp create mode 100644 security/nss/lib/util/Makefile create mode 100644 security/nss/lib/util/SECerrs.h create mode 100644 security/nss/lib/util/base64.h create mode 100644 security/nss/lib/util/ciferfam.h create mode 100644 security/nss/lib/util/config.mk create mode 100644 security/nss/lib/util/derdec.c create mode 100644 security/nss/lib/util/derenc.c create mode 100644 security/nss/lib/util/dersubr.c create mode 100644 security/nss/lib/util/dertime.c create mode 100644 security/nss/lib/util/eccutil.h create mode 100644 security/nss/lib/util/errstrs.c create mode 100644 security/nss/lib/util/exports.gyp create mode 100644 security/nss/lib/util/hasht.h create mode 100644 security/nss/lib/util/kyber.h create mode 100644 security/nss/lib/util/manifest.mn create mode 100644 security/nss/lib/util/nssb64.h create mode 100644 security/nss/lib/util/nssb64d.c create mode 100644 security/nss/lib/util/nssb64e.c create mode 100644 security/nss/lib/util/nssb64t.h create mode 100644 security/nss/lib/util/nssilckt.h create mode 100644 security/nss/lib/util/nssilock.c create mode 100644 security/nss/lib/util/nssilock.h create mode 100644 security/nss/lib/util/nsslocks.h create mode 100644 security/nss/lib/util/nssrwlk.c create mode 100644 security/nss/lib/util/nssrwlk.h create mode 100644 security/nss/lib/util/nssrwlkt.h create mode 100644 security/nss/lib/util/nssutil.def create mode 100644 security/nss/lib/util/nssutil.h create mode 100644 security/nss/lib/util/nssutil.rc create mode 100644 security/nss/lib/util/oidstring.c create mode 100644 security/nss/lib/util/pkcs11.h create mode 100644 security/nss/lib/util/pkcs11f.h create mode 100644 security/nss/lib/util/pkcs11n.h create mode 100644 security/nss/lib/util/pkcs11p.h create mode 100644 security/nss/lib/util/pkcs11t.h create mode 100644 security/nss/lib/util/pkcs11u.h create mode 100644 security/nss/lib/util/pkcs11uri.c create mode 100644 security/nss/lib/util/pkcs11uri.h create mode 100644 security/nss/lib/util/pkcs1sig.c create mode 100644 security/nss/lib/util/pkcs1sig.h create mode 100644 security/nss/lib/util/portreg.c create mode 100644 security/nss/lib/util/portreg.h create mode 100644 security/nss/lib/util/quickder.c create mode 100644 security/nss/lib/util/secalgid.c create mode 100644 security/nss/lib/util/secasn1.h create mode 100644 security/nss/lib/util/secasn1d.c create mode 100644 security/nss/lib/util/secasn1e.c create mode 100644 security/nss/lib/util/secasn1t.h create mode 100644 security/nss/lib/util/secasn1u.c create mode 100644 security/nss/lib/util/seccomon.h create mode 100644 security/nss/lib/util/secder.h create mode 100644 security/nss/lib/util/secdert.h create mode 100644 security/nss/lib/util/secdig.c create mode 100644 security/nss/lib/util/secdig.h create mode 100644 security/nss/lib/util/secdigt.h create mode 100644 security/nss/lib/util/secerr.h create mode 100644 security/nss/lib/util/secitem.c create mode 100644 security/nss/lib/util/secitem.h create mode 100644 security/nss/lib/util/secload.c create mode 100644 security/nss/lib/util/secoid.c create mode 100644 security/nss/lib/util/secoid.h create mode 100644 security/nss/lib/util/secoidt.h create mode 100644 security/nss/lib/util/secplcy.c create mode 100644 security/nss/lib/util/secplcy.h create mode 100644 security/nss/lib/util/secport.c create mode 100644 security/nss/lib/util/secport.h create mode 100644 security/nss/lib/util/sectime.c create mode 100644 security/nss/lib/util/templates.c create mode 100644 security/nss/lib/util/utf8.c create mode 100644 security/nss/lib/util/util.gyp create mode 100644 security/nss/lib/util/utilmod.c create mode 100644 security/nss/lib/util/utilmodt.h create mode 100644 security/nss/lib/util/utilpars.c create mode 100644 security/nss/lib/util/utilpars.h create mode 100644 security/nss/lib/util/utilparst.h create mode 100644 security/nss/lib/util/utilrename.h create mode 100644 security/nss/lib/util/verref.h create mode 100644 security/nss/lib/zlib/LICENSE create mode 100644 security/nss/lib/zlib/Makefile create mode 100644 security/nss/lib/zlib/README create mode 100644 security/nss/lib/zlib/README.nss create mode 100644 security/nss/lib/zlib/adler32.c create mode 100644 security/nss/lib/zlib/compress.c create mode 100644 security/nss/lib/zlib/crc32.c create mode 100644 security/nss/lib/zlib/crc32.h create mode 100644 security/nss/lib/zlib/deflate.c create mode 100644 security/nss/lib/zlib/deflate.h create mode 100644 security/nss/lib/zlib/exports.gyp create mode 100644 security/nss/lib/zlib/gzclose.c create mode 100644 security/nss/lib/zlib/gzguts.h create mode 100644 security/nss/lib/zlib/gzlib.c create mode 100644 security/nss/lib/zlib/gzread.c create mode 100644 security/nss/lib/zlib/gzwrite.c create mode 100644 security/nss/lib/zlib/infback.c create mode 100644 security/nss/lib/zlib/inffast.c create mode 100644 security/nss/lib/zlib/inffast.h create mode 100644 security/nss/lib/zlib/inffixed.h create mode 100644 security/nss/lib/zlib/inflate.c create mode 100644 security/nss/lib/zlib/inflate.h create mode 100644 security/nss/lib/zlib/inftrees.c create mode 100644 security/nss/lib/zlib/inftrees.h create mode 100644 security/nss/lib/zlib/manifest.mn create mode 100644 security/nss/lib/zlib/patches/msvc-vsnprintf.patch create mode 100644 security/nss/lib/zlib/patches/prune-zlib.sh create mode 100644 security/nss/lib/zlib/trees.c create mode 100644 security/nss/lib/zlib/trees.h create mode 100644 security/nss/lib/zlib/uncompr.c create mode 100755 security/nss/lib/zlib/vendor.sh create mode 100644 security/nss/lib/zlib/zconf.h create mode 100644 security/nss/lib/zlib/zlib.gyp create mode 100644 security/nss/lib/zlib/zlib.h create mode 100644 security/nss/lib/zlib/zutil.c create mode 100644 security/nss/lib/zlib/zutil.h (limited to 'security/nss/lib') diff --git a/security/nss/lib/Makefile b/security/nss/lib/Makefile new file mode 100644 index 0000000000..6e26e32410 --- /dev/null +++ b/security/nss/lib/Makefile @@ -0,0 +1,109 @@ +#! gmake +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +CORE_DEPTH = .. +DEPTH = .. + +include $(CORE_DEPTH)/coreconf/config.mk + +# This "jungle" of conditionals evaluates which DIRS to build! +# The main reason for it is AFAIK /automation/taskcluster/scripts/split.sh +# +# The fundamental organisation is: +# if NSS_BUILD_UTIL_ONLY +# else +# if NSS_BUILD_SOFTOKEN_ONLY +# else # build "all" the rest of NSS +# endif +# +# But there is even some build (NSS_BUILD_WITHOUT_SOFTOKEN), which +# depends on a previous partial build! +# +# FWIW, the dependencies are sorted and "compacted" instead of single +# lines for smaller patches, as these probably won't change much and +# it's easier to read this way. + +# Building softoken (and freebl) only requires that the paths +# to the locations where the util headers and libraries were +# previously installed by a prior util-only build - likely in +# in a system location that varies with the distribution. This +# cannot be addressed here and requires that downstream package +# mantainers add suitable modifications. Building full nss will +# not have that problem as everything is available. + +ifndef USE_SYSTEM_ZLIB +ZLIB_SRCDIR = zlib +endif + +ifndef MOZILLA_CLIENT +ifndef NSS_USE_SYSTEM_SQLITE +SQLITE_SRCDIR = sqlite +endif + +ifeq ($(OS_ARCH),Linux) +SYSINIT_SRCDIR = sysinit +endif +endif # MOZILLA_CLIENT + +ifndef NSS_DISABLE_DBM +DBM_SRCDIR = dbm +endif + +ifndef NSS_DISABLE_LIBPKIX +LIBPKIX_SRCDIR = libpkix +endif + +ifeq ($(NSS_BUILD_UTIL_ONLY),1) + UTIL_SRCDIR = util + +else + ifeq ($(NSS_BUILD_SOFTOKEN_ONLY),1) + FREEBL_SRCDIR = freebl + SOFTOKEN_SRCDIR = softoken + else + # Not included when building nss without softoken + # This build type uses the build results of the prior + # NSS_BUILD_UTIL_ONLY and NSS_BUILD_SOFTOKEN_ONLY builds + ifneq ($(NSS_BUILD_WITHOUT_SOFTOKEN),1) + FREEBL_SRCDIR = freebl + SOFTOKEN_SRCDIR = softoken + UTIL_SRCDIR = util + endif + endif + + SOFTOKEN_SRCDIRS = \ + $(DBM_SRCDIR) $(FREEBL_SRCDIR) $(SOFTOKEN_SRCDIR) \ + $(SQLITE_SRCDIR) $(NULL) + + ifneq (,$(FREEBL_SRCDIR)) + $(FREEBL_SRCDIR): $(UTIL_SRCDIR) + endif + + ifneq (,$(SOFTOKEN_SRCDIR)) + $(SOFTOKEN_SRCDIR): $(DBM_SRCDIR) $(FREEBL_SRCDIR) $(SQLITE_SRCDIR) $(UTIL_SRCDIR) + endif + + ifndef NSS_BUILD_SOFTOKEN_ONLY + NSS_SRCDIRS = \ + base certdb certhigh ckfw crmf cryptohi dev jar $(LIBPKIX_SRCDIR) \ + nss pk11wrap pkcs12 pkcs7 pki smime ssl $(SYSINIT_SRCDIR) $(ZLIB_SRCDIR) + + ifneq (,$(SYSINIT_SRCDIR)) + $(SYSINIT_SRCDIR): $(UTIL_SRCDIR) + endif + + nss: base certdb certhigh cryptohi dev $(LIBPKIX_SRCDIR) pk11wrap pki $(UTIL_SRCDIR) + smime: nss pkcs7 pkcs12 + ssl: nss + ckfw: nss + + endif # !NSS_BUILD_SOFTOKEN_ONLY +endif # !NSS_BUILD_UTIL_ONLY + +DIRS = \ + $(NSS_SRCDIRS) $(SOFTOKEN_SRCDIRS) $(UTIL_SRCDIR) $(NULL) + +include $(CORE_DEPTH)/coreconf/rules.mk diff --git a/security/nss/lib/base/Makefile b/security/nss/lib/base/Makefile new file mode 100644 index 0000000000..3f49eaa45d --- /dev/null +++ b/security/nss/lib/base/Makefile @@ -0,0 +1,14 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +include manifest.mn + +include $(CORE_DEPTH)/coreconf/config.mk + +ifdef BUILD_IDG +DEFINES += -DNSSDEBUG +endif + +include $(CORE_DEPTH)/coreconf/rules.mk diff --git a/security/nss/lib/base/arena.c b/security/nss/lib/base/arena.c new file mode 100644 index 0000000000..b8e64643dd --- /dev/null +++ b/security/nss/lib/base/arena.c @@ -0,0 +1,1143 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * arena.c + * + * This contains the implementation of NSS's thread-safe arenas. + */ + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +#ifdef ARENA_THREADMARK +#include "prthread.h" +#endif /* ARENA_THREADMARK */ + +#include "prlock.h" +#include "plarena.h" + +#include + +/* + * NSSArena + * + * This is based on NSPR's arena code, but it is threadsafe. + * + * The public methods relating to this type are: + * + * NSSArena_Create -- constructor + * NSSArena_Destroy + * NSS_ZAlloc + * NSS_ZRealloc + * NSS_ZFreeIf + * + * The nonpublic methods relating to this type are: + * + * nssArena_Create -- constructor + * nssArena_Destroy + * nssArena_Mark + * nssArena_Release + * nssArena_Unmark + * + * nss_ZAlloc + * nss_ZFreeIf + * nss_ZRealloc + * + * In debug builds, the following calls are available: + * + * nssArena_verifyPointer + * nssArena_registerDestructor + * nssArena_deregisterDestructor + */ + +struct NSSArenaStr { + PLArenaPool pool; + PRLock *lock; +#ifdef ARENA_THREADMARK + PRThread *marking_thread; + nssArenaMark *first_mark; + nssArenaMark *last_mark; +#endif /* ARENA_THREADMARK */ +#ifdef ARENA_DESTRUCTOR_LIST + struct arena_destructor_node *first_destructor; + struct arena_destructor_node *last_destructor; +#endif /* ARENA_DESTRUCTOR_LIST */ +}; + +/* + * nssArenaMark + * + * This type is used to mark the current state of an NSSArena. + */ + +struct nssArenaMarkStr { + PRUint32 magic; + void *mark; +#ifdef ARENA_THREADMARK + nssArenaMark *next; +#endif /* ARENA_THREADMARK */ +#ifdef ARENA_DESTRUCTOR_LIST + struct arena_destructor_node *next_destructor; + struct arena_destructor_node *prev_destructor; +#endif /* ARENA_DESTRUCTOR_LIST */ +}; + +#define MARK_MAGIC 0x4d41524b /* "MARK" how original */ + +/* + * But first, the pointer-tracking code + */ +#ifdef DEBUG +extern const NSSError NSS_ERROR_INTERNAL_ERROR; + +static nssPointerTracker arena_pointer_tracker; + +static PRStatus +arena_add_pointer(const NSSArena *arena) +{ + PRStatus rv; + + rv = nssPointerTracker_initialize(&arena_pointer_tracker); + if (PR_SUCCESS != rv) { + return rv; + } + + rv = nssPointerTracker_add(&arena_pointer_tracker, arena); + if (PR_SUCCESS != rv) { + NSSError e = NSS_GetError(); + if (NSS_ERROR_NO_MEMORY != e) { + nss_SetError(NSS_ERROR_INTERNAL_ERROR); + } + + return rv; + } + + return PR_SUCCESS; +} + +static PRStatus +arena_remove_pointer(const NSSArena *arena) +{ + PRStatus rv; + + rv = nssPointerTracker_remove(&arena_pointer_tracker, arena); + if (PR_SUCCESS != rv) { + nss_SetError(NSS_ERROR_INTERNAL_ERROR); + } + + return rv; +} + +/* + * nssArena_verifyPointer + * + * This method is only present in debug builds. + * + * If the specified pointer is a valid pointer to an NSSArena object, + * this routine will return PR_SUCCESS. Otherwise, it will put an + * error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * + * Return value: + * PR_SUCCESS if the pointer is valid + * PR_FAILURE if it isn't + */ + +NSS_IMPLEMENT PRStatus +nssArena_verifyPointer(const NSSArena *arena) +{ + PRStatus rv; + + rv = nssPointerTracker_initialize(&arena_pointer_tracker); + if (PR_SUCCESS != rv) { + /* + * This is a little disingenious. We have to initialize the + * tracker, because someone could "legitimately" try to verify + * an arena pointer before one is ever created. And this step + * might fail, due to lack of memory. But the only way that + * this step can fail is if it's doing the call_once stuff, + * (later calls just no-op). And if it didn't no-op, there + * aren't any valid arenas.. so the argument certainly isn't one. + */ + nss_SetError(NSS_ERROR_INVALID_ARENA); + return PR_FAILURE; + } + + rv = nssPointerTracker_verify(&arena_pointer_tracker, arena); + if (PR_SUCCESS != rv) { + nss_SetError(NSS_ERROR_INVALID_ARENA); + return PR_FAILURE; + } + + return PR_SUCCESS; +} +#endif /* DEBUG */ + +#ifdef ARENA_DESTRUCTOR_LIST + +struct arena_destructor_node { + struct arena_destructor_node *next; + struct arena_destructor_node *prev; + void (*destructor)(void *argument); + void *arg; +}; + +/* + * nssArena_registerDestructor + * + * This routine stores a pointer to a callback and an arbitrary + * pointer-sized argument in the arena, at the current point in + * the mark stack. If the arena is destroyed, or an "earlier" + * mark is released, then this destructor will be called at that + * time. Note that the destructor will be called with the arena + * locked, which means the destructor may free memory in that + * arena, but it may not allocate or cause to be allocated any + * memory. This callback facility was included to support our + * debug-version pointer-tracker feature; overuse runs counter to + * the the original intent of arenas. This routine returns a + * PRStatus value; if successful, it will return PR_SUCCESS. If + * unsuccessful, it will set an error on the error stack and + * return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_IMPLEMENT PRStatus +nssArena_registerDestructor(NSSArena *arena, void (*destructor)(void *argument), + void *arg) +{ + struct arena_destructor_node *it; + +#ifdef NSSDEBUG + if (PR_SUCCESS != nssArena_verifyPointer(arena)) { + return PR_FAILURE; + } +#endif /* NSSDEBUG */ + + it = nss_ZNEW(arena, struct arena_destructor_node); + if ((struct arena_destructor_node *)NULL == it) { + return PR_FAILURE; + } + + it->prev = arena->last_destructor; + arena->last_destructor->next = it; + arena->last_destructor = it; + it->destructor = destructor; + it->arg = arg; + + if ((nssArenaMark *)NULL != arena->last_mark) { + arena->last_mark->prev_destructor = it->prev; + arena->last_mark->next_destructor = it->next; + } + + return PR_SUCCESS; +} + +NSS_IMPLEMENT PRStatus +nssArena_deregisterDestructor(NSSArena *arena, + void (*destructor)(void *argument), void *arg) +{ + struct arena_destructor_node *it; + +#ifdef NSSDEBUG + if (PR_SUCCESS != nssArena_verifyPointer(arena)) { + return PR_FAILURE; + } +#endif /* NSSDEBUG */ + + for (it = arena->first_destructor; it; it = it->next) { + if ((it->destructor == destructor) && (it->arg == arg)) { + break; + } + } + + if ((struct arena_destructor_node *)NULL == it) { + nss_SetError(NSS_ERROR_NOT_FOUND); + return PR_FAILURE; + } + + if (it == arena->first_destructor) { + arena->first_destructor = it->next; + } + + if (it == arena->last_destructor) { + arena->last_destructor = it->prev; + } + + if ((struct arena_destructor_node *)NULL != it->prev) { + it->prev->next = it->next; + } + + if ((struct arena_destructor_node *)NULL != it->next) { + it->next->prev = it->prev; + } + + { + nssArenaMark *m; + for (m = arena->first_mark; m; m = m->next) { + if (m->next_destructor == it) { + m->next_destructor = it->next; + } + if (m->prev_destructor == it) { + m->prev_destructor = it->prev; + } + } + } + + nss_ZFreeIf(it); + return PR_SUCCESS; +} + +static void +nss_arena_call_destructor_chain(struct arena_destructor_node *it) +{ + for (; it; it = it->next) { + (*(it->destructor))(it->arg); + } +} + +#endif /* ARENA_DESTRUCTOR_LIST */ + +/* + * NSSArena_Create + * + * This routine creates a new memory arena. This routine may return + * NULL upon error, in which case it will have created an error stack. + * + * The top-level error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSArena upon success + */ + +NSS_IMPLEMENT NSSArena * +NSSArena_Create(void) +{ + nss_ClearErrorStack(); + return nssArena_Create(); +} + +/* + * nssArena_Create + * + * This routine creates a new memory arena. This routine may return + * NULL upon error, in which case it will have set an error on the + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSArena upon success + */ + +NSS_IMPLEMENT NSSArena * +nssArena_Create(void) +{ + NSSArena *rv = (NSSArena *)NULL; + + rv = nss_ZNEW((NSSArena *)NULL, NSSArena); + if ((NSSArena *)NULL == rv) { + nss_SetError(NSS_ERROR_NO_MEMORY); + return (NSSArena *)NULL; + } + + rv->lock = PR_NewLock(); + if ((PRLock *)NULL == rv->lock) { + (void)nss_ZFreeIf(rv); + nss_SetError(NSS_ERROR_NO_MEMORY); + return (NSSArena *)NULL; + } + + /* + * Arena sizes. The current security code has 229 occurrences of + * PORT_NewArena. The default chunksizes specified break down as + * + * Size Mult. Specified as + * 512 1 512 + * 1024 7 1024 + * 2048 5 2048 + * 2048 5 CRMF_DEFAULT_ARENA_SIZE + * 2048 190 DER_DEFAULT_CHUNKSIZE + * 2048 20 SEC_ASN1_DEFAULT_ARENA_SIZE + * 4096 1 4096 + * + * Obviously this "default chunksize" flexibility isn't very + * useful to us, so I'll just pick 2048. + */ + + PL_InitArenaPool(&rv->pool, "NSS", 2048, sizeof(double)); + +#ifdef DEBUG + { + PRStatus st; + st = arena_add_pointer(rv); + if (PR_SUCCESS != st) { + PL_FinishArenaPool(&rv->pool); + PR_DestroyLock(rv->lock); + (void)nss_ZFreeIf(rv); + return (NSSArena *)NULL; + } + } +#endif /* DEBUG */ + + return rv; +} + +/* + * NSSArena_Destroy + * + * This routine will destroy the specified arena, freeing all memory + * allocated from it. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. If unsuccessful, it will + * create an error stack and return PR_FAILURE. + * + * The top-level error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * + * Return value: + * PR_SUCCESS upon success + * PR_FAILURE upon failure + */ + +NSS_IMPLEMENT PRStatus +NSSArena_Destroy(NSSArena *arena) +{ + nss_ClearErrorStack(); + +#ifdef DEBUG + if (PR_SUCCESS != nssArena_verifyPointer(arena)) { + return PR_FAILURE; + } +#endif /* DEBUG */ + + return nssArena_Destroy(arena); +} + +/* + * nssArena_Destroy + * + * This routine will destroy the specified arena, freeing all memory + * allocated from it. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. If unsuccessful, it will + * set an error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_IMPLEMENT PRStatus +nssArena_Destroy(NSSArena *arena) +{ + PRLock *lock; + +#ifdef NSSDEBUG + if (PR_SUCCESS != nssArena_verifyPointer(arena)) { + return PR_FAILURE; + } +#endif /* NSSDEBUG */ + + if ((PRLock *)NULL == arena->lock) { + /* Just got destroyed */ + nss_SetError(NSS_ERROR_INVALID_ARENA); + return PR_FAILURE; + } + PR_Lock(arena->lock); + +#ifdef DEBUG + if (PR_SUCCESS != arena_remove_pointer(arena)) { + PR_Unlock(arena->lock); + return PR_FAILURE; + } +#endif /* DEBUG */ + +#ifdef ARENA_DESTRUCTOR_LIST + /* Note that the arena is locked at this time */ + nss_arena_call_destructor_chain(arena->first_destructor); +#endif /* ARENA_DESTRUCTOR_LIST */ + + PL_FinishArenaPool(&arena->pool); + lock = arena->lock; + arena->lock = (PRLock *)NULL; + PR_Unlock(lock); + PR_DestroyLock(lock); + (void)nss_ZFreeIf(arena); + return PR_SUCCESS; +} + +static void *nss_zalloc_arena_locked(NSSArena *arena, PRUint32 size); + +/* + * nssArena_Mark + * + * This routine "marks" the current state of an arena. Space + * allocated after the arena has been marked can be freed by + * releasing the arena back to the mark with nssArena_Release, + * or committed by calling nssArena_Unmark. When successful, + * this routine returns a valid nssArenaMark pointer. This + * routine may return NULL upon error, in which case it will + * have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * NULL upon failure + * An nssArenaMark pointer upon success + */ + +NSS_IMPLEMENT nssArenaMark * +nssArena_Mark(NSSArena *arena) +{ + nssArenaMark *rv; + void *p; + +#ifdef NSSDEBUG + if (PR_SUCCESS != nssArena_verifyPointer(arena)) { + return (nssArenaMark *)NULL; + } +#endif /* NSSDEBUG */ + + if ((PRLock *)NULL == arena->lock) { + /* Just got destroyed */ + nss_SetError(NSS_ERROR_INVALID_ARENA); + return (nssArenaMark *)NULL; + } + PR_Lock(arena->lock); + +#ifdef ARENA_THREADMARK + if ((PRThread *)NULL == arena->marking_thread) { + /* Unmarked. Store our thread ID */ + arena->marking_thread = PR_GetCurrentThread(); + /* This call never fails. */ + } else { + /* Marked. Verify it's the current thread */ + if (PR_GetCurrentThread() != arena->marking_thread) { + PR_Unlock(arena->lock); + nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); + return (nssArenaMark *)NULL; + } + } +#endif /* ARENA_THREADMARK */ + + p = PL_ARENA_MARK(&arena->pool); + /* No error possible */ + + /* Do this after the mark */ + rv = (nssArenaMark *)nss_zalloc_arena_locked(arena, sizeof(nssArenaMark)); + if ((nssArenaMark *)NULL == rv) { + PR_Unlock(arena->lock); + nss_SetError(NSS_ERROR_NO_MEMORY); + return (nssArenaMark *)NULL; + } + +#ifdef ARENA_THREADMARK + if ((nssArenaMark *)NULL == arena->first_mark) { + arena->first_mark = rv; + arena->last_mark = rv; + } else { + arena->last_mark->next = rv; + arena->last_mark = rv; + } +#endif /* ARENA_THREADMARK */ + + rv->mark = p; + rv->magic = MARK_MAGIC; + +#ifdef ARENA_DESTRUCTOR_LIST + rv->prev_destructor = arena->last_destructor; +#endif /* ARENA_DESTRUCTOR_LIST */ + + PR_Unlock(arena->lock); + + return rv; +} + +/* + * nss_arena_unmark_release + * + * This static routine implements the routines nssArena_Release + * ans nssArena_Unmark, which are almost identical. + */ + +static PRStatus +nss_arena_unmark_release(NSSArena *arena, nssArenaMark *arenaMark, + PRBool release) +{ + void *inner_mark; + +#ifdef NSSDEBUG + if (PR_SUCCESS != nssArena_verifyPointer(arena)) { + return PR_FAILURE; + } +#endif /* NSSDEBUG */ + + if (MARK_MAGIC != arenaMark->magic) { + nss_SetError(NSS_ERROR_INVALID_ARENA_MARK); + return PR_FAILURE; + } + + if ((PRLock *)NULL == arena->lock) { + /* Just got destroyed */ + nss_SetError(NSS_ERROR_INVALID_ARENA); + return PR_FAILURE; + } + PR_Lock(arena->lock); + +#ifdef ARENA_THREADMARK + if ((PRThread *)NULL != arena->marking_thread) { + if (PR_GetCurrentThread() != arena->marking_thread) { + PR_Unlock(arena->lock); + nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); + return PR_FAILURE; + } + } +#endif /* ARENA_THREADMARK */ + + if (MARK_MAGIC != arenaMark->magic) { + /* Just got released */ + PR_Unlock(arena->lock); + nss_SetError(NSS_ERROR_INVALID_ARENA_MARK); + return PR_FAILURE; + } + + arenaMark->magic = 0; + inner_mark = arenaMark->mark; + +#ifdef ARENA_THREADMARK + { + nssArenaMark **pMark = &arena->first_mark; + nssArenaMark *rest; + nssArenaMark *last = (nssArenaMark *)NULL; + + /* Find this mark */ + while (*pMark != arenaMark) { + last = *pMark; + pMark = &(*pMark)->next; + } + + /* Remember the pointer, then zero it */ + rest = (*pMark)->next; + *pMark = (nssArenaMark *)NULL; + + arena->last_mark = last; + + /* Invalidate any later marks being implicitly released */ + for (; (nssArenaMark *)NULL != rest; rest = rest->next) { + rest->magic = 0; + } + + /* If we just got rid of the first mark, clear the thread ID */ + if ((nssArenaMark *)NULL == arena->first_mark) { + arena->marking_thread = (PRThread *)NULL; + } + } +#endif /* ARENA_THREADMARK */ + + if (release) { +#ifdef ARENA_DESTRUCTOR_LIST + if ((struct arena_destructor_node *)NULL != + arenaMark->prev_destructor) { + arenaMark->prev_destructor->next = + (struct arena_destructor_node *)NULL; + } + arena->last_destructor = arenaMark->prev_destructor; + + /* Note that the arena is locked at this time */ + nss_arena_call_destructor_chain(arenaMark->next_destructor); +#endif /* ARENA_DESTRUCTOR_LIST */ + + PL_ARENA_RELEASE(&arena->pool, inner_mark); + /* No error return */ + } + + PR_Unlock(arena->lock); + return PR_SUCCESS; +} + +/* + * nssArena_Release + * + * This routine invalidates and releases all memory allocated from + * the specified arena after the point at which the specified mark + * was obtained. This routine returns a PRStatus value; if successful, + * it will return PR_SUCCESS. If unsuccessful, it will set an error + * on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_INVALID_ARENA_MARK + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_IMPLEMENT PRStatus +nssArena_Release(NSSArena *arena, nssArenaMark *arenaMark) +{ + return nss_arena_unmark_release(arena, arenaMark, PR_TRUE); +} + +/* + * nssArena_Unmark + * + * This routine "commits" the indicated mark and any marks after + * it, making them unreleasable. Note that any earlier marks can + * still be released, and such a release will invalidate these + * later unmarked regions. If an arena is to be safely shared by + * more than one thread, all marks must be either released or + * unmarked. This routine returns a PRStatus value; if successful, + * it will return PR_SUCCESS. If unsuccessful, it will set an error + * on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_INVALID_ARENA_MARK + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_IMPLEMENT PRStatus +nssArena_Unmark(NSSArena *arena, nssArenaMark *arenaMark) +{ + return nss_arena_unmark_release(arena, arenaMark, PR_FALSE); +} + +/* + * We prefix this header to all allocated blocks. It is a multiple + * of the alignment size. Note that this usage of a header may make + * purify spew bogus warnings about "potentially leaked blocks" of + * memory; if that gets too annoying we can add in a pointer to the + * header in the header itself. There's not a lot of safety here; + * maybe we should add a magic value? + */ +struct pointer_header { + NSSArena *arena; + PRUint32 size; +}; + +static void * +nss_zalloc_arena_locked(NSSArena *arena, PRUint32 size) +{ + void *p; + void *rv; + struct pointer_header *h; + PRUint32 my_size = size + sizeof(struct pointer_header); + PL_ARENA_ALLOCATE(p, &arena->pool, my_size); + if ((void *)NULL == p) { + nss_SetError(NSS_ERROR_NO_MEMORY); + return (void *)NULL; + } + /* + * Do this before we unlock. This way if the user is using + * an arena in one thread while destroying it in another, he'll + * fault/FMR in his code, not ours. + */ + h = (struct pointer_header *)p; + h->arena = arena; + h->size = size; + rv = (void *)((char *)h + sizeof(struct pointer_header)); + (void)nsslibc_memset(rv, 0, size); + return rv; +} + +/* + * NSS_ZAlloc + * + * This routine allocates and zeroes a section of memory of the + * size, and returns to the caller a pointer to that memory. If + * the optional arena argument is non-null, the memory will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in + * which case it will have set an error upon the error stack. The + * value specified for size may be zero; in which case a valid + * zero-length block of memory will be allocated. This block may + * be expanded by calling NSS_ZRealloc. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * NULL upon error + * A pointer to the new segment of zeroed memory + */ + +NSS_IMPLEMENT void * +NSS_ZAlloc(NSSArena *arenaOpt, PRUint32 size) +{ + return nss_ZAlloc(arenaOpt, size); +} + +/* + * nss_ZAlloc + * + * This routine allocates and zeroes a section of memory of the + * size, and returns to the caller a pointer to that memory. If + * the optional arena argument is non-null, the memory will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in + * which case it will have set an error upon the error stack. The + * value specified for size may be zero; in which case a valid + * zero-length block of memory will be allocated. This block may + * be expanded by calling nss_ZRealloc. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * NULL upon error + * A pointer to the new segment of zeroed memory + */ + +NSS_IMPLEMENT void * +nss_ZAlloc(NSSArena *arenaOpt, PRUint32 size) +{ + struct pointer_header *h; + PRUint32 my_size = size + sizeof(struct pointer_header); + + if (my_size < sizeof(struct pointer_header)) { + /* Wrapped */ + nss_SetError(NSS_ERROR_NO_MEMORY); + return (void *)NULL; + } + + if ((NSSArena *)NULL == arenaOpt) { + /* Heap allocation, no locking required. */ + h = (struct pointer_header *)PR_Calloc(1, my_size); + if ((struct pointer_header *)NULL == h) { + nss_SetError(NSS_ERROR_NO_MEMORY); + return (void *)NULL; + } + + h->arena = (NSSArena *)NULL; + h->size = size; + /* We used calloc: it's already zeroed */ + + return (void *)((char *)h + sizeof(struct pointer_header)); + } else { + void *rv; +/* Arena allocation */ +#ifdef NSSDEBUG + if (PR_SUCCESS != nssArena_verifyPointer(arenaOpt)) { + return (void *)NULL; + } +#endif /* NSSDEBUG */ + + if ((PRLock *)NULL == arenaOpt->lock) { + /* Just got destroyed */ + nss_SetError(NSS_ERROR_INVALID_ARENA); + return (void *)NULL; + } + PR_Lock(arenaOpt->lock); + +#ifdef ARENA_THREADMARK + if ((PRThread *)NULL != arenaOpt->marking_thread) { + if (PR_GetCurrentThread() != arenaOpt->marking_thread) { + nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); + PR_Unlock(arenaOpt->lock); + return (void *)NULL; + } + } +#endif /* ARENA_THREADMARK */ + + rv = nss_zalloc_arena_locked(arenaOpt, size); + + PR_Unlock(arenaOpt->lock); + return rv; + } + /*NOTREACHED*/ +} + +/* + * NSS_ZFreeIf + * + * If the specified pointer is non-null, then the region of memory + * to which it points -- which must have been allocated with + * NSS_ZAlloc -- will be zeroed and released. This routine + * returns a PRStatus value; if successful, it will return PR_SUCCESS. + * If unsuccessful, it will set an error on the error stack and return + * PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ +NSS_IMPLEMENT PRStatus +NSS_ZFreeIf(void *pointer) +{ + return nss_ZFreeIf(pointer); +} + +/* + * nss_ZFreeIf + * + * If the specified pointer is non-null, then the region of memory + * to which it points -- which must have been allocated with + * nss_ZAlloc -- will be zeroed and released. This routine + * returns a PRStatus value; if successful, it will return PR_SUCCESS. + * If unsuccessful, it will set an error on the error stack and return + * PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_IMPLEMENT PRStatus +nss_ZFreeIf(void *pointer) +{ + struct pointer_header *h; + + if ((void *)NULL == pointer) { + return PR_SUCCESS; + } + + h = (struct pointer_header *)((char *)pointer - + sizeof(struct pointer_header)); + + /* Check any magic here */ + + if ((NSSArena *)NULL == h->arena) { + /* Heap */ + (void)nsslibc_memset(pointer, 0, h->size); + PR_Free(h); + return PR_SUCCESS; + } else { +/* Arena */ +#ifdef NSSDEBUG + if (PR_SUCCESS != nssArena_verifyPointer(h->arena)) { + return PR_FAILURE; + } +#endif /* NSSDEBUG */ + + if ((PRLock *)NULL == h->arena->lock) { + /* Just got destroyed.. so this pointer is invalid */ + nss_SetError(NSS_ERROR_INVALID_POINTER); + return PR_FAILURE; + } + PR_Lock(h->arena->lock); + + (void)nsslibc_memset(pointer, 0, h->size); + + /* No way to "free" it within an NSPR arena. */ + + PR_Unlock(h->arena->lock); + return PR_SUCCESS; + } + /*NOTREACHED*/ +} + +/* + * NSS_ZRealloc + * + * This routine reallocates a block of memory obtained by calling + * nss_ZAlloc or nss_ZRealloc. The portion of memory + * between the new and old sizes -- which is either being newly + * obtained or released -- is in either case zeroed. This routine + * may return NULL upon failure, in which case it will have placed + * an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * NULL upon error + * A pointer to the replacement segment of memory + */ + +NSS_EXTERN void * +NSS_ZRealloc(void *pointer, PRUint32 newSize) +{ + return nss_ZRealloc(pointer, newSize); +} + +/* + * nss_ZRealloc + * + * This routine reallocates a block of memory obtained by calling + * nss_ZAlloc or nss_ZRealloc. The portion of memory + * between the new and old sizes -- which is either being newly + * obtained or released -- is in either case zeroed. This routine + * may return NULL upon failure, in which case it will have placed + * an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * NULL upon error + * A pointer to the replacement segment of memory + */ + +NSS_EXTERN void * +nss_ZRealloc(void *pointer, PRUint32 newSize) +{ + NSSArena *arena; + struct pointer_header *h, *new_h; + PRUint32 my_newSize = newSize + sizeof(struct pointer_header); + void *rv; + + if (my_newSize < sizeof(struct pointer_header)) { + /* Wrapped */ + nss_SetError(NSS_ERROR_NO_MEMORY); + return (void *)NULL; + } + + if ((void *)NULL == pointer) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (void *)NULL; + } + + h = (struct pointer_header *)((char *)pointer - + sizeof(struct pointer_header)); + + /* Check any magic here */ + + if (newSize == h->size) { + /* saves thrashing */ + return pointer; + } + + arena = h->arena; + if (!arena) { + /* Heap */ + new_h = (struct pointer_header *)PR_Calloc(1, my_newSize); + if ((struct pointer_header *)NULL == new_h) { + nss_SetError(NSS_ERROR_NO_MEMORY); + return (void *)NULL; + } + + new_h->arena = (NSSArena *)NULL; + new_h->size = newSize; + rv = (void *)((char *)new_h + sizeof(struct pointer_header)); + + if (newSize > h->size) { + (void)nsslibc_memcpy(rv, pointer, h->size); + (void)nsslibc_memset(&((char *)rv)[h->size], 0, + (newSize - h->size)); + } else { + (void)nsslibc_memcpy(rv, pointer, newSize); + } + + (void)nsslibc_memset(pointer, 0, h->size); + h->size = 0; + PR_Free(h); + + return rv; + } else { + void *p; +/* Arena */ +#ifdef NSSDEBUG + if (PR_SUCCESS != nssArena_verifyPointer(arena)) { + return (void *)NULL; + } +#endif /* NSSDEBUG */ + + if (!arena->lock) { + /* Just got destroyed.. so this pointer is invalid */ + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (void *)NULL; + } + PR_Lock(arena->lock); + +#ifdef ARENA_THREADMARK + if (arena->marking_thread) { + if (PR_GetCurrentThread() != arena->marking_thread) { + PR_Unlock(arena->lock); + nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); + return (void *)NULL; + } + } +#endif /* ARENA_THREADMARK */ + + if (newSize < h->size) { + /* + * We have no general way of returning memory to the arena + * (mark/release doesn't work because things may have been + * allocated after this object), so the memory is gone + * anyway. We might as well just return the same pointer to + * the user, saying "yeah, uh-hunh, you can only use less of + * it now." We'll zero the leftover part, of course. And + * in fact we might as well *not* adjust h->size-- this way, + * if the user reallocs back up to something not greater than + * the original size, then voila, there's the memory! This + * way a thrash big/small/big/small doesn't burn up the arena. + */ + char *extra = &((char *)pointer)[newSize]; + (void)nsslibc_memset(extra, 0, (h->size - newSize)); + PR_Unlock(arena->lock); + return pointer; + } + + PL_ARENA_ALLOCATE(p, &arena->pool, my_newSize); + if ((void *)NULL == p) { + PR_Unlock(arena->lock); + nss_SetError(NSS_ERROR_NO_MEMORY); + return (void *)NULL; + } + + new_h = (struct pointer_header *)p; + new_h->arena = arena; + new_h->size = newSize; + rv = (void *)((char *)new_h + sizeof(struct pointer_header)); + if (rv != pointer) { + (void)nsslibc_memcpy(rv, pointer, h->size); + (void)nsslibc_memset(pointer, 0, h->size); + } + (void)nsslibc_memset(&((char *)rv)[h->size], 0, (newSize - h->size)); + h->arena = (NSSArena *)NULL; + h->size = 0; + PR_Unlock(arena->lock); + return rv; + } + /*NOTREACHED*/ +} + +PRStatus +nssArena_Shutdown(void) +{ + PRStatus rv = PR_SUCCESS; +#ifdef DEBUG + rv = nssPointerTracker_finalize(&arena_pointer_tracker); +#endif + return rv; +} diff --git a/security/nss/lib/base/base.gyp b/security/nss/lib/base/base.gyp new file mode 100644 index 0000000000..42318c897b --- /dev/null +++ b/security/nss/lib/base/base.gyp @@ -0,0 +1,32 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +{ + 'includes': [ + '../../coreconf/config.gypi' + ], + 'targets': [ + { + 'target_name': 'nssb', + 'type': 'static_library', + 'sources': [ + 'arena.c', + 'error.c', + 'errorval.c', + 'hash.c', + 'hashops.c', + 'item.c', + 'libc.c', + 'list.c', + 'tracker.c', + 'utf8.c' + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_exports' + ] + } + ], + 'variables': { + 'module': 'nss' + } +} \ No newline at end of file diff --git a/security/nss/lib/base/base.h b/security/nss/lib/base/base.h new file mode 100644 index 0000000000..41ab86f6b0 --- /dev/null +++ b/security/nss/lib/base/base.h @@ -0,0 +1,1106 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BASE_H +#define BASE_H + +/* + * base.h + * + * This header file contains basic prototypes and preprocessor + * definitions used throughout nss but not available publicly. + */ + +#ifndef BASET_H +#include "baset.h" +#endif /* BASET_H */ + +#ifndef NSSBASE_H +#include "nssbase.h" +#endif /* NSSBASE_H */ + +#include "plhash.h" + +PR_BEGIN_EXTERN_C + +/* + * NSSArena + * + * The nonpublic methods relating to this type are: + * + * nssArena_Create -- constructor + * nssArena_Destroy + * nssArena_Mark + * nssArena_Release + * nssArena_Unmark + * + * nss_ZAlloc + * nss_ZFreeIf + * nss_ZRealloc + * + * Additionally, there are some preprocessor macros: + * + * nss_ZNEW + * nss_ZNEWARRAY + * + * In debug builds, the following calls are available: + * + * nssArena_verifyPointer + * nssArena_registerDestructor + * nssArena_deregisterDestructor + * + * The following preprocessor macro is also always available: + * + * nssArena_VERIFYPOINTER + * + * A constant PLHashAllocOps structure is available for users + * of the NSPL PLHashTable routines. + * + * nssArenaHashAllocOps + */ + +/* + * nssArena_Create + * + * This routine creates a new memory arena. This routine may return + * NULL upon error, in which case it will have set an error on the + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSArena upon success + */ + +/* + * XXX fgmr + * Arenas can be named upon creation; this is mostly of use when + * debugging. Should we expose that here, allowing an optional + * "const char *name" argument? Should the public version of this + * call (NSSArena_Create) have it too? + */ + +NSS_EXTERN NSSArena *nssArena_Create(void); + +extern const NSSError NSS_ERROR_NO_MEMORY; + +/* + * nssArena_Destroy + * + * This routine will destroy the specified arena, freeing all memory + * allocated from it. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. If unsuccessful, it will + * set an error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_EXTERN PRStatus nssArena_Destroy(NSSArena *arena); + +extern const NSSError NSS_ERROR_INVALID_ARENA; + +/* + * nssArena_Mark + * + * This routine "marks" the current state of an arena. Space + * allocated after the arena has been marked can be freed by + * releasing the arena back to the mark with nssArena_Release, + * or committed by calling nssArena_Unmark. When successful, + * this routine returns a valid nssArenaMark pointer. This + * routine may return NULL upon error, in which case it will + * have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * NULL upon failure + * An nssArenaMark pointer upon success + */ + +NSS_EXTERN nssArenaMark *nssArena_Mark(NSSArena *arena); + +extern const NSSError NSS_ERROR_INVALID_ARENA; +extern const NSSError NSS_ERROR_NO_MEMORY; +extern const NSSError NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD; + +/* + * nssArena_Release + * + * This routine invalidates and releases all memory allocated from + * the specified arena after the point at which the specified mark + * was obtained. This routine returns a PRStatus value; if successful, + * it will return PR_SUCCESS. If unsuccessful, it will set an error + * on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_INVALID_ARENA_MARK + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_EXTERN PRStatus nssArena_Release(NSSArena *arena, nssArenaMark *arenaMark); + +extern const NSSError NSS_ERROR_INVALID_ARENA; +extern const NSSError NSS_ERROR_INVALID_ARENA_MARK; + +/* + * nssArena_Unmark + * + * This routine "commits" the indicated mark and any marks after + * it, making them unreleasable. Note that any earlier marks can + * still be released, and such a release will invalidate these + * later unmarked regions. If an arena is to be safely shared by + * more than one thread, all marks must be either released or + * unmarked. This routine returns a PRStatus value; if successful, + * it will return PR_SUCCESS. If unsuccessful, it will set an error + * on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_INVALID_ARENA_MARK + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_EXTERN PRStatus nssArena_Unmark(NSSArena *arena, nssArenaMark *arenaMark); + +extern const NSSError NSS_ERROR_INVALID_ARENA; +extern const NSSError NSS_ERROR_INVALID_ARENA_MARK; +extern const NSSError NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD; + +#ifdef ARENA_DESTRUCTOR_LIST + +/* + * nssArena_registerDestructor + * + * This routine stores a pointer to a callback and an arbitrary + * pointer-sized argument in the arena, at the current point in + * the mark stack. If the arena is destroyed, or an "earlier" + * mark is released, then this destructor will be called at that + * time. Note that the destructor will be called with the arena + * locked, which means the destructor may free memory in that + * arena, but it may not allocate or cause to be allocated any + * memory. This callback facility was included to support our + * debug-version pointer-tracker feature; overuse runs counter to + * the the original intent of arenas. This routine returns a + * PRStatus value; if successful, it will return PR_SUCCESS. If + * unsuccessful, it will set an error on the error stack and + * return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_EXTERN PRStatus nssArena_registerDestructor( + NSSArena *arena, void (*destructor)(void *argument), void *arg); + +extern const NSSError NSS_ERROR_INVALID_ARENA; +extern const NSSError NSS_ERROR_NO_MEMORY; + +/* + * nssArena_deregisterDestructor + * + * This routine will remove the first destructor in the specified + * arena which has the specified destructor and argument values. + * The destructor will not be called. This routine returns a + * PRStatus value; if successful, it will return PR_SUCCESS. If + * unsuccessful, it will set an error on the error stack and + * return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NOT_FOUND + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_EXTERN PRStatus nssArena_deregisterDestructor( + NSSArena *arena, void (*destructor)(void *argument), void *arg); + +extern const NSSError NSS_ERROR_INVALID_ITEM; +extern const NSSError NSS_ERROR_INVALID_ARENA; +extern const NSSError NSS_ERROR_NOT_FOUND; + +#endif /* ARENA_DESTRUCTOR_LIST */ + +/* + * nss_ZAlloc + * + * This routine allocates and zeroes a section of memory of the + * size, and returns to the caller a pointer to that memory. If + * the optional arena argument is non-null, the memory will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in + * which case it will have set an error upon the error stack. The + * value specified for size may be zero; in which case a valid + * zero-length block of memory will be allocated. This block may + * be expanded by calling nss_ZRealloc. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * NULL upon error + * A pointer to the new segment of zeroed memory + */ + +NSS_EXTERN void *nss_ZAlloc(NSSArena *arenaOpt, PRUint32 size); + +extern const NSSError NSS_ERROR_INVALID_ARENA; +extern const NSSError NSS_ERROR_NO_MEMORY; +extern const NSSError NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD; + +/* + * nss_ZFreeIf + * + * If the specified pointer is non-null, then the region of memory + * to which it points -- which must have been allocated with + * nss_ZAlloc -- will be zeroed and released. This routine + * returns a PRStatus value; if successful, it will return PR_SUCCESS. + * If unsuccessful, it will set an error on the error stack and return + * PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_EXTERN PRStatus nss_ZFreeIf(void *pointer); + +extern const NSSError NSS_ERROR_INVALID_POINTER; + +/* + * nss_ZRealloc + * + * This routine reallocates a block of memory obtained by calling + * nss_ZAlloc or nss_ZRealloc. The portion of memory + * between the new and old sizes -- which is either being newly + * obtained or released -- is in either case zeroed. This routine + * may return NULL upon failure, in which case it will have placed + * an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * NULL upon error + * A pointer to the replacement segment of memory + */ + +NSS_EXTERN void *nss_ZRealloc(void *pointer, PRUint32 newSize); + +extern const NSSError NSS_ERROR_INVALID_POINTER; +extern const NSSError NSS_ERROR_NO_MEMORY; +extern const NSSError NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD; + +/* + * nss_ZNEW + * + * This preprocessor macro will allocate memory for a new object + * of the specified type with nss_ZAlloc, and will cast the + * return value appropriately. If the optional arena argument is + * non-null, the memory will be obtained from that arena; otherwise, + * the memory will be obtained from the heap. This routine may + * return NULL upon error, in which case it will have set an error + * upon the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to the new segment of zeroed memory + */ + +#define nss_ZNEW(arenaOpt, type) ((type *)nss_ZAlloc((arenaOpt), sizeof(type))) + +/* + * nss_ZNEWARRAY + * + * This preprocessor macro will allocate memory for an array of + * new objects, and will cast the return value appropriately. + * If the optional arena argument is non-null, the memory will + * be obtained from that arena; otherwise, the memory will be + * obtained from the heap. This routine may return NULL upon + * error, in which case it will have set an error upon the error + * stack. The array size may be specified as zero. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to the new segment of zeroed memory + */ + +#define nss_ZNEWARRAY(arenaOpt, type, quantity) \ + ((type *)nss_ZAlloc((arenaOpt), sizeof(type) * (quantity))) + +/* + * nss_ZREALLOCARRAY + * + * This preprocessor macro will reallocate memory for an array of + * new objects, and will cast the return value appropriately. + * This routine may return NULL upon error, in which case it will + * have set an error upon the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * NULL upon error + * A pointer to the replacement segment of memory + */ +#define nss_ZREALLOCARRAY(p, type, quantity) \ + ((type *)nss_ZRealloc((p), sizeof(type) * (quantity))) + +/* + * nssArena_verifyPointer + * + * This method is only present in debug builds. + * + * If the specified pointer is a valid pointer to an NSSArena object, + * this routine will return PR_SUCCESS. Otherwise, it will put an + * error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * + * Return value: + * PR_SUCCESS if the pointer is valid + * PR_FAILURE if it isn't + */ + +#ifdef DEBUG +NSS_EXTERN PRStatus nssArena_verifyPointer(const NSSArena *arena); + +extern const NSSError NSS_ERROR_INVALID_ARENA; +#endif /* DEBUG */ + +/* + * nssArena_VERIFYPOINTER + * + * This macro is always available. In debug builds it will call + * nssArena_verifyPointer; in non-debug builds, it will merely + * check that the pointer is not null. Note that in non-debug + * builds it cannot place an error on the error stack. + * + * Return value: + * PR_SUCCESS if the pointer is valid + * PR_FAILURE if it isn't + */ + +#ifdef DEBUG +#define nssArena_VERIFYPOINTER(p) nssArena_verifyPointer(p) +#else /* DEBUG */ + +#define nssArena_VERIFYPOINTER(p) \ + (((NSSArena *)NULL == (p)) ? PR_FAILURE : PR_SUCCESS) +#endif /* DEBUG */ + +/* + * Private function to be called by NSS_Shutdown to cleanup nssArena + * bookkeeping. + */ +extern PRStatus nssArena_Shutdown(void); + +/* + * nssArenaHashAllocOps + * + * This constant structure contains allocation callbacks designed for + * use with the NSPL routine PL_NewHashTable. For example: + * + * NSSArena *hashTableArena = nssArena_Create(); + * PLHashTable *t = PL_NewHashTable(n, hasher, key_compare, + * value_compare, nssArenaHashAllocOps, hashTableArena); + */ + +NSS_EXTERN_DATA PLHashAllocOps nssArenaHashAllocOps; + +/* + * The error stack + * + * The nonpublic methods relating to the error stack are: + * + * nss_SetError + * nss_ClearErrorStack + */ + +/* + * nss_SetError + * + * This routine places a new error code on the top of the calling + * thread's error stack. Calling this routine wiht an error code + * of zero will clear the error stack. + */ + +NSS_EXTERN void nss_SetError(PRUint32 error); + +/* + * nss_ClearErrorStack + * + * This routine clears the calling thread's error stack. + */ + +NSS_EXTERN void nss_ClearErrorStack(void); + +/* + * nss_DestroyErrorStack + * + * This routine frees the calling thread's error stack. + */ + +NSS_EXTERN void nss_DestroyErrorStack(void); + +/* + * NSSItem + * + * nssItem_Create + * nssItem_Duplicate + * nssItem_Equal + */ + +NSS_EXTERN NSSItem *nssItem_Create(NSSArena *arenaOpt, NSSItem *rvOpt, + PRUint32 length, const void *data); + +NSS_EXTERN void nssItem_Destroy(NSSItem *item); + +NSS_EXTERN NSSItem *nssItem_Duplicate(NSSItem *obj, NSSArena *arenaOpt, + NSSItem *rvOpt); + +NSS_EXTERN PRBool nssItem_Equal(const NSSItem *one, const NSSItem *two, + PRStatus *statusOpt); + +/* + * NSSUTF8 + * + * nssUTF8_CaseIgnoreMatch + * nssUTF8_Duplicate + * nssUTF8_Size + * nssUTF8_Length + * nssUTF8_CopyIntoFixedBuffer + */ + +/* + * nssUTF8_CaseIgnoreMatch + * + * Returns true if the two UTF8-encoded strings pointed to by the + * two specified NSSUTF8 pointers differ only in typcase. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * PR_TRUE if the strings match, ignoring case + * PR_FALSE if they don't + * PR_FALSE upon error + */ + +NSS_EXTERN PRBool nssUTF8_CaseIgnoreMatch(const NSSUTF8 *a, const NSSUTF8 *b, + PRStatus *statusOpt); + +/* + * nssUTF8_Duplicate + * + * This routine duplicates the UTF8-encoded string pointed to by the + * specified NSSUTF8 pointer. If the optional arenaOpt argument is + * not null, the memory required will be obtained from that arena; + * otherwise, the memory required will be obtained from the heap. + * A pointer to the new string will be returned. In case of error, + * an error will be placed on the error stack and NULL will be + * returned. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + */ + +NSS_EXTERN NSSUTF8 *nssUTF8_Duplicate(const NSSUTF8 *s, NSSArena *arenaOpt); + +/* + * nssUTF8_PrintableMatch + * + * Returns true if the two Printable strings pointed to by the + * two specified NSSUTF8 pointers match when compared with the + * rules for Printable String (leading and trailing spaces are + * disregarded, extents of whitespace match irregardless of length, + * and case is not significant), then PR_TRUE will be returned. + * Otherwise, PR_FALSE will be returned. Upon failure, PR_FALSE + * will be returned. If the optional statusOpt argument is not + * NULL, then PR_SUCCESS or PR_FAILURE will be stored in that + * location. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * PR_TRUE if the strings match, ignoring case + * PR_FALSE if they don't + * PR_FALSE upon error + */ + +NSS_EXTERN PRBool nssUTF8_PrintableMatch(const NSSUTF8 *a, const NSSUTF8 *b, + PRStatus *statusOpt); + +/* + * nssUTF8_Size + * + * This routine returns the length in bytes (including the terminating + * null) of the UTF8-encoded string pointed to by the specified + * NSSUTF8 pointer. Zero is returned on error. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_VALUE_TOO_LARGE + * + * Return value: + * nonzero size of the string + * 0 on error + */ + +NSS_EXTERN PRUint32 nssUTF8_Size(const NSSUTF8 *s, PRStatus *statusOpt); + +extern const NSSError NSS_ERROR_INVALID_POINTER; +extern const NSSError NSS_ERROR_VALUE_TOO_LARGE; + +/* + * nssUTF8_Length + * + * This routine returns the length in characters (not including the + * terminating null) of the UTF8-encoded string pointed to by the + * specified NSSUTF8 pointer. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_VALUE_TOO_LARGE + * NSS_ERROR_INVALID_STRING + * + * Return value: + * length of the string (which may be zero) + * 0 on error + */ + +NSS_EXTERN PRUint32 nssUTF8_Length(const NSSUTF8 *s, PRStatus *statusOpt); + +extern const NSSError NSS_ERROR_INVALID_POINTER; +extern const NSSError NSS_ERROR_VALUE_TOO_LARGE; +extern const NSSError NSS_ERROR_INVALID_STRING; + +/* + * nssUTF8_Create + * + * This routine creates a UTF8 string from a string in some other + * format. Some types of string may include embedded null characters, + * so for them the length parameter must be used. For string types + * that are null-terminated, the length parameter is optional; if it + * is zero, it will be ignored. If the optional arena argument is + * non-null, the memory used for the new string will be obtained from + * that arena, otherwise it will be obtained from the heap. This + * routine may return NULL upon error, in which case it will have + * placed an error on the error stack. + * + * The error may be one of the following: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_UNSUPPORTED_TYPE + * + * Return value: + * NULL upon error + * A non-null pointer to a new UTF8 string otherwise + */ + +NSS_EXTERN NSSUTF8 *nssUTF8_Create(NSSArena *arenaOpt, nssStringType type, + const void *inputString, + PRUint32 size /* in bytes, not characters */ +); + +extern const NSSError NSS_ERROR_INVALID_POINTER; +extern const NSSError NSS_ERROR_NO_MEMORY; +extern const NSSError NSS_ERROR_UNSUPPORTED_TYPE; + +NSS_EXTERN NSSItem *nssUTF8_GetEncoding(NSSArena *arenaOpt, NSSItem *rvOpt, + nssStringType type, NSSUTF8 *string); + +/* + * nssUTF8_CopyIntoFixedBuffer + * + * This will copy a UTF8 string into a fixed-length buffer, making + * sure that the all characters are valid. Any remaining space will + * be padded with the specified ASCII character, typically either + * null or space. + * + * Blah, blah, blah. + */ + +extern const NSSError NSS_ERROR_INVALID_POINTER; +extern const NSSError NSS_ERROR_INVALID_ARGUMENT; + +NSS_EXTERN PRStatus nssUTF8_CopyIntoFixedBuffer(NSSUTF8 *string, char *buffer, + PRUint32 bufferSize, char pad); + +/* + * nssUTF8_Equal + * + */ + +NSS_EXTERN PRBool nssUTF8_Equal(const NSSUTF8 *a, const NSSUTF8 *b, + PRStatus *statusOpt); + +/* + * nssList + * + * The goal is to provide a simple, optionally threadsafe, linked list + * class. Since NSS did not seem to use the circularity of PRCList + * much before, this provides a list that appears to be a linear, + * NULL-terminated list. + */ + +/* + * nssList_Create + * + * If threadsafe is true, the list will be locked during modifications + * and traversals. + */ +NSS_EXTERN nssList *nssList_Create(NSSArena *arenaOpt, PRBool threadSafe); + +/* + * nssList_Destroy + */ +NSS_EXTERN PRStatus nssList_Destroy(nssList *list); + +NSS_EXTERN void nssList_Clear(nssList *list, + nssListElementDestructorFunc destructor); + +/* + * nssList_SetCompareFunction + * + * By default, two list elements will be compared by comparing their + * data pointers. By setting this function, the user can control + * how elements are compared. + */ +NSS_EXTERN void nssList_SetCompareFunction(nssList *list, + nssListCompareFunc compareFunc); + +/* + * nssList_SetSortFunction + * + * Sort function to use for an ordered list. + */ +NSS_EXTERN void nssList_SetSortFunction(nssList *list, + nssListSortFunc sortFunc); + +/* + * nssList_Add + */ +NSS_EXTERN PRStatus nssList_Add(nssList *list, void *data); + +/* + * nssList_AddUnique + * + * This will use the compare function to see if the element is already + * in the list. + */ +NSS_EXTERN PRStatus nssList_AddUnique(nssList *list, void *data); + +/* + * nssList_Remove + * + * Uses the compare function to locate the element and remove it. + */ +NSS_EXTERN PRStatus nssList_Remove(nssList *list, void *data); + +/* + * nssList_Get + * + * Uses the compare function to locate an element. Also serves as + * nssList_Exists. + */ +NSS_EXTERN void *nssList_Get(nssList *list, void *data); + +/* + * nssList_Count + */ +NSS_EXTERN PRUint32 nssList_Count(nssList *list); + +/* + * nssList_GetArray + * + * Fill rvArray, up to maxElements, with elements in the list. The + * array is NULL-terminated, so its allocated size must be maxElements + 1. + */ +NSS_EXTERN PRStatus nssList_GetArray(nssList *list, void **rvArray, + PRUint32 maxElements); + +/* + * nssList_CreateIterator + * + * Create an iterator for list traversal. + */ +NSS_EXTERN nssListIterator *nssList_CreateIterator(nssList *list); + +NSS_EXTERN nssList *nssList_Clone(nssList *list); + +/* + * nssListIterator_Destroy + */ +NSS_EXTERN void nssListIterator_Destroy(nssListIterator *iter); + +/* + * nssListIterator_Start + * + * Begin a list iteration. After this call, if the list is threadSafe, + * the list is *locked*. + */ +NSS_EXTERN void *nssListIterator_Start(nssListIterator *iter); + +/* + * nssListIterator_Next + * + * Continue a list iteration. + */ +NSS_EXTERN void *nssListIterator_Next(nssListIterator *iter); + +/* + * nssListIterator_Finish + * + * Complete a list iteration. This *must* be called in order for the + * lock to be released. + */ +NSS_EXTERN PRStatus nssListIterator_Finish(nssListIterator *iter); + +/* + * nssHash + * + * nssHash_Create + * nssHash_Destroy + * nssHash_Add + * nssHash_Remove + * nssHash_Count + * nssHash_Exists + * nssHash_Lookup + * nssHash_Iterate + */ + +/* + * nssHash_Create + * + */ + +NSS_EXTERN nssHash *nssHash_Create(NSSArena *arenaOpt, PRUint32 numBuckets, + PLHashFunction keyHash, + PLHashComparator keyCompare, + PLHashComparator valueCompare); + +NSS_EXTERN nssHash *nssHash_CreatePointer(NSSArena *arenaOpt, + PRUint32 numBuckets); + +NSS_EXTERN nssHash *nssHash_CreateString(NSSArena *arenaOpt, + PRUint32 numBuckets); + +NSS_EXTERN nssHash *nssHash_CreateItem(NSSArena *arenaOpt, PRUint32 numBuckets); + +/* + * nssHash_Destroy + * + */ +NSS_EXTERN void nssHash_Destroy(nssHash *hash); + +/* + * nssHash_Add + * + */ + +extern const NSSError NSS_ERROR_HASH_COLLISION; + +NSS_EXTERN PRStatus nssHash_Add(nssHash *hash, const void *key, + const void *value); + +/* + * nssHash_Remove + * + */ +NSS_EXTERN void nssHash_Remove(nssHash *hash, const void *it); + +/* + * nssHash_Count + * + */ +NSS_EXTERN PRUint32 nssHash_Count(nssHash *hash); + +/* + * nssHash_Exists + * + */ +NSS_EXTERN PRBool nssHash_Exists(nssHash *hash, const void *it); + +/* + * nssHash_Lookup + * + */ +NSS_EXTERN void *nssHash_Lookup(nssHash *hash, const void *it); + +/* + * nssHash_Iterate + * + */ +NSS_EXTERN void nssHash_Iterate(nssHash *hash, nssHashIterator fcn, + void *closure); + +/* + * nssPointerTracker + * + * This type and these methods are only present in debug builds. + * + * The nonpublic methods relating to this type are: + * + * nssPointerTracker_initialize + * nssPointerTracker_finalize + * nssPointerTracker_add + * nssPointerTracker_remove + * nssPointerTracker_verify + */ + +/* + * nssPointerTracker_initialize + * + * This method is only present in debug builds. + * + * This routine initializes an nssPointerTracker object. Note that + * the object must have been declared *static* to guarantee that it + * is in a zeroed state initially. This routine is idempotent, and + * may even be safely called by multiple threads simultaneously with + * the same argument. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. On failure it will set an + * error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +#ifdef DEBUG +NSS_EXTERN PRStatus nssPointerTracker_initialize(nssPointerTracker *tracker); + +extern const NSSError NSS_ERROR_NO_MEMORY; +#endif /* DEBUG */ + +/* + * nssPointerTracker_finalize + * + * This method is only present in debug builds. + * + * This routine returns the nssPointerTracker object to the pre- + * initialized state, releasing all resources used by the object. + * It will *NOT* destroy the objects being tracked by the pointer + * (should any remain), and therefore cannot be used to "sweep up" + * remaining objects. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCES. On failure it will set an + * error on the error stack and return PR_FAILURE. If any objects + * remain in the tracker when it is finalized, that will be treated + * as an error. + * + * The error may be one of the following values: + * NSS_ERROR_TRACKER_NOT_EMPTY + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +#ifdef DEBUG +NSS_EXTERN PRStatus nssPointerTracker_finalize(nssPointerTracker *tracker); + +extern const NSSError NSS_ERROR_TRACKER_NOT_EMPTY; +#endif /* DEBUG */ + +/* + * nssPointerTracker_add + * + * This method is only present in debug builds. + * + * This routine adds the specified pointer to the nssPointerTracker + * object. It should be called in constructor objects to register + * new valid objects. The nssPointerTracker is threadsafe, but this + * call is not idempotent. This routine returns a PRStatus value; + * if successful it will return PR_SUCCESS. On failure it will set + * an error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_TRACKER_NOT_INITIALIZED + * NSS_ERROR_DUPLICATE_POINTER + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +#ifdef DEBUG +NSS_EXTERN PRStatus nssPointerTracker_add(nssPointerTracker *tracker, + const void *pointer); + +extern const NSSError NSS_ERROR_NO_MEMORY; +extern const NSSError NSS_ERROR_TRACKER_NOT_INITIALIZED; +extern const NSSError NSS_ERROR_DUPLICATE_POINTER; +#endif /* DEBUG */ + +/* + * nssPointerTracker_remove + * + * This method is only present in debug builds. + * + * This routine removes the specified pointer from the + * nssPointerTracker object. It does not call any destructor for the + * object; rather, this should be called from the object's destructor. + * The nssPointerTracker is threadsafe, but this call is not + * idempotent. This routine returns a PRStatus value; if successful + * it will return PR_SUCCESS. On failure it will set an error on the + * error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_TRACKER_NOT_INITIALIZED + * NSS_ERROR_POINTER_NOT_REGISTERED + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +#ifdef DEBUG +NSS_EXTERN PRStatus nssPointerTracker_remove(nssPointerTracker *tracker, + const void *pointer); + +extern const NSSError NSS_ERROR_TRACKER_NOT_INITIALIZED; +extern const NSSError NSS_ERROR_POINTER_NOT_REGISTERED; +#endif /* DEBUG */ + +/* + * nssPointerTracker_verify + * + * This method is only present in debug builds. + * + * This routine verifies that the specified pointer has been registered + * with the nssPointerTracker object. The nssPointerTracker object is + * threadsafe, and this call may be safely called from multiple threads + * simultaneously with the same arguments. This routine returns a + * PRStatus value; if the pointer is registered this will return + * PR_SUCCESS. Otherwise it will set an error on the error stack and + * return PR_FAILURE. Although the error is suitable for leaving on + * the stack, callers may wish to augment the information available by + * placing a more type-specific error on the stack. + * + * The error may be one of the following values: + * NSS_ERROR_POINTER_NOT_REGISTERED + * + * Return value: + * PR_SUCCESS + * PR_FAILRUE + */ + +#ifdef DEBUG +NSS_EXTERN PRStatus nssPointerTracker_verify(nssPointerTracker *tracker, + const void *pointer); + +extern const NSSError NSS_ERROR_POINTER_NOT_REGISTERED; +#endif /* DEBUG */ + +/* + * libc + * + * nsslibc_memcpy + * nsslibc_memset + * nsslibc_offsetof + */ + +/* + * nsslibc_memcpy + * + * Errors: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * NULL on error + * The destination pointer on success + */ + +NSS_EXTERN void *nsslibc_memcpy(void *dest, const void *source, PRUint32 n); + +extern const NSSError NSS_ERROR_INVALID_POINTER; + +/* + * nsslibc_memset + * + * Errors: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * NULL on error + * The destination pointer on success + */ + +NSS_EXTERN void *nsslibc_memset(void *dest, PRUint8 byte, PRUint32 n); + +extern const NSSError NSS_ERROR_INVALID_POINTER; + +/* + * nsslibc_memequal + * + * Errors: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * PR_TRUE if they match + * PR_FALSE if they don't + * PR_FALSE upon error + */ + +NSS_EXTERN PRBool nsslibc_memequal(const void *a, const void *b, PRUint32 len, + PRStatus *statusOpt); + +extern const NSSError NSS_ERROR_INVALID_POINTER; + +#define nsslibc_offsetof(str, memb) ((PRPtrdiff)(&(((str *)0)->memb))) + +PR_END_EXTERN_C + +#endif /* BASE_H */ diff --git a/security/nss/lib/base/baset.h b/security/nss/lib/base/baset.h new file mode 100644 index 0000000000..3953a755e5 --- /dev/null +++ b/security/nss/lib/base/baset.h @@ -0,0 +1,124 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BASET_H +#define BASET_H + +/* + * baset.h + * + * This file contains definitions for the basic types used throughout + * nss but not available publicly. + */ + +#ifndef NSSBASET_H +#include "nssbaset.h" +#endif /* NSSBASET_H */ + +#include "plhash.h" + +PR_BEGIN_EXTERN_C + +/* + * nssArenaMark + * + * This type is used to mark the current state of an NSSArena. + */ + +struct nssArenaMarkStr; +typedef struct nssArenaMarkStr nssArenaMark; + +#ifdef DEBUG +/* + * ARENA_THREADMARK + * + * Optionally, this arena implementation can be compiled with some + * runtime checking enabled, which will catch the situation where + * one thread "marks" the arena, another thread allocates memory, + * and then the mark is released. Usually this is a surprise to + * the second thread, and this leads to weird runtime errors. + * Define ARENA_THREADMARK to catch these cases; we define it for all + * (internal and external) debug builds. + */ +#define ARENA_THREADMARK + +/* + * ARENA_DESTRUCTOR_LIST + * + * Unfortunately, our pointer-tracker facility, used in debug + * builds to agressively fight invalid pointers, requries that + * pointers be deregistered when objects are destroyed. This + * conflicts with the standard arena usage where "memory-only" + * objects (that don't hold onto resources outside the arena) + * can be allocated in an arena, and never destroyed other than + * when the arena is destroyed. Therefore we have added a + * destructor-registratio facility to our arenas. This was not + * a simple decision, since we're getting ever-further away from + * the original arena philosophy. However, it was felt that + * adding this in debug builds wouldn't be so bad; as it would + * discourage them from being used for "serious" purposes. + * This facility requires ARENA_THREADMARK to be defined. + */ +#ifdef ARENA_THREADMARK +#define ARENA_DESTRUCTOR_LIST +#endif /* ARENA_THREADMARK */ + +#endif /* DEBUG */ + +typedef struct nssListStr nssList; +typedef struct nssListIteratorStr nssListIterator; +typedef PRBool (*nssListCompareFunc)(void *a, void *b); +typedef PRIntn (*nssListSortFunc)(void *a, void *b); +typedef void (*nssListElementDestructorFunc)(void *el); + +typedef struct nssHashStr nssHash; +typedef void(PR_CALLBACK *nssHashIterator)(const void *key, void *value, + void *arg); + +/* + * nssPointerTracker + * + * This type is used in debug builds (both external and internal) to + * track our object pointers. Objects of this type must be statically + * allocated, which means the structure size must be available to the + * compiler. Therefore we must expose the contents of this structure. + * But please don't access elements directly; use the accessors. + */ + +#ifdef DEBUG +struct nssPointerTrackerStr { + PRCallOnceType once; + PZLock *lock; + PLHashTable *table; +}; +typedef struct nssPointerTrackerStr nssPointerTracker; +#endif /* DEBUG */ + +/* + * nssStringType + * + * There are several types of strings in the real world. We try to + * use only UTF8 and avoid the rest, but that's not always possible. + * So we have a couple converter routines to go to and from the other + * string types. We have to be able to specify those string types, + * so we have this enumeration. + */ + +enum nssStringTypeEnum { + nssStringType_DirectoryString, + nssStringType_TeletexString, /* Not "teletext" with trailing 't' */ + nssStringType_PrintableString, + nssStringType_UniversalString, + nssStringType_BMPString, + nssStringType_UTF8String, + nssStringType_PHGString, + nssStringType_GeneralString, + + nssStringType_Unknown = -1 +}; +typedef enum nssStringTypeEnum nssStringType; + +PR_END_EXTERN_C + +#endif /* BASET_H */ diff --git a/security/nss/lib/base/error.c b/security/nss/lib/base/error.c new file mode 100644 index 0000000000..6cbe785c34 --- /dev/null +++ b/security/nss/lib/base/error.c @@ -0,0 +1,301 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * error.c + * + * This file contains the code implementing the per-thread error + * stacks upon which most NSS routines report their errors. + */ + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ +#include /* for UINT_MAX */ +#include /* for memmove */ + +#if defined(__MINGW32__) +#include +#endif + +#define NSS_MAX_ERROR_STACK_COUNT 16 /* error codes */ + +/* + * The stack itself has a header, and a sequence of integers. + * The header records the amount of space (as measured in stack + * slots) already allocated for the stack, and the count of the + * number of records currently being used. + */ + +struct stack_header_str { + PRUint16 space; + PRUint16 count; +}; + +struct error_stack_str { + struct stack_header_str header; + PRInt32 stack[1]; +}; +typedef struct error_stack_str error_stack; + +/* + * error_stack_index + * + * Thread-private data must be indexed. This is that index. + * See PR_NewThreadPrivateIndex for more information. + * + * Thread-private data indexes are in the range [0, 127]. + */ + +#define INVALID_TPD_INDEX UINT_MAX +static PRUintn error_stack_index = INVALID_TPD_INDEX; + +/* + * call_once + * + * The thread-private index must be obtained (once!) at runtime. + * This block is used for that one-time call. + */ + +static PRCallOnceType error_call_once; +static const PRCallOnceType error_call_again; + +/* + * error_once_function + * + * This is the once-called callback. + */ +static PRStatus +error_once_function(void) +{ + +/* + * This #ifdef function is redundant. It performs the same thing as the + * else case. + * + * However, the MinGW version looks up the function from nss3's export + * table, and on MinGW _that_ behaves differently than passing a + * function pointer in a different module because MinGW has + * -mnop-fun-dllimport specified, which generates function thunks for + * cross-module calls. And when a module (like nssckbi) gets unloaded, + * and you try to call into that thunk (which is now missing) you'll + * crash. So we do this bit of ugly to avoid that crash. Fortunately + * this is the only place we've had to do this. + */ +#if defined(__MINGW32__) + HMODULE nss3 = GetModuleHandleW(L"nss3"); + if (nss3) { + PRThreadPrivateDTOR freePtr = (PRThreadPrivateDTOR)GetProcAddress(nss3, "PR_Free"); + if (freePtr) { + return PR_NewThreadPrivateIndex(&error_stack_index, freePtr); + } + } + return PR_NewThreadPrivateIndex(&error_stack_index, PR_Free); +#else + return PR_NewThreadPrivateIndex(&error_stack_index, PR_Free); +#endif +} + +/* + * error_get_my_stack + * + * This routine returns the calling thread's error stack, creating + * it if necessary. It may return NULL upon error, which implicitly + * means that it ran out of memory. + */ + +static error_stack * +error_get_my_stack(void) +{ + PRStatus st; + error_stack *rv; + PRUintn new_size; + PRUint32 new_bytes; + error_stack *new_stack; + + if (INVALID_TPD_INDEX == error_stack_index) { + st = PR_CallOnce(&error_call_once, error_once_function); + if (PR_SUCCESS != st) { + return (error_stack *)NULL; + } + } + + rv = (error_stack *)PR_GetThreadPrivate(error_stack_index); + if ((error_stack *)NULL == rv) { + /* Doesn't exist; create one */ + new_size = 16; + } else if (rv->header.count == rv->header.space && + rv->header.count < NSS_MAX_ERROR_STACK_COUNT) { + /* Too small, expand it */ + new_size = PR_MIN(rv->header.space * 2, NSS_MAX_ERROR_STACK_COUNT); + } else { + /* Okay, return it */ + return rv; + } + + new_bytes = (new_size * sizeof(PRInt32)) + sizeof(error_stack); + /* Use NSPR's calloc/realloc, not NSS's, to avoid loops! */ + new_stack = PR_Calloc(1, new_bytes); + + if ((error_stack *)NULL != new_stack) { + if ((error_stack *)NULL != rv) { + (void)nsslibc_memcpy(new_stack, rv, rv->header.space); + } + new_stack->header.space = new_size; + } + + /* Set the value, whether or not the allocation worked */ + PR_SetThreadPrivate(error_stack_index, new_stack); + return new_stack; +} + +/* + * The error stack + * + * The public methods relating to the error stack are: + * + * NSS_GetError + * NSS_GetErrorStack + * + * The nonpublic methods relating to the error stack are: + * + * nss_SetError + * nss_ClearErrorStack + * + */ + +/* + * NSS_GetError + * + * This routine returns the highest-level (most general) error set + * by the most recent NSS library routine called by the same thread + * calling this routine. + * + * This routine cannot fail. However, it may return zero, which + * indicates that the previous NSS library call did not set an error. + * + * Return value: + * 0 if no error has been set + * A nonzero error number + */ + +NSS_IMPLEMENT PRInt32 +NSS_GetError(void) +{ + error_stack *es = error_get_my_stack(); + + if ((error_stack *)NULL == es) { + return NSS_ERROR_NO_MEMORY; /* Good guess! */ + } + + if (0 == es->header.count) { + return 0; + } + + return es->stack[es->header.count - 1]; +} + +/* + * NSS_GetErrorStack + * + * This routine returns a pointer to an array of integers, containing + * the entire sequence or "stack" of errors set by the most recent NSS + * library routine called by the same thread calling this routine. + * NOTE: the caller DOES NOT OWN the memory pointed to by the return + * value. The pointer will remain valid until the calling thread + * calls another NSS routine. The lowest-level (most specific) error + * is first in the array, and the highest-level is last. The array is + * zero-terminated. This routine may return NULL upon error; this + * indicates a low-memory situation. + * + * Return value: + * NULL upon error, which is an implied NSS_ERROR_NO_MEMORY + * A NON-caller-owned pointer to an array of integers + */ + +NSS_IMPLEMENT PRInt32 * +NSS_GetErrorStack(void) +{ + error_stack *es = error_get_my_stack(); + + if ((error_stack *)NULL == es) { + return (PRInt32 *)NULL; + } + + /* Make sure it's terminated */ + es->stack[es->header.count] = 0; + + return es->stack; +} + +/* + * nss_SetError + * + * This routine places a new error code on the top of the calling + * thread's error stack. Calling this routine wiht an error code + * of zero will clear the error stack. + */ + +NSS_IMPLEMENT void +nss_SetError(PRUint32 error) +{ + error_stack *es; + + if (0 == error) { + nss_ClearErrorStack(); + return; + } + + es = error_get_my_stack(); + if ((error_stack *)NULL == es) { + /* Oh, well. */ + return; + } + + if (es->header.count < es->header.space) { + es->stack[es->header.count++] = error; + } else { + memmove(es->stack, es->stack + 1, + (es->header.space - 1) * (sizeof es->stack[0])); + es->stack[es->header.space - 1] = error; + } + return; +} + +/* + * nss_ClearErrorStack + * + * This routine clears the calling thread's error stack. + */ + +NSS_IMPLEMENT void +nss_ClearErrorStack(void) +{ + error_stack *es = error_get_my_stack(); + if ((error_stack *)NULL == es) { + /* Oh, well. */ + return; + } + + es->header.count = 0; + es->stack[0] = 0; + return; +} + +/* + * nss_DestroyErrorStack + * + * This routine frees the calling thread's error stack. + */ + +NSS_IMPLEMENT void +nss_DestroyErrorStack(void) +{ + if (INVALID_TPD_INDEX != error_stack_index) { + PR_SetThreadPrivate(error_stack_index, NULL); + error_stack_index = INVALID_TPD_INDEX; + error_call_once = error_call_again; /* allow to init again */ + } + return; +} diff --git a/security/nss/lib/base/errorval.c b/security/nss/lib/base/errorval.c new file mode 100644 index 0000000000..b7045a3905 --- /dev/null +++ b/security/nss/lib/base/errorval.c @@ -0,0 +1,65 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * errorval.c + * + * This file contains the actual error constants used in NSS. + */ + +#ifndef NSSBASET_H +#include "nssbaset.h" +#endif /* NSSBASET_H */ + +/* clang-format off */ + +const NSSError NSS_ERROR_NO_ERROR = 0; +const NSSError NSS_ERROR_INTERNAL_ERROR = 1; +const NSSError NSS_ERROR_NO_MEMORY = 2; +const NSSError NSS_ERROR_INVALID_POINTER = 3; +const NSSError NSS_ERROR_INVALID_ARENA = 4; +const NSSError NSS_ERROR_INVALID_ARENA_MARK = 5; +const NSSError NSS_ERROR_DUPLICATE_POINTER = 6; +const NSSError NSS_ERROR_POINTER_NOT_REGISTERED = 7; +const NSSError NSS_ERROR_TRACKER_NOT_EMPTY = 8; +const NSSError NSS_ERROR_TRACKER_NOT_INITIALIZED = 9; +const NSSError NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD = 10; +const NSSError NSS_ERROR_VALUE_TOO_LARGE = 11; +const NSSError NSS_ERROR_UNSUPPORTED_TYPE = 12; +const NSSError NSS_ERROR_BUFFER_TOO_SHORT = 13; +const NSSError NSS_ERROR_INVALID_ATOB_CONTEXT = 14; +const NSSError NSS_ERROR_INVALID_BASE64 = 15; +const NSSError NSS_ERROR_INVALID_BTOA_CONTEXT = 16; +const NSSError NSS_ERROR_INVALID_ITEM = 17; +const NSSError NSS_ERROR_INVALID_STRING = 18; +const NSSError NSS_ERROR_INVALID_ASN1ENCODER = 19; +const NSSError NSS_ERROR_INVALID_ASN1DECODER = 20; + +const NSSError NSS_ERROR_INVALID_BER = 21; +const NSSError NSS_ERROR_INVALID_ATAV = 22; +const NSSError NSS_ERROR_INVALID_ARGUMENT = 23; +const NSSError NSS_ERROR_INVALID_UTF8 = 24; +const NSSError NSS_ERROR_INVALID_NSSOID = 25; +const NSSError NSS_ERROR_UNKNOWN_ATTRIBUTE = 26; + +const NSSError NSS_ERROR_NOT_FOUND = 27; + +const NSSError NSS_ERROR_INVALID_PASSWORD = 28; +const NSSError NSS_ERROR_USER_CANCELED = 29; + +const NSSError NSS_ERROR_MAXIMUM_FOUND = 30; + +const NSSError NSS_ERROR_CERTIFICATE_ISSUER_NOT_FOUND = 31; + +const NSSError NSS_ERROR_CERTIFICATE_IN_CACHE = 32; + +const NSSError NSS_ERROR_HASH_COLLISION = 33; +const NSSError NSS_ERROR_DEVICE_ERROR = 34; +const NSSError NSS_ERROR_INVALID_CERTIFICATE = 35; +const NSSError NSS_ERROR_BUSY = 36; +const NSSError NSS_ERROR_ALREADY_INITIALIZED = 37; + +const NSSError NSS_ERROR_PKCS11 = 38; + +/* clang-format on */ \ No newline at end of file diff --git a/security/nss/lib/base/exports.gyp b/security/nss/lib/base/exports.gyp new file mode 100644 index 0000000000..394bc1dea7 --- /dev/null +++ b/security/nss/lib/base/exports.gyp @@ -0,0 +1,33 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +{ + 'includes': [ + '../../coreconf/config.gypi' + ], + 'targets': [ + { + 'target_name': 'lib_base_exports', + 'type': 'none', + 'copies': [ + { + 'files': [ + 'nssbase.h', + 'nssbaset.h' + ], + 'destination': '<(nss_public_dist_dir)/<(module)' + }, + { + 'files': [ + 'base.h', + 'baset.h' + ], + 'destination': '<(nss_private_dist_dir)/<(module)' + } + ] + } + ], + 'variables': { + 'module': 'nss' + } +} diff --git a/security/nss/lib/base/hash.c b/security/nss/lib/base/hash.c new file mode 100644 index 0000000000..f9ee758038 --- /dev/null +++ b/security/nss/lib/base/hash.c @@ -0,0 +1,314 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * hash.c + * + * This is merely a couple wrappers around NSPR's PLHashTable, using + * the identity hash and arena-aware allocators. + * This is a copy of ckfw/hash.c, with modifications to use NSS types + * (not Cryptoki types). Would like for this to be a single implementation, + * but doesn't seem like it will work. + */ + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +#include "prbit.h" + +/* + * nssHash + * + * nssHash_Create + * nssHash_Destroy + * nssHash_Add + * nssHash_Remove + * nssHash_Count + * nssHash_Exists + * nssHash_Lookup + * nssHash_Iterate + */ + +struct nssHashStr { + NSSArena *arena; + PRBool i_alloced_arena; + PRLock *mutex; + + /* + * The invariant that mutex protects is: + * The count accurately reflects the hashtable state. + */ + + PLHashTable *plHashTable; + PRUint32 count; +}; + +static PLHashNumber +nss_identity_hash(const void *key) +{ + return (PLHashNumber)((char *)key - (char *)NULL); +} + +static PLHashNumber +nss_item_hash(const void *key) +{ + unsigned int i; + PLHashNumber h; + NSSItem *it = (NSSItem *)key; + h = 0; + for (i = 0; i < it->size; i++) + h = PR_ROTATE_LEFT32(h, 4) ^ ((unsigned char *)it->data)[i]; + return h; +} + +static int +nss_compare_items(const void *v1, const void *v2) +{ + PRStatus ignore; + return (int)nssItem_Equal((NSSItem *)v1, (NSSItem *)v2, &ignore); +} + +/* + * nssHash_create + * + */ +NSS_IMPLEMENT nssHash * +nssHash_Create(NSSArena *arenaOpt, PRUint32 numBuckets, PLHashFunction keyHash, + PLHashComparator keyCompare, PLHashComparator valueCompare) +{ + nssHash *rv; + NSSArena *arena; + PRBool i_alloced; + +#ifdef NSSDEBUG + if (arenaOpt && PR_SUCCESS != nssArena_verifyPointer(arenaOpt)) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (nssHash *)NULL; + } +#endif /* NSSDEBUG */ + + if (arenaOpt) { + arena = arenaOpt; + i_alloced = PR_FALSE; + } else { + arena = nssArena_Create(); + i_alloced = PR_TRUE; + } + + rv = nss_ZNEW(arena, nssHash); + if ((nssHash *)NULL == rv) { + goto loser; + } + + rv->mutex = PZ_NewLock(nssILockOther); + if ((PZLock *)NULL == rv->mutex) { + goto loser; + } + + rv->plHashTable = + PL_NewHashTable(numBuckets, keyHash, keyCompare, valueCompare, + &nssArenaHashAllocOps, arena); + if ((PLHashTable *)NULL == rv->plHashTable) { + (void)PZ_DestroyLock(rv->mutex); + goto loser; + } + + rv->count = 0; + rv->arena = arena; + rv->i_alloced_arena = i_alloced; + + return rv; +loser: + (void)nss_ZFreeIf(rv); + return (nssHash *)NULL; +} + +/* + * nssHash_CreatePointer + * + */ +NSS_IMPLEMENT nssHash * +nssHash_CreatePointer(NSSArena *arenaOpt, PRUint32 numBuckets) +{ + return nssHash_Create(arenaOpt, numBuckets, nss_identity_hash, + PL_CompareValues, PL_CompareValues); +} + +/* + * nssHash_CreateString + * + */ +NSS_IMPLEMENT nssHash * +nssHash_CreateString(NSSArena *arenaOpt, PRUint32 numBuckets) +{ + return nssHash_Create(arenaOpt, numBuckets, PL_HashString, + PL_CompareStrings, PL_CompareStrings); +} + +/* + * nssHash_CreateItem + * + */ +NSS_IMPLEMENT nssHash * +nssHash_CreateItem(NSSArena *arenaOpt, PRUint32 numBuckets) +{ + return nssHash_Create(arenaOpt, numBuckets, nss_item_hash, + nss_compare_items, PL_CompareValues); +} + +/* + * nssHash_Destroy + * + */ +NSS_IMPLEMENT void +nssHash_Destroy(nssHash *hash) +{ + (void)PZ_DestroyLock(hash->mutex); + PL_HashTableDestroy(hash->plHashTable); + if (hash->i_alloced_arena) { + nssArena_Destroy(hash->arena); + } else { + nss_ZFreeIf(hash); + } +} + +/* + * nssHash_Add + * + */ +NSS_IMPLEMENT PRStatus +nssHash_Add(nssHash *hash, const void *key, const void *value) +{ + PRStatus error = PR_FAILURE; + PLHashEntry *he; + + PZ_Lock(hash->mutex); + + he = PL_HashTableAdd(hash->plHashTable, key, (void *)value); + if ((PLHashEntry *)NULL == he) { + nss_SetError(NSS_ERROR_NO_MEMORY); + } else if (he->value != value) { + nss_SetError(NSS_ERROR_HASH_COLLISION); + } else { + hash->count++; + error = PR_SUCCESS; + } + + (void)PZ_Unlock(hash->mutex); + + return error; +} + +/* + * nssHash_Remove + * + */ +NSS_IMPLEMENT void +nssHash_Remove(nssHash *hash, const void *it) +{ + PRBool found; + + PZ_Lock(hash->mutex); + + found = PL_HashTableRemove(hash->plHashTable, it); + if (found) { + hash->count--; + } + + (void)PZ_Unlock(hash->mutex); + return; +} + +/* + * nssHash_Count + * + */ +NSS_IMPLEMENT PRUint32 +nssHash_Count(nssHash *hash) +{ + PRUint32 count; + + PZ_Lock(hash->mutex); + + count = hash->count; + + (void)PZ_Unlock(hash->mutex); + + return count; +} + +/* + * nssHash_Exists + * + */ +NSS_IMPLEMENT PRBool +nssHash_Exists(nssHash *hash, const void *it) +{ + void *value; + + PZ_Lock(hash->mutex); + + value = PL_HashTableLookup(hash->plHashTable, it); + + (void)PZ_Unlock(hash->mutex); + + if ((void *)NULL == value) { + return PR_FALSE; + } else { + return PR_TRUE; + } +} + +/* + * nssHash_Lookup + * + */ +NSS_IMPLEMENT void * +nssHash_Lookup(nssHash *hash, const void *it) +{ + void *rv; + + PZ_Lock(hash->mutex); + + rv = PL_HashTableLookup(hash->plHashTable, it); + + (void)PZ_Unlock(hash->mutex); + + return rv; +} + +struct arg_str { + nssHashIterator fcn; + void *closure; +}; + +static PRIntn +nss_hash_enumerator(PLHashEntry *he, PRIntn index, void *arg) +{ + struct arg_str *as = (struct arg_str *)arg; + as->fcn(he->key, he->value, as->closure); + return HT_ENUMERATE_NEXT; +} + +/* + * nssHash_Iterate + * + * NOTE that the iteration function will be called with the hashtable locked. + */ +NSS_IMPLEMENT void +nssHash_Iterate(nssHash *hash, nssHashIterator fcn, void *closure) +{ + struct arg_str as; + as.fcn = fcn; + as.closure = closure; + + PZ_Lock(hash->mutex); + + PL_HashTableEnumerateEntries(hash->plHashTable, nss_hash_enumerator, &as); + + (void)PZ_Unlock(hash->mutex); + + return; +} diff --git a/security/nss/lib/base/hashops.c b/security/nss/lib/base/hashops.c new file mode 100644 index 0000000000..57b30dd1fd --- /dev/null +++ b/security/nss/lib/base/hashops.c @@ -0,0 +1,64 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * hashops.c + * + * This file includes a set of PLHashAllocOps that use NSSArenas. + */ + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +static void *PR_CALLBACK +nss_arena_hash_alloc_table(void *pool, PRSize size) +{ + NSSArena *arena = (NSSArena *)NULL; + +#ifdef NSSDEBUG + if ((void *)NULL != arena) { + if (PR_SUCCESS != nssArena_verifyPointer(arena)) { + return (void *)NULL; + } + } +#endif /* NSSDEBUG */ + + return nss_ZAlloc(arena, size); +} + +static void PR_CALLBACK +nss_arena_hash_free_table(void *pool, void *item) +{ + (void)nss_ZFreeIf(item); +} + +static PLHashEntry *PR_CALLBACK +nss_arena_hash_alloc_entry(void *pool, const void *key) +{ + NSSArena *arena = NULL; + +#ifdef NSSDEBUG + if ((void *)NULL != arena) { + if (PR_SUCCESS != nssArena_verifyPointer(arena)) { + return (void *)NULL; + } + } +#endif /* NSSDEBUG */ + + return nss_ZNEW(arena, PLHashEntry); +} + +static void PR_CALLBACK +nss_arena_hash_free_entry(void *pool, PLHashEntry *he, PRUintn flag) +{ + if (HT_FREE_ENTRY == flag) { + (void)nss_ZFreeIf(he); + } +} + +NSS_IMPLEMENT_DATA PLHashAllocOps nssArenaHashAllocOps = { + nss_arena_hash_alloc_table, nss_arena_hash_free_table, + nss_arena_hash_alloc_entry, nss_arena_hash_free_entry +}; diff --git a/security/nss/lib/base/item.c b/security/nss/lib/base/item.c new file mode 100644 index 0000000000..a1bb802ed6 --- /dev/null +++ b/security/nss/lib/base/item.c @@ -0,0 +1,186 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * item.c + * + * This contains some item-manipulation code. + */ + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +/* + * nssItem_Create + * + * -- fgmr comments -- + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * A pointer to an NSSItem upon success + * NULL upon failure + */ + +NSS_IMPLEMENT NSSItem * +nssItem_Create(NSSArena *arenaOpt, NSSItem *rvOpt, PRUint32 length, + const void *data) +{ + NSSItem *rv = (NSSItem *)NULL; + +#ifdef DEBUG + if ((NSSArena *)NULL != arenaOpt) { + if (PR_SUCCESS != nssArena_verifyPointer(arenaOpt)) { + return (NSSItem *)NULL; + } + } + + if ((const void *)NULL == data) { + if (length > 0) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (NSSItem *)NULL; + } + } +#endif /* DEBUG */ + + if ((NSSItem *)NULL == rvOpt) { + rv = (NSSItem *)nss_ZNEW(arenaOpt, NSSItem); + if ((NSSItem *)NULL == rv) { + goto loser; + } + } else { + rv = rvOpt; + } + + rv->size = length; + rv->data = nss_ZAlloc(arenaOpt, length); + if ((void *)NULL == rv->data) { + goto loser; + } + + if (length > 0) { + (void)nsslibc_memcpy(rv->data, data, length); + } + + return rv; + +loser: + if (rv != rvOpt) { + nss_ZFreeIf(rv); + } + + return (NSSItem *)NULL; +} + +NSS_IMPLEMENT void +nssItem_Destroy(NSSItem *item) +{ + nss_ClearErrorStack(); + + nss_ZFreeIf(item->data); + nss_ZFreeIf(item); +} + +/* + * nssItem_Duplicate + * + * -- fgmr comments -- + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * NSS_ERROR_INVALID_ITEM + * + * Return value: + * A pointer to an NSSItem upon success + * NULL upon failure + */ + +NSS_IMPLEMENT NSSItem * +nssItem_Duplicate(NSSItem *obj, NSSArena *arenaOpt, NSSItem *rvOpt) +{ +#ifdef DEBUG + if ((NSSArena *)NULL != arenaOpt) { + if (PR_SUCCESS != nssArena_verifyPointer(arenaOpt)) { + return (NSSItem *)NULL; + } + } + + if ((NSSItem *)NULL == obj) { + nss_SetError(NSS_ERROR_INVALID_ITEM); + return (NSSItem *)NULL; + } +#endif /* DEBUG */ + + return nssItem_Create(arenaOpt, rvOpt, obj->size, obj->data); +} + +#ifdef DEBUG +/* + * nssItem_verifyPointer + * + * -- fgmr comments -- + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ITEM + * + * Return value: + * PR_SUCCESS upon success + * PR_FAILURE upon failure + */ + +NSS_IMPLEMENT PRStatus +nssItem_verifyPointer(const NSSItem *item) +{ + if (((const NSSItem *)NULL == item) || + (((void *)NULL == item->data) && (item->size > 0))) { + nss_SetError(NSS_ERROR_INVALID_ITEM); + return PR_FAILURE; + } + + return PR_SUCCESS; +} +#endif /* DEBUG */ + +/* + * nssItem_Equal + * + * -- fgmr comments -- + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ITEM + * + * Return value: + * PR_TRUE if the items are identical + * PR_FALSE if they aren't + * PR_FALSE upon error + */ + +NSS_IMPLEMENT PRBool +nssItem_Equal(const NSSItem *one, const NSSItem *two, PRStatus *statusOpt) +{ + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_SUCCESS; + } + + if (((const NSSItem *)NULL == one) && ((const NSSItem *)NULL == two)) { + return PR_TRUE; + } + + if (((const NSSItem *)NULL == one) || ((const NSSItem *)NULL == two)) { + return PR_FALSE; + } + + if (one->size != two->size) { + return PR_FALSE; + } + + return nsslibc_memequal(one->data, two->data, one->size, statusOpt); +} diff --git a/security/nss/lib/base/libc.c b/security/nss/lib/base/libc.c new file mode 100644 index 0000000000..7954a31610 --- /dev/null +++ b/security/nss/lib/base/libc.c @@ -0,0 +1,143 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * libc.c + * + * This file contains our wrappers/reimplementations for "standard" + * libc functions. Things like "memcpy." We add to this as we need + * it. Oh, and let's keep it in alphabetical order, should it ever + * get large. Most string/character stuff should be in utf8.c, not + * here. This file (and maybe utf8.c) should be the only ones in + * NSS to include files with angle brackets. + */ + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +#include /* memcpy, memset */ + +/* + * nsslibc_memcpy + * nsslibc_memset + * nsslibc_offsetof + * nsslibc_memequal + */ + +/* + * nsslibc_memcpy + * + * Errors: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * NULL on error + * The destination pointer on success + */ + +NSS_IMPLEMENT void * +nsslibc_memcpy(void *dest, const void *source, PRUint32 n) +{ +#ifdef NSSDEBUG + if (((void *)NULL == dest) || ((const void *)NULL == source)) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (void *)NULL; + } +#endif /* NSSDEBUG */ + + return memcpy(dest, source, (size_t)n); +} + +/* + * nsslibc_memset + * + * Errors: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * NULL on error + * The destination pointer on success + */ + +NSS_IMPLEMENT void * +nsslibc_memset(void *dest, PRUint8 byte, PRUint32 n) +{ +#ifdef NSSDEBUG + if (((void *)NULL == dest)) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (void *)NULL; + } +#endif /* NSSDEBUG */ + + return memset(dest, (int)byte, (size_t)n); +} + +/* + * nsslibc_memequal + * + * Errors: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * PR_TRUE if they match + * PR_FALSE if they don't + * PR_FALSE upon error + */ + +NSS_IMPLEMENT PRBool +nsslibc_memequal(const void *a, const void *b, PRUint32 len, + PRStatus *statusOpt) +{ +#ifdef NSSDEBUG + if ((((void *)NULL == a) || ((void *)NULL == b))) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_FAILURE; + } + return PR_FALSE; + } +#endif /* NSSDEBUG */ + + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_SUCCESS; + } + + if (0 == memcmp(a, b, len)) { + return PR_TRUE; + } else { + return PR_FALSE; + } +} + +/* + * nsslibc_memcmp + */ + +NSS_IMPLEMENT PRInt32 +nsslibc_memcmp(const void *a, const void *b, PRUint32 len, PRStatus *statusOpt) +{ + int v; + +#ifdef NSSDEBUG + if ((((void *)NULL == a) || ((void *)NULL == b))) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_FAILURE; + } + return -2; + } +#endif /* NSSDEBUG */ + + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_SUCCESS; + } + + v = memcmp(a, b, len); + return (PRInt32)v; +} + +/* + * offsetof is a preprocessor definition + */ diff --git a/security/nss/lib/base/list.c b/security/nss/lib/base/list.c new file mode 100644 index 0000000000..e798cf09cd --- /dev/null +++ b/security/nss/lib/base/list.c @@ -0,0 +1,405 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * list.c + * + * This contains the implementation of NSS's thread-safe linked list. + */ + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +struct nssListElementStr { + PRCList link; + void *data; +}; + +typedef struct nssListElementStr nssListElement; + +struct nssListStr { + NSSArena *arena; + PZLock *lock; + nssListElement *head; + PRUint32 count; + nssListCompareFunc compareFunc; + nssListSortFunc sortFunc; + PRBool i_alloced_arena; +}; + +struct nssListIteratorStr { + PZLock *lock; + nssList *list; + nssListElement *current; +}; + +#define NSSLIST_LOCK_IF(list) \ + if ((list)->lock) \ + PZ_Lock((list)->lock) + +#define NSSLIST_UNLOCK_IF(list) \ + if ((list)->lock) \ + PZ_Unlock((list)->lock) + +static PRBool +pointer_compare(void *a, void *b) +{ + return (PRBool)(a == b); +} + +static nssListElement * +nsslist_get_matching_element(nssList *list, void *data) +{ + nssListElement *node; + node = list->head; + if (!node) { + return NULL; + } + while (node) { + /* using a callback slows things down when it's just compare ... */ + if (list->compareFunc(node->data, data)) { + break; + } + if (&node->link == PR_LIST_TAIL(&list->head->link)) { + node = NULL; + break; + } + node = (nssListElement *)PR_NEXT_LINK(&node->link); + } + return node; +} + +NSS_IMPLEMENT nssList * +nssList_Create(NSSArena *arenaOpt, PRBool threadSafe) +{ + NSSArena *arena; + nssList *list; + PRBool i_alloced; + if (arenaOpt) { + arena = arenaOpt; + i_alloced = PR_FALSE; + } else { + arena = nssArena_Create(); + i_alloced = PR_TRUE; + } + if (!arena) { + return (nssList *)NULL; + } + list = nss_ZNEW(arena, nssList); + if (!list) { + if (!arenaOpt) { + NSSArena_Destroy(arena); + } + return (nssList *)NULL; + } + if (threadSafe) { + list->lock = PZ_NewLock(nssILockOther); + if (!list->lock) { + if (arenaOpt) { + nss_ZFreeIf(list); + } else { + NSSArena_Destroy(arena); + } + return (nssList *)NULL; + } + } + list->arena = arena; + list->i_alloced_arena = i_alloced; + list->compareFunc = pointer_compare; + return list; +} + +NSS_IMPLEMENT PRStatus +nssList_Destroy(nssList *list) +{ + if (!list) { + return PR_SUCCESS; + } + if (!list->i_alloced_arena) { + nssList_Clear(list, NULL); + } + if (list->lock) { + (void)PZ_DestroyLock(list->lock); + } + if (list->i_alloced_arena) { + NSSArena_Destroy(list->arena); + list = NULL; + } + nss_ZFreeIf(list); + return PR_SUCCESS; +} + +NSS_IMPLEMENT void +nssList_SetCompareFunction(nssList *list, nssListCompareFunc compareFunc) +{ + list->compareFunc = compareFunc; +} + +NSS_IMPLEMENT void +nssList_SetSortFunction(nssList *list, nssListSortFunc sortFunc) +{ + /* XXX if list already has elements, sort them */ + list->sortFunc = sortFunc; +} + +NSS_IMPLEMENT nssListCompareFunc +nssList_GetCompareFunction(nssList *list) +{ + return list->compareFunc; +} + +NSS_IMPLEMENT void +nssList_Clear(nssList *list, nssListElementDestructorFunc destructor) +{ + PRCList *link; + nssListElement *node, *tmp; + if (!list) { + return; + } + NSSLIST_LOCK_IF(list); + node = list->head; + list->head = NULL; + while (node && list->count > 0) { + if (destructor) + (*destructor)(node->data); + link = &node->link; + tmp = (nssListElement *)PR_NEXT_LINK(link); + PR_REMOVE_LINK(link); + nss_ZFreeIf(node); + node = tmp; + --list->count; + } + NSSLIST_UNLOCK_IF(list); +} + +static PRStatus +nsslist_add_element(nssList *list, void *data) +{ + nssListElement *node = nss_ZNEW(list->arena, nssListElement); + if (!node) { + return PR_FAILURE; + } + PR_INIT_CLIST(&node->link); + node->data = data; + if (list->head) { + if (list->sortFunc) { + PRCList *link; + nssListElement *currNode; + currNode = list->head; + /* insert in ordered list */ + while (currNode) { + link = &currNode->link; + if (list->sortFunc(data, currNode->data) <= 0) { + /* new element goes before current node */ + PR_INSERT_BEFORE(&node->link, link); + /* reset head if this is first */ + if (currNode == list->head) + list->head = node; + break; + } + if (link == PR_LIST_TAIL(&list->head->link)) { + /* reached end of list, append */ + PR_INSERT_AFTER(&node->link, link); + break; + } + currNode = (nssListElement *)PR_NEXT_LINK(&currNode->link); + } + } else { + /* not sorting */ + PR_APPEND_LINK(&node->link, &list->head->link); + } + } else { + list->head = node; + } + ++list->count; + return PR_SUCCESS; +} + +NSS_IMPLEMENT PRStatus +nssList_Add(nssList *list, void *data) +{ + NSSLIST_LOCK_IF(list); + (void)nsslist_add_element(list, data); + NSSLIST_UNLOCK_IF(list); + return PR_SUCCESS; +} + +NSS_IMPLEMENT PRStatus +nssList_AddUnique(nssList *list, void *data) +{ + PRStatus nssrv; + nssListElement *node; + NSSLIST_LOCK_IF(list); + node = nsslist_get_matching_element(list, data); + if (node) { + /* already in, finish */ + NSSLIST_UNLOCK_IF(list); + return PR_SUCCESS; + } + nssrv = nsslist_add_element(list, data); + NSSLIST_UNLOCK_IF(list); + return nssrv; +} + +NSS_IMPLEMENT PRStatus +nssList_Remove(nssList *list, void *data) +{ + nssListElement *node; + NSSLIST_LOCK_IF(list); + node = nsslist_get_matching_element(list, data); + if (node) { + if (node == list->head) { + list->head = (nssListElement *)PR_NEXT_LINK(&node->link); + } + PR_REMOVE_LINK(&node->link); + nss_ZFreeIf(node); + if (--list->count == 0) { + list->head = NULL; + } + } + NSSLIST_UNLOCK_IF(list); + return PR_SUCCESS; +} + +NSS_IMPLEMENT void * +nssList_Get(nssList *list, void *data) +{ + nssListElement *node; + NSSLIST_LOCK_IF(list); + node = nsslist_get_matching_element(list, data); + NSSLIST_UNLOCK_IF(list); + return (node) ? node->data : NULL; +} + +NSS_IMPLEMENT PRUint32 +nssList_Count(nssList *list) +{ + return list->count; +} + +NSS_IMPLEMENT PRStatus +nssList_GetArray(nssList *list, void **rvArray, PRUint32 maxElements) +{ + nssListElement *node; + PRUint32 i = 0; + PR_ASSERT(maxElements > 0); + node = list->head; + if (!node) { + return PR_SUCCESS; + } + NSSLIST_LOCK_IF(list); + while (node) { + rvArray[i++] = node->data; + if (i == maxElements) + break; + node = (nssListElement *)PR_NEXT_LINK(&node->link); + if (node == list->head) { + break; + } + } + NSSLIST_UNLOCK_IF(list); + return PR_SUCCESS; +} + +NSS_IMPLEMENT nssList * +nssList_Clone(nssList *list) +{ + nssList *rvList; + nssListElement *node; + rvList = nssList_Create(NULL, (list->lock != NULL)); + if (!rvList) { + return NULL; + } + NSSLIST_LOCK_IF(list); + if (list->count > 0) { + node = list->head; + while (PR_TRUE) { + nssList_Add(rvList, node->data); + node = (nssListElement *)PR_NEXT_LINK(&node->link); + if (node == list->head) { + break; + } + } + } + NSSLIST_UNLOCK_IF(list); + return rvList; +} + +NSS_IMPLEMENT nssListIterator * +nssList_CreateIterator(nssList *list) +{ + nssListIterator *rvIterator; + rvIterator = nss_ZNEW(NULL, nssListIterator); + if (!rvIterator) { + return NULL; + } + rvIterator->list = nssList_Clone(list); + if (!rvIterator->list) { + nss_ZFreeIf(rvIterator); + return NULL; + } + rvIterator->current = rvIterator->list->head; + if (list->lock) { + rvIterator->lock = PZ_NewLock(nssILockOther); + if (!rvIterator->lock) { + nssList_Destroy(rvIterator->list); + nss_ZFreeIf(rvIterator); + rvIterator = NULL; + } + } + return rvIterator; +} + +NSS_IMPLEMENT void +nssListIterator_Destroy(nssListIterator *iter) +{ + if (iter->lock) { + (void)PZ_DestroyLock(iter->lock); + } + if (iter->list) { + nssList_Destroy(iter->list); + } + nss_ZFreeIf(iter); +} + +NSS_IMPLEMENT void * +nssListIterator_Start(nssListIterator *iter) +{ + NSSLIST_LOCK_IF(iter); + if (iter->list->count == 0) { + return NULL; + } + iter->current = iter->list->head; + return iter->current->data; +} + +NSS_IMPLEMENT void * +nssListIterator_Next(nssListIterator *iter) +{ + nssListElement *node; + PRCList *link; + if (iter->list->count == 1 || iter->current == NULL) { + /* Reached the end of the list. Don't change the state, force to + * user to call nssList_Finish to clean up. + */ + return NULL; + } + node = (nssListElement *)PR_NEXT_LINK(&iter->current->link); + link = &node->link; + if (link == PR_LIST_TAIL(&iter->list->head->link)) { + /* Signal the end of the list. */ + iter->current = NULL; + return node->data; + } + iter->current = node; + return node->data; +} + +NSS_IMPLEMENT PRStatus +nssListIterator_Finish(nssListIterator *iter) +{ + iter->current = iter->list->head; + return (iter->lock) ? PZ_Unlock(iter->lock) : PR_SUCCESS; +} diff --git a/security/nss/lib/base/manifest.mn b/security/nss/lib/base/manifest.mn new file mode 100644 index 0000000000..6c4b2db98a --- /dev/null +++ b/security/nss/lib/base/manifest.mn @@ -0,0 +1,39 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +CORE_DEPTH = ../.. + +PRIVATE_EXPORTS = \ + baset.h \ + base.h \ + $(NULL) + +EXPORTS = \ + nssbaset.h \ + nssbase.h \ + $(NULL) + +MODULE = nss + +CSRCS = \ + arena.c \ + error.c \ + errorval.c \ + hashops.c \ + libc.c \ + tracker.c \ + item.c \ + utf8.c \ + list.c \ + hash.c \ + $(NULL) + +REQUIRES = nspr + +LIBRARY_NAME = nssb +SHARED_LIBRARY = $(NULL) + +# This part of the code, including all sub-dirs, can be optimized for size +export ALLOW_OPT_CODE_SIZE = 1 diff --git a/security/nss/lib/base/nssbase.h b/security/nss/lib/base/nssbase.h new file mode 100644 index 0000000000..02e285fba8 --- /dev/null +++ b/security/nss/lib/base/nssbase.h @@ -0,0 +1,233 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef NSSBASE_H +#define NSSBASE_H + +/* + * nssbase.h + * + * This header file contains the prototypes of the basic public + * NSS routines. + */ + +#ifndef NSSBASET_H +#include "nssbaset.h" +#endif /* NSSBASET_H */ + +PR_BEGIN_EXTERN_C + +/* + * NSSArena + * + * The public methods relating to this type are: + * + * NSSArena_Create -- constructor + * NSSArena_Destroy + * NSS_ZAlloc + * NSS_ZRealloc + * NSS_ZFreeIf + */ + +/* + * NSSArena_Create + * + * This routine creates a new memory arena. This routine may return + * NULL upon error, in which case it will have created an error stack. + * + * The top-level error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSArena upon success + */ + +NSS_EXTERN NSSArena *NSSArena_Create(void); + +extern const NSSError NSS_ERROR_NO_MEMORY; + +/* + * NSSArena_Destroy + * + * This routine will destroy the specified arena, freeing all memory + * allocated from it. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. If unsuccessful, it will + * create an error stack and return PR_FAILURE. + * + * The top-level error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * + * Return value: + * PR_SUCCESS upon success + * PR_FAILURE upon failure + */ + +NSS_EXTERN PRStatus NSSArena_Destroy(NSSArena *arena); + +extern const NSSError NSS_ERROR_INVALID_ARENA; + +/* + * The error stack + * + * The public methods relating to the error stack are: + * + * NSS_GetError + * NSS_GetErrorStack + */ + +/* + * NSS_GetError + * + * This routine returns the highest-level (most general) error set + * by the most recent NSS library routine called by the same thread + * calling this routine. + * + * This routine cannot fail. It may return NSS_ERROR_NO_ERROR, which + * indicates that the previous NSS library call did not set an error. + * + * Return value: + * 0 if no error has been set + * A nonzero error number + */ + +NSS_EXTERN NSSError NSS_GetError(void); + +extern const NSSError NSS_ERROR_NO_ERROR; + +/* + * NSS_GetErrorStack + * + * This routine returns a pointer to an array of NSSError values, + * containingthe entire sequence or "stack" of errors set by the most + * recent NSS library routine called by the same thread calling this + * routine. NOTE: the caller DOES NOT OWN the memory pointed to by + * the return value. The pointer will remain valid until the calling + * thread calls another NSS routine. The lowest-level (most specific) + * error is first in the array, and the highest-level is last. The + * array is zero-terminated. This routine may return NULL upon error; + * this indicates a low-memory situation. + * + * Return value: + * NULL upon error, which is an implied NSS_ERROR_NO_MEMORY + * A NON-caller-owned pointer to an array of NSSError values + */ + +NSS_EXTERN NSSError *NSS_GetErrorStack(void); + +/* + * NSS_ZNEW + * + * This preprocessor macro will allocate memory for a new object + * of the specified type with nss_ZAlloc, and will cast the + * return value appropriately. If the optional arena argument is + * non-null, the memory will be obtained from that arena; otherwise, + * the memory will be obtained from the heap. This routine may + * return NULL upon error, in which case it will have set an error + * upon the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to the new segment of zeroed memory + */ + +#define NSS_ZNEW(arenaOpt, type) ((type *)NSS_ZAlloc((arenaOpt), sizeof(type))) + +/* + * NSS_ZNEWARRAY + * + * This preprocessor macro will allocate memory for an array of + * new objects, and will cast the return value appropriately. + * If the optional arena argument is non-null, the memory will + * be obtained from that arena; otherwise, the memory will be + * obtained from the heap. This routine may return NULL upon + * error, in which case it will have set an error upon the error + * stack. The array size may be specified as zero. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to the new segment of zeroed memory + */ + +#define NSS_ZNEWARRAY(arenaOpt, type, quantity) \ + ((type *)NSS_ZAlloc((arenaOpt), sizeof(type) * (quantity))) + +/* + * NSS_ZAlloc + * + * This routine allocates and zeroes a section of memory of the + * size, and returns to the caller a pointer to that memory. If + * the optional arena argument is non-null, the memory will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in + * which case it will have set an error upon the error stack. The + * value specified for size may be zero; in which case a valid + * zero-length block of memory will be allocated. This block may + * be expanded by calling NSS_ZRealloc. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * NULL upon error + * A pointer to the new segment of zeroed memory + */ + +NSS_EXTERN void *NSS_ZAlloc(NSSArena *arenaOpt, PRUint32 size); + +/* + * NSS_ZRealloc + * + * This routine reallocates a block of memory obtained by calling + * nss_ZAlloc or nss_ZRealloc. The portion of memory + * between the new and old sizes -- which is either being newly + * obtained or released -- is in either case zeroed. This routine + * may return NULL upon failure, in which case it will have placed + * an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * NULL upon error + * A pointer to the replacement segment of memory + */ + +NSS_EXTERN void *NSS_ZRealloc(void *pointer, PRUint32 newSize); + +/* + * NSS_ZFreeIf + * + * If the specified pointer is non-null, then the region of memory + * to which it points -- which must have been allocated with + * nss_ZAlloc -- will be zeroed and released. This routine + * returns a PRStatus value; if successful, it will return PR_SUCCESS. + * If unsuccessful, it will set an error on the error stack and return + * PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_EXTERN PRStatus NSS_ZFreeIf(void *pointer); + +PR_END_EXTERN_C + +#endif /* NSSBASE_H */ diff --git a/security/nss/lib/base/nssbaset.h b/security/nss/lib/base/nssbaset.h new file mode 100644 index 0000000000..8bc556e6e9 --- /dev/null +++ b/security/nss/lib/base/nssbaset.h @@ -0,0 +1,118 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef NSSBASET_H +#define NSSBASET_H + +/* + * nssbaset.h + * + * This file contains the most low-level, fundamental public types. + */ + +#include "nspr.h" +#include "nssilock.h" + +/* + * NSS_EXTERN, NSS_IMPLEMENT, NSS_EXTERN_DATA, NSS_IMPLEMENT_DATA + * + * NSS has its own versions of these NSPR macros, in a form which + * does not confuse ctags and other related utilities. NSPR + * defines these macros to take the type as an argument, because + * of certain OS requirements on platforms not supported by NSS. + */ + +#define DUMMY /* dummy */ +#define NSS_EXTERN extern +#define NSS_EXTERN_DATA extern +#define NSS_IMPLEMENT +#define NSS_IMPLEMENT_DATA + +PR_BEGIN_EXTERN_C + +/* + * NSSError + * + * Calls to NSS routines may result in one or more errors being placed + * on the calling thread's "error stack." Every possible error that + * may be returned from a function is declared where the function is + * prototyped. All errors are of the following type. + */ + +typedef PRInt32 NSSError; + +/* + * NSSArena + * + * Arenas are logical sets of heap memory, from which memory may be + * allocated. When an arena is destroyed, all memory allocated within + * that arena is implicitly freed. These arenas are thread-safe: + * an arena pointer may be used by multiple threads simultaneously. + * However, as they are not backed by shared memory, they may only be + * used within one process. + */ + +struct NSSArenaStr; +typedef struct NSSArenaStr NSSArena; + +/* + * NSSItem + * + * This is the basic type used to refer to an unconstrained datum of + * arbitrary size. + */ + +struct NSSItemStr { + void *data; + PRUint32 size; +}; +typedef struct NSSItemStr NSSItem; + +/* + * NSSBER + * + * Data packed according to the Basic Encoding Rules of ASN.1. + */ + +typedef NSSItem NSSBER; + +/* + * NSSDER + * + * Data packed according to the Distinguished Encoding Rules of ASN.1; + * this form is also known as the Canonical Encoding Rules form (CER). + */ + +typedef NSSBER NSSDER; + +/* + * NSSBitString + * + * Some ASN.1 types use "bit strings," which are passed around as + * octet strings but whose length is counted in bits. We use this + * typedef of NSSItem to point out the occasions when the length + * is counted in bits, not octets. + */ + +typedef NSSItem NSSBitString; + +/* + * NSSUTF8 + * + * Character strings encoded in UTF-8, as defined by RFC 2279. + */ + +typedef char NSSUTF8; + +/* + * NSSASCII7 + * + * Character strings guaranteed to be 7-bit ASCII. + */ + +typedef char NSSASCII7; + +PR_END_EXTERN_C + +#endif /* NSSBASET_H */ diff --git a/security/nss/lib/base/tracker.c b/security/nss/lib/base/tracker.c new file mode 100644 index 0000000000..850add7c4b --- /dev/null +++ b/security/nss/lib/base/tracker.c @@ -0,0 +1,378 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * tracker.c + * + * This file contains the code used by the pointer-tracking calls used + * in the debug builds to catch bad pointers. The entire contents are + * only available in debug builds (both internal and external builds). + */ + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +#ifdef DEBUG +/* + * identity_hash + * + * This static callback is a PLHashFunction as defined in plhash.h + * It merely returns the value of the object pointer as its hash. + * There are no possible errors. + */ + +static PLHashNumber PR_CALLBACK +identity_hash(const void *key) +{ + return (PLHashNumber)((char *)key - (char *)NULL); +} + +/* + * trackerOnceFunc + * + * This function is called once, using the nssCallOnce function above. + * It creates a new pointer tracker object; initialising its hash + * table and protective lock. + */ + +static PRStatus +trackerOnceFunc(void *arg) +{ + nssPointerTracker *tracker = (nssPointerTracker *)arg; + + tracker->lock = PZ_NewLock(nssILockOther); + if ((PZLock *)NULL == tracker->lock) { + return PR_FAILURE; + } + + tracker->table = + PL_NewHashTable(0, identity_hash, PL_CompareValues, PL_CompareValues, + (PLHashAllocOps *)NULL, (void *)NULL); + if ((PLHashTable *)NULL == tracker->table) { + PZ_DestroyLock(tracker->lock); + tracker->lock = (PZLock *)NULL; + return PR_FAILURE; + } + + return PR_SUCCESS; +} + +/* + * nssPointerTracker_initialize + * + * This method is only present in debug builds. + * + * This routine initializes an nssPointerTracker object. Note that + * the object must have been declared *static* to guarantee that it + * is in a zeroed state initially. This routine is idempotent, and + * may even be safely called by multiple threads simultaneously with + * the same argument. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. On failure it will set an + * error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_IMPLEMENT PRStatus +nssPointerTracker_initialize(nssPointerTracker *tracker) +{ + PRStatus rv = PR_CallOnceWithArg(&tracker->once, trackerOnceFunc, tracker); + if (PR_SUCCESS != rv) { + nss_SetError(NSS_ERROR_NO_MEMORY); + } + + return rv; +} + +#ifdef DONT_DESTROY_EMPTY_TABLES +/* See same #ifdef below */ +/* + * count_entries + * + * This static routine is a PLHashEnumerator, as defined in plhash.h. + * It merely causes the enumeration function to count the number of + * entries. + */ + +static PRIntn PR_CALLBACK +count_entries(PLHashEntry *he, PRIntn index, void *arg) +{ + return HT_ENUMERATE_NEXT; +} +#endif /* DONT_DESTROY_EMPTY_TABLES */ + +/* + * zero_once + * + * This is a guaranteed zeroed once block. It's used to help clear + * the tracker. + */ + +static const PRCallOnceType zero_once; + +/* + * nssPointerTracker_finalize + * + * This method is only present in debug builds. + * + * This routine returns the nssPointerTracker object to the pre- + * initialized state, releasing all resources used by the object. + * It will *NOT* destroy the objects being tracked by the pointer + * (should any remain), and therefore cannot be used to "sweep up" + * remaining objects. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCES. On failure it will set an + * error on the error stack and return PR_FAILURE. If any objects + * remain in the tracker when it is finalized, that will be treated + * as an error. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_TRACKER_NOT_INITIALIZED + * NSS_ERROR_TRACKER_NOT_EMPTY + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_IMPLEMENT PRStatus +nssPointerTracker_finalize(nssPointerTracker *tracker) +{ + PZLock *lock; + + if ((nssPointerTracker *)NULL == tracker) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return PR_FAILURE; + } + + if ((PZLock *)NULL == tracker->lock) { + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); + return PR_FAILURE; + } + + lock = tracker->lock; + PZ_Lock(lock); + + if ((PLHashTable *)NULL == tracker->table) { + PZ_Unlock(lock); + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); + return PR_FAILURE; + } + +#ifdef DONT_DESTROY_EMPTY_TABLES + /* + * I changed my mind; I think we don't want this after all. + * Comments? + */ + count = PL_HashTableEnumerateEntries(tracker->table, count_entries, + (void *)NULL); + + if (0 != count) { + PZ_Unlock(lock); + nss_SetError(NSS_ERROR_TRACKER_NOT_EMPTY); + return PR_FAILURE; + } +#endif /* DONT_DESTROY_EMPTY_TABLES */ + + PL_HashTableDestroy(tracker->table); + /* memset(tracker, 0, sizeof(nssPointerTracker)); */ + tracker->once = zero_once; + tracker->lock = (PZLock *)NULL; + tracker->table = (PLHashTable *)NULL; + + PZ_Unlock(lock); + PZ_DestroyLock(lock); + + return PR_SUCCESS; +} + +/* + * nssPointerTracker_add + * + * This method is only present in debug builds. + * + * This routine adds the specified pointer to the nssPointerTracker + * object. It should be called in constructor objects to register + * new valid objects. The nssPointerTracker is threadsafe, but this + * call is not idempotent. This routine returns a PRStatus value; + * if successful it will return PR_SUCCESS. On failure it will set + * an error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_TRACKER_NOT_INITIALIZED + * NSS_ERROR_DUPLICATE_POINTER + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_IMPLEMENT PRStatus +nssPointerTracker_add(nssPointerTracker *tracker, const void *pointer) +{ + void *check; + PLHashEntry *entry; + + if ((nssPointerTracker *)NULL == tracker) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return PR_FAILURE; + } + + if ((PZLock *)NULL == tracker->lock) { + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); + return PR_FAILURE; + } + + PZ_Lock(tracker->lock); + + if ((PLHashTable *)NULL == tracker->table) { + PZ_Unlock(tracker->lock); + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); + return PR_FAILURE; + } + + check = PL_HashTableLookup(tracker->table, pointer); + if ((void *)NULL != check) { + PZ_Unlock(tracker->lock); + nss_SetError(NSS_ERROR_DUPLICATE_POINTER); + return PR_FAILURE; + } + + entry = PL_HashTableAdd(tracker->table, pointer, (void *)pointer); + + PZ_Unlock(tracker->lock); + + if ((PLHashEntry *)NULL == entry) { + nss_SetError(NSS_ERROR_NO_MEMORY); + return PR_FAILURE; + } + + return PR_SUCCESS; +} + +/* + * nssPointerTracker_remove + * + * This method is only present in debug builds. + * + * This routine removes the specified pointer from the + * nssPointerTracker object. It does not call any destructor for the + * object; rather, this should be called from the object's destructor. + * The nssPointerTracker is threadsafe, but this call is not + * idempotent. This routine returns a PRStatus value; if successful + * it will return PR_SUCCESS. On failure it will set an error on the + * error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_TRACKER_NOT_INITIALIZED + * NSS_ERROR_POINTER_NOT_REGISTERED + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_IMPLEMENT PRStatus +nssPointerTracker_remove(nssPointerTracker *tracker, const void *pointer) +{ + PRBool registered; + + if ((nssPointerTracker *)NULL == tracker) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return PR_FAILURE; + } + + if ((PZLock *)NULL == tracker->lock) { + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); + return PR_FAILURE; + } + + PZ_Lock(tracker->lock); + + if ((PLHashTable *)NULL == tracker->table) { + PZ_Unlock(tracker->lock); + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); + return PR_FAILURE; + } + + registered = PL_HashTableRemove(tracker->table, pointer); + PZ_Unlock(tracker->lock); + + if (!registered) { + nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED); + return PR_FAILURE; + } + + return PR_SUCCESS; +} + +/* + * nssPointerTracker_verify + * + * This method is only present in debug builds. + * + * This routine verifies that the specified pointer has been registered + * with the nssPointerTracker object. The nssPointerTracker object is + * threadsafe, and this call may be safely called from multiple threads + * simultaneously with the same arguments. This routine returns a + * PRStatus value; if the pointer is registered this will return + * PR_SUCCESS. Otherwise it will set an error on the error stack and + * return PR_FAILURE. Although the error is suitable for leaving on + * the stack, callers may wish to augment the information available by + * placing a more type-specific error on the stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_TRACKER_NOT_INITIALIZED + * NSS_ERROR_POINTER_NOT_REGISTERED + * + * Return value: + * PR_SUCCESS + * PR_FAILRUE + */ + +NSS_IMPLEMENT PRStatus +nssPointerTracker_verify(nssPointerTracker *tracker, const void *pointer) +{ + void *check; + + if ((nssPointerTracker *)NULL == tracker) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return PR_FAILURE; + } + + if ((PZLock *)NULL == tracker->lock) { + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); + return PR_FAILURE; + } + + PZ_Lock(tracker->lock); + + if ((PLHashTable *)NULL == tracker->table) { + PZ_Unlock(tracker->lock); + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); + return PR_FAILURE; + } + + check = PL_HashTableLookup(tracker->table, pointer); + PZ_Unlock(tracker->lock); + + if ((void *)NULL == check) { + nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED); + return PR_FAILURE; + } + + return PR_SUCCESS; +} + +#endif /* DEBUG */ diff --git a/security/nss/lib/base/utf8.c b/security/nss/lib/base/utf8.c new file mode 100644 index 0000000000..885a27afa6 --- /dev/null +++ b/security/nss/lib/base/utf8.c @@ -0,0 +1,702 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * utf8.c + * + * This file contains some additional utility routines required for + * handling UTF8 strings. + */ + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +#include "plstr.h" + +/* + * NOTES: + * + * There's an "is hex string" function in pki1/atav.c. If we need + * it in more places, pull that one out. + */ + +/* + * nssUTF8_CaseIgnoreMatch + * + * Returns true if the two UTF8-encoded strings pointed to by the + * two specified NSSUTF8 pointers differ only in typcase. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * PR_TRUE if the strings match, ignoring case + * PR_FALSE if they don't + * PR_FALSE upon error + */ + +NSS_IMPLEMENT PRBool +nssUTF8_CaseIgnoreMatch(const NSSUTF8 *a, const NSSUTF8 *b, PRStatus *statusOpt) +{ +#ifdef NSSDEBUG + if (((const NSSUTF8 *)NULL == a) || ((const NSSUTF8 *)NULL == b)) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_FAILURE; + } + return PR_FALSE; + } +#endif /* NSSDEBUG */ + + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_SUCCESS; + } + + /* + * XXX fgmr + * + * This is, like, so wrong! + */ + if (0 == PL_strcasecmp((const char *)a, (const char *)b)) { + return PR_TRUE; + } else { + return PR_FALSE; + } +} + +/* + * nssUTF8_PrintableMatch + * + * Returns true if the two Printable strings pointed to by the + * two specified NSSUTF8 pointers match when compared with the + * rules for Printable String (leading and trailing spaces are + * disregarded, extents of whitespace match irregardless of length, + * and case is not significant), then PR_TRUE will be returned. + * Otherwise, PR_FALSE will be returned. Upon failure, PR_FALSE + * will be returned. If the optional statusOpt argument is not + * NULL, then PR_SUCCESS or PR_FAILURE will be stored in that + * location. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * PR_TRUE if the strings match, ignoring case + * PR_FALSE if they don't + * PR_FALSE upon error + */ + +NSS_IMPLEMENT PRBool +nssUTF8_PrintableMatch(const NSSUTF8 *a, const NSSUTF8 *b, PRStatus *statusOpt) +{ + PRUint8 *c; + PRUint8 *d; + +#ifdef NSSDEBUG + if (((const NSSUTF8 *)NULL == a) || ((const NSSUTF8 *)NULL == b)) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_FAILURE; + } + return PR_FALSE; + } +#endif /* NSSDEBUG */ + + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_SUCCESS; + } + + c = (PRUint8 *)a; + d = (PRUint8 *)b; + + while (' ' == *c) { + c++; + } + + while (' ' == *d) { + d++; + } + + while (('\0' != *c) && ('\0' != *d)) { + PRUint8 e, f; + + e = *c; + f = *d; + + if (('a' <= e) && (e <= 'z')) { + e -= ('a' - 'A'); + } + + if (('a' <= f) && (f <= 'z')) { + f -= ('a' - 'A'); + } + + if (e != f) { + return PR_FALSE; + } + + c++; + d++; + + if (' ' == *c) { + while (' ' == *c) { + c++; + } + c--; + } + + if (' ' == *d) { + while (' ' == *d) { + d++; + } + d--; + } + } + + while (' ' == *c) { + c++; + } + + while (' ' == *d) { + d++; + } + + if (*c == *d) { + /* And both '\0', btw */ + return PR_TRUE; + } else { + return PR_FALSE; + } +} + +/* + * nssUTF8_Duplicate + * + * This routine duplicates the UTF8-encoded string pointed to by the + * specified NSSUTF8 pointer. If the optional arenaOpt argument is + * not null, the memory required will be obtained from that arena; + * otherwise, the memory required will be obtained from the heap. + * A pointer to the new string will be returned. In case of error, + * an error will be placed on the error stack and NULL will be + * returned. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + */ + +NSS_IMPLEMENT NSSUTF8 * +nssUTF8_Duplicate(const NSSUTF8 *s, NSSArena *arenaOpt) +{ + NSSUTF8 *rv; + PRUint32 len; + +#ifdef NSSDEBUG + if ((const NSSUTF8 *)NULL == s) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (NSSUTF8 *)NULL; + } + + if ((NSSArena *)NULL != arenaOpt) { + if (PR_SUCCESS != nssArena_verifyPointer(arenaOpt)) { + return (NSSUTF8 *)NULL; + } + } +#endif /* NSSDEBUG */ + + len = PL_strlen((const char *)s); +#ifdef PEDANTIC + if ('\0' != ((const char *)s)[len]) { + /* must have wrapped, e.g., too big for PRUint32 */ + nss_SetError(NSS_ERROR_NO_MEMORY); + return (NSSUTF8 *)NULL; + } +#endif /* PEDANTIC */ + len++; /* zero termination */ + + rv = nss_ZAlloc(arenaOpt, len); + if ((void *)NULL == rv) { + return (NSSUTF8 *)NULL; + } + + (void)nsslibc_memcpy(rv, s, len); + return rv; +} + +/* + * nssUTF8_Size + * + * This routine returns the length in bytes (including the terminating + * null) of the UTF8-encoded string pointed to by the specified + * NSSUTF8 pointer. Zero is returned on error. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_VALUE_TOO_LARGE + * + * Return value: + * 0 on error + * nonzero length of the string. + */ + +NSS_IMPLEMENT PRUint32 +nssUTF8_Size(const NSSUTF8 *s, PRStatus *statusOpt) +{ + PRUint32 sv; + +#ifdef NSSDEBUG + if ((const NSSUTF8 *)NULL == s) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_FAILURE; + } + return 0; + } +#endif /* NSSDEBUG */ + + sv = PL_strlen((const char *)s) + 1; +#ifdef PEDANTIC + if ('\0' != ((const char *)s)[sv - 1]) { + /* wrapped */ + nss_SetError(NSS_ERROR_VALUE_TOO_LARGE); + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_FAILURE; + } + return 0; + } +#endif /* PEDANTIC */ + + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_SUCCESS; + } + + return sv; +} + +/* + * nssUTF8_Length + * + * This routine returns the length in characters (not including the + * terminating null) of the UTF8-encoded string pointed to by the + * specified NSSUTF8 pointer. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_VALUE_TOO_LARGE + * NSS_ERROR_INVALID_STRING + * + * Return value: + * length of the string (which may be zero) + * 0 on error + */ + +NSS_IMPLEMENT PRUint32 +nssUTF8_Length(const NSSUTF8 *s, PRStatus *statusOpt) +{ + PRUint32 l = 0; + const PRUint8 *c = (const PRUint8 *)s; + +#ifdef NSSDEBUG + if ((const NSSUTF8 *)NULL == s) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + goto loser; + } +#endif /* NSSDEBUG */ + + /* + * From RFC 3629: + * + * UTF8-octets = *( UTF8-char ) + * UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4 + * UTF8-1 = %x00-7F + * UTF8-2 = %xC2-DF UTF8-tail + * UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / + * %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) + * UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / + * %xF4 %x80-8F 2( UTF8-tail ) + * UTF8-tail = %x80-BF + */ + + while (0 != *c) { + PRUint32 incr; + if (*c < 0x80) { + incr = 1; + } else if (*c < 0xC2) { + nss_SetError(NSS_ERROR_INVALID_STRING); + goto loser; + } else if (*c < 0xE0) { + incr = 2; + } else if (*c == 0xE0) { + if (c[1] < 0xA0) { + nss_SetError(NSS_ERROR_INVALID_STRING); + goto loser; + } + incr = 3; + } else if (*c < 0xF0) { + if (*c == 0xED && c[1] > 0x9F) { + nss_SetError(NSS_ERROR_INVALID_STRING); + goto loser; + } + incr = 3; + } else if (*c == 0xF0) { + if (c[1] < 0x90) { + nss_SetError(NSS_ERROR_INVALID_STRING); + goto loser; + } + incr = 4; + } else if (*c < 0xF4) { + incr = 4; + } else if (*c == 0xF4) { + if (c[1] > 0x8F) { + nss_SetError(NSS_ERROR_INVALID_STRING); + goto loser; + } + incr = 4; + } else { + nss_SetError(NSS_ERROR_INVALID_STRING); + goto loser; + } + + l += incr; + +#ifdef PEDANTIC + if (l < incr) { + /* Wrapped-- too big */ + nss_SetError(NSS_ERROR_VALUE_TOO_LARGE); + goto loser; + } +#endif /* PEDANTIC */ + + { + const PRUint8 *d; + for (d = &c[1]; d < &c[incr]; d++) { + if ((*d & 0xC0) != 0x80) { + nss_SetError(NSS_ERROR_INVALID_STRING); + goto loser; + } + } + } + + c += incr; + } + + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_SUCCESS; + } + + return l; + +loser: + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_FAILURE; + } + + return 0; +} + +/* + * nssUTF8_Create + * + * This routine creates a UTF8 string from a string in some other + * format. Some types of string may include embedded null characters, + * so for them the length parameter must be used. For string types + * that are null-terminated, the length parameter is optional; if it + * is zero, it will be ignored. If the optional arena argument is + * non-null, the memory used for the new string will be obtained from + * that arena, otherwise it will be obtained from the heap. This + * routine may return NULL upon error, in which case it will have + * placed an error on the error stack. + * + * The error may be one of the following: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_UNSUPPORTED_TYPE + * + * Return value: + * NULL upon error + * A non-null pointer to a new UTF8 string otherwise + */ + +extern const NSSError NSS_ERROR_INTERNAL_ERROR; /* XXX fgmr */ + +NSS_IMPLEMENT NSSUTF8 * +nssUTF8_Create(NSSArena *arenaOpt, nssStringType type, const void *inputString, + PRUint32 size /* in bytes, not characters */ +) +{ + NSSUTF8 *rv = NULL; + +#ifdef NSSDEBUG + if ((NSSArena *)NULL != arenaOpt) { + if (PR_SUCCESS != nssArena_verifyPointer(arenaOpt)) { + return (NSSUTF8 *)NULL; + } + } + + if ((const void *)NULL == inputString) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (NSSUTF8 *)NULL; + } +#endif /* NSSDEBUG */ + + switch (type) { + case nssStringType_DirectoryString: + /* This is a composite type requiring BER */ + nss_SetError(NSS_ERROR_UNSUPPORTED_TYPE); + break; + case nssStringType_TeletexString: + /* + * draft-ietf-pkix-ipki-part1-11 says in part: + * + * In addition, many legacy implementations support names encoded + * in the ISO 8859-1 character set (Latin1String) but tag them as + * TeletexString. The Latin1String includes characters used in + * Western European countries which are not part of the + * TeletexString charcter set. Implementations that process + * TeletexString SHOULD be prepared to handle the entire ISO + * 8859-1 character set.[ISO 8859-1]. + */ + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + case nssStringType_PrintableString: + /* + * PrintableString consists of A-Za-z0-9 ,()+,-./:=? + * This is a subset of ASCII, which is a subset of UTF8. + * So we can just duplicate the string over. + */ + + if (0 == size) { + rv = nssUTF8_Duplicate((const NSSUTF8 *)inputString, arenaOpt); + } else { + rv = nss_ZAlloc(arenaOpt, size + 1); + if ((NSSUTF8 *)NULL == rv) { + return (NSSUTF8 *)NULL; + } + + (void)nsslibc_memcpy(rv, inputString, size); + } + + break; + case nssStringType_UniversalString: + /* 4-byte unicode */ + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + case nssStringType_BMPString: + /* Base Multilingual Plane of Unicode */ + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + case nssStringType_UTF8String: + if (0 == size) { + rv = nssUTF8_Duplicate((const NSSUTF8 *)inputString, arenaOpt); + } else { + rv = nss_ZAlloc(arenaOpt, size + 1); + if ((NSSUTF8 *)NULL == rv) { + return (NSSUTF8 *)NULL; + } + + (void)nsslibc_memcpy(rv, inputString, size); + } + + break; + case nssStringType_PHGString: + /* + * PHGString is an IA5String (with case-insensitive comparisons). + * IA5 is ~almost~ ascii; ascii has dollar-sign where IA5 has + * currency symbol. + */ + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + case nssStringType_GeneralString: + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + default: + nss_SetError(NSS_ERROR_UNSUPPORTED_TYPE); + break; + } + + return rv; +} + +NSS_IMPLEMENT NSSItem * +nssUTF8_GetEncoding(NSSArena *arenaOpt, NSSItem *rvOpt, nssStringType type, + NSSUTF8 *string) +{ + NSSItem *rv = (NSSItem *)NULL; + PRStatus status = PR_SUCCESS; + +#ifdef NSSDEBUG + if ((NSSArena *)NULL != arenaOpt) { + if (PR_SUCCESS != nssArena_verifyPointer(arenaOpt)) { + return (NSSItem *)NULL; + } + } + + if ((NSSUTF8 *)NULL == string) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (NSSItem *)NULL; + } +#endif /* NSSDEBUG */ + + switch (type) { + case nssStringType_DirectoryString: + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + case nssStringType_TeletexString: + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + case nssStringType_PrintableString: + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + case nssStringType_UniversalString: + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + case nssStringType_BMPString: + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + case nssStringType_UTF8String: { + NSSUTF8 *dup = nssUTF8_Duplicate(string, arenaOpt); + if ((NSSUTF8 *)NULL == dup) { + return (NSSItem *)NULL; + } + + if ((NSSItem *)NULL == rvOpt) { + rv = nss_ZNEW(arenaOpt, NSSItem); + if ((NSSItem *)NULL == rv) { + (void)nss_ZFreeIf(dup); + return (NSSItem *)NULL; + } + } else { + rv = rvOpt; + } + + rv->data = dup; + dup = (NSSUTF8 *)NULL; + rv->size = nssUTF8_Size(rv->data, &status); + if ((0 == rv->size) && (PR_SUCCESS != status)) { + if ((NSSItem *)NULL == rvOpt) { + (void)nss_ZFreeIf(rv); + } + return (NSSItem *)NULL; + } + } break; + case nssStringType_PHGString: + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + default: + nss_SetError(NSS_ERROR_UNSUPPORTED_TYPE); + break; + } + + return rv; +} + +/* + * nssUTF8_CopyIntoFixedBuffer + * + * This will copy a UTF8 string into a fixed-length buffer, making + * sure that the all characters are valid. Any remaining space will + * be padded with the specified ASCII character, typically either + * null or space. + * + * Blah, blah, blah. + */ + +NSS_IMPLEMENT PRStatus +nssUTF8_CopyIntoFixedBuffer(NSSUTF8 *string, char *buffer, PRUint32 bufferSize, + char pad) +{ + PRUint32 stringSize = 0; + +#ifdef NSSDEBUG + if ((char *)NULL == buffer) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return PR_FALSE; + } + + if (0 == bufferSize) { + nss_SetError(NSS_ERROR_INVALID_ARGUMENT); + return PR_FALSE; + } + + if ((pad & 0x80) != 0x00) { + nss_SetError(NSS_ERROR_INVALID_ARGUMENT); + return PR_FALSE; + } +#endif /* NSSDEBUG */ + + if ((NSSUTF8 *)NULL == string) { + string = (NSSUTF8 *)""; + } + + stringSize = nssUTF8_Size(string, (PRStatus *)NULL); + stringSize--; /* don't count the trailing null */ + if (stringSize > bufferSize) { + PRUint32 bs = bufferSize; + (void)nsslibc_memcpy(buffer, string, bufferSize); + + if ((((buffer[bs - 1] & 0x80) == 0x00)) || + ((bs > 1) && ((buffer[bs - 2] & 0xE0) == 0xC0)) || + ((bs > 2) && ((buffer[bs - 3] & 0xF0) == 0xE0)) || + ((bs > 3) && ((buffer[bs - 4] & 0xF8) == 0xF0)) || + ((bs > 4) && ((buffer[bs - 5] & 0xFC) == 0xF8)) || + ((bs > 5) && ((buffer[bs - 6] & 0xFE) == 0xFC))) { + /* It fit exactly */ + return PR_SUCCESS; + } + + /* Too long. We have to trim the last character */ + for (/*bs*/; bs != 0; bs--) { + if ((buffer[bs - 1] & 0xC0) != 0x80) { + buffer[bs - 1] = pad; + break; + } else { + buffer[bs - 1] = pad; + } + } + } else { + (void)nsslibc_memset(buffer, pad, bufferSize); + (void)nsslibc_memcpy(buffer, string, stringSize); + } + + return PR_SUCCESS; +} + +/* + * nssUTF8_Equal + * + */ + +NSS_IMPLEMENT PRBool +nssUTF8_Equal(const NSSUTF8 *a, const NSSUTF8 *b, PRStatus *statusOpt) +{ + PRUint32 la, lb; + +#ifdef NSSDEBUG + if (((const NSSUTF8 *)NULL == a) || ((const NSSUTF8 *)NULL == b)) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_FAILURE; + } + return PR_FALSE; + } +#endif /* NSSDEBUG */ + + la = nssUTF8_Size(a, statusOpt); + if (0 == la) { + return PR_FALSE; + } + + lb = nssUTF8_Size(b, statusOpt); + if (0 == lb) { + return PR_FALSE; + } + + if (la != lb) { + return PR_FALSE; + } + + return nsslibc_memequal(a, b, la, statusOpt); +} diff --git a/security/nss/lib/certdb/Makefile b/security/nss/lib/certdb/Makefile new file mode 100644 index 0000000000..bb88772853 --- /dev/null +++ b/security/nss/lib/certdb/Makefile @@ -0,0 +1,45 @@ +#! gmake +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +####################################################################### +# (1) Include initial platform-independent assignments (MANDATORY). # +####################################################################### + +include manifest.mn + +####################################################################### +# (2) Include "global" configuration information. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/config.mk + +####################################################################### +# (3) Include "component" configuration information. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (4) Include "local" platform-dependent assignments (OPTIONAL). # +####################################################################### + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### + + diff --git a/security/nss/lib/certdb/alg1485.c b/security/nss/lib/certdb/alg1485.c new file mode 100644 index 0000000000..0cf9602f18 --- /dev/null +++ b/security/nss/lib/certdb/alg1485.c @@ -0,0 +1,1590 @@ +/* alg1485.c - implementation of RFCs 1485, 1779 and 2253. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prprf.h" +#include "cert.h" +#include "certi.h" +#include "xconst.h" +#include "genname.h" +#include "secitem.h" +#include "secerr.h" + +typedef struct NameToKindStr { + const char* name; + unsigned int maxLen; /* max bytes in UTF8 encoded string value */ + SECOidTag kind; + int valueType; +} NameToKind; + +/* local type for directory string--could be printable_string or utf8 */ +#define SEC_ASN1_DS SEC_ASN1_HIGH_TAG_NUMBER + +/* clang-format off */ + +/* Add new entries to this table, and maybe to function ParseRFC1485AVA */ +static const NameToKind name2kinds[] = { +/* IANA registered type names + * (See: http://www.iana.org/assignments/ldap-parameters) + */ +/* RFC 3280, 4630 MUST SUPPORT */ + { "CN", 640, SEC_OID_AVA_COMMON_NAME, SEC_ASN1_DS}, + { "ST", 128, SEC_OID_AVA_STATE_OR_PROVINCE, + SEC_ASN1_DS}, + { "O", 128, SEC_OID_AVA_ORGANIZATION_NAME, + SEC_ASN1_DS}, + { "OU", 128, SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME, + SEC_ASN1_DS}, + { "dnQualifier", 32767, SEC_OID_AVA_DN_QUALIFIER, SEC_ASN1_PRINTABLE_STRING}, + { "C", 2, SEC_OID_AVA_COUNTRY_NAME, SEC_ASN1_PRINTABLE_STRING}, + { "serialNumber", 64, SEC_OID_AVA_SERIAL_NUMBER,SEC_ASN1_PRINTABLE_STRING}, + +/* RFC 3280, 4630 SHOULD SUPPORT */ + { "L", 128, SEC_OID_AVA_LOCALITY, SEC_ASN1_DS}, + { "title", 64, SEC_OID_AVA_TITLE, SEC_ASN1_DS}, + { "SN", 64, SEC_OID_AVA_SURNAME, SEC_ASN1_DS}, + { "givenName", 64, SEC_OID_AVA_GIVEN_NAME, SEC_ASN1_DS}, + { "initials", 64, SEC_OID_AVA_INITIALS, SEC_ASN1_DS}, + { "generationQualifier", + 64, SEC_OID_AVA_GENERATION_QUALIFIER, + SEC_ASN1_DS}, +/* RFC 3280, 4630 MAY SUPPORT */ + { "DC", 128, SEC_OID_AVA_DC, SEC_ASN1_IA5_STRING}, + { "MAIL", 256, SEC_OID_RFC1274_MAIL, SEC_ASN1_IA5_STRING}, + { "UID", 256, SEC_OID_RFC1274_UID, SEC_ASN1_DS}, + +/* ------------------ "strict" boundary --------------------------------- + * In strict mode, cert_NameToAscii does not encode any of the attributes + * below this line. The first SECOidTag below this line must be used to + * conditionally define the "endKind" in function AppendAVA() below. + * Most new attribute names should be added below this line. + * Maybe this line should be up higher? Say, after the 3280 MUSTs and + * before the 3280 SHOULDs? + */ + +/* values from draft-ietf-ldapbis-user-schema-05 (not in RFC 3280) */ + { "postalAddress", 128, SEC_OID_AVA_POSTAL_ADDRESS, SEC_ASN1_DS}, + { "postalCode", 40, SEC_OID_AVA_POSTAL_CODE, SEC_ASN1_DS}, + { "postOfficeBox", 40, SEC_OID_AVA_POST_OFFICE_BOX,SEC_ASN1_DS}, + { "houseIdentifier",64, SEC_OID_AVA_HOUSE_IDENTIFIER,SEC_ASN1_DS}, +/* end of IANA registered type names */ + +/* legacy keywords */ + { "E", 128, SEC_OID_PKCS9_EMAIL_ADDRESS,SEC_ASN1_IA5_STRING}, + { "STREET", 128, SEC_OID_AVA_STREET_ADDRESS, SEC_ASN1_DS}, + { "pseudonym", 64, SEC_OID_AVA_PSEUDONYM, SEC_ASN1_DS}, + +/* values defined by the CAB Forum for EV */ + { "incorporationLocality", 128, SEC_OID_EV_INCORPORATION_LOCALITY, + SEC_ASN1_DS}, + { "incorporationState", 128, SEC_OID_EV_INCORPORATION_STATE, + SEC_ASN1_DS}, + { "incorporationCountry", 2, SEC_OID_EV_INCORPORATION_COUNTRY, + SEC_ASN1_PRINTABLE_STRING}, + { "businessCategory", 64, SEC_OID_BUSINESS_CATEGORY, SEC_ASN1_DS}, + +/* values defined in X.520 */ + { "name", 64, SEC_OID_AVA_NAME, SEC_ASN1_DS}, + + { 0, 256, SEC_OID_UNKNOWN, 0}, +}; + +/* Table facilitates conversion of ASCII hex to binary. */ +static const PRInt16 x2b[256] = { +/* #0x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +/* #1x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +/* #2x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +/* #3x */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, +/* #4x */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, +/* #5x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +/* #6x */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, +/* #7x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +/* #8x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +/* #9x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +/* #ax */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +/* #bx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +/* #cx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +/* #dx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +/* #ex */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +/* #fx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +#define IS_HEX(c) (x2b[(PRUint8)(c)] >= 0) + +#define C_DOUBLE_QUOTE '\042' + +#define C_BACKSLASH '\134' + +#define C_EQUAL '=' + +#define OPTIONAL_SPACE(c) \ + (((c) == ' ') || ((c) == '\r') || ((c) == '\n')) + +#define SPECIAL_CHAR(c) \ + (((c) == ',') || ((c) == '=') || ((c) == C_DOUBLE_QUOTE) || \ + ((c) == '\r') || ((c) == '\n') || ((c) == '+') || \ + ((c) == '<') || ((c) == '>') || ((c) == '#') || \ + ((c) == ';') || ((c) == C_BACKSLASH)) + + +#define IS_PRINTABLE(c) \ + ((((c) >= 'a') && ((c) <= 'z')) || \ + (((c) >= 'A') && ((c) <= 'Z')) || \ + (((c) >= '0') && ((c) <= '9')) || \ + ((c) == ' ') || \ + ((c) == '\'') || \ + ((c) == '\050') || /* ( */ \ + ((c) == '\051') || /* ) */ \ + (((c) >= '+') && ((c) <= '/')) || /* + , - . / */ \ + ((c) == ':') || \ + ((c) == '=') || \ + ((c) == '?')) + +/* clang-format on */ + +/* RFC 2253 says we must escape ",+\"\\<>;=" EXCEPT inside a quoted string. + * Inside a quoted string, we only need to escape " and \ + * We choose to quote strings containing any of those special characters, + * so we only need to escape " and \ + */ +#define NEEDS_ESCAPE(c) (c == C_DOUBLE_QUOTE || c == C_BACKSLASH) + +#define NEEDS_HEX_ESCAPE(c) ((PRUint8)c < 0x20 || c == 0x7f) + +int +cert_AVAOidTagToMaxLen(SECOidTag tag) +{ + const NameToKind* n2k = name2kinds; + + while (n2k->kind != tag && n2k->kind != SEC_OID_UNKNOWN) { + ++n2k; + } + return (n2k->kind != SEC_OID_UNKNOWN) ? n2k->maxLen : -1; +} + +static PRBool +IsPrintable(unsigned char* data, unsigned len) +{ + unsigned char ch, *end; + + end = data + len; + while (data < end) { + ch = *data++; + if (!IS_PRINTABLE(ch)) { + return PR_FALSE; + } + } + return PR_TRUE; +} + +static void +skipSpace(const char** pbp, const char* endptr) +{ + const char* bp = *pbp; + while (bp < endptr && OPTIONAL_SPACE(*bp)) { + bp++; + } + *pbp = bp; +} + +static SECStatus +scanTag(const char** pbp, const char* endptr, char* tagBuf, int tagBufSize) +{ + const char* bp; + char* tagBufp; + int taglen; + + PORT_Assert(tagBufSize > 0); + + /* skip optional leading space */ + skipSpace(pbp, endptr); + if (*pbp == endptr) { + /* nothing left */ + return SECFailure; + } + + /* fill tagBuf */ + taglen = 0; + bp = *pbp; + tagBufp = tagBuf; + while (bp < endptr && !OPTIONAL_SPACE(*bp) && (*bp != C_EQUAL)) { + if (++taglen >= tagBufSize) { + *pbp = bp; + return SECFailure; + } + *tagBufp++ = *bp++; + } + /* null-terminate tagBuf -- guaranteed at least one space left */ + *tagBufp++ = 0; + *pbp = bp; + + /* skip trailing spaces till we hit something - should be an equal sign */ + skipSpace(pbp, endptr); + if (*pbp == endptr) { + /* nothing left */ + return SECFailure; + } + if (**pbp != C_EQUAL) { + /* should be an equal sign */ + return SECFailure; + } + /* skip over the equal sign */ + (*pbp)++; + + return SECSuccess; +} + +/* Returns the number of bytes in the value. 0 means failure. */ +static int +scanVal(const char** pbp, const char* endptr, char* valBuf, int valBufSize) +{ + const char* bp; + char* valBufp; + int vallen = 0; + PRBool isQuoted; + + PORT_Assert(valBufSize > 0); + + /* skip optional leading space */ + skipSpace(pbp, endptr); + if (*pbp == endptr) { + /* nothing left */ + return 0; + } + + bp = *pbp; + + /* quoted? */ + if (*bp == C_DOUBLE_QUOTE) { + isQuoted = PR_TRUE; + /* skip over it */ + bp++; + } else { + isQuoted = PR_FALSE; + } + + valBufp = valBuf; + while (bp < endptr) { + char c = *bp; + if (c == C_BACKSLASH) { + /* escape character */ + bp++; + if (bp >= endptr) { + /* escape charater must appear with paired char */ + *pbp = bp; + return 0; + } + c = *bp; + if (IS_HEX(c) && (endptr - bp) >= 2 && IS_HEX(bp[1])) { + bp++; + c = (char)((x2b[(PRUint8)c] << 4) | x2b[(PRUint8)*bp]); + } + } else if (c == '#' && bp == *pbp) { + /* ignore leading #, quotation not required for it. */ + } else if (!isQuoted && SPECIAL_CHAR(c)) { + /* unescaped special and not within quoted value */ + break; + } else if (c == C_DOUBLE_QUOTE) { + /* reached unescaped double quote */ + break; + } + /* append character */ + vallen++; + if (vallen >= valBufSize) { + *pbp = bp; + return 0; + } + *valBufp++ = c; + bp++; + } + + /* strip trailing spaces from unquoted values */ + if (!isQuoted) { + while (valBufp > valBuf) { + char c = valBufp[-1]; + if (!OPTIONAL_SPACE(c)) + break; + --valBufp; + } + vallen = valBufp - valBuf; + } + + if (isQuoted) { + /* insist that we stopped on a double quote */ + if (*bp != C_DOUBLE_QUOTE) { + *pbp = bp; + return 0; + } + /* skip over the quote and skip optional space */ + bp++; + skipSpace(&bp, endptr); + } + + *pbp = bp; + + /* null-terminate valBuf -- guaranteed at least one space left */ + *valBufp = 0; + + return vallen; +} + +/* Caller must set error code upon failure */ +static SECStatus +hexToBin(PLArenaPool* pool, SECItem* destItem, const char* src, int len) +{ + PRUint8* dest; + + destItem->data = NULL; + if (len <= 0 || (len & 1)) { + goto loser; + } + len >>= 1; + if (!SECITEM_AllocItem(pool, destItem, len)) { + goto loser; + } + dest = destItem->data; + for (; len > 0; len--, src += 2) { + PRUint16 bin = ((PRUint16)x2b[(PRUint8)src[0]] << 4); + bin |= (PRUint16)x2b[(PRUint8)src[1]]; + if (bin >> 15) { /* is negative */ + goto loser; + } + *dest++ = (PRUint8)bin; + } + return SECSuccess; +loser: + if (!pool) + SECITEM_FreeItem(destItem, PR_FALSE); + return SECFailure; +} + +/* Parses one AVA, starting at *pbp. Stops at endptr. + * Advances *pbp past parsed AVA and trailing separator (if present). + * On any error, returns NULL and *pbp is undefined. + * On success, returns CERTAVA allocated from arena, and (*pbp)[-1] was + * the last character parsed. *pbp is either equal to endptr or + * points to first character after separator. + */ +static CERTAVA* +ParseRFC1485AVA(PLArenaPool* arena, const char** pbp, const char* endptr) +{ + CERTAVA* a; + const NameToKind* n2k; + const char* bp; + int vt = -1; + int valLen; + PRBool isDottedOid = PR_FALSE; + SECOidTag kind = SEC_OID_UNKNOWN; + SECStatus rv = SECFailure; + SECItem derOid = { 0, NULL, 0 }; + SECItem derVal = { 0, NULL, 0 }; + char sep = 0; + + char tagBuf[32]; + char valBuf[1024]; + + PORT_Assert(arena); + if (SECSuccess != scanTag(pbp, endptr, tagBuf, sizeof tagBuf) || + !(valLen = scanVal(pbp, endptr, valBuf, sizeof valBuf))) { + goto loser; + } + + bp = *pbp; + if (bp < endptr) { + sep = *bp++; /* skip over separator */ + } + *pbp = bp; + /* if we haven't finished, insist that we've stopped on a separator */ + if (sep && sep != ',' && sep != ';' && sep != '+') { + goto loser; + } + + /* is this a dotted decimal OID attribute type ? */ + if (!PL_strncasecmp("oid.", tagBuf, 4) || isdigit(tagBuf[0])) { + rv = SEC_StringToOID(arena, &derOid, tagBuf, strlen(tagBuf)); + isDottedOid = (PRBool)(rv == SECSuccess); + } else { + for (n2k = name2kinds; n2k->name; n2k++) { + SECOidData* oidrec; + if (PORT_Strcasecmp(n2k->name, tagBuf) == 0) { + kind = n2k->kind; + vt = n2k->valueType; + oidrec = SECOID_FindOIDByTag(kind); + if (oidrec == NULL) + goto loser; + derOid = oidrec->oid; + break; + } + } + } + if (kind == SEC_OID_UNKNOWN && rv != SECSuccess) + goto loser; + + /* Is this a hex encoding of a DER attribute value ? */ + if ('#' == valBuf[0]) { + /* convert attribute value from hex to binary */ + rv = hexToBin(arena, &derVal, valBuf + 1, valLen - 1); + if (rv) + goto loser; + a = CERT_CreateAVAFromRaw(arena, &derOid, &derVal); + } else { + if (kind == SEC_OID_AVA_COUNTRY_NAME && valLen != 2) + goto loser; + if (vt == SEC_ASN1_PRINTABLE_STRING && + !IsPrintable((unsigned char*)valBuf, valLen)) + goto loser; + if (vt == SEC_ASN1_DS) { + /* RFC 4630: choose PrintableString or UTF8String */ + if (IsPrintable((unsigned char*)valBuf, valLen)) + vt = SEC_ASN1_PRINTABLE_STRING; + else + vt = SEC_ASN1_UTF8_STRING; + } + + derVal.data = (unsigned char*)valBuf; + derVal.len = valLen; + if (kind == SEC_OID_UNKNOWN && isDottedOid) { + a = CERT_CreateAVAFromRaw(arena, &derOid, &derVal); + } else { + a = CERT_CreateAVAFromSECItem(arena, kind, vt, &derVal); + } + } + return a; + +loser: + /* matched no kind -- invalid tag */ + PORT_SetError(SEC_ERROR_INVALID_AVA); + return 0; +} + +static CERTName* +ParseRFC1485Name(const char* buf, int len) +{ + SECStatus rv; + CERTName* name; + const char *bp, *e; + CERTAVA* ava; + CERTRDN* rdn = NULL; + + name = CERT_CreateName(NULL); + if (name == NULL) { + return NULL; + } + + e = buf + len; + bp = buf; + while (bp < e) { + ava = ParseRFC1485AVA(name->arena, &bp, e); + if (ava == 0) + goto loser; + if (!rdn) { + rdn = CERT_CreateRDN(name->arena, ava, (CERTAVA*)0); + if (rdn == 0) + goto loser; + rv = CERT_AddRDN(name, rdn); + } else { + rv = CERT_AddAVA(name->arena, rdn, ava); + } + if (rv) + goto loser; + if (bp[-1] != '+') + rdn = NULL; /* done with this RDN */ + skipSpace(&bp, e); + } + + if (name->rdns[0] == 0) { + /* empty name -- illegal */ + goto loser; + } + + /* Reverse order of RDNS to comply with RFC */ + { + CERTRDN** firstRdn; + CERTRDN** lastRdn; + CERTRDN* tmp; + + /* get first one */ + firstRdn = name->rdns; + + /* find last one */ + lastRdn = name->rdns; + while (*lastRdn) + lastRdn++; + lastRdn--; + + /* reverse list */ + for (; firstRdn < lastRdn; firstRdn++, lastRdn--) { + tmp = *firstRdn; + *firstRdn = *lastRdn; + *lastRdn = tmp; + } + } + + /* return result */ + return name; + +loser: + CERT_DestroyName(name); + return NULL; +} + +CERTName* +CERT_AsciiToName(const char* string) +{ + CERTName* name; + name = ParseRFC1485Name(string, PORT_Strlen(string)); + return name; +} + +/************************************************************************/ + +typedef struct stringBufStr { + char* buffer; + unsigned offset; + unsigned size; +} stringBuf; + +#define DEFAULT_BUFFER_SIZE 200 + +static SECStatus +AppendStr(stringBuf* bufp, char* str) +{ + char* buf; + unsigned bufLen, bufSize, len; + int size = 0; + + /* Figure out how much to grow buf by (add in the '\0') */ + buf = bufp->buffer; + bufLen = bufp->offset; + len = PORT_Strlen(str); + bufSize = bufLen + len; + if (!buf) { + bufSize++; + size = PR_MAX(DEFAULT_BUFFER_SIZE, bufSize * 2); + buf = (char*)PORT_Alloc(size); + bufp->size = size; + } else if (bufp->size < bufSize) { + size = bufSize * 2; + buf = (char*)PORT_Realloc(buf, size); + bufp->size = size; + } + if (!buf) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + bufp->buffer = buf; + bufp->offset = bufSize; + + /* Concatenate str onto buf */ + buf = buf + bufLen; + if (bufLen) + buf--; /* stomp on old '\0' */ + PORT_Memcpy(buf, str, len + 1); /* put in new null */ + return SECSuccess; +} + +typedef enum { + minimalEscape = 0, /* only hex escapes, and " and \ */ + minimalEscapeAndQuote, /* as above, plus quoting */ + fullEscape /* no quoting, full escaping */ +} EQMode; + +/* Some characters must be escaped as a hex string, e.g. c -> \nn . + * Others must be escaped by preceding with a '\', e.g. c -> \c , but + * there are certain "special characters" that may be handled by either + * escaping them, or by enclosing the entire attribute value in quotes. + * A NULL value for pEQMode implies selecting minimalEscape mode. + * Some callers will do quoting when needed, others will not. + * If a caller selects minimalEscapeAndQuote, and the string does not + * need quoting, then this function changes it to minimalEscape. + */ +static int +cert_RFC1485_GetRequiredLen(const char* src, int srclen, EQMode* pEQMode) +{ + int i, reqLen = 0; + EQMode mode = pEQMode ? *pEQMode : minimalEscape; + PRBool needsQuoting = PR_FALSE; + char lastC = 0; + + /* need to make an initial pass to determine if quoting is needed */ + for (i = 0; i < srclen; i++) { + char c = src[i]; + reqLen++; + if (NEEDS_HEX_ESCAPE(c)) { /* c -> \xx */ + reqLen += 2; + } else if (NEEDS_ESCAPE(c)) { /* c -> \c */ + reqLen++; + } else if (SPECIAL_CHAR(c)) { + if (mode == minimalEscapeAndQuote) /* quoting is allowed */ + needsQuoting = PR_TRUE; /* entirety will need quoting */ + else if (mode == fullEscape) + reqLen++; /* MAY escape this character */ + } else if (OPTIONAL_SPACE(c) && OPTIONAL_SPACE(lastC)) { + if (mode == minimalEscapeAndQuote) /* quoting is allowed */ + needsQuoting = PR_TRUE; /* entirety will need quoting */ + } + lastC = c; + } + /* if it begins or ends in optional space it needs quoting */ + if (!needsQuoting && srclen > 0 && mode == minimalEscapeAndQuote && + (OPTIONAL_SPACE(src[srclen - 1]) || OPTIONAL_SPACE(src[0]))) { + needsQuoting = PR_TRUE; + } + + if (needsQuoting) + reqLen += 2; + if (pEQMode && mode == minimalEscapeAndQuote && !needsQuoting) + *pEQMode = minimalEscape; + return reqLen; +} + +static const char hexChars[16] = { "0123456789abcdef" }; + +static SECStatus +escapeAndQuote(char* dst, int dstlen, char* src, int srclen, EQMode* pEQMode) +{ + int i, reqLen = 0; + EQMode mode = pEQMode ? *pEQMode : minimalEscape; + + /* space for terminal null */ + reqLen = cert_RFC1485_GetRequiredLen(src, srclen, &mode) + 1; + if (reqLen > dstlen) { + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + return SECFailure; + } + + if (mode == minimalEscapeAndQuote) + *dst++ = C_DOUBLE_QUOTE; + for (i = 0; i < srclen; i++) { + char c = src[i]; + if (NEEDS_HEX_ESCAPE(c)) { + *dst++ = C_BACKSLASH; + *dst++ = hexChars[(c >> 4) & 0x0f]; + *dst++ = hexChars[c & 0x0f]; + } else { + if (NEEDS_ESCAPE(c) || (SPECIAL_CHAR(c) && mode == fullEscape)) { + *dst++ = C_BACKSLASH; + } + *dst++ = c; + } + } + if (mode == minimalEscapeAndQuote) + *dst++ = C_DOUBLE_QUOTE; + *dst++ = 0; + if (pEQMode) + *pEQMode = mode; + return SECSuccess; +} + +SECStatus +CERT_RFC1485_EscapeAndQuote(char* dst, int dstlen, char* src, int srclen) +{ + EQMode mode = minimalEscapeAndQuote; + return escapeAndQuote(dst, dstlen, src, srclen, &mode); +} + +/* convert an OID to dotted-decimal representation */ +/* Returns a string that must be freed with PR_smprintf_free(), */ +char* +CERT_GetOidString(const SECItem* oid) +{ + PRUint8* stop; /* points to first byte after OID string */ + PRUint8* first; /* byte of an OID component integer */ + PRUint8* last; /* byte of an OID component integer */ + char* rvString = NULL; + char* prefix = NULL; + +#define MAX_OID_LEN 1024 /* bytes */ + + if (oid->len > MAX_OID_LEN) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + return NULL; + } + + /* If the OID has length 1, we bail. */ + if (oid->len < 2) { + return NULL; + } + + /* first will point to the next sequence of bytes to decode */ + first = (PRUint8*)oid->data; + /* stop points to one past the legitimate data */ + stop = &first[oid->len]; + + /* + * Check for our pseudo-encoded single-digit OIDs + */ + if ((*first == 0x80) && (2 == oid->len)) { + /* Funky encoding. The second byte is the number */ + rvString = PR_smprintf("%lu", (PRUint32)first[1]); + if (!rvString) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + return rvString; + } + + for (; first < stop; first = last + 1) { + unsigned int bytesBeforeLast; + + for (last = first; last < stop; last++) { + if (0 == (*last & 0x80)) { + break; + } + } + /* There's no first bit set, so this isn't valid. Bail.*/ + if (last == stop) { + goto unsupported; + } + bytesBeforeLast = (unsigned int)(last - first); + if (bytesBeforeLast <= 3U) { /* 0-28 bit number */ + PRUint32 n = 0; + PRUint32 c; + +#define CGET(i, m) \ + c = last[-i] & m; \ + n |= c << (7 * i) + +#define CASE(i, m) \ + case i: \ + CGET(i, m); \ + if (!n) \ + goto unsupported /* fall-through */ + + switch (bytesBeforeLast) { + CASE(3, 0x7f); + CASE(2, 0x7f); + CASE(1, 0x7f); + case 0: + n |= last[0] & 0x7f; + break; + } + if (last[0] & 0x80) { + goto unsupported; + } + + if (!rvString) { + /* This is the first number.. decompose it */ + PRUint32 one = PR_MIN(n / 40, 2); /* never > 2 */ + PRUint32 two = n - (one * 40); + + rvString = PR_smprintf("OID.%lu.%lu", one, two); + } else { + prefix = rvString; + rvString = PR_smprintf("%s.%lu", prefix, n); + } + } else if (bytesBeforeLast <= 9U) { /* 29-64 bit number */ + PRUint64 n = 0; + PRUint64 c; + + switch (bytesBeforeLast) { + CASE(9, 0x01); + CASE(8, 0x7f); + CASE(7, 0x7f); + CASE(6, 0x7f); + CASE(5, 0x7f); + CASE(4, 0x7f); + CGET(3, 0x7f); + CGET(2, 0x7f); + CGET(1, 0x7f); + CGET(0, 0x7f); + break; + } + if (last[0] & 0x80) + goto unsupported; + + if (!rvString) { + /* This is the first number.. decompose it */ + PRUint64 one = PR_MIN(n / 40, 2); /* never > 2 */ + PRUint64 two = n - (one * 40); + + rvString = PR_smprintf("OID.%llu.%llu", one, two); + } else { + prefix = rvString; + rvString = PR_smprintf("%s.%llu", prefix, n); + } + } else { + /* More than a 64-bit number, or not minimal encoding. */ + unsupported: + if (!rvString) + rvString = PR_smprintf("OID.UNSUPPORTED"); + else { + prefix = rvString; + rvString = PR_smprintf("%s.UNSUPPORTED", prefix); + } + } + + if (prefix) { + PR_smprintf_free(prefix); + prefix = NULL; + } + if (!rvString) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + break; + } + } + return rvString; +} + +/* convert DER-encoded hex to a string */ +static SECItem* +get_hex_string(SECItem* data) +{ + SECItem* rv; + unsigned int i, j; + static const char hex[] = { "0123456789ABCDEF" }; + + /* '#' + 2 chars per octet + terminator */ + rv = SECITEM_AllocItem(NULL, NULL, data->len * 2 + 2); + if (!rv) { + return NULL; + } + rv->data[0] = '#'; + rv->len = 1 + 2 * data->len; + for (i = 0; i < data->len; i++) { + j = data->data[i]; + rv->data[2 * i + 1] = hex[j >> 4]; + rv->data[2 * i + 2] = hex[j & 15]; + } + rv->data[rv->len] = 0; + return rv; +} + +/* For compliance with RFC 2253, RFC 3280 and RFC 4630, we choose to + * use the NAME=STRING form, rather than the OID.N.N=#hexXXXX form, + * when both of these conditions are met: + * 1) The attribute name OID (kind) has a known name string that is + * defined in one of those RFCs, or in RFCs that they cite, AND + * 2) The attribute's value encoding is RFC compliant for the kind + * (e.g., the value's encoding tag is correct for the kind, and + * the value's length is in the range allowed for the kind, and + * the value's contents are appropriate for the encoding tag). + * Otherwise, we use the OID.N.N=#hexXXXX form. + * + * If the caller prefers maximum human readability to RFC compliance, + * then + * - We print the kind in NAME= string form if we know the name + * string for the attribute type OID, regardless of whether the + * value is correctly encoded or not. else we use the OID.N.N= form. + * - We use the non-hex STRING form for the attribute value if the + * value can be represented in such a form. Otherwise, we use + * the hex string form. + * This implies that, for maximum human readability, in addition to + * the two forms allowed by the RFC, we allow two other forms of output: + * - the OID.N.N=STRING form, and + * - the NAME=#hexXXXX form + * When the caller prefers maximum human readability, we do not allow + * the value of any attribute to exceed the length allowed by the RFC. + * If the attribute value exceeds the allowed length, we truncate it to + * the allowed length and append "...". + * Also in this case, we arbitrarily impose a limit on the length of the + * entire AVA encoding, regardless of the form, of 384 bytes per AVA. + * This limit includes the trailing NULL character. If the encoded + * AVA length exceeds that limit, this function reports failure to encode + * the AVA. + * + * An ASCII representation of an AVA is said to be "invertible" if + * conversion back to DER reproduces the original DER encoding exactly. + * The RFC 2253 rules do not ensure that all ASCII AVAs derived according + * to its rules are invertible. That is because the RFCs allow some + * attribute values to be encoded in any of a number of encodings, + * and the encoding type information is lost in the non-hex STRING form. + * This is particularly true of attributes of type DirectoryString. + * The encoding type information is always preserved in the hex string + * form, because the hex includes the entire DER encoding of the value. + * + * So, when the caller perfers maximum invertibility, we apply the + * RFC compliance rules stated above, and add a third required + * condition on the use of the NAME=STRING form. + * 3) The attribute's kind is not is allowed to be encoded in any of + * several different encodings, such as DirectoryStrings. + * + * The chief difference between CERT_N2A_STRICT and CERT_N2A_INVERTIBLE + * is that the latter forces DirectoryStrings to be hex encoded. + * + * As a simplification, we assume the value is correctly encoded for + * its encoding type. That is, we do not test that all the characters + * in a string encoded type are allowed by that type. We assume it. + */ +static SECStatus +AppendAVA(stringBuf* bufp, CERTAVA* ava, CertStrictnessLevel strict) +{ +#define TMPBUF_LEN 2048 + const NameToKind* pn2k = name2kinds; + SECItem* avaValue = NULL; + char* unknownTag = NULL; + char* encodedAVA = NULL; + PRBool useHex = PR_FALSE; /* use =#hexXXXX form */ + PRBool truncateName = PR_FALSE; + PRBool truncateValue = PR_FALSE; + SECOidTag endKind; + SECStatus rv; + unsigned int len; + unsigned int nameLen, valueLen; + unsigned int maxName, maxValue; + EQMode mode = minimalEscapeAndQuote; + NameToKind n2k = { NULL, 32767, SEC_OID_UNKNOWN, SEC_ASN1_DS }; + char tmpBuf[TMPBUF_LEN]; + +#define tagName n2k.name /* non-NULL means use NAME= form */ +#define maxBytes n2k.maxLen +#define tag n2k.kind +#define vt n2k.valueType + + /* READABLE mode recognizes more names from the name2kinds table + * than do STRICT or INVERTIBLE modes. This assignment chooses the + * point in the table where the attribute type name scanning stops. + */ + endKind = (strict == CERT_N2A_READABLE) ? SEC_OID_UNKNOWN + : SEC_OID_AVA_POSTAL_ADDRESS; + tag = CERT_GetAVATag(ava); + while (pn2k->kind != tag && pn2k->kind != endKind) { + ++pn2k; + } + + if (pn2k->kind != endKind) { + n2k = *pn2k; + } else if (strict != CERT_N2A_READABLE) { + useHex = PR_TRUE; + } + /* For invertable form, force Directory Strings to use hex form. */ + if (strict == CERT_N2A_INVERTIBLE && vt == SEC_ASN1_DS) { + tagName = NULL; /* must use OID.N form */ + useHex = PR_TRUE; /* must use hex string */ + } + if (!useHex) { + avaValue = CERT_DecodeAVAValue(&ava->value); + if (!avaValue) { + useHex = PR_TRUE; + if (strict != CERT_N2A_READABLE) { + tagName = NULL; /* must use OID.N form */ + } + } + } + if (!tagName) { + /* handle unknown attribute types per RFC 2253 */ + tagName = unknownTag = CERT_GetOidString(&ava->type); + if (!tagName) { + if (avaValue) + SECITEM_FreeItem(avaValue, PR_TRUE); + return SECFailure; + } + } + if (useHex) { + avaValue = get_hex_string(&ava->value); + if (!avaValue) { + if (unknownTag) + PR_smprintf_free(unknownTag); + return SECFailure; + } + } + + nameLen = strlen(tagName); + valueLen = + (useHex ? avaValue->len : cert_RFC1485_GetRequiredLen((char*)avaValue->data, avaValue->len, &mode)); + len = nameLen + valueLen + 2; /* Add 2 for '=' and trailing NUL */ + + maxName = nameLen; + maxValue = valueLen; + if (len <= sizeof(tmpBuf)) { + encodedAVA = tmpBuf; + } else if (strict != CERT_N2A_READABLE) { + encodedAVA = PORT_Alloc(len); + if (!encodedAVA) { + SECITEM_FreeItem(avaValue, PR_TRUE); + if (unknownTag) + PR_smprintf_free(unknownTag); + return SECFailure; + } + } else { + /* Must make output fit in tmpbuf */ + unsigned int fair = (sizeof tmpBuf) / 2 - 1; /* for = and \0 */ + + if (nameLen < fair) { + /* just truncate the value */ + maxValue = (sizeof tmpBuf) - (nameLen + 6); /* for "=...\0", + and possibly '"' */ + } else if (valueLen < fair) { + /* just truncate the name */ + maxName = (sizeof tmpBuf) - (valueLen + 5); /* for "=...\0" */ + } else { + /* truncate both */ + maxName = maxValue = fair - 3; /* for "..." */ + } + if (nameLen > maxName) { + PORT_Assert(unknownTag && unknownTag == tagName); + truncateName = PR_TRUE; + nameLen = maxName; + } + encodedAVA = tmpBuf; + } + + memcpy(encodedAVA, tagName, nameLen); + if (truncateName) { + /* If tag name is too long, we know it is an OID form that was + * allocated from the heap, so we can modify it in place + */ + encodedAVA[nameLen - 1] = '.'; + encodedAVA[nameLen - 2] = '.'; + encodedAVA[nameLen - 3] = '.'; + } + encodedAVA[nameLen++] = '='; + if (unknownTag) + PR_smprintf_free(unknownTag); + + if (strict == CERT_N2A_READABLE && maxValue > maxBytes) + maxValue = maxBytes; + if (valueLen > maxValue) { + valueLen = maxValue; + truncateValue = PR_TRUE; + } + /* escape and quote as necessary - don't quote hex strings */ + if (useHex) { + char* end = encodedAVA + nameLen + valueLen; + memcpy(encodedAVA + nameLen, (char*)avaValue->data, valueLen); + end[0] = '\0'; + if (truncateValue) { + end[-1] = '.'; + end[-2] = '.'; + end[-3] = '.'; + } + rv = SECSuccess; + } else if (!truncateValue) { + rv = escapeAndQuote(encodedAVA + nameLen, len - nameLen, + (char*)avaValue->data, avaValue->len, &mode); + } else { + /* must truncate the escaped and quoted value */ + char bigTmpBuf[TMPBUF_LEN * 3 + 3]; + PORT_Assert(valueLen < sizeof tmpBuf); + rv = escapeAndQuote(bigTmpBuf, sizeof bigTmpBuf, (char*)avaValue->data, + PR_MIN(avaValue->len, valueLen), &mode); + + bigTmpBuf[valueLen--] = '\0'; /* hard stop here */ + /* See if we're in the middle of a multi-byte UTF8 character */ + while (((bigTmpBuf[valueLen] & 0xc0) == 0x80) && valueLen > 0) { + bigTmpBuf[valueLen--] = '\0'; + } + /* add ellipsis to signify truncation. */ + bigTmpBuf[++valueLen] = '.'; + bigTmpBuf[++valueLen] = '.'; + bigTmpBuf[++valueLen] = '.'; + if (bigTmpBuf[0] == '"') + bigTmpBuf[++valueLen] = '"'; + bigTmpBuf[++valueLen] = '\0'; + PORT_Assert(nameLen + valueLen <= (sizeof tmpBuf) - 1); + memcpy(encodedAVA + nameLen, bigTmpBuf, valueLen + 1); + } + + SECITEM_FreeItem(avaValue, PR_TRUE); + if (rv == SECSuccess) + rv = AppendStr(bufp, encodedAVA); + if (encodedAVA != tmpBuf) + PORT_Free(encodedAVA); + return rv; +} + +#undef tagName +#undef maxBytes +#undef tag +#undef vt + +char* +CERT_NameToAsciiInvertible(CERTName* name, CertStrictnessLevel strict) +{ + CERTRDN** rdns; + CERTRDN** lastRdn; + CERTRDN** rdn; + PRBool first = PR_TRUE; + stringBuf strBuf = { NULL, 0, 0 }; + + rdns = name->rdns; + if (rdns == NULL) { + return NULL; + } + + /* find last RDN */ + lastRdn = rdns; + while (*lastRdn) + lastRdn++; + lastRdn--; + + /* + * Loop over name contents in _reverse_ RDN order appending to string + */ + for (rdn = lastRdn; rdn >= rdns; rdn--) { + CERTAVA** avas = (*rdn)->avas; + CERTAVA* ava; + PRBool newRDN = PR_TRUE; + + /* + * XXX Do we need to traverse the AVAs in reverse order, too? + */ + while (avas && (ava = *avas++) != NULL) { + SECStatus rv; + /* Put in comma or plus separator */ + if (!first) { + /* Use of spaces is deprecated in RFC 2253. */ + rv = AppendStr(&strBuf, newRDN ? "," : "+"); + if (rv) + goto loser; + } else { + first = PR_FALSE; + } + + /* Add in tag type plus value into strBuf */ + rv = AppendAVA(&strBuf, ava, strict); + if (rv) + goto loser; + newRDN = PR_FALSE; + } + } + return strBuf.buffer; +loser: + if (strBuf.buffer) { + PORT_Free(strBuf.buffer); + } + return NULL; +} + +char* +CERT_NameToAscii(CERTName* name) +{ + return CERT_NameToAsciiInvertible(name, CERT_N2A_READABLE); +} + +/* + * Return the string representation of a DER encoded distinguished name + * "dername" - The DER encoded name to convert + */ +char* +CERT_DerNameToAscii(SECItem* dername) +{ + int rv; + PLArenaPool* arena = NULL; + CERTName name; + char* retstr = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if (arena == NULL) { + goto loser; + } + + rv = SEC_QuickDERDecodeItem(arena, &name, CERT_NameTemplate, dername); + + if (rv != SECSuccess) { + goto loser; + } + + retstr = CERT_NameToAscii(&name); + +loser: + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + + return (retstr); +} + +static char* +avaToString(PLArenaPool* arena, CERTAVA* ava) +{ + char* buf = NULL; + SECItem* avaValue; + int valueLen; + + avaValue = CERT_DecodeAVAValue(&ava->value); + if (!avaValue) { + return buf; + } + valueLen = + cert_RFC1485_GetRequiredLen((char*)avaValue->data, avaValue->len, NULL) + 1; + if (arena) { + buf = (char*)PORT_ArenaZAlloc(arena, valueLen); + } else { + buf = (char*)PORT_ZAlloc(valueLen); + } + if (buf) { + SECStatus rv = + escapeAndQuote(buf, valueLen, (char*)avaValue->data, avaValue->len, NULL); + if (rv != SECSuccess) { + if (!arena) + PORT_Free(buf); + buf = NULL; + } + } + SECITEM_FreeItem(avaValue, PR_TRUE); + return buf; +} + +/* RDNs are sorted from most general to most specific. + * This code returns the FIRST one found, the most general one found. + */ +static char* +CERT_GetNameElement(PLArenaPool* arena, const CERTName* name, int wantedTag) +{ + CERTRDN** rdns = name->rdns; + CERTRDN* rdn; + CERTAVA* ava = NULL; + + while (rdns && (rdn = *rdns++) != 0) { + CERTAVA** avas = rdn->avas; + while (avas && (ava = *avas++) != 0) { + int tag = CERT_GetAVATag(ava); + if (tag == wantedTag) { + avas = NULL; + rdns = NULL; /* break out of all loops */ + } + } + } + return ava ? avaToString(arena, ava) : NULL; +} + +/* RDNs are sorted from most general to most specific. + * This code returns the LAST one found, the most specific one found. + * This is particularly appropriate for Common Name. See RFC 2818. + */ +static char* +CERT_GetLastNameElement(PLArenaPool* arena, const CERTName* name, int wantedTag) +{ + CERTRDN** rdns = name->rdns; + CERTRDN* rdn; + CERTAVA* lastAva = NULL; + + while (rdns && (rdn = *rdns++) != 0) { + CERTAVA** avas = rdn->avas; + CERTAVA* ava; + while (avas && (ava = *avas++) != 0) { + int tag = CERT_GetAVATag(ava); + if (tag == wantedTag) { + lastAva = ava; + } + } + } + return lastAva ? avaToString(arena, lastAva) : NULL; +} + +char* +CERT_GetCertificateEmailAddress(CERTCertificate* cert) +{ + char* rawEmailAddr = NULL; + SECItem subAltName; + SECStatus rv; + CERTGeneralName* nameList = NULL; + CERTGeneralName* current; + PLArenaPool* arena = NULL; + int i; + + subAltName.data = NULL; + + rawEmailAddr = CERT_GetNameElement(cert->arena, &(cert->subject), + SEC_OID_PKCS9_EMAIL_ADDRESS); + if (rawEmailAddr == NULL) { + rawEmailAddr = + CERT_GetNameElement(cert->arena, &(cert->subject), SEC_OID_RFC1274_MAIL); + } + if (rawEmailAddr == NULL) { + + rv = + CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, &subAltName); + if (rv != SECSuccess) { + goto finish; + } + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + goto finish; + } + nameList = current = CERT_DecodeAltNameExtension(arena, &subAltName); + if (!nameList) { + goto finish; + } + if (nameList != NULL) { + do { + if (current->type == certDirectoryName) { + rawEmailAddr = + CERT_GetNameElement(cert->arena, &(current->name.directoryName), + SEC_OID_PKCS9_EMAIL_ADDRESS); + if (rawEmailAddr == + NULL) { + rawEmailAddr = + CERT_GetNameElement(cert->arena, &(current->name.directoryName), + SEC_OID_RFC1274_MAIL); + } + } else if (current->type == certRFC822Name) { + rawEmailAddr = + (char*)PORT_ArenaZAlloc(cert->arena, current->name.other.len + 1); + if (!rawEmailAddr) { + goto finish; + } + PORT_Memcpy(rawEmailAddr, current->name.other.data, + current->name.other.len); + rawEmailAddr[current->name.other.len] = + '\0'; + } + if (rawEmailAddr) { + break; + } + current = CERT_GetNextGeneralName(current); + } while (current != nameList); + } + } + if (rawEmailAddr) { + for (i = 0; i <= (int)PORT_Strlen(rawEmailAddr); i++) { + rawEmailAddr[i] = tolower(rawEmailAddr[i]); + } + } + +finish: + + /* Don't free nameList, it's part of the arena. */ + + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + + if (subAltName.data) { + SECITEM_FreeItem(&subAltName, PR_FALSE); + } + + return (rawEmailAddr); +} + +static char* +appendStringToBuf(char* dest, char* src, PRUint32* pRemaining) +{ + PRUint32 len; + if (dest && src && src[0] && *pRemaining > (len = PL_strlen(src))) { + PRUint32 i; + for (i = 0; i < len; ++i) + dest[i] = tolower(src[i]); + dest[len] = 0; + dest += len + 1; + *pRemaining -= len + 1; + } + return dest; +} + +#undef NEEDS_HEX_ESCAPE +#define NEEDS_HEX_ESCAPE(c) (c < 0x20) + +static char* +appendItemToBuf(char* dest, SECItem* src, PRUint32* pRemaining) +{ + if (dest && src && src->data && src->len && src->data[0]) { + PRUint32 len = src->len; + PRUint32 i; + PRUint32 reqLen = len + 1; + /* are there any embedded control characters ? */ + for (i = 0; i < len; i++) { + if (NEEDS_HEX_ESCAPE(src->data[i])) + reqLen += 2; + } + if (*pRemaining > reqLen) { + for (i = 0; i < len; ++i) { + PRUint8 c = src->data[i]; + if (NEEDS_HEX_ESCAPE(c)) { + *dest++ = + C_BACKSLASH; + *dest++ = + hexChars[(c >> 4) & 0x0f]; + *dest++ = + hexChars[c & 0x0f]; + } else { + *dest++ = + tolower(c); + } + } + *dest++ = '\0'; + *pRemaining -= reqLen; + } + } + return dest; +} + +/* Returns a pointer to an environment-like string, a series of +** null-terminated strings, terminated by a zero-length string. +** This function is intended to be internal to NSS. +*/ +char* +cert_GetCertificateEmailAddresses(CERTCertificate* cert) +{ + char* rawEmailAddr = NULL; + char* addrBuf = NULL; + char* pBuf = NULL; + PORTCheapArenaPool tmpArena; + PRUint32 maxLen = 0; + PRInt32 finalLen = 0; + SECStatus rv; + SECItem subAltName; + + PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); + + subAltName.data = NULL; + maxLen = cert->derCert.len; + PORT_Assert(maxLen); + if (!maxLen) + maxLen = 2000; /* a guess, should never happen */ + + pBuf = addrBuf = (char*)PORT_ArenaZAlloc(&tmpArena.arena, maxLen + 1); + if (!addrBuf) + goto loser; + + rawEmailAddr = CERT_GetNameElement(&tmpArena.arena, &cert->subject, + SEC_OID_PKCS9_EMAIL_ADDRESS); + pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen); + + rawEmailAddr = CERT_GetNameElement(&tmpArena.arena, &cert->subject, + SEC_OID_RFC1274_MAIL); + pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen); + + rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, &subAltName); + if (rv == SECSuccess && subAltName.data) { + CERTGeneralName* nameList = NULL; + + if (!!(nameList = CERT_DecodeAltNameExtension(&tmpArena.arena, &subAltName))) { + CERTGeneralName* current = nameList; + do { + if (current->type == certDirectoryName) { + rawEmailAddr = + CERT_GetNameElement(&tmpArena.arena, + ¤t->name.directoryName, + SEC_OID_PKCS9_EMAIL_ADDRESS); + pBuf = + appendStringToBuf(pBuf, rawEmailAddr, &maxLen); + + rawEmailAddr = + CERT_GetNameElement(&tmpArena.arena, + ¤t->name.directoryName, + SEC_OID_RFC1274_MAIL); + pBuf = + appendStringToBuf(pBuf, rawEmailAddr, &maxLen); + } else if (current->type == certRFC822Name) { + pBuf = + appendItemToBuf(pBuf, ¤t->name.other, &maxLen); + } + current = CERT_GetNextGeneralName(current); + } while (current != nameList); + } + SECITEM_FreeItem(&subAltName, PR_FALSE); + /* Don't free nameList, it's part of the tmpArena. */ + } + /* now copy superstring to cert's arena */ + finalLen = (pBuf - addrBuf) + 1; + pBuf = NULL; + if (finalLen > 1) { + pBuf = PORT_ArenaAlloc(cert->arena, finalLen); + if (pBuf) { + PORT_Memcpy(pBuf, addrBuf, finalLen); + } + } +loser: + PORT_DestroyCheapArena(&tmpArena); + + return pBuf; +} + +/* returns pointer to storage in cert's arena. Storage remains valid +** as long as cert's reference count doesn't go to zero. +** Caller should strdup or otherwise copy. +*/ +const char* /* const so caller won't muck with it. */ +CERT_GetFirstEmailAddress(CERTCertificate* cert) +{ + if (cert && cert->emailAddr && cert->emailAddr[0]) + return (const char*)cert->emailAddr; + return NULL; +} + +/* returns pointer to storage in cert's arena. Storage remains valid +** as long as cert's reference count doesn't go to zero. +** Caller should strdup or otherwise copy. +*/ +const char* /* const so caller won't muck with it. */ +CERT_GetNextEmailAddress(CERTCertificate* cert, const char* prev) +{ + if (cert && prev && prev[0]) { + PRUint32 len = PL_strlen(prev); + prev += len + 1; + if (prev && prev[0]) + return prev; + } + return NULL; +} + +/* This is seriously bogus, now that certs store their email addresses in +** subject Alternative Name extensions. +** Returns a string allocated by PORT_StrDup, which the caller must free. +*/ +char* +CERT_GetCertEmailAddress(const CERTName* name) +{ + char* rawEmailAddr; + char* emailAddr; + + rawEmailAddr = CERT_GetNameElement(NULL, name, SEC_OID_PKCS9_EMAIL_ADDRESS); + if (rawEmailAddr == NULL) { + rawEmailAddr = CERT_GetNameElement(NULL, name, SEC_OID_RFC1274_MAIL); + } + emailAddr = CERT_FixupEmailAddr(rawEmailAddr); + if (rawEmailAddr) { + PORT_Free(rawEmailAddr); + } + return (emailAddr); +} + +/* The return value must be freed with PORT_Free. */ +char* +CERT_GetCommonName(const CERTName* name) +{ + return (CERT_GetLastNameElement(NULL, name, SEC_OID_AVA_COMMON_NAME)); +} + +char* +CERT_GetCountryName(const CERTName* name) +{ + return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_COUNTRY_NAME)); +} + +char* +CERT_GetLocalityName(const CERTName* name) +{ + return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_LOCALITY)); +} + +char* +CERT_GetStateName(const CERTName* name) +{ + return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_STATE_OR_PROVINCE)); +} + +char* +CERT_GetOrgName(const CERTName* name) +{ + return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_ORGANIZATION_NAME)); +} + +char* +CERT_GetDomainComponentName(const CERTName* name) +{ + return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_DC)); +} + +char* +CERT_GetOrgUnitName(const CERTName* name) +{ + return ( + CERT_GetNameElement(NULL, name, SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME)); +} + +char* +CERT_GetDnQualifier(const CERTName* name) +{ + return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_DN_QUALIFIER)); +} + +char* +CERT_GetCertUid(const CERTName* name) +{ + return (CERT_GetNameElement(NULL, name, SEC_OID_RFC1274_UID)); +} diff --git a/security/nss/lib/certdb/cert.h b/security/nss/lib/certdb/cert.h new file mode 100644 index 0000000000..33d37b39a0 --- /dev/null +++ b/security/nss/lib/certdb/cert.h @@ -0,0 +1,1606 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * cert.h - public data structures and prototypes for the certificate library + */ + +#ifndef _CERT_H_ +#define _CERT_H_ + +#include "utilrename.h" +#include "plarena.h" +#include "plhash.h" +#include "prlong.h" +#include "prlog.h" + +#include "seccomon.h" +#include "secdert.h" +#include "secoidt.h" +#include "keythi.h" +#include "certt.h" + +SEC_BEGIN_PROTOS + +/**************************************************************************** + * + * RFC1485 ascii to/from X.? RelativeDistinguishedName (aka CERTName) + * + ****************************************************************************/ + +/* +** Convert an ascii RFC1485 encoded name into its CERTName equivalent. +*/ +extern CERTName *CERT_AsciiToName(const char *string); + +/* +** Convert an CERTName into its RFC1485 encoded equivalent. +** Returns a string that must be freed with PORT_Free(). +** This version produces a string for maximum human readability, +** not for strict RFC compliance. +*/ +extern char *CERT_NameToAscii(CERTName *name); + +/* +** Convert an CERTName into its RFC1485 encoded equivalent. +** Returns a string that must be freed with PORT_Free(). +** Caller chooses encoding rules. +*/ +extern char *CERT_NameToAsciiInvertible(CERTName *name, + CertStrictnessLevel strict); + +extern CERTAVA *CERT_CopyAVA(PLArenaPool *arena, CERTAVA *src); + +/* convert an OID to dotted-decimal representation */ +/* Returns a string that must be freed with PR_smprintf_free(). */ +extern char *CERT_GetOidString(const SECItem *oid); + +/* +** Examine an AVA and return the tag that refers to it. The AVA tags are +** defined as SEC_OID_AVA*. +*/ +extern SECOidTag CERT_GetAVATag(CERTAVA *ava); + +/* +** Compare two AVA's, returning the difference between them. +*/ +extern SECComparison CERT_CompareAVA(const CERTAVA *a, const CERTAVA *b); + +/* +** Create an RDN (relative-distinguished-name). The argument list is a +** NULL terminated list of AVA's. +*/ +extern CERTRDN *CERT_CreateRDN(PLArenaPool *arena, CERTAVA *avas, ...); + +/* +** Make a copy of "src" storing it in "dest". +*/ +extern SECStatus CERT_CopyRDN(PLArenaPool *arena, CERTRDN *dest, CERTRDN *src); + +/* +** Add an AVA to an RDN. +** "rdn" the RDN to add to +** "ava" the AVA to add +*/ +extern SECStatus CERT_AddAVA(PLArenaPool *arena, CERTRDN *rdn, CERTAVA *ava); + +/* +** Compare two RDN's, returning the difference between them. +*/ +extern SECComparison CERT_CompareRDN(const CERTRDN *a, const CERTRDN *b); + +/* +** Create an X.500 style name using a NULL terminated list of RDN's. +*/ +extern CERTName *CERT_CreateName(CERTRDN *rdn, ...); + +/* +** Make a copy of "src" storing it in "dest". Memory is allocated in +** "dest" for each of the appropriate sub objects. Memory is not freed in +** "dest" before allocation is done (use CERT_DestroyName(dest, PR_FALSE) to +** do that). +*/ +extern SECStatus CERT_CopyName(PLArenaPool *arena, CERTName *dest, + const CERTName *src); + +/* +** Destroy a Name object. +** "name" the CERTName to destroy +** "freeit" if PR_TRUE then free the object as well as its sub-objects +*/ +extern void CERT_DestroyName(CERTName *name); + +/* +** Add an RDN to a name. +** "name" the name to add the RDN to +** "rdn" the RDN to add to name +*/ +extern SECStatus CERT_AddRDN(CERTName *name, CERTRDN *rdn); + +/* +** Compare two names, returning the difference between them. +*/ +extern SECComparison CERT_CompareName(const CERTName *a, const CERTName *b); + +/* +** Convert a CERTName into something readable +*/ +extern char *CERT_FormatName(CERTName *name); + +/* +** Convert a der-encoded integer to a hex printable string form. +** Perhaps this should be a SEC function but it's only used for certs. +*/ +extern char *CERT_Hexify(SECItem *i, int do_colon); + +/* +** Converts DER string (with explicit length) into zString, if destination +** buffer is big enough to receive it. Does quoting and/or escaping as +** specified in RFC 1485. Input string must be single or multi-byte DER +** character set, (ASCII, UTF8, or ISO 8851-x) not a wide character set. +** Returns SECSuccess or SECFailure with error code set. If output buffer +** is too small, sets error code SEC_ERROR_OUTPUT_LEN. +*/ +extern SECStatus CERT_RFC1485_EscapeAndQuote(char *dst, int dstlen, char *src, + int srclen); + +/****************************************************************************** + * + * Certificate handling operations + * + *****************************************************************************/ + +/* +** Create a new validity object given two unix time values. +** "notBefore" the time before which the validity is not valid +** "notAfter" the time after which the validity is not valid +*/ +extern CERTValidity *CERT_CreateValidity(PRTime notBefore, PRTime notAfter); + +/* +** Destroy a validity object. +** "v" the validity to destroy +** "freeit" if PR_TRUE then free the object as well as its sub-objects +*/ +extern void CERT_DestroyValidity(CERTValidity *v); + +/* +** Copy the "src" object to "dest". Memory is allocated in "dest" for +** each of the appropriate sub-objects. Memory in "dest" is not freed +** before memory is allocated (use CERT_DestroyValidity(v, PR_FALSE) to do +** that). +*/ +extern SECStatus CERT_CopyValidity(PLArenaPool *arena, CERTValidity *dest, + CERTValidity *src); + +/* +** The cert lib considers a cert or CRL valid if the "notBefore" time is +** in the not-too-distant future, e.g. within the next 24 hours. This +** prevents freshly issued certificates from being considered invalid +** because the local system's time zone is incorrectly set. +** The amount of "pending slop time" is adjustable by the application. +** Units of SlopTime are seconds. Default is 86400 (24 hours). +** Negative SlopTime values are not allowed. +*/ +PRInt32 CERT_GetSlopTime(void); + +SECStatus CERT_SetSlopTime(PRInt32 slop); + +/* +** Create a new certificate object. The result must be wrapped with an +** CERTSignedData to create a signed certificate. +** "serialNumber" the serial number +** "issuer" the name of the certificate issuer +** "validity" the validity period of the certificate +** "req" the certificate request that prompted the certificate issuance +*/ +extern CERTCertificate *CERT_CreateCertificate(unsigned long serialNumber, + CERTName *issuer, + CERTValidity *validity, + CERTCertificateRequest *req); + +/* +** Destroy a certificate object +** "cert" the certificate to destroy +** NOTE: certificate's are reference counted. This call decrements the +** reference count, and if the result is zero, then the object is destroyed +** and optionally freed. +*/ +extern void CERT_DestroyCertificate(CERTCertificate *cert); + +/* +** Make a shallow copy of a certificate "c". Just increments the +** reference count on "c". +*/ +extern CERTCertificate *CERT_DupCertificate(CERTCertificate *c); + +/* Access the DER of the certificate. This only creates a reference to the DER + * in the outparam not a copy. To avoid the pointer becoming invalid, use + * CERT_DupCertificate() and keep a reference to the duplicate alive. + */ +extern SECStatus CERT_GetCertificateDer(const CERTCertificate *c, SECItem *der); + +/* +** Create a new certificate request. This result must be wrapped with an +** CERTSignedData to create a signed certificate request. +** "name" the subject name (who the certificate request is from) +** "spki" describes/defines the public key the certificate is for +** "attributes" if non-zero, some optional attribute data +*/ +extern CERTCertificateRequest *CERT_CreateCertificateRequest( + CERTName *name, CERTSubjectPublicKeyInfo *spki, SECItem **attributes); + +/* +** Destroy a certificate-request object +** "r" the certificate-request to destroy +** "freeit" if PR_TRUE then free the object as well as its sub-objects +*/ +extern void CERT_DestroyCertificateRequest(CERTCertificateRequest *r); + +/* +** Start adding extensions to a certificate request. +*/ +void *CERT_StartCertificateRequestAttributes(CERTCertificateRequest *req); + +/* +** Reformat the certificate extension list into a CertificateRequest +** attribute list. +*/ +SECStatus CERT_FinishCertificateRequestAttributes(CERTCertificateRequest *req); + +/* +** Extract the Extension Requests from a DER CertRequest attribute list. +*/ +SECStatus CERT_GetCertificateRequestExtensions(CERTCertificateRequest *req, + CERTCertExtension ***exts); + +/* +** Extract a public key object from a certificate +*/ +extern SECKEYPublicKey *CERT_ExtractPublicKey(CERTCertificate *cert); + +/* +** Retrieve the Key Type associated with the cert we're dealing with +*/ + +extern KeyType CERT_GetCertKeyType(const CERTSubjectPublicKeyInfo *spki); + +/* +** Initialize the certificate database. This is called to create +** the initial list of certificates in the database. +*/ +extern SECStatus CERT_InitCertDB(CERTCertDBHandle *handle); + +extern int CERT_GetDBContentVersion(CERTCertDBHandle *handle); + +/* +** Default certificate database routines +*/ +extern void CERT_SetDefaultCertDB(CERTCertDBHandle *handle); + +extern CERTCertDBHandle *CERT_GetDefaultCertDB(void); + +extern CERTCertList *CERT_GetCertChainFromCert(CERTCertificate *cert, + PRTime time, SECCertUsage usage); +extern CERTCertificate *CERT_NewTempCertificate(CERTCertDBHandle *handle, + SECItem *derCert, + char *nickname, PRBool isperm, + PRBool copyDER); + +/****************************************************************************** + * + * X.500 Name handling operations + * + *****************************************************************************/ + +/* +** Create an AVA (attribute-value-assertion) +** "arena" the memory arena to alloc from +** "kind" is one of SEC_OID_AVA_* +** "valueType" is one of DER_PRINTABLE_STRING, DER_IA5_STRING, or +** DER_T61_STRING +** "value" is the null terminated string containing the value +*/ +extern CERTAVA *CERT_CreateAVA(PLArenaPool *arena, SECOidTag kind, + int valueType, char *value); + +/* +** Extract the Distinguished Name from a DER encoded certificate +** "derCert" is the DER encoded certificate +** "derName" is the SECItem that the name is returned in +*/ +extern SECStatus CERT_NameFromDERCert(SECItem *derCert, SECItem *derName); + +/* +** Extract the Issuers Distinguished Name from a DER encoded certificate +** "derCert" is the DER encoded certificate +** "derName" is the SECItem that the name is returned in +*/ +extern SECStatus CERT_IssuerNameFromDERCert(SECItem *derCert, SECItem *derName); + +extern SECItem *CERT_EncodeGeneralName(CERTGeneralName *genName, SECItem *dest, + PLArenaPool *arena); + +extern CERTGeneralName *CERT_DecodeGeneralName(PLArenaPool *reqArena, + SECItem *encodedName, + CERTGeneralName *genName); + +/* +** Generate a database search key for a certificate, based on the +** issuer and serial number. +** "arena" the memory arena to alloc from +** "derCert" the DER encoded certificate +** "key" the returned key +*/ +extern SECStatus CERT_KeyFromDERCert(PLArenaPool *reqArena, SECItem *derCert, + SECItem *key); + +extern SECStatus CERT_KeyFromIssuerAndSN(PLArenaPool *arena, SECItem *issuer, + SECItem *sn, SECItem *key); + +extern SECStatus CERT_SerialNumberFromDERCert(SECItem *derCert, + SECItem *derName); + +/* +** Generate a database search key for a crl, based on the +** issuer. +** "arena" the memory arena to alloc from +** "derCrl" the DER encoded crl +** "key" the returned key +*/ +extern SECStatus CERT_KeyFromDERCrl(PLArenaPool *arena, SECItem *derCrl, + SECItem *key); + +/* +** Open the certificate database. Use callback to get name of database. +*/ +extern SECStatus CERT_OpenCertDB(CERTCertDBHandle *handle, PRBool readOnly, + CERTDBNameFunc namecb, void *cbarg); + +/* Open the certificate database. Use given filename for database. */ +extern SECStatus CERT_OpenCertDBFilename(CERTCertDBHandle *handle, + char *certdbname, PRBool readOnly); + +/* +** Open and initialize a cert database that is entirely in memory. This +** can be used when the permanent database can not be opened or created. +*/ +extern SECStatus CERT_OpenVolatileCertDB(CERTCertDBHandle *handle); + +/* +** Extract the list of host names, host name patters, IP address strings +** this cert is valid for. +** This function does NOT return nicknames. +** Type CERTCertNicknames is being used because it's a convenient +** data structure to carry a list of strings and its count. +*/ +extern CERTCertNicknames *CERT_GetValidDNSPatternsFromCert( + CERTCertificate *cert); + +/* +** Check the hostname to make sure that it matches the shexp that +** is given in the common name of the certificate. +*/ +extern SECStatus CERT_VerifyCertName(const CERTCertificate *cert, + const char *hostname); + +/* +** Add a domain name to the list of names that the user has explicitly +** allowed (despite cert name mismatches) for use with a server cert. +*/ +extern SECStatus CERT_AddOKDomainName(CERTCertificate *cert, + const char *hostname); + +/* +** Decode a DER encoded certificate into an CERTCertificate structure +** "derSignedCert" is the DER encoded signed certificate +** "copyDER" is true if the DER should be copied, false if the +** existing copy should be referenced +** "nickname" is the nickname to use in the database. If it is NULL +** then a temporary nickname is generated. +*/ +extern CERTCertificate *CERT_DecodeDERCertificate(SECItem *derSignedCert, + PRBool copyDER, + char *nickname); +/* +** Decode a DER encoded CRL into a CERTSignedCrl structure +** "derSignedCrl" is the DER encoded signed CRL. +** "type" must be SEC_CRL_TYPE. +*/ +#define SEC_CRL_TYPE 1 +#define SEC_KRL_TYPE 0 /* deprecated */ + +extern CERTSignedCrl *CERT_DecodeDERCrl(PLArenaPool *arena, + SECItem *derSignedCrl, int type); + +/* + * same as CERT_DecodeDERCrl, plus allow options to be passed in + */ + +extern CERTSignedCrl *CERT_DecodeDERCrlWithFlags(PLArenaPool *narena, + SECItem *derSignedCrl, + int type, PRInt32 options); + +/* CRL options to pass */ + +#define CRL_DECODE_DEFAULT_OPTIONS 0x00000000 + +/* when CRL_DECODE_DONT_COPY_DER is set, the DER is not copied . The + application must then keep derSignedCrl until it destroys the + CRL . Ideally, it should allocate derSignedCrl in an arena + and pass that arena in as the first argument to + CERT_DecodeDERCrlWithFlags */ + +#define CRL_DECODE_DONT_COPY_DER 0x00000001 +#define CRL_DECODE_SKIP_ENTRIES 0x00000002 +#define CRL_DECODE_KEEP_BAD_CRL 0x00000004 +#define CRL_DECODE_ADOPT_HEAP_DER 0x00000008 + +/* complete the decoding of a partially decoded CRL, ie. decode the + entries. Note that entries is an optional field in a CRL, so the + "entries" pointer in CERTCrlStr may still be NULL even after + function returns SECSuccess */ + +extern SECStatus CERT_CompleteCRLDecodeEntries(CERTSignedCrl *crl); + +/* Validate CRL then import it to the dbase. If there is already a CRL with the + * same CA in the dbase, it will be replaced if derCRL is more up to date. + * If the process successes, a CRL will be returned. Otherwise, a NULL will + * be returned. The caller should call PORT_GetError() for the exactly error + * code. + */ +extern CERTSignedCrl *CERT_ImportCRL(CERTCertDBHandle *handle, SECItem *derCRL, + char *url, int type, void *wincx); + +extern void CERT_DestroyCrl(CERTSignedCrl *crl); + +/* this is a hint to flush the CRL cache. crlKey is the DER subject of + the issuer (CA). */ +void CERT_CRLCacheRefreshIssuer(CERTCertDBHandle *dbhandle, SECItem *crlKey); + +/* add the specified DER CRL object to the CRL cache. Doing so will allow + certificate verification functions (such as CERT_VerifyCertificate) + to automatically find and make use of this CRL object. + Once a CRL is added to the CRL cache, the application must hold on to + the object's memory, because the cache will reference it directly. The + application can only free the object after it calls CERT_UncacheCRL to + remove it from the CRL cache. +*/ +SECStatus CERT_CacheCRL(CERTCertDBHandle *dbhandle, SECItem *newcrl); + +/* remove a previously added CRL object from the CRL cache. It is OK + for the application to free the memory after a successful removal +*/ +SECStatus CERT_UncacheCRL(CERTCertDBHandle *dbhandle, SECItem *oldcrl); + +/* +** Find a certificate in the database +** "key" is the database key to look for +*/ +extern CERTCertificate *CERT_FindCertByKey(CERTCertDBHandle *handle, + SECItem *key); + +/* +** Find a certificate in the database by name +** "name" is the distinguished name to look up +*/ +extern CERTCertificate *CERT_FindCertByName(CERTCertDBHandle *handle, + SECItem *name); + +/* +** Find a certificate in the database by name +** "name" is the distinguished name to look up (in ascii) +*/ +extern CERTCertificate *CERT_FindCertByNameString(CERTCertDBHandle *handle, + char *name); + +/* +** Find a certificate in the database by name and keyid +** "name" is the distinguished name to look up +** "keyID" is the value of the subjectKeyID to match +*/ +extern CERTCertificate *CERT_FindCertByKeyID(CERTCertDBHandle *handle, + SECItem *name, SECItem *keyID); + +/* +** Generate a certificate key from the issuer and serialnumber, then look it +** up in the database. Return the cert if found. +** "issuerAndSN" is the issuer and serial number to look for +*/ +extern CERTCertificate *CERT_FindCertByIssuerAndSN( + CERTCertDBHandle *handle, CERTIssuerAndSN *issuerAndSN); +extern CERTCertificate *CERT_FindCertByIssuerAndSNCX( + CERTCertDBHandle *handle, CERTIssuerAndSN *issuerAndSN, void *wincx); + +/* +** Find a certificate in the database by a subject key ID +** "subjKeyID" is the subject Key ID to look for +*/ +extern CERTCertificate *CERT_FindCertBySubjectKeyID(CERTCertDBHandle *handle, + SECItem *subjKeyID); + +/* +** Encode Certificate SKID (Subject Key ID) extension. +** +*/ +extern SECStatus CERT_EncodeSubjectKeyID(PLArenaPool *arena, + const SECItem *srcString, + SECItem *encodedValue); + +/* +** Find a certificate in the database by a nickname +** "nickname" is the ascii string nickname to look for +*/ +extern CERTCertificate *CERT_FindCertByNickname(CERTCertDBHandle *handle, + const char *nickname); + +/* +** Find a certificate in the database by a DER encoded certificate +** "derCert" is the DER encoded certificate +*/ +extern CERTCertificate *CERT_FindCertByDERCert(CERTCertDBHandle *handle, + SECItem *derCert); + +/* +** Find a certificate in the database by a email address +** "emailAddr" is the email address to look up +*/ +CERTCertificate *CERT_FindCertByEmailAddr(CERTCertDBHandle *handle, + char *emailAddr); + +/* +** Find a certificate in the database by a email address or nickname +** "name" is the email address or nickname to look up +*/ +CERTCertificate *CERT_FindCertByNicknameOrEmailAddr(CERTCertDBHandle *handle, + const char *name); +CERTCertificate *CERT_FindCertByNicknameOrEmailAddrCX(CERTCertDBHandle *handle, + const char *name, + void *wincx); + +/* +** Find a certificate in the database by a email address or nickname +** and require it to have the given usage. +** "name" is the email address or nickname to look up +*/ +CERTCertificate *CERT_FindCertByNicknameOrEmailAddrForUsage( + CERTCertDBHandle *handle, const char *name, SECCertUsage lookingForUsage); +CERTCertificate *CERT_FindCertByNicknameOrEmailAddrForUsageCX( + CERTCertDBHandle *handle, const char *name, SECCertUsage lookingForUsage, + void *wincx); + +/* +** Find a certificate in the database by a digest of a subject public key +** "spkDigest" is the digest to look up +*/ +extern CERTCertificate *CERT_FindCertBySPKDigest(CERTCertDBHandle *handle, + SECItem *spkDigest); + +/* + * Find the issuer of a cert + */ +CERTCertificate *CERT_FindCertIssuer(CERTCertificate *cert, PRTime validTime, + SECCertUsage usage); + +/* +** Check the validity times of a certificate vs. time 't', allowing +** some slop for broken clocks and stuff. +** "cert" is the certificate to be checked +** "t" is the time to check against +** "allowOverride" if true then check to see if the invalidity has +** been overridden by the user. +*/ +extern SECCertTimeValidity CERT_CheckCertValidTimes(const CERTCertificate *cert, + PRTime t, + PRBool allowOverride); + +/* +** WARNING - this function is deprecated, and will either go away or have +** a new API in the near future. +** +** Check the validity times of a certificate vs. the current time, allowing +** some slop for broken clocks and stuff. +** "cert" is the certificate to be checked +*/ +extern SECStatus CERT_CertTimesValid(CERTCertificate *cert); + +/* +** Extract the validity times from a certificate +** "c" is the certificate +** "notBefore" is the start of the validity period +** "notAfter" is the end of the validity period +*/ +extern SECStatus CERT_GetCertTimes(const CERTCertificate *c, PRTime *notBefore, + PRTime *notAfter); + +/* +** Extract the issuer and serial number from a certificate +*/ +extern CERTIssuerAndSN *CERT_GetCertIssuerAndSN(PLArenaPool *, + CERTCertificate *); + +/* +** verify the signature of a signed data object with a given certificate +** "sd" the signed data object to be verified +** "cert" the certificate to use to check the signature +*/ +extern SECStatus CERT_VerifySignedData(CERTSignedData *sd, + CERTCertificate *cert, PRTime t, + void *wincx); +/* +** verify the signature of a signed data object with the given DER publickey +*/ +extern SECStatus CERT_VerifySignedDataWithPublicKeyInfo( + CERTSignedData *sd, CERTSubjectPublicKeyInfo *pubKeyInfo, void *wincx); + +/* +** verify the signature of a signed data object with a SECKEYPublicKey. +*/ +extern SECStatus CERT_VerifySignedDataWithPublicKey(const CERTSignedData *sd, + SECKEYPublicKey *pubKey, + void *wincx); + +/* +** NEW FUNCTIONS with new bit-field-FIELD SECCertificateUsage - please use +** verify a certificate by checking validity times against a certain time, +** that we trust the issuer, and that the signature on the certificate is +** valid. +** "cert" the certificate to verify +** "checkSig" only check signatures if true +*/ +extern SECStatus CERT_VerifyCertificate(CERTCertDBHandle *handle, + CERTCertificate *cert, PRBool checkSig, + SECCertificateUsage requiredUsages, + PRTime t, void *wincx, + CERTVerifyLog *log, + SECCertificateUsage *returnedUsages); + +/* same as above, but uses current time */ +extern SECStatus CERT_VerifyCertificateNow(CERTCertDBHandle *handle, + CERTCertificate *cert, + PRBool checkSig, + SECCertificateUsage requiredUsages, + void *wincx, + SECCertificateUsage *returnedUsages); + +/* +** Verify that a CA cert can certify some (unspecified) leaf cert for a given +** purpose. This is used by UI code to help identify where a chain may be +** broken and why. This takes identical parameters to CERT_VerifyCert +*/ +extern SECStatus CERT_VerifyCACertForUsage(CERTCertDBHandle *handle, + CERTCertificate *cert, + PRBool checkSig, + SECCertUsage certUsage, PRTime t, + void *wincx, CERTVerifyLog *log); + +/* +** OLD OBSOLETE FUNCTIONS with enum SECCertUsage - DO NOT USE FOR NEW CODE +** verify a certificate by checking validity times against a certain time, +** that we trust the issuer, and that the signature on the certificate is +** valid. +** "cert" the certificate to verify +** "checkSig" only check signatures if true +*/ +extern SECStatus CERT_VerifyCert(CERTCertDBHandle *handle, + CERTCertificate *cert, PRBool checkSig, + SECCertUsage certUsage, PRTime t, void *wincx, + CERTVerifyLog *log); + +/* same as above, but uses current time */ +extern SECStatus CERT_VerifyCertNow(CERTCertDBHandle *handle, + CERTCertificate *cert, PRBool checkSig, + SECCertUsage certUsage, void *wincx); + +SECStatus CERT_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertUsage certUsage, + PRTime t, void *wincx, CERTVerifyLog *log); + +/* +** Read a base64 ascii encoded DER certificate and convert it to our +** internal format. +** "certstr" is a null-terminated string containing the certificate +*/ +extern CERTCertificate *CERT_ConvertAndDecodeCertificate(char *certstr); + +/* +** Read a certificate in some foreign format, and convert it to our +** internal format. +** "certbuf" is the buffer containing the certificate +** "certlen" is the length of the buffer +** NOTE - currently supports netscape base64 ascii encoded raw certs +** and netscape binary DER typed files. +*/ +extern CERTCertificate *CERT_DecodeCertFromPackage(char *certbuf, int certlen); + +extern SECStatus CERT_ImportCAChain(SECItem *certs, int numcerts, + SECCertUsage certUsage); + +extern SECStatus CERT_ImportCAChainTrusted(SECItem *certs, int numcerts, + SECCertUsage certUsage); + +/* +** Read a certificate chain in some foreign format, and pass it to a +** callback function. +** "certbuf" is the buffer containing the certificate +** "certlen" is the length of the buffer +** "f" is the callback function +** "arg" is the callback argument +*/ +typedef SECStatus(PR_CALLBACK *CERTImportCertificateFunc)(void *arg, + SECItem **certs, + int numcerts); + +extern SECStatus CERT_DecodeCertPackage(char *certbuf, int certlen, + CERTImportCertificateFunc f, void *arg); + +/* +** Returns the value of an AVA. This was a formerly static +** function that has been exposed due to the need to decode +** and convert unicode strings to UTF8. +** +** XXX This function resides in certhtml.c, should it be +** moved elsewhere? +*/ +extern SECItem *CERT_DecodeAVAValue(const SECItem *derAVAValue); + +/* +** extract various element strings from a distinguished name. +** "name" the distinguished name +*/ + +extern char *CERT_GetCertificateEmailAddress(CERTCertificate *cert); + +extern char *CERT_GetCertEmailAddress(const CERTName *name); + +extern const char *CERT_GetFirstEmailAddress(CERTCertificate *cert); + +extern const char *CERT_GetNextEmailAddress(CERTCertificate *cert, + const char *prev); + +/* The return value must be freed with PORT_Free. */ +extern char *CERT_GetCommonName(const CERTName *name); + +extern char *CERT_GetCountryName(const CERTName *name); + +extern char *CERT_GetLocalityName(const CERTName *name); + +extern char *CERT_GetStateName(const CERTName *name); + +extern char *CERT_GetOrgName(const CERTName *name); + +extern char *CERT_GetOrgUnitName(const CERTName *name); + +extern char *CERT_GetDomainComponentName(const CERTName *name); + +extern char *CERT_GetCertUid(const CERTName *name); + +/* manipulate the trust parameters of a certificate */ + +extern SECStatus CERT_GetCertTrust(const CERTCertificate *cert, + CERTCertTrust *trust); + +extern SECStatus CERT_ChangeCertTrust(CERTCertDBHandle *handle, + CERTCertificate *cert, + CERTCertTrust *trust); + +extern SECStatus CERT_ChangeCertTrustByUsage(CERTCertDBHandle *certdb, + CERTCertificate *cert, + SECCertUsage usage); + +/************************************************************************* + * + * manipulate the extensions of a certificate + * + ************************************************************************/ + +/* +** Set up a cert for adding X509v3 extensions. Returns an opaque handle +** used by the next two routines. +** "cert" is the certificate we are adding extensions to +*/ +extern void *CERT_StartCertExtensions(CERTCertificate *cert); + +/* +** Add an extension to a certificate. +** "exthandle" is the handle returned by the previous function +** "idtag" is the integer tag for the OID that should ID this extension +** "value" is the value of the extension +** "critical" is the critical extension flag +** "copyData" is a flag indicating whether the value data should be +** copied. +*/ +extern SECStatus CERT_AddExtension(void *exthandle, int idtag, SECItem *value, + PRBool critical, PRBool copyData); + +extern SECStatus CERT_AddExtensionByOID(void *exthandle, SECItem *oid, + SECItem *value, PRBool critical, + PRBool copyData); + +extern SECStatus CERT_EncodeAndAddExtension(void *exthandle, int idtag, + void *value, PRBool critical, + const SEC_ASN1Template *atemplate); + +extern SECStatus CERT_EncodeAndAddBitStrExtension(void *exthandle, int idtag, + SECItem *value, + PRBool critical); + +extern SECStatus CERT_EncodeAltNameExtension(PLArenaPool *arena, + CERTGeneralName *value, + SECItem *encodedValue); + +/* +** Finish adding cert extensions. Does final processing on extension +** data, putting it in the right format, and freeing any temporary +** storage. +** "exthandle" is the handle used to add extensions to a certificate +*/ +extern SECStatus CERT_FinishExtensions(void *exthandle); + +/* +** Merge an external list of extensions into a cert's extension list, adding one +** only when its OID matches none of the cert's existing extensions. Call this +** immediately before calling CERT_FinishExtensions(). +*/ +SECStatus CERT_MergeExtensions(void *exthandle, CERTCertExtension **exts); + +/* If the extension is found, return its criticality and value. +** This allocate storage for the returning extension value. +*/ +extern SECStatus CERT_GetExtenCriticality(CERTCertExtension **extensions, + int tag, PRBool *isCritical); + +extern void CERT_DestroyOidSequence(CERTOidSequence *oidSeq); + +/**************************************************************************** + * + * DER encode and decode extension values + * + ****************************************************************************/ + +/* Encode the value of the basicConstraint extension. +** arena - where to allocate memory for the encoded value. +** value - extension value to encode +** encodedValue - output encoded value +*/ +extern SECStatus CERT_EncodeBasicConstraintValue(PLArenaPool *arena, + CERTBasicConstraints *value, + SECItem *encodedValue); + +/* +** Encode the value of the authorityKeyIdentifier extension. +*/ +extern SECStatus CERT_EncodeAuthKeyID(PLArenaPool *arena, CERTAuthKeyID *value, + SECItem *encodedValue); + +/* +** Encode the value of the crlDistributionPoints extension. +*/ +extern SECStatus CERT_EncodeCRLDistributionPoints( + PLArenaPool *arena, CERTCrlDistributionPoints *value, SECItem *derValue); + +/* +** Decodes a DER encoded basicConstaint extension value into a readable format +** value - decoded value +** encodedValue - value to decoded +*/ +extern SECStatus CERT_DecodeBasicConstraintValue(CERTBasicConstraints *value, + const SECItem *encodedValue); + +/* Decodes a DER encoded authorityKeyIdentifier extension value into a +** readable format. +** arena - where to allocate memory for the decoded value +** encodedValue - value to be decoded +** Returns a CERTAuthKeyID structure which contains the decoded value +*/ +extern CERTAuthKeyID *CERT_DecodeAuthKeyID(PLArenaPool *arena, + const SECItem *encodedValue); + +/* Decodes a DER encoded crlDistributionPoints extension value into a +** readable format. +** arena - where to allocate memory for the decoded value +** der - value to be decoded +** Returns a CERTCrlDistributionPoints structure which contains the +** decoded value +*/ +extern CERTCrlDistributionPoints *CERT_DecodeCRLDistributionPoints( + PLArenaPool *arena, SECItem *der); + +/* Extract certain name type from a generalName */ +extern void *CERT_GetGeneralNameByType(CERTGeneralName *genNames, + CERTGeneralNameType type, + PRBool derFormat); + +extern CERTOidSequence *CERT_DecodeOidSequence(const SECItem *seqItem); + +/**************************************************************************** + * + * Find extension values of a certificate + * + ***************************************************************************/ + +extern SECStatus CERT_FindCertExtension(const CERTCertificate *cert, int tag, + SECItem *value); + +extern SECStatus CERT_FindNSCertTypeExtension(CERTCertificate *cert, + SECItem *value); + +extern char *CERT_FindNSStringExtension(CERTCertificate *cert, int oidtag); + +extern SECStatus CERT_FindCertExtensionByOID(CERTCertificate *cert, + SECItem *oid, SECItem *value); + +/* Returns the decoded value of the authKeyID extension. +** Note that this uses passed in the arena to allocate storage for the result +*/ +extern CERTAuthKeyID *CERT_FindAuthKeyIDExten(PLArenaPool *arena, + CERTCertificate *cert); + +/* Returns the decoded value of the basicConstraint extension. + */ +extern SECStatus CERT_FindBasicConstraintExten(CERTCertificate *cert, + CERTBasicConstraints *value); + +/* Returns the decoded value of the crlDistributionPoints extension. +** Note that the arena in cert is used to allocate storage for the result +*/ +extern CERTCrlDistributionPoints *CERT_FindCRLDistributionPoints( + CERTCertificate *cert); + +/* Returns value of the keyUsage extension. This uses PR_Alloc to allocate +** buffer for the decoded value. The caller should free up the storage +** allocated in value->data. +*/ +extern SECStatus CERT_FindKeyUsageExtension(CERTCertificate *cert, + SECItem *value); + +/* Return the decoded value of the subjectKeyID extension. The caller should +** free up the storage allocated in retItem->data. +*/ +extern SECStatus CERT_FindSubjectKeyIDExtension(CERTCertificate *cert, + SECItem *retItem); + +/* +** If cert is a v3 certificate, and a critical keyUsage extension is included, +** then check the usage against the extension value. If a non-critical +** keyUsage extension is included, this will return SECSuccess without +** checking, since the extension is an advisory field, not a restriction. +** If cert is not a v3 certificate, this will return SECSuccess. +** cert - certificate +** usage - one of the x.509 v3 the Key Usage Extension flags +*/ +extern SECStatus CERT_CheckCertUsage(CERTCertificate *cert, + unsigned char usage); + +/**************************************************************************** + * + * CRL v2 Extensions supported routines + * + ****************************************************************************/ + +extern SECStatus CERT_FindCRLExtensionByOID(CERTCrl *crl, SECItem *oid, + SECItem *value); + +extern SECStatus CERT_FindCRLExtension(CERTCrl *crl, int tag, SECItem *value); + +extern SECStatus CERT_FindInvalidDateExten(CERTCrl *crl, PRTime *value); + +/* +** Set up a crl for adding X509v3 extensions. Returns an opaque handle +** used by routines that take an exthandle (void*) argument . +** "crl" is the CRL we are adding extensions to +*/ +extern void *CERT_StartCRLExtensions(CERTCrl *crl); + +/* +** Set up a crl entry for adding X509v3 extensions. Returns an opaque handle +** used by routines that take an exthandle (void*) argument . +** "crl" is the crl we are adding certs entries to +** "entry" is the crl entry we are adding extensions to +*/ +extern void *CERT_StartCRLEntryExtensions(CERTCrl *crl, CERTCrlEntry *entry); + +extern CERTCertNicknames *CERT_GetCertNicknames(CERTCertDBHandle *handle, + int what, void *wincx); + +/* +** Finds the crlNumber extension and decodes its value into 'value' +*/ +extern SECStatus CERT_FindCRLNumberExten(PLArenaPool *arena, CERTCrl *crl, + SECItem *value); + +extern SECStatus CERT_FindCRLEntryReasonExten(CERTCrlEntry *crlEntry, + CERTCRLEntryReasonCode *value); + +extern void CERT_FreeNicknames(CERTCertNicknames *nicknames); + +extern PRBool CERT_CompareCerts(const CERTCertificate *c1, + const CERTCertificate *c2); + +extern PRBool CERT_CompareCertsForRedirection(CERTCertificate *c1, + CERTCertificate *c2); + +/* +** Generate an array of the Distinguished Names that the given cert database +** "trusts" +*/ +extern CERTDistNames *CERT_GetSSLCACerts(CERTCertDBHandle *handle); + +extern void CERT_FreeDistNames(CERTDistNames *names); + +/* Duplicate distinguished name array */ +extern CERTDistNames *CERT_DupDistNames(CERTDistNames *orig); + +/* +** Generate an array of Distinguished names from an array of nicknames +*/ +extern CERTDistNames *CERT_DistNamesFromNicknames(CERTCertDBHandle *handle, + char **nicknames, int nnames); + +/* +** Generate an array of Distinguished names from a list of certs. +*/ +extern CERTDistNames *CERT_DistNamesFromCertList(CERTCertList *list); + +/* +** Generate a certificate chain from a certificate. +*/ +extern CERTCertificateList *CERT_CertChainFromCert(CERTCertificate *cert, + SECCertUsage usage, + PRBool includeRoot); + +extern CERTCertificateList *CERT_CertListFromCert(CERTCertificate *cert); + +extern CERTCertificateList *CERT_DupCertList( + const CERTCertificateList *oldList); + +extern void CERT_DestroyCertificateList(CERTCertificateList *list); + +/* +** is cert a user cert? i.e. does it have CERTDB_USER trust, +** i.e. a private key? +*/ +PRBool CERT_IsUserCert(CERTCertificate *cert); + +/* is cert a newer than cert b? */ +PRBool CERT_IsNewer(CERTCertificate *certa, CERTCertificate *certb); + +/* currently a stub for address book */ +PRBool CERT_IsCertRevoked(CERTCertificate *cert); + +void CERT_DestroyCertArray(CERTCertificate **certs, unsigned int ncerts); + +/* convert an email address to lower case */ +char *CERT_FixupEmailAddr(const char *emailAddr); + +/* decode string representation of trust flags into trust struct */ +SECStatus CERT_DecodeTrustString(CERTCertTrust *trust, const char *trusts); + +/* encode trust struct into string representation of trust flags */ +char *CERT_EncodeTrustString(CERTCertTrust *trust); + +/* find the next or prev cert in a subject list */ +CERTCertificate *CERT_PrevSubjectCert(CERTCertificate *cert); +CERTCertificate *CERT_NextSubjectCert(CERTCertificate *cert); + +/* + * import a collection of certs into the temporary or permanent cert + * database + */ +SECStatus CERT_ImportCerts(CERTCertDBHandle *certdb, SECCertUsage usage, + unsigned int ncerts, SECItem **derCerts, + CERTCertificate ***retCerts, PRBool keepCerts, + PRBool caOnly, char *nickname); + +char *CERT_MakeCANickname(CERTCertificate *cert); + +PRBool CERT_IsCACert(CERTCertificate *cert, unsigned int *rettype); + +PRBool CERT_IsCADERCert(SECItem *derCert, unsigned int *rettype); + +PRBool CERT_IsRootDERCert(SECItem *derCert); + +SECStatus CERT_SaveSMimeProfile(CERTCertificate *cert, SECItem *emailProfile, + SECItem *profileTime); + +/* + * find the smime symmetric capabilities profile for a given cert + */ +SECItem *CERT_FindSMimeProfile(CERTCertificate *cert); + +SECStatus CERT_AddNewCerts(CERTCertDBHandle *handle); + +CERTCertificatePolicies *CERT_DecodeCertificatePoliciesExtension( + const SECItem *extnValue); + +void CERT_DestroyCertificatePoliciesExtension( + CERTCertificatePolicies *policies); + +CERTCertificatePolicyMappings *CERT_DecodePolicyMappingsExtension( + SECItem *encodedCertPolicyMaps); + +SECStatus CERT_DestroyPolicyMappingsExtension( + CERTCertificatePolicyMappings *mappings); + +SECStatus CERT_DecodePolicyConstraintsExtension( + CERTCertificatePolicyConstraints *decodedValue, + const SECItem *encodedValue); + +SECStatus CERT_DecodeInhibitAnyExtension( + CERTCertificateInhibitAny *decodedValue, SECItem *extnValue); + +CERTUserNotice *CERT_DecodeUserNotice(SECItem *noticeItem); + +extern CERTGeneralName *CERT_DecodeAltNameExtension(PLArenaPool *reqArena, + SECItem *EncodedAltName); + +extern CERTNameConstraints *CERT_DecodeNameConstraintsExtension( + PLArenaPool *arena, const SECItem *encodedConstraints); + +/* returns addr of a NULL termainated array of pointers to CERTAuthInfoAccess */ +extern CERTAuthInfoAccess **CERT_DecodeAuthInfoAccessExtension( + PLArenaPool *reqArena, const SECItem *encodedExtension); + +extern CERTPrivKeyUsagePeriod *CERT_DecodePrivKeyUsagePeriodExtension( + PLArenaPool *arena, SECItem *extnValue); + +extern CERTGeneralName *CERT_GetNextGeneralName(CERTGeneralName *current); + +extern CERTGeneralName *CERT_GetPrevGeneralName(CERTGeneralName *current); + +/* + * Look up name constraints for some certs that do not include name constraints + * (Most importantly, root certificates) + * + * If a matching subject is found, |extensions| will be populated with a copy of + * the + * DER-encoded name constraints extension. The data in |extensions| will point + * to + * memory that the caller owns. + * + * There is no mechanism to configure imposed name constraints right now. All + * imposed name constraints are built into NSS. + */ +SECStatus CERT_GetImposedNameConstraints(const SECItem *derSubject, + SECItem *extensions); + +CERTNameConstraint *CERT_GetNextNameConstraint(CERTNameConstraint *current); + +CERTNameConstraint *CERT_GetPrevNameConstraint(CERTNameConstraint *current); + +void CERT_DestroyUserNotice(CERTUserNotice *userNotice); + +typedef char *(*CERTPolicyStringCallback)(char *org, unsigned long noticeNumber, + void *arg); +void CERT_SetCAPolicyStringCallback(CERTPolicyStringCallback cb, void *cbarg); + +char *CERT_GetCertCommentString(CERTCertificate *cert); + +PRBool CERT_GovtApprovedBitSet(CERTCertificate *cert); + +SECStatus CERT_AddPermNickname(CERTCertificate *cert, char *nickname); + +CERTCertList *CERT_MatchUserCert(CERTCertDBHandle *handle, SECCertUsage usage, + int nCANames, char **caNames, void *proto_win); + +CERTCertList *CERT_NewCertList(void); + +/* free the cert list and all the certs in the list */ +void CERT_DestroyCertList(CERTCertList *certs); + +/* remove the node and free the cert */ +void CERT_RemoveCertListNode(CERTCertListNode *node); + +/* equivalent to CERT_AddCertToListTailWithData(certs, cert, NULL) */ +SECStatus CERT_AddCertToListTail(CERTCertList *certs, CERTCertificate *cert); + +/* equivalent to CERT_AddCertToListHeadWithData(certs, cert, NULL) */ +SECStatus CERT_AddCertToListHead(CERTCertList *certs, CERTCertificate *cert); + +/* + * The new cert list node takes ownership of "cert". "cert" is freed + * when the list node is removed. + */ +SECStatus CERT_AddCertToListTailWithData(CERTCertList *certs, + CERTCertificate *cert, void *appData); + +/* + * The new cert list node takes ownership of "cert". "cert" is freed + * when the list node is removed. + */ +SECStatus CERT_AddCertToListHeadWithData(CERTCertList *certs, + CERTCertificate *cert, void *appData); + +typedef PRBool (*CERTSortCallback)(CERTCertificate *certa, + CERTCertificate *certb, void *arg); +SECStatus CERT_AddCertToListSorted(CERTCertList *certs, CERTCertificate *cert, + CERTSortCallback f, void *arg); + +/* callback for CERT_AddCertToListSorted that sorts based on validity + * period and a given time. + */ +PRBool CERT_SortCBValidity(CERTCertificate *certa, CERTCertificate *certb, + void *arg); + +SECStatus CERT_CheckForEvilCert(CERTCertificate *cert); + +CERTGeneralName *CERT_GetCertificateNames(CERTCertificate *cert, + PLArenaPool *arena); + +CERTGeneralName *CERT_GetConstrainedCertificateNames( + const CERTCertificate *cert, PLArenaPool *arena, + PRBool includeSubjectCommonName); + +/* + * Creates or adds to a list of all certs with a give subject name, sorted by + * validity time, newest first. Invalid certs are considered older than + * valid certs. If validOnly is set, do not include invalid certs on list. + */ +CERTCertList *CERT_CreateSubjectCertList(CERTCertList *certList, + CERTCertDBHandle *handle, + const SECItem *name, PRTime sorttime, + PRBool validOnly); + +/* + * remove certs from a list that don't have keyUsage and certType + * that match the given usage. + */ +SECStatus CERT_FilterCertListByUsage(CERTCertList *certList, SECCertUsage usage, + PRBool ca); + +/* + * check the key usage of a cert against a set of required values + */ +SECStatus CERT_CheckKeyUsage(CERTCertificate *cert, unsigned int requiredUsage); + +/* + * return required key usage and cert type based on cert usage + */ +SECStatus CERT_KeyUsageAndTypeForCertUsage(SECCertUsage usage, PRBool ca, + unsigned int *retKeyUsage, + unsigned int *retCertType); +/* + * return required trust flags for various cert usages for CAs + */ +SECStatus CERT_TrustFlagsForCACertUsage(SECCertUsage usage, + unsigned int *retFlags, + SECTrustType *retTrustType); + +/* + * Find all user certificates that match the given criteria. + * + * "handle" - database to search + * "usage" - certificate usage to match + * "oneCertPerName" - if set then only return the "best" cert per + * name + * "validOnly" - only return certs that are curently valid + * "proto_win" - window handle passed to pkcs11 + */ +CERTCertList *CERT_FindUserCertsByUsage(CERTCertDBHandle *handle, + SECCertUsage usage, + PRBool oneCertPerName, PRBool validOnly, + void *proto_win); + +/* + * Find a user certificate that matchs the given criteria. + * + * "handle" - database to search + * "nickname" - nickname to match + * "usage" - certificate usage to match + * "validOnly" - only return certs that are curently valid + * "proto_win" - window handle passed to pkcs11 + */ +CERTCertificate *CERT_FindUserCertByUsage(CERTCertDBHandle *handle, + const char *nickname, + SECCertUsage usage, PRBool validOnly, + void *proto_win); + +/* + * Filter a list of certificates, removing those certs that do not have + * one of the named CA certs somewhere in their cert chain. + * + * "certList" - the list of certificates to filter + * "nCANames" - number of CA names + * "caNames" - array of CA names in string(rfc 1485) form + * "usage" - what use the certs are for, this is used when + * selecting CA certs + */ +SECStatus CERT_FilterCertListByCANames(CERTCertList *certList, int nCANames, + char **caNames, SECCertUsage usage); + +/* + * Filter a list of certificates, removing those certs that aren't user certs + */ +SECStatus CERT_FilterCertListForUserCerts(CERTCertList *certList); + +/* + * Filter a list of certificates, removing those certs that don't match the + * nickname. + */ +SECStatus CERT_FilterCertListByNickname(CERTCertList *certList, char *nickname, + void *pwarg); + +/* return true if cert is in cert list */ +PRBool CERT_IsInList(const CERTCertificate *cert, const CERTCertList *certList); + +/* returned certList is the intersection of the certs on certList and the + * certs on filterList */ +SECStatus CERT_FilterCertListByCertList(CERTCertList *certList, + const CERTCertList *filterList); + +/* + * Collect the nicknames from all certs in a CertList. If the cert is not + * valid, append a string to that nickname. + * + * "certList" - the list of certificates + * "expiredString" - the string to append to the nickname of any expired cert + * "notYetGoodString" - the string to append to the nickname of any cert + * that is not yet valid + */ +CERTCertNicknames *CERT_NicknameStringsFromCertList(CERTCertList *certList, + char *expiredString, + char *notYetGoodString); + +/* + * Extract the nickname from a nickmake string that may have either + * expiredString or notYetGoodString appended. + * + * Args: + * "namestring" - the string containing the nickname, and possibly + * one of the validity label strings + * "expiredString" - the expired validity label string + * "notYetGoodString" - the not yet good validity label string + * + * Returns the raw nickname + */ +char *CERT_ExtractNicknameString(char *namestring, char *expiredString, + char *notYetGoodString); + +/* + * Given a certificate, return a string containing the nickname, and possibly + * one of the validity strings, based on the current validity state of the + * certificate. + * + * "arena" - arena to allocate returned string from. If NULL, then heap + * is used. + * "cert" - the cert to get nickname from + * "expiredString" - the string to append to the nickname if the cert is + * expired. + * "notYetGoodString" - the string to append to the nickname if the cert is + * not yet good. + */ +char *CERT_GetCertNicknameWithValidity(PLArenaPool *arena, + CERTCertificate *cert, + char *expiredString, + char *notYetGoodString); + +/* + * Return the string representation of a DER encoded distinguished name + * "dername" - The DER encoded name to convert + */ +char *CERT_DerNameToAscii(SECItem *dername); + +/* + * Supported usage values and types: + * certUsageSSLClient + * certUsageSSLServer + * certUsageSSLServerWithStepUp + * certUsageEmailSigner + * certUsageEmailRecipient + * certUsageObjectSigner + */ + +CERTCertificate *CERT_FindMatchingCert(CERTCertDBHandle *handle, + SECItem *derName, CERTCertOwner owner, + SECCertUsage usage, PRBool preferTrusted, + PRTime validTime, PRBool validOnly); + +/* + * Acquire the global lock on the cert database. + * This lock is currently used for the following operations: + * adding or deleting a cert to either the temp or perm databases + * converting a temp to perm or perm to temp + * changing(maybe just adding?) the trust of a cert + * adjusting the reference count of a cert + */ +void CERT_LockDB(CERTCertDBHandle *handle); + +/* + * Free the global cert database lock. + */ +void CERT_UnlockDB(CERTCertDBHandle *handle); + +/* + * Get the certificate status checking configuratino data for + * the certificate database + */ +CERTStatusConfig *CERT_GetStatusConfig(CERTCertDBHandle *handle); + +/* + * Set the certificate status checking information for the + * database. The input structure becomes part of the certificate + * database and will be freed by calling the 'Destroy' function in + * the configuration object. + */ +void CERT_SetStatusConfig(CERTCertDBHandle *handle, CERTStatusConfig *config); + +/* + * Acquire the cert reference count lock + * There is currently one global lock for all certs, but I'm putting a cert + * arg here so that it will be easy to make it per-cert in the future if + * that turns out to be necessary. + */ +void CERT_LockCertRefCount(CERTCertificate *cert); + +/* + * Release the cert reference count lock + */ +void CERT_UnlockCertRefCount(CERTCertificate *cert); + +/* + * Digest the cert's subject public key using the specified algorithm. + * NOTE: this digests the value of the BIT STRING subjectPublicKey (excluding + * the tag, length, and number of unused bits) rather than the whole + * subjectPublicKeyInfo field. + * + * The necessary storage for the digest data is allocated. If "fill" is + * non-null, the data is put there, otherwise a SECItem is allocated. + * Allocation from "arena" if it is non-null, heap otherwise. Any problem + * results in a NULL being returned (and an appropriate error set). + */ +extern SECItem *CERT_GetSubjectPublicKeyDigest(PLArenaPool *arena, + const CERTCertificate *cert, + SECOidTag digestAlg, + SECItem *fill); + +/* + * Digest the cert's subject name using the specified algorithm. + */ +extern SECItem *CERT_GetSubjectNameDigest(PLArenaPool *arena, + const CERTCertificate *cert, + SECOidTag digestAlg, SECItem *fill); + +SECStatus CERT_CheckCRL(CERTCertificate *cert, CERTCertificate *issuer, + const SECItem *dp, PRTime t, void *wincx); + +/* + * Add a CERTNameConstraint to the CERTNameConstraint list + */ +extern CERTNameConstraint *CERT_AddNameConstraint( + CERTNameConstraint *list, CERTNameConstraint *constraint); + +/* + * Allocate space and copy CERTNameConstraint from src to dest. + * Arena is used to allocate result(if dest eq NULL) and its members + * SECItem data. + */ +extern CERTNameConstraint *CERT_CopyNameConstraint(PLArenaPool *arena, + CERTNameConstraint *dest, + CERTNameConstraint *src); + +/* + * Verify name against all the constraints relevant to that type of + * the name. + */ +extern SECStatus CERT_CheckNameSpace(PLArenaPool *arena, + const CERTNameConstraints *constraints, + const CERTGeneralName *currentName); + +/* + * Extract and allocate the name constraints extension from the CA cert. + * If the certificate contains no name constraints extension, but + * CERT_GetImposedNameConstraints returns a name constraints extension + * for the subject of the certificate, then that extension will be returned. + */ +extern SECStatus CERT_FindNameConstraintsExten( + PLArenaPool *arena, CERTCertificate *cert, + CERTNameConstraints **constraints); + +/* + * Initialize a new GERTGeneralName fields (link) + */ +extern CERTGeneralName *CERT_NewGeneralName(PLArenaPool *arena, + CERTGeneralNameType type); + +/* + * Lookup a CERTGeneralNameType constant by its human readable string. + */ +extern CERTGeneralNameType CERT_GetGeneralNameTypeFromString( + const char *string); + +/* + * PKIX extension encoding routines + */ +extern SECStatus CERT_EncodePolicyConstraintsExtension( + PLArenaPool *arena, CERTCertificatePolicyConstraints *constr, + SECItem *dest); +extern SECStatus CERT_EncodeInhibitAnyExtension( + PLArenaPool *arena, CERTCertificateInhibitAny *inhibitAny, SECItem *dest); +extern SECStatus CERT_EncodePolicyMappingExtension( + PLArenaPool *arena, CERTCertificatePolicyMappings *maps, SECItem *dest); + +extern SECStatus CERT_EncodeInfoAccessExtension(PLArenaPool *arena, + CERTAuthInfoAccess **info, + SECItem *dest); +extern SECStatus CERT_EncodeUserNotice(PLArenaPool *arena, + CERTUserNotice *notice, SECItem *dest); + +extern SECStatus CERT_EncodeDisplayText(PLArenaPool *arena, SECItem *text, + SECItem *dest); + +extern SECStatus CERT_EncodeCertPoliciesExtension(PLArenaPool *arena, + CERTPolicyInfo **info, + SECItem *dest); +extern SECStatus CERT_EncodeNoticeReference(PLArenaPool *arena, + CERTNoticeReference *reference, + SECItem *dest); + +/* + * Returns a pointer to a static structure. + */ +extern const CERTRevocationFlags *CERT_GetPKIXVerifyNistRevocationPolicy(void); + +/* + * Returns a pointer to a static structure. + */ +extern const CERTRevocationFlags *CERT_GetClassicOCSPEnabledSoftFailurePolicy( + void); + +/* + * Returns a pointer to a static structure. + */ +extern const CERTRevocationFlags *CERT_GetClassicOCSPEnabledHardFailurePolicy( + void); + +/* + * Returns a pointer to a static structure. + */ +extern const CERTRevocationFlags *CERT_GetClassicOCSPDisabledPolicy(void); + +/* + * Verify a Cert with libpkix + * paramsIn control the verification options. If a value isn't specified + * in paramsIn, it reverts to the application default. + * paramsOut specifies the parameters the caller would like to get back. + * the caller may pass NULL, in which case no parameters are returned. + */ +extern SECStatus CERT_PKIXVerifyCert(CERTCertificate *cert, + SECCertificateUsage usages, + CERTValInParam *paramsIn, + CERTValOutParam *paramsOut, void *wincx); + +/* Makes old cert validation APIs(CERT_VerifyCert, CERT_VerifyCertificate) + * to use libpkix validation engine. The function should be called ones at + * application initialization time. + * Function is not thread safe.*/ +extern SECStatus CERT_SetUsePKIXForValidation(PRBool enable); + +/* The function return PR_TRUE if cert validation should use + * libpkix cert validation engine. */ +extern PRBool CERT_GetUsePKIXForValidation(void); + +/* + * Allocate a parameter container of type CERTRevocationFlags, + * and allocate the inner arrays of the given sizes. + * To cleanup call CERT_DestroyCERTRevocationFlags. + */ +extern CERTRevocationFlags *CERT_AllocCERTRevocationFlags( + PRUint32 number_leaf_methods, PRUint32 number_leaf_pref_methods, + PRUint32 number_chain_methods, PRUint32 number_chain_pref_methods); + +/* + * Destroy the arrays inside flags, + * and destroy the object pointed to by flags, too. + */ +extern void CERT_DestroyCERTRevocationFlags(CERTRevocationFlags *flags); + +/* + * Get istemp and isperm fields from a cert in a thread safe way. + */ +extern SECStatus CERT_GetCertIsTemp(const CERTCertificate *cert, PRBool *istemp); +extern SECStatus CERT_GetCertIsPerm(const CERTCertificate *cert, PRBool *isperm); + +SEC_END_PROTOS + +#endif /* _CERT_H_ */ diff --git a/security/nss/lib/certdb/certdb.c b/security/nss/lib/certdb/certdb.c new file mode 100644 index 0000000000..0d7cdab768 --- /dev/null +++ b/security/nss/lib/certdb/certdb.c @@ -0,0 +1,3411 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Certificate handling code + */ + +#include "nssilock.h" +#include "prmon.h" +#include "prtime.h" +#include "cert.h" +#include "certi.h" +#include "secder.h" +#include "secoid.h" +#include "secasn1.h" +#include "genname.h" +#include "keyhi.h" +#include "secitem.h" +#include "certdb.h" +#include "prprf.h" +#include "sechash.h" +#include "prlong.h" +#include "certxutl.h" +#include "portreg.h" +#include "secerr.h" +#include "sslerr.h" +#include "pk11func.h" +#include "xconst.h" /* for CERT_DecodeAltNameExtension */ + +#include "pki.h" +#include "pki3hack.h" + +SEC_ASN1_MKSUB(CERT_TimeChoiceTemplate) +SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate) +SEC_ASN1_MKSUB(SEC_BitStringTemplate) +SEC_ASN1_MKSUB(SEC_IntegerTemplate) +SEC_ASN1_MKSUB(SEC_SkipTemplate) + +/* + * Certificate database handling code + */ + +const SEC_ASN1Template CERT_CertExtensionTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCertExtension) }, + { SEC_ASN1_OBJECT_ID, offsetof(CERTCertExtension, id) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN, /* XXX DER_DEFAULT */ + offsetof(CERTCertExtension, critical) }, + { SEC_ASN1_OCTET_STRING, offsetof(CERTCertExtension, value) }, + { 0 } +}; + +const SEC_ASN1Template CERT_SequenceOfCertExtensionTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, 0, CERT_CertExtensionTemplate } +}; + +const SEC_ASN1Template CERT_TimeChoiceTemplate[] = { + { SEC_ASN1_CHOICE, offsetof(SECItem, type), 0, sizeof(SECItem) }, + { SEC_ASN1_UTC_TIME, 0, 0, siUTCTime }, + { SEC_ASN1_GENERALIZED_TIME, 0, 0, siGeneralizedTime }, + { 0 } +}; + +const SEC_ASN1Template CERT_ValidityTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTValidity) }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTValidity, notBefore), + SEC_ASN1_SUB(CERT_TimeChoiceTemplate), 0 }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTValidity, notAfter), + SEC_ASN1_SUB(CERT_TimeChoiceTemplate), 0 }, + { 0 } +}; + +const SEC_ASN1Template CERT_CertificateTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCertificate) }, + { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, /* XXX DER_DEFAULT */ + offsetof(CERTCertificate, version), + SEC_ASN1_SUB(SEC_IntegerTemplate) }, + { SEC_ASN1_INTEGER, offsetof(CERTCertificate, serialNumber) }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTCertificate, signature), + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, + { SEC_ASN1_SAVE, offsetof(CERTCertificate, derIssuer) }, + { SEC_ASN1_INLINE, offsetof(CERTCertificate, issuer), CERT_NameTemplate }, + { SEC_ASN1_INLINE, offsetof(CERTCertificate, validity), + CERT_ValidityTemplate }, + { SEC_ASN1_SAVE, offsetof(CERTCertificate, derSubject) }, + { SEC_ASN1_INLINE, offsetof(CERTCertificate, subject), CERT_NameTemplate }, + { SEC_ASN1_SAVE, offsetof(CERTCertificate, derPublicKey) }, + { SEC_ASN1_INLINE, offsetof(CERTCertificate, subjectPublicKeyInfo), + CERT_SubjectPublicKeyInfoTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1, + offsetof(CERTCertificate, issuerID), + SEC_ASN1_SUB(SEC_BitStringTemplate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2, + offsetof(CERTCertificate, subjectID), + SEC_ASN1_SUB(SEC_BitStringTemplate) }, + { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | 3, + offsetof(CERTCertificate, extensions), + CERT_SequenceOfCertExtensionTemplate }, + { 0 } +}; + +const SEC_ASN1Template SEC_SignedCertificateTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCertificate) }, + { SEC_ASN1_SAVE, offsetof(CERTCertificate, signatureWrap.data) }, + { SEC_ASN1_INLINE, 0, CERT_CertificateTemplate }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, + offsetof(CERTCertificate, signatureWrap.signatureAlgorithm), + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, + { SEC_ASN1_BIT_STRING, offsetof(CERTCertificate, signatureWrap.signature) }, + { 0 } +}; + +/* + * Find the subjectName in a DER encoded certificate + */ +const SEC_ASN1Template SEC_CertSubjectTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECItem) }, + { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, + 0, SEC_ASN1_SUB(SEC_SkipTemplate) }, /* version */ + { SEC_ASN1_SKIP }, /* serial number */ + { SEC_ASN1_SKIP }, /* signature algorithm */ + { SEC_ASN1_SKIP }, /* issuer */ + { SEC_ASN1_SKIP }, /* validity */ + { SEC_ASN1_ANY, 0, NULL }, /* subject */ + { SEC_ASN1_SKIP_REST }, + { 0 } +}; + +/* + * Find the issuerName in a DER encoded certificate + */ +const SEC_ASN1Template SEC_CertIssuerTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECItem) }, + { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, + 0, SEC_ASN1_SUB(SEC_SkipTemplate) }, /* version */ + { SEC_ASN1_SKIP }, /* serial number */ + { SEC_ASN1_SKIP }, /* signature algorithm */ + { SEC_ASN1_ANY, 0, NULL }, /* issuer */ + { SEC_ASN1_SKIP_REST }, + { 0 } +}; +/* + * Find the subjectName in a DER encoded certificate + */ +const SEC_ASN1Template SEC_CertSerialNumberTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECItem) }, + { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, + 0, SEC_ASN1_SUB(SEC_SkipTemplate) }, /* version */ + { SEC_ASN1_ANY, 0, NULL }, /* serial number */ + { SEC_ASN1_SKIP_REST }, + { 0 } +}; + +/* + * Find the issuer and serialNumber in a DER encoded certificate. + * This data is used as the database lookup key since its the unique + * identifier of a certificate. + */ +const SEC_ASN1Template CERT_CertKeyTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCertKey) }, + { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, + 0, SEC_ASN1_SUB(SEC_SkipTemplate) }, /* version */ + { SEC_ASN1_INTEGER, offsetof(CERTCertKey, serialNumber) }, + { SEC_ASN1_SKIP }, /* signature algorithm */ + { SEC_ASN1_ANY, offsetof(CERTCertKey, derIssuer) }, + { SEC_ASN1_SKIP_REST }, + { 0 } +}; + +SEC_ASN1_CHOOSER_IMPLEMENT(CERT_TimeChoiceTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(CERT_CertificateTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(SEC_SignedCertificateTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SequenceOfCertExtensionTemplate) + +SECStatus +CERT_KeyFromIssuerAndSN(PLArenaPool *arena, SECItem *issuer, SECItem *sn, + SECItem *key) +{ + key->len = sn->len + issuer->len; + + if ((sn->data == NULL) || (issuer->data == NULL)) { + goto loser; + } + + key->data = (unsigned char *)PORT_ArenaAlloc(arena, key->len); + if (!key->data) { + goto loser; + } + + /* copy the serialNumber */ + PORT_Memcpy(key->data, sn->data, sn->len); + + /* copy the issuer */ + PORT_Memcpy(&key->data[sn->len], issuer->data, issuer->len); + + return (SECSuccess); + +loser: + return (SECFailure); +} + +/* + * Extract the subject name from a DER certificate + */ +SECStatus +CERT_NameFromDERCert(SECItem *derCert, SECItem *derName) +{ + int rv; + PLArenaPool *arena; + CERTSignedData sd; + void *tmpptr; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if (!arena) { + return (SECFailure); + } + + PORT_Memset(&sd, 0, sizeof(CERTSignedData)); + rv = SEC_QuickDERDecodeItem(arena, &sd, CERT_SignedDataTemplate, derCert); + + if (rv) { + goto loser; + } + + PORT_Memset(derName, 0, sizeof(SECItem)); + rv = SEC_QuickDERDecodeItem(arena, derName, SEC_CertSubjectTemplate, + &sd.data); + + if (rv) { + goto loser; + } + + tmpptr = derName->data; + derName->data = (unsigned char *)PORT_Alloc(derName->len); + if (derName->data == NULL) { + goto loser; + } + + PORT_Memcpy(derName->data, tmpptr, derName->len); + + PORT_FreeArena(arena, PR_FALSE); + return (SECSuccess); + +loser: + PORT_FreeArena(arena, PR_FALSE); + return (SECFailure); +} + +SECStatus +CERT_IssuerNameFromDERCert(SECItem *derCert, SECItem *derName) +{ + int rv; + PORTCheapArenaPool tmpArena; + CERTSignedData sd; + void *tmpptr; + + PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); + + PORT_Memset(&sd, 0, sizeof(CERTSignedData)); + rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &sd, CERT_SignedDataTemplate, + derCert); + if (rv) { + goto loser; + } + + PORT_Memset(derName, 0, sizeof(SECItem)); + rv = SEC_QuickDERDecodeItem(&tmpArena.arena, derName, + SEC_CertIssuerTemplate, &sd.data); + if (rv) { + goto loser; + } + + tmpptr = derName->data; + derName->data = (unsigned char *)PORT_Alloc(derName->len); + if (derName->data == NULL) { + goto loser; + } + + PORT_Memcpy(derName->data, tmpptr, derName->len); + + PORT_DestroyCheapArena(&tmpArena); + return (SECSuccess); + +loser: + PORT_DestroyCheapArena(&tmpArena); + return (SECFailure); +} + +SECStatus +CERT_SerialNumberFromDERCert(SECItem *derCert, SECItem *derName) +{ + int rv; + PORTCheapArenaPool tmpArena; + CERTSignedData sd; + void *tmpptr; + + PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); + + PORT_Memset(&sd, 0, sizeof(CERTSignedData)); + rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &sd, CERT_SignedDataTemplate, + derCert); + if (rv) { + goto loser; + } + + PORT_Memset(derName, 0, sizeof(SECItem)); + rv = SEC_QuickDERDecodeItem(&tmpArena.arena, derName, + SEC_CertSerialNumberTemplate, &sd.data); + if (rv) { + goto loser; + } + + tmpptr = derName->data; + derName->data = (unsigned char *)PORT_Alloc(derName->len); + if (derName->data == NULL) { + goto loser; + } + + PORT_Memcpy(derName->data, tmpptr, derName->len); + + PORT_DestroyCheapArena(&tmpArena); + return (SECSuccess); + +loser: + PORT_DestroyCheapArena(&tmpArena); + return (SECFailure); +} + +/* + * Generate a database key, based on serial number and issuer, from a + * DER certificate. + */ +SECStatus +CERT_KeyFromDERCert(PLArenaPool *reqArena, SECItem *derCert, SECItem *key) +{ + int rv; + CERTSignedData sd; + CERTCertKey certkey; + + if (!reqArena) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + PORT_Memset(&sd, 0, sizeof(CERTSignedData)); + rv = + SEC_QuickDERDecodeItem(reqArena, &sd, CERT_SignedDataTemplate, derCert); + + if (rv) { + goto loser; + } + + PORT_Memset(&certkey, 0, sizeof(CERTCertKey)); + rv = SEC_QuickDERDecodeItem(reqArena, &certkey, CERT_CertKeyTemplate, + &sd.data); + + if (rv) { + goto loser; + } + + return (CERT_KeyFromIssuerAndSN(reqArena, &certkey.derIssuer, + &certkey.serialNumber, key)); +loser: + return (SECFailure); +} + +/* + * fill in keyUsage field of the cert based on the cert extension + * if the extension is not critical, then we allow all uses + */ +static SECStatus +GetKeyUsage(CERTCertificate *cert) +{ + SECStatus rv; + SECItem tmpitem; + + rv = CERT_FindKeyUsageExtension(cert, &tmpitem); + if (rv == SECSuccess) { + /* remember the actual value of the extension */ + cert->rawKeyUsage = tmpitem.len ? tmpitem.data[0] : 0; + cert->keyUsagePresent = PR_TRUE; + cert->keyUsage = cert->rawKeyUsage; + + PORT_Free(tmpitem.data); + tmpitem.data = NULL; + } else { + /* if the extension is not present, then we allow all uses */ + cert->keyUsage = KU_ALL; + cert->rawKeyUsage = KU_ALL; + cert->keyUsagePresent = PR_FALSE; + } + + if (CERT_GovtApprovedBitSet(cert)) { + cert->keyUsage |= KU_NS_GOVT_APPROVED; + cert->rawKeyUsage |= KU_NS_GOVT_APPROVED; + } + + return (SECSuccess); +} + +static SECStatus +findOIDinOIDSeqByTagNum(CERTOidSequence *seq, SECOidTag tagnum) +{ + SECItem **oids; + SECItem *oid; + SECStatus rv = SECFailure; + + if (seq != NULL) { + oids = seq->oids; + while (oids != NULL && *oids != NULL) { + oid = *oids; + if (SECOID_FindOIDTag(oid) == tagnum) { + rv = SECSuccess; + break; + } + oids++; + } + } + return rv; +} + +/* + * fill in nsCertType field of the cert based on the cert extension + */ +SECStatus +cert_GetCertType(CERTCertificate *cert) +{ + PRUint32 nsCertType; + + if (cert->nsCertType) { + /* once set, no need to recalculate */ + return SECSuccess; + } + nsCertType = cert_ComputeCertType(cert); + + /* Assert that it is safe to cast &cert->nsCertType to "PRInt32 *" */ + PORT_Assert(sizeof(cert->nsCertType) == sizeof(PRInt32)); + PR_ATOMIC_SET((PRInt32 *)&cert->nsCertType, nsCertType); + return SECSuccess; +} + +PRBool +cert_IsIPsecOID(CERTOidSequence *extKeyUsage) +{ + if (findOIDinOIDSeqByTagNum( + extKeyUsage, SEC_OID_EXT_KEY_USAGE_IPSEC_IKE) == SECSuccess) { + return PR_TRUE; + } + if (findOIDinOIDSeqByTagNum( + extKeyUsage, SEC_OID_IPSEC_IKE_END) == SECSuccess) { + return PR_TRUE; + } + if (findOIDinOIDSeqByTagNum( + extKeyUsage, SEC_OID_IPSEC_IKE_INTERMEDIATE) == SECSuccess) { + return PR_TRUE; + } + /* these are now deprecated, but may show up. Treat them the same as IKE */ + if (findOIDinOIDSeqByTagNum( + extKeyUsage, SEC_OID_EXT_KEY_USAGE_IPSEC_END) == SECSuccess) { + return PR_TRUE; + } + if (findOIDinOIDSeqByTagNum( + extKeyUsage, SEC_OID_EXT_KEY_USAGE_IPSEC_TUNNEL) == SECSuccess) { + return PR_TRUE; + } + if (findOIDinOIDSeqByTagNum( + extKeyUsage, SEC_OID_EXT_KEY_USAGE_IPSEC_USER) == SECSuccess) { + return PR_TRUE; + } + /* this one should probably be in cert_ComputeCertType and set all usages? */ + if (findOIDinOIDSeqByTagNum( + extKeyUsage, SEC_OID_X509_ANY_EXT_KEY_USAGE) == SECSuccess) { + return PR_TRUE; + } + return PR_FALSE; +} + +PRUint32 +cert_ComputeCertType(CERTCertificate *cert) +{ + SECStatus rv; + SECItem tmpitem; + SECItem encodedExtKeyUsage; + CERTOidSequence *extKeyUsage = NULL; + CERTBasicConstraints basicConstraint; + PRUint32 nsCertType = 0; + PRBool isCA = PR_FALSE; + + tmpitem.data = NULL; + CERT_FindNSCertTypeExtension(cert, &tmpitem); + encodedExtKeyUsage.data = NULL; + rv = CERT_FindCertExtension(cert, SEC_OID_X509_EXT_KEY_USAGE, + &encodedExtKeyUsage); + if (rv == SECSuccess) { + extKeyUsage = CERT_DecodeOidSequence(&encodedExtKeyUsage); + } + rv = CERT_FindBasicConstraintExten(cert, &basicConstraint); + if (rv == SECSuccess) { + isCA = basicConstraint.isCA; + } + if (tmpitem.data != NULL || extKeyUsage != NULL) { + if (tmpitem.data == NULL || tmpitem.len == 0) { + nsCertType = 0; + } else { + nsCertType = tmpitem.data[0]; + } + + /* free tmpitem data pointer to avoid memory leak */ + PORT_Free(tmpitem.data); + tmpitem.data = NULL; + + /* + * for this release, we will allow SSL certs with an email address + * to be used for email + */ + if ((nsCertType & NS_CERT_TYPE_SSL_CLIENT) && cert->emailAddr && + cert->emailAddr[0]) { + nsCertType |= NS_CERT_TYPE_EMAIL; + } + /* + * for this release, we will allow SSL intermediate CAs to be + * email intermediate CAs too. + */ + if (nsCertType & NS_CERT_TYPE_SSL_CA) { + nsCertType |= NS_CERT_TYPE_EMAIL_CA; + } + /* + * allow a cert with the extended key usage of EMail Protect + * to be used for email or as an email CA, if basic constraints + * indicates that it is a CA. + */ + if (findOIDinOIDSeqByTagNum(extKeyUsage, + SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT) == + SECSuccess) { + nsCertType |= isCA ? NS_CERT_TYPE_EMAIL_CA : NS_CERT_TYPE_EMAIL; + } + if (findOIDinOIDSeqByTagNum( + extKeyUsage, SEC_OID_EXT_KEY_USAGE_SERVER_AUTH) == SECSuccess) { + nsCertType |= isCA ? NS_CERT_TYPE_SSL_CA : NS_CERT_TYPE_SSL_SERVER; + } + /* + * Treat certs with step-up OID as also having SSL server type. + * COMODO needs this behaviour until June 2020. See Bug 737802. + */ + if (findOIDinOIDSeqByTagNum(extKeyUsage, + SEC_OID_NS_KEY_USAGE_GOVT_APPROVED) == + SECSuccess) { + nsCertType |= isCA ? NS_CERT_TYPE_SSL_CA : NS_CERT_TYPE_SSL_SERVER; + } + if (findOIDinOIDSeqByTagNum( + extKeyUsage, SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH) == SECSuccess) { + nsCertType |= isCA ? NS_CERT_TYPE_SSL_CA : NS_CERT_TYPE_SSL_CLIENT; + } + if (cert_IsIPsecOID(extKeyUsage)) { + nsCertType |= isCA ? NS_CERT_TYPE_IPSEC_CA : NS_CERT_TYPE_IPSEC; + } + if (findOIDinOIDSeqByTagNum( + extKeyUsage, SEC_OID_EXT_KEY_USAGE_CODE_SIGN) == SECSuccess) { + nsCertType |= isCA ? NS_CERT_TYPE_OBJECT_SIGNING_CA : NS_CERT_TYPE_OBJECT_SIGNING; + } + if (findOIDinOIDSeqByTagNum( + extKeyUsage, SEC_OID_EXT_KEY_USAGE_TIME_STAMP) == SECSuccess) { + nsCertType |= EXT_KEY_USAGE_TIME_STAMP; + } + if (findOIDinOIDSeqByTagNum(extKeyUsage, SEC_OID_OCSP_RESPONDER) == + SECSuccess) { + nsCertType |= EXT_KEY_USAGE_STATUS_RESPONDER; + } + } else { + /* If no NS Cert Type extension and no EKU extension, then */ + nsCertType = 0; + if (CERT_IsCACert(cert, &nsCertType)) + nsCertType |= EXT_KEY_USAGE_STATUS_RESPONDER; + /* if the basic constraint extension says the cert is a CA, then + allow SSL CA and EMAIL CA and Status Responder */ + if (isCA) { + nsCertType |= (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA | + EXT_KEY_USAGE_STATUS_RESPONDER); + } + /* allow any ssl or email (no ca or object signing. */ + nsCertType |= NS_CERT_TYPE_SSL_CLIENT | NS_CERT_TYPE_SSL_SERVER | + NS_CERT_TYPE_EMAIL; + } + + /* IPSEC is allowed to use SSL client and server certs as well as email certs */ + if (nsCertType & (NS_CERT_TYPE_SSL_CLIENT | NS_CERT_TYPE_SSL_SERVER | NS_CERT_TYPE_EMAIL)) { + nsCertType |= NS_CERT_TYPE_IPSEC; + } + if (nsCertType & (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA)) { + nsCertType |= NS_CERT_TYPE_IPSEC_CA; + } + + if (encodedExtKeyUsage.data != NULL) { + PORT_Free(encodedExtKeyUsage.data); + } + if (extKeyUsage != NULL) { + CERT_DestroyOidSequence(extKeyUsage); + } + return nsCertType; +} + +/* + * cert_GetKeyID() - extract or generate the subjectKeyID from a certificate + */ +SECStatus +cert_GetKeyID(CERTCertificate *cert) +{ + SECItem tmpitem; + SECStatus rv; + + cert->subjectKeyID.len = 0; + + /* see of the cert has a key identifier extension */ + rv = CERT_FindSubjectKeyIDExtension(cert, &tmpitem); + if (rv == SECSuccess) { + cert->subjectKeyID.data = + (unsigned char *)PORT_ArenaAlloc(cert->arena, tmpitem.len); + if (cert->subjectKeyID.data != NULL) { + PORT_Memcpy(cert->subjectKeyID.data, tmpitem.data, tmpitem.len); + cert->subjectKeyID.len = tmpitem.len; + cert->keyIDGenerated = PR_FALSE; + } + + PORT_Free(tmpitem.data); + } + + /* if the cert doesn't have a key identifier extension, then generate one*/ + if (cert->subjectKeyID.len == 0) { + /* + * pkix says that if the subjectKeyID is not present, then we should + * use the SHA-1 hash of the DER-encoded publicKeyInfo from the cert + */ + cert->subjectKeyID.data = + (unsigned char *)PORT_ArenaAlloc(cert->arena, SHA1_LENGTH); + if (cert->subjectKeyID.data != NULL) { + rv = PK11_HashBuf(SEC_OID_SHA1, cert->subjectKeyID.data, + cert->derPublicKey.data, cert->derPublicKey.len); + if (rv == SECSuccess) { + cert->subjectKeyID.len = SHA1_LENGTH; + } + } + } + + if (cert->subjectKeyID.len == 0) { + return (SECFailure); + } + return (SECSuccess); +} + +static PRBool +cert_IsRootCert(CERTCertificate *cert) +{ + SECStatus rv; + SECItem tmpitem; + + /* cache the authKeyID extension, if present */ + cert->authKeyID = CERT_FindAuthKeyIDExten(cert->arena, cert); + + /* it MUST be self-issued to be a root */ + if (cert->derIssuer.len == 0 || + !SECITEM_ItemsAreEqual(&cert->derIssuer, &cert->derSubject)) { + return PR_FALSE; + } + + /* check the authKeyID extension */ + if (cert->authKeyID) { + /* authority key identifier is present */ + if (cert->authKeyID->keyID.len > 0) { + /* the keyIdentifier field is set, look for subjectKeyID */ + rv = CERT_FindSubjectKeyIDExtension(cert, &tmpitem); + if (rv == SECSuccess) { + PRBool match; + /* also present, they MUST match for it to be a root */ + match = + SECITEM_ItemsAreEqual(&cert->authKeyID->keyID, &tmpitem); + PORT_Free(tmpitem.data); + if (!match) + return PR_FALSE; /* else fall through */ + } else { + /* the subject key ID is required when AKI is present */ + return PR_FALSE; + } + } + if (cert->authKeyID->authCertIssuer) { + SECItem *caName; + caName = (SECItem *)CERT_GetGeneralNameByType( + cert->authKeyID->authCertIssuer, certDirectoryName, PR_TRUE); + if (caName) { + if (!SECITEM_ItemsAreEqual(&cert->derIssuer, caName)) { + return PR_FALSE; + } /* else fall through */ + } /* else ??? could not get general name as directory name? */ + } + if (cert->authKeyID->authCertSerialNumber.len > 0) { + if (!SECITEM_ItemsAreEqual( + &cert->serialNumber, + &cert->authKeyID->authCertSerialNumber)) { + return PR_FALSE; + } /* else fall through */ + } + /* all of the AKI fields that were present passed the test */ + return PR_TRUE; + } + /* else the AKI was not present, so this is a root */ + return PR_TRUE; +} + +/* + * take a DER certificate and decode it into a certificate structure + */ +CERTCertificate * +CERT_DecodeDERCertificate(SECItem *derSignedCert, PRBool copyDER, + char *nickname) +{ + CERTCertificate *cert; + PLArenaPool *arena; + void *data; + int rv; + int len; + char *tmpname; + + /* make a new arena */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if (!arena) { + return 0; + } + + /* allocate the certificate structure */ + cert = (CERTCertificate *)PORT_ArenaZAlloc(arena, sizeof(CERTCertificate)); + + if (!cert) { + goto loser; + } + + cert->arena = arena; + + if (copyDER) { + /* copy the DER data for the cert into this arena */ + data = (void *)PORT_ArenaAlloc(arena, derSignedCert->len); + if (!data) { + goto loser; + } + cert->derCert.data = (unsigned char *)data; + cert->derCert.len = derSignedCert->len; + PORT_Memcpy(data, derSignedCert->data, derSignedCert->len); + } else { + /* point to passed in DER data */ + cert->derCert = *derSignedCert; + } + + /* decode the certificate info */ + rv = SEC_QuickDERDecodeItem(arena, cert, SEC_SignedCertificateTemplate, + &cert->derCert); + + if (rv) { + goto loser; + } + + if (cert_HasUnknownCriticalExten(cert->extensions) == PR_TRUE) { + cert->options.bits.hasUnsupportedCriticalExt = PR_TRUE; + } + + /* generate and save the database key for the cert */ + rv = CERT_KeyFromIssuerAndSN(arena, &cert->derIssuer, &cert->serialNumber, + &cert->certKey); + if (rv) { + goto loser; + } + + /* set the nickname */ + if (nickname == NULL) { + cert->nickname = NULL; + } else { + /* copy and install the nickname */ + len = PORT_Strlen(nickname) + 1; + cert->nickname = (char *)PORT_ArenaAlloc(arena, len); + if (cert->nickname == NULL) { + goto loser; + } + + PORT_Memcpy(cert->nickname, nickname, len); + } + + /* set the email address */ + cert->emailAddr = cert_GetCertificateEmailAddresses(cert); + + /* initialize the subjectKeyID */ + rv = cert_GetKeyID(cert); + if (rv != SECSuccess) { + goto loser; + } + + /* initialize keyUsage */ + rv = GetKeyUsage(cert); + if (rv != SECSuccess) { + goto loser; + } + + /* determine if this is a root cert */ + cert->isRoot = cert_IsRootCert(cert); + + /* initialize the certType */ + rv = cert_GetCertType(cert); + if (rv != SECSuccess) { + goto loser; + } + + tmpname = CERT_NameToAscii(&cert->subject); + if (tmpname != NULL) { + cert->subjectName = PORT_ArenaStrdup(cert->arena, tmpname); + PORT_Free(tmpname); + } + + tmpname = CERT_NameToAscii(&cert->issuer); + if (tmpname != NULL) { + cert->issuerName = PORT_ArenaStrdup(cert->arena, tmpname); + PORT_Free(tmpname); + } + + cert->referenceCount = 1; + cert->slot = NULL; + cert->pkcs11ID = CK_INVALID_HANDLE; + cert->dbnickname = NULL; + + return (cert); + +loser: + + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + + return (0); +} + +CERTCertificate * +__CERT_DecodeDERCertificate(SECItem *derSignedCert, PRBool copyDER, + char *nickname) +{ + return CERT_DecodeDERCertificate(derSignedCert, copyDER, nickname); +} + +CERTValidity * +CERT_CreateValidity(PRTime notBefore, PRTime notAfter) +{ + CERTValidity *v; + int rv; + PLArenaPool *arena; + + if (notBefore > notAfter) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if (!arena) { + return (0); + } + + v = (CERTValidity *)PORT_ArenaZAlloc(arena, sizeof(CERTValidity)); + if (v) { + v->arena = arena; + rv = DER_EncodeTimeChoice(arena, &v->notBefore, notBefore); + if (rv) + goto loser; + rv = DER_EncodeTimeChoice(arena, &v->notAfter, notAfter); + if (rv) + goto loser; + } + return v; + +loser: + CERT_DestroyValidity(v); + return 0; +} + +SECStatus +CERT_CopyValidity(PLArenaPool *arena, CERTValidity *to, CERTValidity *from) +{ + SECStatus rv; + + CERT_DestroyValidity(to); + to->arena = arena; + + rv = SECITEM_CopyItem(arena, &to->notBefore, &from->notBefore); + if (rv) + return rv; + rv = SECITEM_CopyItem(arena, &to->notAfter, &from->notAfter); + return rv; +} + +void +CERT_DestroyValidity(CERTValidity *v) +{ + if (v && v->arena) { + PORT_FreeArena(v->arena, PR_FALSE); + } + return; +} + +/* +** Amount of time that a certifiate is allowed good before it is actually +** good. This is used for pending certificates, ones that are about to be +** valid. The slop is designed to allow for some variance in the clocks +** of the machine checking the certificate. +*/ +#define PENDING_SLOP (24L * 60L * 60L) /* seconds per day */ +static PRInt32 pendingSlop = PENDING_SLOP; /* seconds */ + +PRInt32 +CERT_GetSlopTime(void) +{ + return pendingSlop; /* seconds */ +} + +SECStatus +CERT_SetSlopTime(PRInt32 slop) /* seconds */ +{ + if (slop < 0) + return SECFailure; + pendingSlop = slop; + return SECSuccess; +} + +SECStatus +CERT_GetCertTimes(const CERTCertificate *c, PRTime *notBefore, PRTime *notAfter) +{ + SECStatus rv; + + if (!c || !notBefore || !notAfter) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* convert DER not-before time */ + rv = DER_DecodeTimeChoice(notBefore, &c->validity.notBefore); + if (rv) { + return (SECFailure); + } + + /* convert DER not-after time */ + rv = DER_DecodeTimeChoice(notAfter, &c->validity.notAfter); + if (rv) { + return (SECFailure); + } + + return (SECSuccess); +} + +/* + * Check the validity times of a certificate + */ +SECCertTimeValidity +CERT_CheckCertValidTimes(const CERTCertificate *c, PRTime t, + PRBool allowOverride) +{ + PRTime notBefore, notAfter, llPendingSlop, tmp1; + SECStatus rv; + + if (!c) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return (secCertTimeUndetermined); + } + /* if cert is already marked OK, then don't bother to check */ + if (allowOverride && c->timeOK) { + return (secCertTimeValid); + } + + rv = CERT_GetCertTimes(c, ¬Before, ¬After); + + if (rv) { + return (secCertTimeExpired); /*XXX is this the right thing to do here?*/ + } + + LL_I2L(llPendingSlop, pendingSlop); + /* convert to micro seconds */ + LL_UI2L(tmp1, PR_USEC_PER_SEC); + LL_MUL(llPendingSlop, llPendingSlop, tmp1); + LL_SUB(notBefore, notBefore, llPendingSlop); + if (LL_CMP(t, <, notBefore)) { + PORT_SetError(SEC_ERROR_EXPIRED_CERTIFICATE); + return (secCertTimeNotValidYet); + } + if (LL_CMP(t, >, notAfter)) { + PORT_SetError(SEC_ERROR_EXPIRED_CERTIFICATE); + return (secCertTimeExpired); + } + + return (secCertTimeValid); +} + +SECStatus +SEC_GetCrlTimes(CERTCrl *date, PRTime *notBefore, PRTime *notAfter) +{ + int rv; + + /* convert DER not-before time */ + rv = DER_DecodeTimeChoice(notBefore, &date->lastUpdate); + if (rv) { + return (SECFailure); + } + + /* convert DER not-after time */ + if (date->nextUpdate.data) { + rv = DER_DecodeTimeChoice(notAfter, &date->nextUpdate); + if (rv) { + return (SECFailure); + } + } else { + LL_I2L(*notAfter, 0L); + } + return (SECSuccess); +} + +/* These routines should probably be combined with the cert + * routines using an common extraction routine. + */ +SECCertTimeValidity +SEC_CheckCrlTimes(CERTCrl *crl, PRTime t) +{ + PRTime notBefore, notAfter, llPendingSlop, tmp1; + SECStatus rv; + + if (!crl) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return (secCertTimeUndetermined); + } + + rv = SEC_GetCrlTimes(crl, ¬Before, ¬After); + + if (rv) { + return (secCertTimeExpired); + } + + LL_I2L(llPendingSlop, pendingSlop); + /* convert to micro seconds */ + LL_I2L(tmp1, PR_USEC_PER_SEC); + LL_MUL(llPendingSlop, llPendingSlop, tmp1); + LL_SUB(notBefore, notBefore, llPendingSlop); + if (LL_CMP(t, <, notBefore)) { + PORT_SetError(SEC_ERROR_CRL_EXPIRED); + return (secCertTimeNotValidYet); + } + + /* If next update is omitted and the test for notBefore passes, then + we assume that the crl is up to date. + */ + if (LL_IS_ZERO(notAfter)) { + return (secCertTimeValid); + } + + if (LL_CMP(t, >, notAfter)) { + PORT_SetError(SEC_ERROR_CRL_EXPIRED); + return (secCertTimeExpired); + } + + return (secCertTimeValid); +} + +PRBool +SEC_CrlIsNewer(CERTCrl *inNew, CERTCrl *old) +{ + PRTime newNotBefore, newNotAfter; + PRTime oldNotBefore, oldNotAfter; + SECStatus rv; + + /* problems with the new CRL? reject it */ + rv = SEC_GetCrlTimes(inNew, &newNotBefore, &newNotAfter); + if (rv) + return PR_FALSE; + + /* problems with the old CRL? replace it */ + rv = SEC_GetCrlTimes(old, &oldNotBefore, &oldNotAfter); + if (rv) + return PR_TRUE; + + /* Question: what about the notAfter's? */ + return ((PRBool)LL_CMP(oldNotBefore, <, newNotBefore)); +} + +/* + * return required key usage and cert type based on cert usage + */ +SECStatus +CERT_KeyUsageAndTypeForCertUsage(SECCertUsage usage, PRBool ca, + unsigned int *retKeyUsage, + unsigned int *retCertType) +{ + unsigned int requiredKeyUsage = 0; + unsigned int requiredCertType = 0; + + if (ca) { + switch (usage) { + case certUsageSSLServerWithStepUp: + requiredKeyUsage = KU_NS_GOVT_APPROVED | KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_SSL_CA; + break; + case certUsageSSLClient: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_SSL_CA; + break; + case certUsageSSLServer: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_SSL_CA; + break; + case certUsageIPsec: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_IPSEC_CA; + break; + case certUsageSSLCA: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_SSL_CA; + break; + case certUsageEmailSigner: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_EMAIL_CA; + break; + case certUsageEmailRecipient: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_EMAIL_CA; + break; + case certUsageObjectSigner: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_OBJECT_SIGNING_CA; + break; + case certUsageAnyCA: + case certUsageVerifyCA: + case certUsageStatusResponder: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_OBJECT_SIGNING_CA | + NS_CERT_TYPE_EMAIL_CA | NS_CERT_TYPE_SSL_CA; + break; + default: + PORT_Assert(0); + goto loser; + } + } else { + switch (usage) { + case certUsageSSLClient: + /* + * RFC 5280 lists digitalSignature and keyAgreement for + * id-kp-clientAuth. NSS does not support the *_fixed_dh and + * *_fixed_ecdh client certificate types. + */ + requiredKeyUsage = KU_DIGITAL_SIGNATURE; + requiredCertType = NS_CERT_TYPE_SSL_CLIENT; + break; + case certUsageSSLServer: + requiredKeyUsage = KU_KEY_AGREEMENT_OR_ENCIPHERMENT; + requiredCertType = NS_CERT_TYPE_SSL_SERVER; + break; + case certUsageIPsec: + /* RFC 4945 Section 5.1.3.2 */ + requiredKeyUsage = KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION; + requiredCertType = NS_CERT_TYPE_IPSEC; + break; + case certUsageSSLServerWithStepUp: + requiredKeyUsage = + KU_KEY_AGREEMENT_OR_ENCIPHERMENT | KU_NS_GOVT_APPROVED; + requiredCertType = NS_CERT_TYPE_SSL_SERVER; + break; + case certUsageSSLCA: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_SSL_CA; + break; + case certUsageEmailSigner: + requiredKeyUsage = KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION; + requiredCertType = NS_CERT_TYPE_EMAIL; + break; + case certUsageEmailRecipient: + requiredKeyUsage = KU_KEY_AGREEMENT_OR_ENCIPHERMENT; + requiredCertType = NS_CERT_TYPE_EMAIL; + break; + case certUsageObjectSigner: + /* RFC 5280 lists only digitalSignature for id-kp-codeSigning. + */ + requiredKeyUsage = KU_DIGITAL_SIGNATURE; + requiredCertType = NS_CERT_TYPE_OBJECT_SIGNING; + break; + case certUsageStatusResponder: + requiredKeyUsage = KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION; + requiredCertType = EXT_KEY_USAGE_STATUS_RESPONDER; + break; + default: + PORT_Assert(0); + goto loser; + } + } + + if (retKeyUsage != NULL) { + *retKeyUsage = requiredKeyUsage; + } + if (retCertType != NULL) { + *retCertType = requiredCertType; + } + + return (SECSuccess); +loser: + return (SECFailure); +} + +/* + * check the key usage of a cert against a set of required values + */ +SECStatus +CERT_CheckKeyUsage(CERTCertificate *cert, unsigned int requiredUsage) +{ + if (!cert) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + /* choose between key agreement or key encipherment based on key + * type in cert + */ + if (requiredUsage & KU_KEY_AGREEMENT_OR_ENCIPHERMENT) { + KeyType keyType = CERT_GetCertKeyType(&cert->subjectPublicKeyInfo); + /* turn off the special bit */ + requiredUsage &= (~KU_KEY_AGREEMENT_OR_ENCIPHERMENT); + + switch (keyType) { + case rsaKey: + requiredUsage |= KU_KEY_ENCIPHERMENT; + break; + case rsaPssKey: + case dsaKey: + requiredUsage |= KU_DIGITAL_SIGNATURE; + break; + case dhKey: + requiredUsage |= KU_KEY_AGREEMENT; + break; + case ecKey: + /* Accept either signature or agreement. */ + if (!(cert->keyUsage & + (KU_DIGITAL_SIGNATURE | KU_KEY_AGREEMENT))) + goto loser; + break; + default: + goto loser; + } + } + + /* Allow either digital signature or non-repudiation */ + if (requiredUsage & KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION) { + /* turn off the special bit */ + requiredUsage &= (~KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION); + + if (!(cert->keyUsage & (KU_DIGITAL_SIGNATURE | KU_NON_REPUDIATION))) + goto loser; + } + + if ((cert->keyUsage & requiredUsage) == requiredUsage) + return SECSuccess; + +loser: + PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE); + return SECFailure; +} + +CERTCertificate * +CERT_DupCertificate(CERTCertificate *c) +{ + if (c) { + NSSCertificate *tmp = STAN_GetNSSCertificate(c); + nssCertificate_AddRef(tmp); + } + return c; +} + +SECStatus +CERT_GetCertificateDer(const CERTCertificate *c, SECItem *der) +{ + if (!c || !der) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + *der = c->derCert; + return SECSuccess; +} + +/* + * Allow use of default cert database, so that apps(such as mozilla) don't + * have to pass the handle all over the place. + */ +static CERTCertDBHandle *default_cert_db_handle = 0; + +void +CERT_SetDefaultCertDB(CERTCertDBHandle *handle) +{ + default_cert_db_handle = handle; + + return; +} + +CERTCertDBHandle * +CERT_GetDefaultCertDB(void) +{ + return (default_cert_db_handle); +} + +/* XXX this would probably be okay/better as an xp routine? */ +static void +sec_lower_string(char *s) +{ + if (s == NULL) { + return; + } + + while (*s) { + *s = PORT_Tolower(*s); + s++; + } + + return; +} + +static PRBool +cert_IsIPAddr(const char *hn) +{ + PRBool isIPaddr = PR_FALSE; + PRNetAddr netAddr; + isIPaddr = (PR_SUCCESS == PR_StringToNetAddr(hn, &netAddr)); + return isIPaddr; +} + +/* +** Add a domain name to the list of names that the user has explicitly +** allowed (despite cert name mismatches) for use with a server cert. +*/ +SECStatus +CERT_AddOKDomainName(CERTCertificate *cert, const char *hn) +{ + CERTOKDomainName *domainOK; + int newNameLen; + + if (!hn || !(newNameLen = strlen(hn))) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + domainOK = (CERTOKDomainName *)PORT_ArenaZAlloc(cert->arena, sizeof(*domainOK)); + if (!domainOK) { + return SECFailure; /* error code is already set. */ + } + domainOK->name = (char *)PORT_ArenaZAlloc(cert->arena, newNameLen + 1); + if (!domainOK->name) { + return SECFailure; /* error code is already set. */ + } + + PORT_Strncpy(domainOK->name, hn, newNameLen + 1); + sec_lower_string(domainOK->name); + + /* put at head of list. */ + domainOK->next = cert->domainOK; + cert->domainOK = domainOK; + return SECSuccess; +} + +/* returns SECSuccess if hn matches pattern cn, +** returns SECFailure with SSL_ERROR_BAD_CERT_DOMAIN if no match, +** returns SECFailure with some other error code if another error occurs. +** +** This function may modify string cn, so caller must pass a modifiable copy. +*/ +static SECStatus +cert_TestHostName(char *cn, const char *hn) +{ + static int useShellExp = -1; + + if (useShellExp < 0) { + useShellExp = (NULL != PR_GetEnvSecure("NSS_USE_SHEXP_IN_CERT_NAME")); + } + if (useShellExp) { + /* Backward compatible code, uses Shell Expressions (SHEXP). */ + int regvalid = PORT_RegExpValid(cn); + if (regvalid != NON_SXP) { + SECStatus rv; + /* cn is a regular expression, try to match the shexp */ + int match = PORT_RegExpCaseSearch(hn, cn); + + if (match == 0) { + rv = SECSuccess; + } else { + PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN); + rv = SECFailure; + } + return rv; + } + } else { + /* New approach conforms to RFC 6125. */ + char *wildcard = PORT_Strchr(cn, '*'); + char *firstcndot = PORT_Strchr(cn, '.'); + char *secondcndot = + firstcndot ? PORT_Strchr(firstcndot + 1, '.') : NULL; + char *firsthndot = PORT_Strchr(hn, '.'); + + /* For a cn pattern to be considered valid, the wildcard character... + * - may occur only in a DNS name with at least 3 components, and + * - may occur only as last character in the first component, and + * - may be preceded by additional characters, and + * - must not be preceded by an IDNA ACE prefix (xn--) + */ + if (wildcard && secondcndot && secondcndot[1] && firsthndot && + firstcndot - wildcard == 1 /* wildcard is last char in first component */ + && secondcndot - firstcndot > 1 /* second component is non-empty */ + && PORT_Strrchr(cn, '*') == wildcard /* only one wildcard in cn */ + && !PORT_Strncasecmp(cn, hn, wildcard - cn) && + !PORT_Strcasecmp(firstcndot, firsthndot) + /* If hn starts with xn--, then cn must start with wildcard */ + && (PORT_Strncasecmp(hn, "xn--", 4) || wildcard == cn)) { + /* valid wildcard pattern match */ + return SECSuccess; + } + } + /* String cn has no wildcard or shell expression. + * Compare entire string hn with cert name. + */ + if (PORT_Strcasecmp(hn, cn) == 0) { + return SECSuccess; + } + + PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN); + return SECFailure; +} + +SECStatus +cert_VerifySubjectAltName(const CERTCertificate *cert, const char *hn) +{ + PLArenaPool *arena = NULL; + CERTGeneralName *nameList = NULL; + CERTGeneralName *current; + char *cn; + int cnBufLen; + int DNSextCount = 0; + int IPextCount = 0; + PRBool isIPaddr = PR_FALSE; + SECStatus rv = SECFailure; + SECItem subAltName; + PRNetAddr netAddr; + char cnbuf[128]; + + subAltName.data = NULL; + cn = cnbuf; + cnBufLen = sizeof cnbuf; + + rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, + &subAltName); + if (rv != SECSuccess) { + goto fail; + } + isIPaddr = (PR_SUCCESS == PR_StringToNetAddr(hn, &netAddr)); + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) + goto fail; + + nameList = current = CERT_DecodeAltNameExtension(arena, &subAltName); + if (!current) + goto fail; + + do { + switch (current->type) { + case certDNSName: + if (!isIPaddr) { + /* DNS name current->name.other.data is not null terminated. + ** so must copy it. + */ + int cnLen = current->name.other.len; + rv = CERT_RFC1485_EscapeAndQuote( + cn, cnBufLen, (char *)current->name.other.data, cnLen); + if (rv != SECSuccess && + PORT_GetError() == SEC_ERROR_OUTPUT_LEN) { + cnBufLen = + cnLen * 3 + 3; /* big enough for worst case */ + cn = (char *)PORT_ArenaAlloc(arena, cnBufLen); + if (!cn) + goto fail; + rv = CERT_RFC1485_EscapeAndQuote( + cn, cnBufLen, (char *)current->name.other.data, + cnLen); + } + if (rv == SECSuccess) + rv = cert_TestHostName(cn, hn); + if (rv == SECSuccess) + goto finish; + } + DNSextCount++; + break; + case certIPAddress: + if (isIPaddr) { + int match = 0; + PRIPv6Addr v6Addr; + if (current->name.other.len == 4 && /* IP v4 address */ + netAddr.inet.family == PR_AF_INET) { + match = !memcmp(&netAddr.inet.ip, + current->name.other.data, 4); + } else if (current->name.other.len == + 16 && /* IP v6 address */ + netAddr.ipv6.family == PR_AF_INET6) { + match = !memcmp(&netAddr.ipv6.ip, + current->name.other.data, 16); + } else if (current->name.other.len == + 16 && /* IP v6 address */ + netAddr.inet.family == PR_AF_INET) { + /* convert netAddr to ipv6, then compare. */ + /* ipv4 must be in Network Byte Order on input. */ + PR_ConvertIPv4AddrToIPv6(netAddr.inet.ip, &v6Addr); + match = !memcmp(&v6Addr, current->name.other.data, 16); + } else if (current->name.other.len == 4 && /* IP v4 address */ + netAddr.inet.family == PR_AF_INET6) { + /* convert netAddr to ipv6, then compare. */ + PRUint32 ipv4 = (current->name.other.data[0] << 24) | + (current->name.other.data[1] << 16) | + (current->name.other.data[2] << 8) | + current->name.other.data[3]; + /* ipv4 must be in Network Byte Order on input. */ + PR_ConvertIPv4AddrToIPv6(PR_htonl(ipv4), &v6Addr); + match = !memcmp(&netAddr.ipv6.ip, &v6Addr, 16); + } + if (match) { + rv = SECSuccess; + goto finish; + } + } + IPextCount++; + break; + default: + break; + } + current = CERT_GetNextGeneralName(current); + } while (current != nameList); + +fail: + + if (!(isIPaddr ? IPextCount : DNSextCount)) { + /* no relevant value in the extension was found. */ + PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND); + } else { + PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN); + } + rv = SECFailure; + +finish: + + /* Don't free nameList, it's part of the arena. */ + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + + if (subAltName.data) { + SECITEM_FreeItem(&subAltName, PR_FALSE); + } + + return rv; +} + +/* + * If found: + * - subAltName contains the extension (caller must free) + * - return value is the decoded namelist (allocated off arena) + * if not found, or if failure to decode: + * - return value is NULL + */ +CERTGeneralName * +cert_GetSubjectAltNameList(const CERTCertificate *cert, PLArenaPool *arena) +{ + CERTGeneralName *nameList = NULL; + SECStatus rv = SECFailure; + SECItem subAltName; + + if (!cert || !arena) + return NULL; + + subAltName.data = NULL; + + rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, + &subAltName); + if (rv != SECSuccess) + return NULL; + + nameList = CERT_DecodeAltNameExtension(arena, &subAltName); + SECITEM_FreeItem(&subAltName, PR_FALSE); + return nameList; +} + +PRUint32 +cert_CountDNSPatterns(CERTGeneralName *firstName) +{ + CERTGeneralName *current; + PRUint32 count = 0; + + if (!firstName) + return 0; + + current = firstName; + do { + switch (current->type) { + case certDNSName: + case certIPAddress: + ++count; + break; + default: + break; + } + current = CERT_GetNextGeneralName(current); + } while (current != firstName); + + return count; +} + +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 46 +#endif + +/* will fill nickNames, + * will allocate all data from nickNames->arena, + * numberOfGeneralNames should have been obtained from cert_CountDNSPatterns, + * will ensure the numberOfGeneralNames matches the number of output entries. + */ +SECStatus +cert_GetDNSPatternsFromGeneralNames(CERTGeneralName *firstName, + PRUint32 numberOfGeneralNames, + CERTCertNicknames *nickNames) +{ + CERTGeneralName *currentInput; + char **currentOutput; + + if (!firstName || !nickNames || !numberOfGeneralNames) + return SECFailure; + + nickNames->numnicknames = numberOfGeneralNames; + nickNames->nicknames = PORT_ArenaAlloc( + nickNames->arena, sizeof(char *) * numberOfGeneralNames); + if (!nickNames->nicknames) + return SECFailure; + + currentInput = firstName; + currentOutput = nickNames->nicknames; + do { + char *cn = NULL; + char ipbuf[INET6_ADDRSTRLEN]; + PRNetAddr addr; + + if (numberOfGeneralNames < 1) { + /* internal consistency error */ + return SECFailure; + } + + switch (currentInput->type) { + case certDNSName: + /* DNS name currentInput->name.other.data is not null + *terminated. + ** so must copy it. + */ + cn = (char *)PORT_ArenaAlloc(nickNames->arena, + currentInput->name.other.len + 1); + if (!cn) + return SECFailure; + PORT_Memcpy(cn, currentInput->name.other.data, + currentInput->name.other.len); + cn[currentInput->name.other.len] = 0; + break; + case certIPAddress: + if (currentInput->name.other.len == 4) { + addr.inet.family = PR_AF_INET; + memcpy(&addr.inet.ip, currentInput->name.other.data, + currentInput->name.other.len); + } else if (currentInput->name.other.len == 16) { + addr.ipv6.family = PR_AF_INET6; + memcpy(&addr.ipv6.ip, currentInput->name.other.data, + currentInput->name.other.len); + } + if (PR_NetAddrToString(&addr, ipbuf, sizeof(ipbuf)) == + PR_FAILURE) + return SECFailure; + cn = PORT_ArenaStrdup(nickNames->arena, ipbuf); + if (!cn) + return SECFailure; + break; + default: + break; + } + if (cn) { + *currentOutput = cn; + nickNames->totallen += PORT_Strlen(cn); + ++currentOutput; + --numberOfGeneralNames; + } + currentInput = CERT_GetNextGeneralName(currentInput); + } while (currentInput != firstName); + + return (numberOfGeneralNames == 0) ? SECSuccess : SECFailure; +} + +/* + * Collect all valid DNS names from the given cert. + * The output arena will reference some temporaray data, + * but this saves us from dealing with two arenas. + * The caller may free all data by freeing CERTCertNicknames->arena. + */ +CERTCertNicknames * +CERT_GetValidDNSPatternsFromCert(CERTCertificate *cert) +{ + CERTGeneralName *generalNames; + CERTCertNicknames *nickNames; + PLArenaPool *arena; + char *singleName; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + return NULL; + } + + nickNames = PORT_ArenaAlloc(arena, sizeof(CERTCertNicknames)); + if (!nickNames) { + PORT_FreeArena(arena, PR_FALSE); + return NULL; + } + + /* init the structure */ + nickNames->arena = arena; + nickNames->head = NULL; + nickNames->numnicknames = 0; + nickNames->nicknames = NULL; + nickNames->totallen = 0; + + generalNames = cert_GetSubjectAltNameList(cert, arena); + if (generalNames) { + SECStatus rv_getnames = SECFailure; + PRUint32 numNames = cert_CountDNSPatterns(generalNames); + + if (numNames) { + rv_getnames = cert_GetDNSPatternsFromGeneralNames( + generalNames, numNames, nickNames); + } + + /* if there were names, we'll exit now, either with success or failure + */ + if (numNames) { + if (rv_getnames == SECSuccess) { + return nickNames; + } + + /* failure to produce output */ + PORT_FreeArena(arena, PR_FALSE); + return NULL; + } + } + + /* no SAN extension or no names found in extension */ + singleName = CERT_GetCommonName(&cert->subject); + if (singleName) { + nickNames->numnicknames = 1; + nickNames->nicknames = PORT_ArenaAlloc(arena, sizeof(char *)); + if (nickNames->nicknames) { + *nickNames->nicknames = PORT_ArenaStrdup(arena, singleName); + } + PORT_Free(singleName); + + /* Did we allocate both the buffer of pointers and the string? */ + if (nickNames->nicknames && *nickNames->nicknames) { + return nickNames; + } + } + + PORT_FreeArena(arena, PR_FALSE); + return NULL; +} + +/* Make sure that the name of the host we are connecting to matches the + * name that is incoded in the common-name component of the certificate + * that they are using. + */ +SECStatus +CERT_VerifyCertName(const CERTCertificate *cert, const char *hn) +{ + char *cn; + SECStatus rv; + CERTOKDomainName *domainOK; + + if (!hn || !strlen(hn)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* if the name is one that the user has already approved, it's OK. */ + for (domainOK = cert->domainOK; domainOK; domainOK = domainOK->next) { + if (0 == PORT_Strcasecmp(hn, domainOK->name)) { + return SECSuccess; + } + } + + /* Per RFC 2818, if the SubjectAltName extension is present, it must + ** be used as the cert's identity. + */ + rv = cert_VerifySubjectAltName(cert, hn); + if (rv == SECSuccess || PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) + return rv; + + cn = CERT_GetCommonName(&cert->subject); + if (cn) { + PRBool isIPaddr = cert_IsIPAddr(hn); + if (isIPaddr) { + if (PORT_Strcasecmp(hn, cn) == 0) { + rv = SECSuccess; + } else { + PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN); + rv = SECFailure; + } + } else { + rv = cert_TestHostName(cn, hn); + } + PORT_Free(cn); + } else + PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN); + return rv; +} + +PRBool +CERT_CompareCerts(const CERTCertificate *c1, const CERTCertificate *c2) +{ + SECComparison comp; + + comp = SECITEM_CompareItem(&c1->derCert, &c2->derCert); + if (comp == SECEqual) { /* certs are the same */ + return (PR_TRUE); + } else { + return (PR_FALSE); + } +} + +static SECStatus +StringsEqual(char *s1, char *s2) +{ + if ((s1 == NULL) || (s2 == NULL)) { + if (s1 != s2) { /* only one is null */ + return (SECFailure); + } + return (SECSuccess); /* both are null */ + } + + if (PORT_Strcmp(s1, s2) != 0) { + return (SECFailure); /* not equal */ + } + + return (SECSuccess); /* strings are equal */ +} + +PRBool +CERT_CompareCertsForRedirection(CERTCertificate *c1, CERTCertificate *c2) +{ + SECComparison comp; + char *c1str, *c2str; + SECStatus eq; + + comp = SECITEM_CompareItem(&c1->derCert, &c2->derCert); + if (comp == SECEqual) { /* certs are the same */ + return (PR_TRUE); + } + + /* check if they are issued by the same CA */ + comp = SECITEM_CompareItem(&c1->derIssuer, &c2->derIssuer); + if (comp != SECEqual) { /* different issuer */ + return (PR_FALSE); + } + + /* check country name */ + c1str = CERT_GetCountryName(&c1->subject); + c2str = CERT_GetCountryName(&c2->subject); + eq = StringsEqual(c1str, c2str); + PORT_Free(c1str); + PORT_Free(c2str); + if (eq != SECSuccess) { + return (PR_FALSE); + } + + /* check locality name */ + c1str = CERT_GetLocalityName(&c1->subject); + c2str = CERT_GetLocalityName(&c2->subject); + eq = StringsEqual(c1str, c2str); + PORT_Free(c1str); + PORT_Free(c2str); + if (eq != SECSuccess) { + return (PR_FALSE); + } + + /* check state name */ + c1str = CERT_GetStateName(&c1->subject); + c2str = CERT_GetStateName(&c2->subject); + eq = StringsEqual(c1str, c2str); + PORT_Free(c1str); + PORT_Free(c2str); + if (eq != SECSuccess) { + return (PR_FALSE); + } + + /* check org name */ + c1str = CERT_GetOrgName(&c1->subject); + c2str = CERT_GetOrgName(&c2->subject); + eq = StringsEqual(c1str, c2str); + PORT_Free(c1str); + PORT_Free(c2str); + if (eq != SECSuccess) { + return (PR_FALSE); + } + +#ifdef NOTDEF + /* check orgUnit name */ + /* + * We need to revisit this and decide which fields should be allowed to be + * different + */ + c1str = CERT_GetOrgUnitName(&c1->subject); + c2str = CERT_GetOrgUnitName(&c2->subject); + eq = StringsEqual(c1str, c2str); + PORT_Free(c1str); + PORT_Free(c2str); + if (eq != SECSuccess) { + return (PR_FALSE); + } +#endif + + return (PR_TRUE); /* all fields but common name are the same */ +} + +/* CERT_CertChainFromCert and CERT_DestroyCertificateList moved + to certhigh.c */ + +CERTIssuerAndSN * +CERT_GetCertIssuerAndSN(PLArenaPool *arena, CERTCertificate *cert) +{ + CERTIssuerAndSN *result; + SECStatus rv; + + if (arena == NULL) { + arena = cert->arena; + } + + result = (CERTIssuerAndSN *)PORT_ArenaZAlloc(arena, sizeof(*result)); + if (result == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + rv = SECITEM_CopyItem(arena, &result->derIssuer, &cert->derIssuer); + if (rv != SECSuccess) + return NULL; + + rv = CERT_CopyName(arena, &result->issuer, &cert->issuer); + if (rv != SECSuccess) + return NULL; + + rv = SECITEM_CopyItem(arena, &result->serialNumber, &cert->serialNumber); + if (rv != SECSuccess) + return NULL; + + return result; +} + +char * +CERT_MakeCANickname(CERTCertificate *cert) +{ + char *firstname = NULL; + char *org = NULL; + char *nickname = NULL; + int count; + CERTCertificate *dummycert; + + firstname = CERT_GetCommonName(&cert->subject); + if (firstname == NULL) { + firstname = CERT_GetOrgUnitName(&cert->subject); + } + + org = CERT_GetOrgName(&cert->issuer); + if (org == NULL) { + org = CERT_GetDomainComponentName(&cert->issuer); + if (org == NULL) { + if (firstname) { + org = firstname; + firstname = NULL; + } else { + org = PORT_Strdup("Unknown CA"); + } + } + } + + /* can only fail if PORT_Strdup fails, in which case + * we're having memory problems. */ + if (org == NULL) { + goto done; + } + + count = 1; + while (1) { + + if (firstname) { + if (count == 1) { + nickname = PR_smprintf("%s - %s", firstname, org); + } else { + nickname = PR_smprintf("%s - %s #%d", firstname, org, count); + } + } else { + if (count == 1) { + nickname = PR_smprintf("%s", org); + } else { + nickname = PR_smprintf("%s #%d", org, count); + } + } + if (nickname == NULL) { + goto done; + } + + /* look up the nickname to make sure it isn't in use already */ + dummycert = CERT_FindCertByNickname(cert->dbhandle, nickname); + + if (dummycert == NULL) { + goto done; + } + + /* found a cert, destroy it and loop */ + CERT_DestroyCertificate(dummycert); + + /* free the nickname */ + PORT_Free(nickname); + + count++; + } + +done: + if (firstname) { + PORT_Free(firstname); + } + if (org) { + PORT_Free(org); + } + + return (nickname); +} + +/* CERT_Import_CAChain moved to certhigh.c */ + +void +CERT_DestroyCrl(CERTSignedCrl *crl) +{ + SEC_DestroyCrl(crl); +} + +static int +cert_Version(CERTCertificate *cert) +{ + int version = 0; + if (cert && cert->version.data && cert->version.len) { + version = DER_GetInteger(&cert->version); + if (version < 0) + version = 0; + } + return version; +} + +static unsigned int +cert_ComputeTrustOverrides(CERTCertificate *cert, unsigned int cType) +{ + CERTCertTrust trust; + SECStatus rv = SECFailure; + + rv = CERT_GetCertTrust(cert, &trust); + + if (rv == SECSuccess && + (trust.sslFlags | trust.emailFlags | trust.objectSigningFlags)) { + + if (trust.sslFlags & (CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED)) + cType |= NS_CERT_TYPE_SSL_SERVER | NS_CERT_TYPE_SSL_CLIENT; + if (trust.sslFlags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) + cType |= NS_CERT_TYPE_SSL_CA; +#if defined(CERTDB_NOT_TRUSTED) + if (trust.sslFlags & CERTDB_NOT_TRUSTED) + cType &= ~(NS_CERT_TYPE_SSL_SERVER | NS_CERT_TYPE_SSL_CLIENT | + NS_CERT_TYPE_SSL_CA); +#endif + if (trust.emailFlags & (CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED)) + cType |= NS_CERT_TYPE_EMAIL; + if (trust.emailFlags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) + cType |= NS_CERT_TYPE_EMAIL_CA; +#if defined(CERTDB_NOT_TRUSTED) + if (trust.emailFlags & CERTDB_NOT_TRUSTED) + cType &= ~(NS_CERT_TYPE_EMAIL | NS_CERT_TYPE_EMAIL_CA); +#endif + if (trust.objectSigningFlags & + (CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED)) + cType |= NS_CERT_TYPE_OBJECT_SIGNING; + if (trust.objectSigningFlags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) + cType |= NS_CERT_TYPE_OBJECT_SIGNING_CA; +#if defined(CERTDB_NOT_TRUSTED) + if (trust.objectSigningFlags & CERTDB_NOT_TRUSTED) + cType &= + ~(NS_CERT_TYPE_OBJECT_SIGNING | NS_CERT_TYPE_OBJECT_SIGNING_CA); +#endif + } + return cType; +} + +/* + * Does a cert belong to a CA? We decide based on perm database trust + * flags, Netscape Cert Type Extension, and KeyUsage Extension. + */ +PRBool +CERT_IsCACert(CERTCertificate *cert, unsigned int *rettype) +{ + unsigned int cType = cert->nsCertType; + PRBool ret = PR_FALSE; + + /* + * Check if the constraints are available and it's a CA, OR if it's + * a X.509 v1 Root CA. + */ + CERTBasicConstraints constraints; + if ((CERT_FindBasicConstraintExten(cert, &constraints) == SECSuccess && + constraints.isCA) || + (cert->isRoot && cert_Version(cert) < SEC_CERTIFICATE_VERSION_3)) + cType |= (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA); + + /* + * Apply trust overrides, if any. + */ + cType = cert_ComputeTrustOverrides(cert, cType); + ret = (cType & (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA | + NS_CERT_TYPE_OBJECT_SIGNING_CA)) + ? PR_TRUE + : PR_FALSE; + + if (rettype) { + *rettype = cType; + } + + return ret; +} + +PRBool +CERT_IsCADERCert(SECItem *derCert, unsigned int *type) +{ + CERTCertificate *cert; + PRBool isCA; + + /* This is okay -- only looks at extensions */ + cert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL); + if (cert == NULL) + return PR_FALSE; + + isCA = CERT_IsCACert(cert, type); + CERT_DestroyCertificate(cert); + return isCA; +} + +PRBool +CERT_IsRootDERCert(SECItem *derCert) +{ + CERTCertificate *cert; + PRBool isRoot; + + /* This is okay -- only looks at extensions */ + cert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL); + if (cert == NULL) + return PR_FALSE; + + isRoot = cert->isRoot; + CERT_DestroyCertificate(cert); + return isRoot; +} + +CERTCompareValidityStatus +CERT_CompareValidityTimes(CERTValidity *val_a, CERTValidity *val_b) +{ + PRTime notBeforeA, notBeforeB, notAfterA, notAfterB; + + if (!val_a || !val_b) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return certValidityUndetermined; + } + + if (SECSuccess != DER_DecodeTimeChoice(¬BeforeA, &val_a->notBefore) || + SECSuccess != DER_DecodeTimeChoice(¬BeforeB, &val_b->notBefore) || + SECSuccess != DER_DecodeTimeChoice(¬AfterA, &val_a->notAfter) || + SECSuccess != DER_DecodeTimeChoice(¬AfterB, &val_b->notAfter)) { + return certValidityUndetermined; + } + + /* sanity check */ + if (LL_CMP(notBeforeA, >, notAfterA) || LL_CMP(notBeforeB, >, notAfterB)) { + PORT_SetError(SEC_ERROR_INVALID_TIME); + return certValidityUndetermined; + } + + if (LL_CMP(notAfterA, !=, notAfterB)) { + /* one cert validity goes farther into the future, select it */ + return LL_CMP(notAfterA, <, notAfterB) ? certValidityChooseB + : certValidityChooseA; + } + /* the two certs have the same expiration date */ + PORT_Assert(LL_CMP(notAfterA, ==, notAfterB)); + /* do they also have the same start date ? */ + if (LL_CMP(notBeforeA, ==, notBeforeB)) { + return certValidityEqual; + } + /* choose cert with the later start date */ + return LL_CMP(notBeforeA, <, notBeforeB) ? certValidityChooseB + : certValidityChooseA; +} + +/* + * is certa newer than certb? If one is expired, pick the other one. + */ +PRBool +CERT_IsNewer(CERTCertificate *certa, CERTCertificate *certb) +{ + PRTime notBeforeA, notAfterA, notBeforeB, notAfterB, now; + SECStatus rv; + PRBool newerbefore, newerafter; + + rv = CERT_GetCertTimes(certa, ¬BeforeA, ¬AfterA); + if (rv != SECSuccess) { + return (PR_FALSE); + } + + rv = CERT_GetCertTimes(certb, ¬BeforeB, ¬AfterB); + if (rv != SECSuccess) { + return (PR_TRUE); + } + + newerbefore = PR_FALSE; + if (LL_CMP(notBeforeA, >, notBeforeB)) { + newerbefore = PR_TRUE; + } + + newerafter = PR_FALSE; + if (LL_CMP(notAfterA, >, notAfterB)) { + newerafter = PR_TRUE; + } + + if (newerbefore && newerafter) { + return (PR_TRUE); + } + + if ((!newerbefore) && (!newerafter)) { + return (PR_FALSE); + } + + /* get current time */ + now = PR_Now(); + + if (newerbefore) { + /* cert A was issued after cert B, but expires sooner */ + /* if A is expired, then pick B */ + if (LL_CMP(notAfterA, <, now)) { + return (PR_FALSE); + } + return (PR_TRUE); + } else { + /* cert B was issued after cert A, but expires sooner */ + /* if B is expired, then pick A */ + if (LL_CMP(notAfterB, <, now)) { + return (PR_TRUE); + } + return (PR_FALSE); + } +} + +void +CERT_DestroyCertArray(CERTCertificate **certs, unsigned int ncerts) +{ + unsigned int i; + + if (certs) { + for (i = 0; i < ncerts; i++) { + if (certs[i]) { + CERT_DestroyCertificate(certs[i]); + } + } + + PORT_Free(certs); + } + + return; +} + +char * +CERT_FixupEmailAddr(const char *emailAddr) +{ + char *retaddr; + char *str; + + if (emailAddr == NULL) { + return (NULL); + } + + /* copy the string */ + str = retaddr = PORT_Strdup(emailAddr); + if (str == NULL) { + return (NULL); + } + + /* make it lower case */ + while (*str) { + *str = tolower(*str); + str++; + } + + return (retaddr); +} + +/* + * NOTE - don't allow encode of govt-approved or invisible bits + */ +SECStatus +CERT_DecodeTrustString(CERTCertTrust *trust, const char *trusts) +{ + unsigned int i; + unsigned int *pflags; + + if (!trust) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + trust->sslFlags = 0; + trust->emailFlags = 0; + trust->objectSigningFlags = 0; + if (!trusts) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + pflags = &trust->sslFlags; + + for (i = 0; i < PORT_Strlen(trusts); i++) { + switch (trusts[i]) { + case 'p': + *pflags = *pflags | CERTDB_TERMINAL_RECORD; + break; + + case 'P': + *pflags = *pflags | CERTDB_TRUSTED | CERTDB_TERMINAL_RECORD; + break; + + case 'w': + *pflags = *pflags | CERTDB_SEND_WARN; + break; + + case 'c': + *pflags = *pflags | CERTDB_VALID_CA; + break; + + case 'T': + *pflags = *pflags | CERTDB_TRUSTED_CLIENT_CA | CERTDB_VALID_CA; + break; + + case 'C': + *pflags = *pflags | CERTDB_TRUSTED_CA | CERTDB_VALID_CA; + break; + + case 'u': + *pflags = *pflags | CERTDB_USER; + break; + + case 'i': + *pflags = *pflags | CERTDB_INVISIBLE_CA; + break; + case 'g': + *pflags = *pflags | CERTDB_GOVT_APPROVED_CA; + break; + + case ',': + if (pflags == &trust->sslFlags) { + pflags = &trust->emailFlags; + } else { + pflags = &trust->objectSigningFlags; + } + break; + default: + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + } + + return SECSuccess; +} + +static void +EncodeFlags(char *trusts, unsigned int flags) +{ + if (flags & CERTDB_VALID_CA) + if (!(flags & CERTDB_TRUSTED_CA) && !(flags & CERTDB_TRUSTED_CLIENT_CA)) + PORT_Strcat(trusts, "c"); + if (flags & CERTDB_TERMINAL_RECORD) + if (!(flags & CERTDB_TRUSTED)) + PORT_Strcat(trusts, "p"); + if (flags & CERTDB_TRUSTED_CA) + PORT_Strcat(trusts, "C"); + if (flags & CERTDB_TRUSTED_CLIENT_CA) + PORT_Strcat(trusts, "T"); + if (flags & CERTDB_TRUSTED) + PORT_Strcat(trusts, "P"); + if (flags & CERTDB_USER) + PORT_Strcat(trusts, "u"); + if (flags & CERTDB_SEND_WARN) + PORT_Strcat(trusts, "w"); + if (flags & CERTDB_INVISIBLE_CA) + PORT_Strcat(trusts, "I"); + if (flags & CERTDB_GOVT_APPROVED_CA) + PORT_Strcat(trusts, "G"); + return; +} + +char * +CERT_EncodeTrustString(CERTCertTrust *trust) +{ + char tmpTrustSSL[32]; + char tmpTrustEmail[32]; + char tmpTrustSigning[32]; + char *retstr = NULL; + + if (trust) { + tmpTrustSSL[0] = '\0'; + tmpTrustEmail[0] = '\0'; + tmpTrustSigning[0] = '\0'; + + EncodeFlags(tmpTrustSSL, trust->sslFlags); + EncodeFlags(tmpTrustEmail, trust->emailFlags); + EncodeFlags(tmpTrustSigning, trust->objectSigningFlags); + + retstr = PR_smprintf("%s,%s,%s", tmpTrustSSL, tmpTrustEmail, + tmpTrustSigning); + } + + return (retstr); +} + +SECStatus +CERT_ImportCerts(CERTCertDBHandle *certdb, SECCertUsage usage, + unsigned int ncerts, SECItem **derCerts, + CERTCertificate ***retCerts, PRBool keepCerts, PRBool caOnly, + char *nickname) +{ + unsigned int i; + CERTCertificate **certs = NULL; + unsigned int fcerts = 0; + + if (ncerts) { + certs = PORT_ZNewArray(CERTCertificate *, ncerts); + if (certs == NULL) { + return (SECFailure); + } + + /* decode all of the certs into the temporary DB */ + for (i = 0, fcerts = 0; i < ncerts; i++) { + certs[fcerts] = CERT_NewTempCertificate(certdb, derCerts[i], NULL, + PR_FALSE, PR_TRUE); + if (certs[fcerts]) { + SECItem subjKeyID = { siBuffer, NULL, 0 }; + if (CERT_FindSubjectKeyIDExtension(certs[fcerts], &subjKeyID) == + SECSuccess) { + if (subjKeyID.data) { + cert_AddSubjectKeyIDMapping(&subjKeyID, certs[fcerts]); + } + SECITEM_FreeItem(&subjKeyID, PR_FALSE); + } + fcerts++; + } + } + + if (keepCerts) { + for (i = 0; i < fcerts; i++) { + char *canickname = NULL; + PRBool isCA; + + SECKEY_UpdateCertPQG(certs[i]); + + isCA = CERT_IsCACert(certs[i], NULL); + if (isCA) { + canickname = CERT_MakeCANickname(certs[i]); + } + + if (isCA && (fcerts > 1)) { + /* if we are importing only a single cert and specifying + * a nickname, we want to use that nickname if it a CA, + * otherwise if there are more than one cert, we don't + * know which cert it belongs to. But we still may try + * the individual canickname from the cert itself. + */ + /* Bug 1192442 - propagate errors from these calls. */ + (void)CERT_AddTempCertToPerm(certs[i], canickname, NULL); + } else { + (void)CERT_AddTempCertToPerm( + certs[i], nickname ? nickname : canickname, NULL); + } + + PORT_Free(canickname); + /* don't care if it fails - keep going */ + } + } + } + + if (retCerts) { + *retCerts = certs; + } else { + if (certs) { + CERT_DestroyCertArray(certs, fcerts); + } + } + + return (fcerts || !ncerts) ? SECSuccess : SECFailure; +} + +/* + * a real list of certificates - need to convert CERTCertificateList + * stuff and ASN 1 encoder/decoder over to using this... + */ +CERTCertList * +CERT_NewCertList(void) +{ + PLArenaPool *arena = NULL; + CERTCertList *ret = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + goto loser; + } + + ret = (CERTCertList *)PORT_ArenaZAlloc(arena, sizeof(CERTCertList)); + if (ret == NULL) { + goto loser; + } + + ret->arena = arena; + + PR_INIT_CLIST(&ret->list); + + return (ret); + +loser: + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + + return (NULL); +} + +void +CERT_DestroyCertList(CERTCertList *certs) +{ + PRCList *node; + + if (!certs) { + return; + } + + while (!PR_CLIST_IS_EMPTY(&certs->list)) { + node = PR_LIST_HEAD(&certs->list); + CERT_DestroyCertificate(((CERTCertListNode *)node)->cert); + PR_REMOVE_LINK(node); + } + + PORT_FreeArena(certs->arena, PR_FALSE); + + return; +} + +void +CERT_RemoveCertListNode(CERTCertListNode *node) +{ + CERT_DestroyCertificate(node->cert); + PR_REMOVE_LINK(&node->links); + return; +} + +SECStatus +CERT_AddCertToListTailWithData(CERTCertList *certs, CERTCertificate *cert, + void *appData) +{ + CERTCertListNode *node; + + node = (CERTCertListNode *)PORT_ArenaZAlloc(certs->arena, + sizeof(CERTCertListNode)); + if (node == NULL) { + goto loser; + } + + PR_INSERT_BEFORE(&node->links, &certs->list); + /* certs->count++; */ + node->cert = cert; + node->appData = appData; + return (SECSuccess); + +loser: + return (SECFailure); +} + +SECStatus +CERT_AddCertToListTail(CERTCertList *certs, CERTCertificate *cert) +{ + return CERT_AddCertToListTailWithData(certs, cert, NULL); +} + +SECStatus +CERT_AddCertToListHeadWithData(CERTCertList *certs, CERTCertificate *cert, + void *appData) +{ + CERTCertListNode *node; + CERTCertListNode *head; + + head = CERT_LIST_HEAD(certs); + if (head == NULL) { + goto loser; + } + + node = (CERTCertListNode *)PORT_ArenaZAlloc(certs->arena, + sizeof(CERTCertListNode)); + if (node == NULL) { + goto loser; + } + + PR_INSERT_BEFORE(&node->links, &head->links); + /* certs->count++; */ + node->cert = cert; + node->appData = appData; + return (SECSuccess); + +loser: + return (SECFailure); +} + +SECStatus +CERT_AddCertToListHead(CERTCertList *certs, CERTCertificate *cert) +{ + return CERT_AddCertToListHeadWithData(certs, cert, NULL); +} + +/* + * Sort callback function to determine if cert a is newer than cert b. + * Not valid certs are considered older than valid certs. + */ +PRBool +CERT_SortCBValidity(CERTCertificate *certa, CERTCertificate *certb, void *arg) +{ + PRTime sorttime; + PRTime notBeforeA, notAfterA, notBeforeB, notAfterB; + SECStatus rv; + PRBool newerbefore, newerafter; + PRBool aNotValid = PR_FALSE, bNotValid = PR_FALSE; + + sorttime = *(PRTime *)arg; + + rv = CERT_GetCertTimes(certa, ¬BeforeA, ¬AfterA); + if (rv != SECSuccess) { + return (PR_FALSE); + } + + rv = CERT_GetCertTimes(certb, ¬BeforeB, ¬AfterB); + if (rv != SECSuccess) { + return (PR_TRUE); + } + newerbefore = PR_FALSE; + if (LL_CMP(notBeforeA, >, notBeforeB)) { + newerbefore = PR_TRUE; + } + newerafter = PR_FALSE; + if (LL_CMP(notAfterA, >, notAfterB)) { + newerafter = PR_TRUE; + } + + /* check if A is valid at sorttime */ + if (CERT_CheckCertValidTimes(certa, sorttime, PR_FALSE) != + secCertTimeValid) { + aNotValid = PR_TRUE; + } + + /* check if B is valid at sorttime */ + if (CERT_CheckCertValidTimes(certb, sorttime, PR_FALSE) != + secCertTimeValid) { + bNotValid = PR_TRUE; + } + + /* a is valid, b is not */ + if (bNotValid && (!aNotValid)) { + return (PR_TRUE); + } + + /* b is valid, a is not */ + if (aNotValid && (!bNotValid)) { + return (PR_FALSE); + } + + /* a and b are either valid or not valid */ + if (newerbefore && newerafter) { + return (PR_TRUE); + } + + if ((!newerbefore) && (!newerafter)) { + return (PR_FALSE); + } + + if (newerbefore) { + /* cert A was issued after cert B, but expires sooner */ + return (PR_TRUE); + } else { + /* cert B was issued after cert A, but expires sooner */ + return (PR_FALSE); + } +} + +SECStatus +CERT_AddCertToListSorted(CERTCertList *certs, CERTCertificate *cert, + CERTSortCallback f, void *arg) +{ + CERTCertListNode *node; + CERTCertListNode *head; + PRBool ret; + + node = (CERTCertListNode *)PORT_ArenaZAlloc(certs->arena, + sizeof(CERTCertListNode)); + if (node == NULL) { + goto loser; + } + + head = CERT_LIST_HEAD(certs); + + while (!CERT_LIST_END(head, certs)) { + + /* if cert is already in the list, then don't add it again */ + if (cert == head->cert) { + /*XXX*/ + /* don't keep a reference */ + CERT_DestroyCertificate(cert); + goto done; + } + + ret = (*f)(cert, head->cert, arg); + /* if sort function succeeds, then insert before current node */ + if (ret) { + PR_INSERT_BEFORE(&node->links, &head->links); + goto done; + } + + head = CERT_LIST_NEXT(head); + } + /* if we get to the end, then just insert it at the tail */ + PR_INSERT_BEFORE(&node->links, &certs->list); + +done: + /* certs->count++; */ + node->cert = cert; + return (SECSuccess); + +loser: + return (SECFailure); +} + +/* This routine is here because pcertdb.c still has a call to it. + * The SMIME profile code in pcertdb.c should be split into high (find + * the email cert) and low (store the profile) code. At that point, we + * can move this to certhigh.c where it belongs. + * + * remove certs from a list that don't have keyUsage and certType + * that match the given usage. + */ +SECStatus +CERT_FilterCertListByUsage(CERTCertList *certList, SECCertUsage usage, + PRBool ca) +{ + unsigned int requiredKeyUsage; + unsigned int requiredCertType; + CERTCertListNode *node, *savenode; + SECStatus rv; + + if (certList == NULL) + goto loser; + + rv = CERT_KeyUsageAndTypeForCertUsage(usage, ca, &requiredKeyUsage, + &requiredCertType); + if (rv != SECSuccess) { + goto loser; + } + + node = CERT_LIST_HEAD(certList); + + while (!CERT_LIST_END(node, certList)) { + + PRBool bad = (PRBool)(!node->cert); + + /* bad key usage ? */ + if (!bad && + CERT_CheckKeyUsage(node->cert, requiredKeyUsage) != SECSuccess) { + bad = PR_TRUE; + } + /* bad cert type ? */ + if (!bad) { + unsigned int certType = 0; + if (ca) { + /* This function returns a more comprehensive cert type that + * takes trust flags into consideration. Should probably + * fix the cert decoding code to do this. + */ + (void)CERT_IsCACert(node->cert, &certType); + } else { + certType = node->cert->nsCertType; + } + if (!(certType & requiredCertType)) { + bad = PR_TRUE; + } + } + + if (bad) { + /* remove the node if it is bad */ + savenode = CERT_LIST_NEXT(node); + CERT_RemoveCertListNode(node); + node = savenode; + } else { + node = CERT_LIST_NEXT(node); + } + } + return (SECSuccess); + +loser: + return (SECFailure); +} + +PRBool +CERT_IsUserCert(CERTCertificate *cert) +{ + CERTCertTrust trust; + SECStatus rv = SECFailure; + + rv = CERT_GetCertTrust(cert, &trust); + if (rv == SECSuccess && + ((trust.sslFlags & CERTDB_USER) || (trust.emailFlags & CERTDB_USER) || + (trust.objectSigningFlags & CERTDB_USER))) { + return PR_TRUE; + } else { + return PR_FALSE; + } +} + +SECStatus +CERT_FilterCertListForUserCerts(CERTCertList *certList) +{ + CERTCertListNode *node, *freenode; + CERTCertificate *cert; + + if (!certList) { + return SECFailure; + } + + node = CERT_LIST_HEAD(certList); + + while (!CERT_LIST_END(node, certList)) { + cert = node->cert; + if (PR_TRUE != CERT_IsUserCert(cert)) { + /* Not a User Cert, so remove this cert from the list */ + freenode = node; + node = CERT_LIST_NEXT(node); + CERT_RemoveCertListNode(freenode); + } else { + /* Is a User cert, so leave it in the list */ + node = CERT_LIST_NEXT(node); + } + } + + return (SECSuccess); +} + +/* return true if cert is in the list */ +PRBool +CERT_IsInList(const CERTCertificate *cert, const CERTCertList *certList) +{ + CERTCertListNode *node; + for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList); + node = CERT_LIST_NEXT(node)) { + if (node->cert == cert) { + return PR_TRUE; + } + } + return PR_FALSE; +} + +/* returned certList is the intersection of the certs on certList and the + * certs on filterList */ +SECStatus +CERT_FilterCertListByCertList(CERTCertList *certList, + const CERTCertList *filterList) +{ + CERTCertListNode *node, *freenode; + CERTCertificate *cert; + + if (!certList) { + return SECFailure; + } + + if (!filterList || CERT_LIST_EMPTY(certList)) { + /* if the filterList is empty, just clear out certList and return */ + for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);) { + freenode = node; + node = CERT_LIST_NEXT(node); + CERT_RemoveCertListNode(freenode); + } + return SECSuccess; + } + + node = CERT_LIST_HEAD(certList); + + while (!CERT_LIST_END(node, certList)) { + cert = node->cert; + if (!CERT_IsInList(cert, filterList)) { + // no matching cert on filter list, remove it from certlist */ + freenode = node; + node = CERT_LIST_NEXT(node); + CERT_RemoveCertListNode(freenode); + } else { + /* matching cert, keep it around */ + node = CERT_LIST_NEXT(node); + } + } + + return (SECSuccess); +} + +SECStatus +CERT_FilterCertListByNickname(CERTCertList *certList, char *nickname, + void *pwarg) +{ + CERTCertList *nameList; + SECStatus rv; + + if (!certList) { + return SECFailure; + } + + /* we could try to match the nickname to the individual cert, + * but nickname parsing is quite complicated, so it's best just + * to use the existing code and get a list of certs that match the + * nickname. We can then compare that list with our input cert list + * and return only those certs that are on both. */ + nameList = PK11_FindCertsFromNickname(nickname, pwarg); + + /* namelist could be NULL, this will force certList to become empty */ + rv = CERT_FilterCertListByCertList(certList, nameList); + /* CERT_DestroyCertList can now accept a NULL pointer */ + CERT_DestroyCertList(nameList); + return rv; +} + +static PZLock *certRefCountLock = NULL; + +/* + * Acquire the cert reference count lock + * There is currently one global lock for all certs, but I'm putting a cert + * arg here so that it will be easy to make it per-cert in the future if + * that turns out to be necessary. + */ +void +CERT_LockCertRefCount(CERTCertificate *cert) +{ + PORT_Assert(certRefCountLock != NULL); + PZ_Lock(certRefCountLock); + return; +} + +/* + * Free the cert reference count lock + */ +void +CERT_UnlockCertRefCount(CERTCertificate *cert) +{ + PORT_Assert(certRefCountLock != NULL); + PRStatus prstat = PZ_Unlock(certRefCountLock); + PORT_AssertArg(prstat == PR_SUCCESS); +} + +static PZLock *certTrustLock = NULL; + +/* + * Acquire the cert trust lock + * There is currently one global lock for all certs, but I'm putting a cert + * arg here so that it will be easy to make it per-cert in the future if + * that turns out to be necessary. + */ +void +CERT_LockCertTrust(const CERTCertificate *cert) +{ + PORT_Assert(certTrustLock != NULL); + PZ_Lock(certTrustLock); +} + +static PZLock *certTempPermCertLock = NULL; + +/* + * Acquire the cert temp/perm/nssCert lock + */ +void +CERT_LockCertTempPerm(const CERTCertificate *cert) +{ + PORT_Assert(certTempPermCertLock != NULL); + PZ_Lock(certTempPermCertLock); +} + +/* Maybe[Lock, Unlock] variants are only to be used by + * CERT_DestroyCertificate, since an application could + * call this after NSS_Shutdown destroys cert locks. */ +void +CERT_MaybeLockCertTempPerm(const CERTCertificate *cert) +{ + if (certTempPermCertLock) { + PZ_Lock(certTempPermCertLock); + } +} + +SECStatus +cert_InitLocks(void) +{ + if (certRefCountLock == NULL) { + certRefCountLock = PZ_NewLock(nssILockRefLock); + PORT_Assert(certRefCountLock != NULL); + if (!certRefCountLock) { + return SECFailure; + } + } + + if (certTrustLock == NULL) { + certTrustLock = PZ_NewLock(nssILockCertDB); + PORT_Assert(certTrustLock != NULL); + if (!certTrustLock) { + PZ_DestroyLock(certRefCountLock); + certRefCountLock = NULL; + return SECFailure; + } + } + + if (certTempPermCertLock == NULL) { + certTempPermCertLock = PZ_NewLock(nssILockCertDB); + PORT_Assert(certTempPermCertLock != NULL); + if (!certTempPermCertLock) { + PZ_DestroyLock(certTrustLock); + PZ_DestroyLock(certRefCountLock); + certRefCountLock = NULL; + certTrustLock = NULL; + return SECFailure; + } + } + + return SECSuccess; +} + +SECStatus +cert_DestroyLocks(void) +{ + SECStatus rv = SECSuccess; + + PORT_Assert(certRefCountLock != NULL); + if (certRefCountLock) { + PZ_DestroyLock(certRefCountLock); + certRefCountLock = NULL; + } else { + rv = SECFailure; + } + + PORT_Assert(certTrustLock != NULL); + if (certTrustLock) { + PZ_DestroyLock(certTrustLock); + certTrustLock = NULL; + } else { + rv = SECFailure; + } + + PORT_Assert(certTempPermCertLock != NULL); + if (certTempPermCertLock) { + PZ_DestroyLock(certTempPermCertLock); + certTempPermCertLock = NULL; + } else { + rv = SECFailure; + } + return rv; +} + +/* + * Free the cert trust lock + */ +void +CERT_UnlockCertTrust(const CERTCertificate *cert) +{ + PORT_Assert(certTrustLock != NULL); + PRStatus prstat = PZ_Unlock(certTrustLock); + PORT_AssertArg(prstat == PR_SUCCESS); +} + +/* + * Free the temp/perm/nssCert lock + */ +void +CERT_UnlockCertTempPerm(const CERTCertificate *cert) +{ + PORT_Assert(certTempPermCertLock != NULL); + PRStatus prstat = PZ_Unlock(certTempPermCertLock); + PORT_AssertArg(prstat == PR_SUCCESS); +} + +void +CERT_MaybeUnlockCertTempPerm(const CERTCertificate *cert) +{ + if (certTempPermCertLock) { + PZ_Unlock(certTempPermCertLock); + } +} + +/* + * Get the StatusConfig data for this handle + */ +CERTStatusConfig * +CERT_GetStatusConfig(CERTCertDBHandle *handle) +{ + return handle->statusConfig; +} + +/* + * Set the StatusConfig data for this handle. There + * should not be another configuration set. + */ +void +CERT_SetStatusConfig(CERTCertDBHandle *handle, CERTStatusConfig *statusConfig) +{ + PORT_Assert(handle->statusConfig == NULL); + handle->statusConfig = statusConfig; +} + +/* + * Code for dealing with subjKeyID to cert mappings. + */ + +static PLHashTable *gSubjKeyIDHash = NULL; +static PRLock *gSubjKeyIDLock = NULL; +static PLHashTable *gSubjKeyIDSlotCheckHash = NULL; +static PRLock *gSubjKeyIDSlotCheckLock = NULL; + +static void * +cert_AllocTable(void *pool, PRSize size) +{ + return PORT_Alloc(size); +} + +static void +cert_FreeTable(void *pool, void *item) +{ + PORT_Free(item); +} + +static PLHashEntry * +cert_AllocEntry(void *pool, const void *key) +{ + return PORT_New(PLHashEntry); +} + +static void +cert_FreeEntry(void *pool, PLHashEntry *he, PRUintn flag) +{ + SECITEM_FreeItem((SECItem *)(he->value), PR_TRUE); + if (flag == HT_FREE_ENTRY) { + SECITEM_FreeItem((SECItem *)(he->key), PR_TRUE); + PORT_Free(he); + } +} + +static PLHashAllocOps cert_AllocOps = { cert_AllocTable, cert_FreeTable, + cert_AllocEntry, cert_FreeEntry }; + +SECStatus +cert_CreateSubjectKeyIDSlotCheckHash(void) +{ + /* + * This hash is used to remember the series of a slot + * when we last checked for user certs + */ + gSubjKeyIDSlotCheckHash = + PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare, + SECITEM_HashCompare, &cert_AllocOps, NULL); + if (!gSubjKeyIDSlotCheckHash) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + gSubjKeyIDSlotCheckLock = PR_NewLock(); + if (!gSubjKeyIDSlotCheckLock) { + PL_HashTableDestroy(gSubjKeyIDSlotCheckHash); + gSubjKeyIDSlotCheckHash = NULL; + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + return SECSuccess; +} + +SECStatus +cert_CreateSubjectKeyIDHashTable(void) +{ + gSubjKeyIDHash = PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare, + SECITEM_HashCompare, &cert_AllocOps, NULL); + if (!gSubjKeyIDHash) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + gSubjKeyIDLock = PR_NewLock(); + if (!gSubjKeyIDLock) { + PL_HashTableDestroy(gSubjKeyIDHash); + gSubjKeyIDHash = NULL; + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + /* initialize the companion hash (for remembering slot series) */ + if (cert_CreateSubjectKeyIDSlotCheckHash() != SECSuccess) { + cert_DestroySubjectKeyIDHashTable(); + return SECFailure; + } + return SECSuccess; +} + +SECStatus +cert_AddSubjectKeyIDMapping(SECItem *subjKeyID, CERTCertificate *cert) +{ + SECItem *newKeyID, *oldVal, *newVal; + SECStatus rv = SECFailure; + + if (!gSubjKeyIDLock) { + /* If one is created, then both are there. So only check for one. */ + return SECFailure; + } + + newVal = SECITEM_DupItem(&cert->derCert); + if (!newVal) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto done; + } + newKeyID = SECITEM_DupItem(subjKeyID); + if (!newKeyID) { + SECITEM_FreeItem(newVal, PR_TRUE); + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto done; + } + + PR_Lock(gSubjKeyIDLock); + /* The hash table implementation does not free up the memory + * associated with the key of an already existing entry if we add a + * duplicate, so we would wind up leaking the previously allocated + * key if we don't remove before adding. + */ + oldVal = (SECItem *)PL_HashTableLookup(gSubjKeyIDHash, subjKeyID); + if (oldVal) { + PL_HashTableRemove(gSubjKeyIDHash, subjKeyID); + } + + rv = (PL_HashTableAdd(gSubjKeyIDHash, newKeyID, newVal)) ? SECSuccess + : SECFailure; + PR_Unlock(gSubjKeyIDLock); +done: + return rv; +} + +SECStatus +cert_RemoveSubjectKeyIDMapping(SECItem *subjKeyID) +{ + SECStatus rv; + if (!gSubjKeyIDLock) + return SECFailure; + + PR_Lock(gSubjKeyIDLock); + rv = (PL_HashTableRemove(gSubjKeyIDHash, subjKeyID)) ? SECSuccess + : SECFailure; + PR_Unlock(gSubjKeyIDLock); + return rv; +} + +SECStatus +cert_UpdateSubjectKeyIDSlotCheck(SECItem *slotid, int series) +{ + SECItem *oldSeries, *newSlotid, *newSeries; + SECStatus rv = SECFailure; + + if (!gSubjKeyIDSlotCheckLock) { + return rv; + } + + newSlotid = SECITEM_DupItem(slotid); + newSeries = SECITEM_AllocItem(NULL, NULL, sizeof(int)); + if (!newSlotid || !newSeries) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + PORT_Memcpy(newSeries->data, &series, sizeof(int)); + + PR_Lock(gSubjKeyIDSlotCheckLock); + oldSeries = (SECItem *)PL_HashTableLookup(gSubjKeyIDSlotCheckHash, slotid); + if (oldSeries) { + /* + * make sure we don't leak the key of an existing entry + * (similar to cert_AddSubjectKeyIDMapping, see comment there) + */ + PL_HashTableRemove(gSubjKeyIDSlotCheckHash, slotid); + } + rv = (PL_HashTableAdd(gSubjKeyIDSlotCheckHash, newSlotid, newSeries)) + ? SECSuccess + : SECFailure; + PR_Unlock(gSubjKeyIDSlotCheckLock); + if (rv == SECSuccess) { + return rv; + } + +loser: + if (newSlotid) { + SECITEM_FreeItem(newSlotid, PR_TRUE); + } + if (newSeries) { + SECITEM_FreeItem(newSeries, PR_TRUE); + } + return rv; +} + +int +cert_SubjectKeyIDSlotCheckSeries(SECItem *slotid) +{ + SECItem *seriesItem = NULL; + int series; + + if (!gSubjKeyIDSlotCheckLock) { + PORT_SetError(SEC_ERROR_NOT_INITIALIZED); + return -1; + } + + PR_Lock(gSubjKeyIDSlotCheckLock); + seriesItem = (SECItem *)PL_HashTableLookup(gSubjKeyIDSlotCheckHash, slotid); + PR_Unlock(gSubjKeyIDSlotCheckLock); + /* getting a null series just means we haven't registered one yet, + * just return 0 */ + if (seriesItem == NULL) { + return 0; + } + /* if we got a series back, assert if it's not the proper length. */ + PORT_Assert(seriesItem->len == sizeof(int)); + if (seriesItem->len != sizeof(int)) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return -1; + } + PORT_Memcpy(&series, seriesItem->data, sizeof(int)); + return series; +} + +SECStatus +cert_DestroySubjectKeyIDSlotCheckHash(void) +{ + if (gSubjKeyIDSlotCheckHash) { + PR_Lock(gSubjKeyIDSlotCheckLock); + PL_HashTableDestroy(gSubjKeyIDSlotCheckHash); + gSubjKeyIDSlotCheckHash = NULL; + PR_Unlock(gSubjKeyIDSlotCheckLock); + PR_DestroyLock(gSubjKeyIDSlotCheckLock); + gSubjKeyIDSlotCheckLock = NULL; + } + return SECSuccess; +} + +SECStatus +cert_DestroySubjectKeyIDHashTable(void) +{ + if (gSubjKeyIDHash) { + PR_Lock(gSubjKeyIDLock); + PL_HashTableDestroy(gSubjKeyIDHash); + gSubjKeyIDHash = NULL; + PR_Unlock(gSubjKeyIDLock); + PR_DestroyLock(gSubjKeyIDLock); + gSubjKeyIDLock = NULL; + } + cert_DestroySubjectKeyIDSlotCheckHash(); + return SECSuccess; +} + +SECItem * +cert_FindDERCertBySubjectKeyID(SECItem *subjKeyID) +{ + SECItem *val; + + if (!gSubjKeyIDLock) + return NULL; + + PR_Lock(gSubjKeyIDLock); + val = (SECItem *)PL_HashTableLookup(gSubjKeyIDHash, subjKeyID); + if (val) { + val = SECITEM_DupItem(val); + } + PR_Unlock(gSubjKeyIDLock); + return val; +} + +CERTCertificate * +CERT_FindCertBySubjectKeyID(CERTCertDBHandle *handle, SECItem *subjKeyID) +{ + CERTCertificate *cert = NULL; + SECItem *derCert; + + derCert = cert_FindDERCertBySubjectKeyID(subjKeyID); + if (derCert) { + cert = CERT_FindCertByDERCert(handle, derCert); + SECITEM_FreeItem(derCert, PR_TRUE); + } + return cert; +} diff --git a/security/nss/lib/certdb/certdb.gyp b/security/nss/lib/certdb/certdb.gyp new file mode 100644 index 0000000000..673d56da31 --- /dev/null +++ b/security/nss/lib/certdb/certdb.gyp @@ -0,0 +1,34 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +{ + 'includes': [ + '../../coreconf/config.gypi' + ], + 'targets': [ + { + 'target_name': 'certdb', + 'type': 'static_library', + 'sources': [ + 'alg1485.c', + 'certdb.c', + 'certv3.c', + 'certxutl.c', + 'crl.c', + 'genname.c', + 'polcyxtn.c', + 'secname.c', + 'stanpcertdb.c', + 'xauthkid.c', + 'xbsconst.c', + 'xconst.c' + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_exports' + ] + } + ], + 'variables': { + 'module': 'nss' + } +} \ No newline at end of file diff --git a/security/nss/lib/certdb/certdb.h b/security/nss/lib/certdb/certdb.h new file mode 100644 index 0000000000..53d8a39190 --- /dev/null +++ b/security/nss/lib/certdb/certdb.h @@ -0,0 +1,89 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _CERTDB_H_ +#define _CERTDB_H_ + +/* common flags for all types of certificates */ +#define CERTDB_TERMINAL_RECORD (1u << 0) +#define CERTDB_TRUSTED (1u << 1) +#define CERTDB_SEND_WARN (1u << 2) +#define CERTDB_VALID_CA (1u << 3) +#define CERTDB_TRUSTED_CA (1u << 4) /* trusted for issuing server certs */ +#define CERTDB_NS_TRUSTED_CA (1u << 5) +#define CERTDB_USER (1u << 6) +#define CERTDB_TRUSTED_CLIENT_CA (1u << 7) /* trusted for issuing client certs */ +#define CERTDB_INVISIBLE_CA (1u << 8) /* don't show in UI */ +#define CERTDB_GOVT_APPROVED_CA (1u << 9) /* can do strong crypto in export ver */ + +/* old usage, to keep old programs compiling */ +/* On Windows, Mac, and Linux (and other gcc platforms), we can give compile + * time deprecation warnings when applications use the old CERTDB_VALID_PEER + * define */ +#if __GNUC__ > 3 +#if (__GNUC__ == 4) && (__GNUC_MINOR__ < 5) +typedef unsigned int __CERTDB_VALID_PEER __attribute__((deprecated)); +#else +typedef unsigned int __CERTDB_VALID_PEER __attribute__(( + deprecated("CERTDB_VALID_PEER is now CERTDB_TERMINAL_RECORD"))); +#endif +#define CERTDB_VALID_PEER ((__CERTDB_VALID_PEER)CERTDB_TERMINAL_RECORD) +#else +#ifdef _WIN32 +#pragma deprecated(CERTDB_VALID_PEER) +#endif +#define CERTDB_VALID_PEER CERTDB_TERMINAL_RECORD +#endif + +SEC_BEGIN_PROTOS + +CERTSignedCrl *SEC_FindCrlByKey(CERTCertDBHandle *handle, SECItem *crlKey, + int type); + +CERTSignedCrl *SEC_FindCrlByName(CERTCertDBHandle *handle, SECItem *crlKey, + int type); + +CERTSignedCrl *SEC_FindCrlByDERCert(CERTCertDBHandle *handle, SECItem *derCrl, + int type); + +PRBool SEC_CertNicknameConflict(const char *nickname, const SECItem *derSubject, + CERTCertDBHandle *handle); +CERTSignedCrl *SEC_NewCrl(CERTCertDBHandle *handle, char *url, SECItem *derCrl, + int type); + +SECStatus SEC_DeletePermCRL(CERTSignedCrl *crl); + +SECStatus SEC_LookupCrls(CERTCertDBHandle *handle, CERTCrlHeadNode **nodes, + int type); + +SECStatus SEC_DestroyCrl(CERTSignedCrl *crl); + +CERTSignedCrl *SEC_DupCrl(CERTSignedCrl *acrl); + +SECStatus CERT_AddTempCertToPerm(CERTCertificate *cert, char *nickname, + CERTCertTrust *trust); + +SECStatus SEC_DeletePermCertificate(CERTCertificate *cert); + +PRBool SEC_CrlIsNewer(CERTCrl *inNew, CERTCrl *old); + +/* +** Extract the validity times from a CRL +** "crl" is the CRL +** "notBefore" is the start of the validity period (last update) +** "notAfter" is the end of the validity period (next update) +*/ +SECStatus SEC_GetCrlTimes(CERTCrl *crl, PRTime *notBefore, PRTime *notAfter); + +/* +** Check the validity times of a crl vs. time 't', allowing +** some slop for broken clocks and stuff. +** "crl" is the certificate to be checked +** "t" is the time to check against +*/ +SECCertTimeValidity SEC_CheckCrlTimes(CERTCrl *crl, PRTime t); + +SEC_END_PROTOS + +#endif /* _CERTDB_H_ */ diff --git a/security/nss/lib/certdb/certi.h b/security/nss/lib/certdb/certi.h new file mode 100644 index 0000000000..2a8ae2758f --- /dev/null +++ b/security/nss/lib/certdb/certi.h @@ -0,0 +1,407 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* + * certi.h - private data structures for the certificate library + */ +#ifndef _CERTI_H_ +#define _CERTI_H_ + +#include "certt.h" +#include "nssrwlkt.h" + +/* +#define GLOBAL_RWLOCK 1 +*/ + +#define DPC_RWLOCK 1 + +/* all definitions in this file are subject to change */ + +typedef struct OpaqueCRLFieldsStr OpaqueCRLFields; +typedef struct CRLEntryCacheStr CRLEntryCache; +typedef struct CRLDPCacheStr CRLDPCache; +typedef struct CRLIssuerCacheStr CRLIssuerCache; +typedef struct CRLCacheStr CRLCache; +typedef struct CachedCrlStr CachedCrl; +typedef struct NamedCRLCacheStr NamedCRLCache; +typedef struct NamedCRLCacheEntryStr NamedCRLCacheEntry; + +struct OpaqueCRLFieldsStr { + PRBool partial; + PRBool decodingError; + PRBool badEntries; + PRBool badDER; + PRBool badExtensions; + PRBool heapDER; +}; + +typedef struct PreAllocatorStr PreAllocator; + +struct PreAllocatorStr { + PRSize len; + void* data; + PRSize used; + PLArenaPool* arena; + PRSize extra; +}; + +/* CRL entry cache. + This is the same as an entry plus the next/prev pointers for the hash table +*/ + +struct CRLEntryCacheStr { + CERTCrlEntry entry; + CRLEntryCache *prev, *next; +}; + +#define CRL_CACHE_INVALID_CRLS 0x0001 /* this state will be set \ + if we have CRL objects with an invalid DER or signature. Can be \ + cleared if the invalid objects are deleted from the token */ +#define CRL_CACHE_LAST_FETCH_FAILED 0x0002 /* this state will be set \ + if the last CRL fetch encountered an error. Can be cleared if a \ + new fetch succeeds */ + +#define CRL_CACHE_OUT_OF_MEMORY 0x0004 /* this state will be set \ + if we don't have enough memory to build the hash table of entries */ + +typedef enum { + CRL_OriginToken = 0, /* CRL came from PKCS#11 token */ + CRL_OriginExplicit = 1 /* CRL was explicitly added to the cache, from RAM */ +} CRLOrigin; + +typedef enum { + dpcacheNoEntry = 0, /* no entry found for this SN */ + dpcacheFoundEntry = 1, /* entry found for this SN */ + dpcacheCallerError = 2, /* invalid args */ + dpcacheInvalidCacheError = 3, /* CRL in cache may be bad DER */ + /* or unverified */ + dpcacheEmpty = 4, /* no CRL in cache */ + dpcacheLookupError = 5 /* internal error */ +} dpcacheStatus; + +struct CachedCrlStr { + CERTSignedCrl* crl; + CRLOrigin origin; + /* hash table of entries. We use a PLHashTable and pre-allocate the + required amount of memory in one shot, so that our allocator can + simply pass offsets into it when hashing. + + This won't work anymore when we support delta CRLs and iCRLs, because + the size of the hash table will vary over time. At that point, the best + solution will be to allocate large CRLEntry structures by modifying + the DER decoding template. The extra space would be for next/prev + pointers. This would allow entries from different CRLs to be mixed in + the same hash table. + */ + PLHashTable* entries; + PreAllocator* prebuffer; /* big pre-allocated buffer mentioned above */ + PRBool sigChecked; /* this CRL signature has already been checked */ + PRBool sigValid; /* signature verification status . + Only meaningful if checked is PR_TRUE . */ + PRBool unbuildable; /* Avoid using assosiated CRL is it fails + * a decoding step */ +}; + +/* CRL distribution point cache object + This is a cache of CRL entries for a given distribution point of an issuer + It is built from a collection of one full and 0 or more delta CRLs. +*/ + +struct CRLDPCacheStr { +#ifdef DPC_RWLOCK + NSSRWLock* lock; +#else + PRLock* lock; +#endif + SECItem* issuerDERCert; /* issuer DER cert. Don't hold a reference + to the actual cert so the trust can be + updated on the cert automatically. + XXX there may be multiple issuer certs, + with different validity dates. Also + need to deal with SKID/AKID . See + bugzilla 217387, 233118 */ + + CERTCertDBHandle* dbHandle; + + SECItem* subject; /* DER of issuer subject */ + SECItem* distributionPoint; /* DER of distribution point. This may be + NULL when distribution points aren't + in use (ie. the CA has a single CRL). + Currently not used. */ + + /* array of full CRLs matching this distribution point */ + PRUint32 ncrls; /* total number of CRLs in crls */ + CachedCrl** crls; /* array of all matching CRLs */ + /* XCRL With iCRLs and multiple DPs, the CRL can be shared accross several + issuers. In the future, we'll need to globally recycle the CRL in a + separate list in order to avoid extra lookups, decodes, and copies */ + + /* pointers to good decoded CRLs used to build the cache */ + CachedCrl* selected; /* full CRL selected for use in the cache */ +#if 0 + /* for future use */ + PRInt32 numdeltas; /* number of delta CRLs used for the cache */ + CachedCrl** deltas; /* delta CRLs used for the cache */ +#endif + /* cache invalidity bitflag */ + PRUint16 invalid; /* this state will be set if either + CRL_CACHE_INVALID_CRLS or CRL_CACHE_LAST_FETCH_FAILED is set. + In those cases, all certs are considered to have unknown status. + The invalid state can only be cleared during an update if all + error states are cleared */ + PRBool refresh; /* manual refresh from tokens has been forced */ + PRBool mustchoose; /* trigger reselection algorithm, for case when + RAM CRL objects are dropped from the cache */ + PRTime lastfetch; /* time a CRL token fetch was last performed */ + PRTime lastcheck; /* time CRL token objects were last checked for + existence */ +}; + +/* CRL issuer cache object + This object tracks all the distribution point caches for a given issuer. + XCRL once we support multiple issuing distribution points, this object + will be a hash table. For now, it just holds the single CRL distribution + point cache structure. +*/ + +struct CRLIssuerCacheStr { + SECItem* subject; /* DER of issuer subject */ + CRLDPCache* dpp; +}; + +/* CRL revocation cache object + This object tracks all the issuer caches +*/ + +struct CRLCacheStr { +#ifdef GLOBAL_RWLOCK + NSSRWLock* lock; +#else + PRLock* lock; +#endif + /* hash table of issuer to CRLIssuerCacheStr, + indexed by issuer DER subject */ + PLHashTable* issuers; +}; + +SECStatus InitCRLCache(void); +SECStatus ShutdownCRLCache(void); + +/* Returns a pointer to an environment-like string, a series of +** null-terminated strings, terminated by a zero-length string. +** This function is intended to be internal to NSS. +*/ +extern char* cert_GetCertificateEmailAddresses(CERTCertificate* cert); + +/* + * These functions are used to map subjectKeyID extension values to certs + * and to keep track of the checks for user certificates in each slot + */ +SECStatus cert_CreateSubjectKeyIDHashTable(void); + +SECStatus cert_AddSubjectKeyIDMapping(SECItem* subjKeyID, + CERTCertificate* cert); + +SECStatus cert_UpdateSubjectKeyIDSlotCheck(SECItem* slotid, int series); + +int cert_SubjectKeyIDSlotCheckSeries(SECItem* slotid); + +/* + * Call this function to remove an entry from the mapping table. + */ +SECStatus cert_RemoveSubjectKeyIDMapping(SECItem* subjKeyID); + +SECStatus cert_DestroySubjectKeyIDHashTable(void); + +SECItem* cert_FindDERCertBySubjectKeyID(SECItem* subjKeyID); + +/* return maximum length of AVA value based on its type OID tag. */ +extern int cert_AVAOidTagToMaxLen(SECOidTag tag); + +/* Make an AVA, allocated from pool, from OID and DER encoded value */ +extern CERTAVA* CERT_CreateAVAFromRaw(PLArenaPool* pool, const SECItem* OID, + const SECItem* value); + +/* Make an AVA from binary input specified by SECItem */ +extern CERTAVA* CERT_CreateAVAFromSECItem(PLArenaPool* arena, SECOidTag kind, + int valueType, SECItem* value); + +/* + * get a DPCache object for the given issuer subject and dp + * Automatically creates the cache object if it doesn't exist yet. + */ +SECStatus AcquireDPCache(CERTCertificate* issuer, const SECItem* subject, + const SECItem* dp, PRTime t, void* wincx, + CRLDPCache** dpcache, PRBool* writeLocked); + +/* check if a particular SN is in the CRL cache and return its entry */ +dpcacheStatus DPCache_Lookup(CRLDPCache* cache, const SECItem* sn, + CERTCrlEntry** returned); + +/* release a DPCache object that was previously acquired */ +void ReleaseDPCache(CRLDPCache* dpcache, PRBool writeLocked); + +/* + * map Stan errors into NSS errors + * This function examines the stan error stack and automatically sets + * PORT_SetError(); to the appropriate SEC_ERROR value. + */ +void CERT_MapStanError(); + +/* Like CERT_VerifyCert, except with an additional argument, flags. The + * flags are defined immediately below. + */ +SECStatus cert_VerifyCertWithFlags(CERTCertDBHandle* handle, + CERTCertificate* cert, PRBool checkSig, + SECCertUsage certUsage, PRTime t, + PRUint32 flags, void* wincx, + CERTVerifyLog* log); + +/* Use the default settings. + * cert_VerifyCertWithFlags(..., CERT_VERIFYCERT_USE_DEFAULTS, ...) is + * equivalent to CERT_VerifyCert(...); + */ +#define CERT_VERIFYCERT_USE_DEFAULTS 0 + +/* Skip all the OCSP checks during certificate verification, regardless of + * the global OCSP settings. By default, certificate |cert| will have its + * revocation status checked via OCSP according to the global OCSP settings. + * + * OCSP checking is always skipped when certUsage is certUsageStatusResponder. + */ +#define CERT_VERIFYCERT_SKIP_OCSP 1 + +/* Interface function for libpkix cert validation engine: + * cert_verify wrapper. */ +SECStatus cert_VerifyCertChainPkix(CERTCertificate* cert, PRBool checkSig, + SECCertUsage requiredUsage, PRTime time, + void* wincx, CERTVerifyLog* log, + PRBool* sigError, PRBool* revoked); + +SECStatus cert_InitLocks(void); + +SECStatus cert_DestroyLocks(void); + +/* + * fill in nsCertType field of the cert based on the cert extension + */ +extern SECStatus cert_GetCertType(CERTCertificate* cert); + +/* + * compute and return the value of nsCertType for cert, but do not + * update the CERTCertificate. + */ +extern PRUint32 cert_ComputeCertType(CERTCertificate* cert); + +extern PRBool cert_EKUAllowsIPsecIKE(CERTCertificate* cert, + PRBool* isCritical); + +void cert_AddToVerifyLog(CERTVerifyLog* log, CERTCertificate* cert, + long errorCode, unsigned int depth, void* arg); + +/* Insert a DER CRL into the CRL cache, and take ownership of it. + * + * cert_CacheCRLByGeneralName takes ownership of the memory in crl argument + * completely. crl must be freeable by SECITEM_FreeItem. It will be freed + * immediately if it is rejected from the CRL cache, or later during cache + * updates when a new crl is available, or at shutdown time. + * + * canonicalizedName represents the source of the CRL, a GeneralName. + * The format of the encoding is not restricted, but all callers of + * cert_CacheCRLByGeneralName and cert_FindCRLByGeneralName must use + * the same encoding. To facilitate X.500 name matching, a canonicalized + * encoding of the GeneralName should be used, if available. + */ + +SECStatus cert_CacheCRLByGeneralName(CERTCertDBHandle* dbhandle, SECItem* crl, + const SECItem* canonicalizedName); + +struct NamedCRLCacheStr { + PRLock* lock; + PLHashTable* entries; +}; + +/* NamedCRLCacheEntryStr is filled in by cert_CacheCRLByGeneralName, + * and read by cert_FindCRLByGeneralName */ +struct NamedCRLCacheEntryStr { + SECItem* canonicalizedName; + SECItem* crl; /* DER, kept only if CRL + * is successfully cached */ + PRBool inCRLCache; + PRTime successfulInsertionTime; /* insertion time */ + PRTime lastAttemptTime; /* time of last call to + cert_CacheCRLByGeneralName with this name */ + PRBool badDER; /* ASN.1 error */ + PRBool dupe; /* matching DER CRL already in CRL cache */ + PRBool unsupported; /* IDP, delta, any other reason */ +}; + +typedef enum { + certRevocationStatusRevoked = 0, + certRevocationStatusValid = 1, + certRevocationStatusUnknown = 2 +} CERTRevocationStatus; + +/* Returns detailed status of the cert(revStatus variable). Tells if + * issuer cache has OriginFetchedWithTimeout crl in it. */ +SECStatus cert_CheckCertRevocationStatus(CERTCertificate* cert, + CERTCertificate* issuer, + const SECItem* dp, PRTime t, + void* wincx, + CERTRevocationStatus* revStatus, + CERTCRLEntryReasonCode* revReason); + +SECStatus cert_AcquireNamedCRLCache(NamedCRLCache** returned); + +/* cert_FindCRLByGeneralName must be called only while the named cache is + * acquired, and the entry is only valid until cache is released. + */ +SECStatus cert_FindCRLByGeneralName(NamedCRLCache* ncc, + const SECItem* canonicalizedName, + NamedCRLCacheEntry** retEntry); + +SECStatus cert_ReleaseNamedCRLCache(NamedCRLCache* ncc); + +/* This is private for now. Maybe shoule be public. */ +CERTGeneralName* cert_GetSubjectAltNameList(const CERTCertificate* cert, + PLArenaPool* arena); + +/* Count DNS names and IP addresses in a list of GeneralNames */ +PRUint32 cert_CountDNSPatterns(CERTGeneralName* firstName); + +/* + * returns the trust status of the leaf certificate based on usage. + * If the leaf is explicitly untrusted, this function will fail and + * failedFlags will be set to the trust bit value that lead to the failure. + * If the leaf is trusted, isTrusted is set to true and the function returns + * SECSuccess. This function does not check if the cert is fit for a + * particular usage. + */ +SECStatus cert_CheckLeafTrust(CERTCertificate* cert, SECCertUsage usage, + unsigned int* failedFlags, PRBool* isTrusted); + +/* + * Acquire the cert temp/perm lock + */ +void CERT_LockCertTempPerm(const CERTCertificate* cert); + +/* + * Release the temp/perm lock + */ +void CERT_UnlockCertTempPerm(const CERTCertificate* cert); + +/* + * Acquire the cert trust lock + * There is currently one global lock for all certs, but I'm putting a cert + * arg here so that it will be easy to make it per-cert in the future if + * that turns out to be necessary. + */ +void CERT_LockCertTrust(const CERTCertificate* cert); + +/* + * Release the cert trust lock + */ +void CERT_UnlockCertTrust(const CERTCertificate* cert); + +#endif /* _CERTI_H_ */ diff --git a/security/nss/lib/certdb/certt.h b/security/nss/lib/certdb/certt.h new file mode 100644 index 0000000000..aae1184a84 --- /dev/null +++ b/security/nss/lib/certdb/certt.h @@ -0,0 +1,1359 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* + * certt.h - public data structures for the certificate library + */ +#ifndef _CERTT_H_ +#define _CERTT_H_ + +#include "prclist.h" +#include "pkcs11t.h" +#include "seccomon.h" +#include "secmodt.h" +#include "secoidt.h" +#include "plarena.h" +#include "prcvar.h" +#include "nssilock.h" +#include "prio.h" +#include "prmon.h" + +/* Stan data types */ +struct NSSCertificateStr; +struct NSSTrustDomainStr; + +/* Non-opaque objects */ +typedef struct CERTAVAStr CERTAVA; +typedef struct CERTAttributeStr CERTAttribute; +typedef struct CERTAuthInfoAccessStr CERTAuthInfoAccess; +typedef struct CERTAuthKeyIDStr CERTAuthKeyID; +typedef struct CERTBasicConstraintsStr CERTBasicConstraints; +typedef struct NSSTrustDomainStr CERTCertDBHandle; +typedef struct CERTCertExtensionStr CERTCertExtension; +typedef struct CERTCertKeyStr CERTCertKey; +typedef struct CERTCertListStr CERTCertList; +typedef struct CERTCertListNodeStr CERTCertListNode; +typedef struct CERTCertNicknamesStr CERTCertNicknames; +typedef struct CERTCertTrustStr CERTCertTrust; +typedef struct CERTCertDistrustStr CERTCertDistrust; +typedef struct CERTCertificateStr CERTCertificate; +typedef struct CERTCertificateListStr CERTCertificateList; +typedef struct CERTCertificateRequestStr CERTCertificateRequest; +typedef struct CERTCrlStr CERTCrl; +typedef struct CERTCrlDistributionPointsStr CERTCrlDistributionPoints; +typedef struct CERTCrlEntryStr CERTCrlEntry; +typedef struct CERTCrlHeadNodeStr CERTCrlHeadNode; +typedef struct CERTCrlKeyStr CERTCrlKey; +typedef struct CERTCrlNodeStr CERTCrlNode; +typedef struct CERTDERCertsStr CERTDERCerts; +typedef struct CERTDistNamesStr CERTDistNames; +typedef struct CERTGeneralNameStr CERTGeneralName; +typedef struct CERTGeneralNameListStr CERTGeneralNameList; +typedef struct CERTIssuerAndSNStr CERTIssuerAndSN; +typedef struct CERTNameStr CERTName; +typedef struct CERTNameConstraintStr CERTNameConstraint; +typedef struct CERTNameConstraintsStr CERTNameConstraints; +typedef struct CERTOKDomainNameStr CERTOKDomainName; +typedef struct CERTPrivKeyUsagePeriodStr CERTPrivKeyUsagePeriod; +typedef struct CERTPublicKeyAndChallengeStr CERTPublicKeyAndChallenge; +typedef struct CERTRDNStr CERTRDN; +typedef struct CERTSignedCrlStr CERTSignedCrl; +typedef struct CERTSignedDataStr CERTSignedData; +typedef struct CERTStatusConfigStr CERTStatusConfig; +typedef struct CERTSubjectListStr CERTSubjectList; +typedef struct CERTSubjectNodeStr CERTSubjectNode; +typedef struct CERTSubjectPublicKeyInfoStr CERTSubjectPublicKeyInfo; +typedef struct CERTValidityStr CERTValidity; +typedef struct CERTVerifyLogStr CERTVerifyLog; +typedef struct CERTVerifyLogNodeStr CERTVerifyLogNode; +typedef struct CRLDistributionPointStr CRLDistributionPoint; + +/* CRL extensions type */ +typedef unsigned long CERTCrlNumber; + +/* +** An X.500 AVA object +*/ +struct CERTAVAStr { + SECItem type; + SECItem value; +}; + +/* +** An X.500 RDN object +*/ +struct CERTRDNStr { + CERTAVA **avas; +}; + +/* +** An X.500 name object +*/ +struct CERTNameStr { + PLArenaPool *arena; + CERTRDN **rdns; +}; + +/* +** An X.509 validity object +*/ +struct CERTValidityStr { + PLArenaPool *arena; + SECItem notBefore; + SECItem notAfter; +}; + +/* + * A serial number and issuer name, which is used as a database key + */ +struct CERTCertKeyStr { + SECItem serialNumber; + SECItem derIssuer; +}; + +/* +** A signed data object. Used to implement the "signed" macro used +** in the X.500 specs. +*/ +struct CERTSignedDataStr { + SECItem data; + SECAlgorithmID signatureAlgorithm; + SECItem signature; +}; + +/* +** An X.509 subject-public-key-info object +*/ +struct CERTSubjectPublicKeyInfoStr { + PLArenaPool *arena; + SECAlgorithmID algorithm; + SECItem subjectPublicKey; +}; + +struct CERTPublicKeyAndChallengeStr { + SECItem spki; + SECItem challenge; +}; + +struct CERTCertTrustStr { + unsigned int sslFlags; + unsigned int emailFlags; + unsigned int objectSigningFlags; +}; + +/* + * Distrust dates for specific certificate usages. + * These dates are hardcoded in nssckbi/builtins. They are DER encoded to be + * compatible with the format of certdata.txt, other date fields in certs and + * existing functions to read these dates. Clients should check the distrust + * date in certificates to avoid trusting a CA for service they have ceased to + * support */ +struct CERTCertDistrustStr { + SECItem serverDistrustAfter; + SECItem emailDistrustAfter; +}; + +/* + * defined the types of trust that exist + */ +typedef enum SECTrustTypeEnum { + trustSSL = 0, + trustEmail = 1, + trustObjectSigning = 2, + trustTypeNone = 3 +} SECTrustType; + +#define SEC_GET_TRUST_FLAGS(trust, type) \ + (((type) == trustSSL) \ + ? ((trust)->sslFlags) \ + : (((type) == trustEmail) ? ((trust)->emailFlags) \ + : (((type) == trustObjectSigning) \ + ? ((trust)->objectSigningFlags) \ + : 0))) + +/* +** An X.509.3 certificate extension +*/ +struct CERTCertExtensionStr { + SECItem id; + SECItem critical; + SECItem value; +}; + +struct CERTSubjectNodeStr { + struct CERTSubjectNodeStr *next; + struct CERTSubjectNodeStr *prev; + SECItem certKey; + SECItem keyID; +}; + +struct CERTSubjectListStr { + PLArenaPool *arena; + int ncerts; + char *emailAddr; + CERTSubjectNode *head; + CERTSubjectNode *tail; /* do we need tail? */ + void *entry; +}; + +/* +** An X.509 certificate object (the unsigned form) +*/ +struct CERTCertificateStr { + /* the arena is used to allocate any data structures that have the same + * lifetime as the cert. This is all stuff that hangs off of the cert + * structure, and is all freed at the same time. It is used when the + * cert is decoded, destroyed, and at some times when it changes + * state + */ + PLArenaPool *arena; + + /* The following fields are static after the cert has been decoded */ + char *subjectName; + char *issuerName; + CERTSignedData signatureWrap; /* XXX */ + SECItem derCert; /* original DER for the cert */ + SECItem derIssuer; /* DER for issuer name */ + SECItem derSubject; /* DER for subject name */ + SECItem derPublicKey; /* DER for the public key */ + SECItem certKey; /* database key for this cert */ + SECItem version; + SECItem serialNumber; + SECAlgorithmID signature; + CERTName issuer; + CERTValidity validity; + CERTName subject; + CERTSubjectPublicKeyInfo subjectPublicKeyInfo; + SECItem issuerID; + SECItem subjectID; + CERTCertExtension **extensions; + char *emailAddr; + CERTCertDBHandle *dbhandle; + SECItem subjectKeyID; /* x509v3 subject key identifier */ + PRBool keyIDGenerated; /* was the keyid generated? */ + unsigned int keyUsage; /* what uses are allowed for this cert */ + unsigned int rawKeyUsage; /* value of the key usage extension */ + PRBool keyUsagePresent; /* was the key usage extension present */ + PRUint32 nsCertType; /* value of the ns cert type extension */ + /* must be 32-bit for PR_ATOMIC_SET */ + + /* these values can be set by the application to bypass certain checks + * or to keep the cert in memory for an entire session. + * XXX - need an api to set these + */ + PRBool keepSession; /* keep this cert for entire session*/ + PRBool timeOK; /* is the bad validity time ok? */ + CERTOKDomainName *domainOK; /* these domain names are ok */ + + /* + * these values can change when the cert changes state. These state + * changes include transitions from temp to perm or vice-versa, and + * changes of trust flags + */ + PRBool isperm; + PRBool istemp; + char *nickname; + char *dbnickname; + struct NSSCertificateStr *nssCertificate; /* This is Stan stuff. */ + CERTCertTrust *trust; + + /* the reference count is modified whenever someone looks up, dups + * or destroys a certificate + */ + int referenceCount; + + /* The subject list is a list of all certs with the same subject name. + * It can be modified any time a cert is added or deleted from either + * the in-memory(temporary) or on-disk(permanent) database. + */ + CERTSubjectList *subjectList; + + /* these belong in the static section, but are here to maintain + * the structure's integrity + */ + CERTAuthKeyID *authKeyID; /* x509v3 authority key identifier */ + PRBool isRoot; /* cert is the end of a chain */ + + /* these fields are used by client GUI code to keep track of ssl sockets + * that are blocked waiting on GUI feedback related to this cert. + * XXX - these should be moved into some sort of application specific + * data structure. They are only used by the browser right now. + */ + union { + void *apointer; /* was struct SECSocketNode* authsocketlist */ + struct { + unsigned int hasUnsupportedCriticalExt : 1; + /* add any new option bits needed here */ + } bits; + } options; + int series; /* was int authsocketcount; record the series of the pkcs11ID */ + + /* This is PKCS #11 stuff. */ + PK11SlotInfo *slot; /*if this cert came of a token, which is it*/ + CK_OBJECT_HANDLE pkcs11ID; /*and which object on that token is it */ + PRBool ownSlot; /*true if the cert owns the slot reference */ + /* These fields are used in nssckbi/builtins CAs. */ + CERTCertDistrust *distrust; +}; +#define SEC_CERTIFICATE_VERSION_1 0 /* default created */ +#define SEC_CERTIFICATE_VERSION_2 1 /* v2 */ +#define SEC_CERTIFICATE_VERSION_3 2 /* v3 extensions */ + +#define SEC_CRL_VERSION_1 0 /* default */ +#define SEC_CRL_VERSION_2 1 /* v2 extensions */ + +/* + * used to identify class of cert in mime stream code + */ +#define SEC_CERT_CLASS_CA 1 +#define SEC_CERT_CLASS_SERVER 2 +#define SEC_CERT_CLASS_USER 3 +#define SEC_CERT_CLASS_EMAIL 4 + +struct CERTDERCertsStr { + PLArenaPool *arena; + int numcerts; + SECItem *rawCerts; +}; + +/* +** A PKCS ? Attribute +** XXX this is duplicated through out the code, it *should* be moved +** to a central location. Where would be appropriate? +*/ +struct CERTAttributeStr { + SECItem attrType; + SECItem **attrValue; +}; + +/* +** A PKCS#10 certificate-request object (the unsigned form) +*/ +struct CERTCertificateRequestStr { + PLArenaPool *arena; + SECItem version; + CERTName subject; + CERTSubjectPublicKeyInfo subjectPublicKeyInfo; + CERTAttribute **attributes; +}; +#define SEC_CERTIFICATE_REQUEST_VERSION 0 /* what we *create* */ + +/* +** A certificate list object. +*/ +struct CERTCertificateListStr { + SECItem *certs; + int len; /* number of certs */ + PLArenaPool *arena; +}; + +struct CERTCertListNodeStr { + PRCList links; + CERTCertificate *cert; + void *appData; +}; + +struct CERTCertListStr { + PRCList list; + PLArenaPool *arena; +}; + +#define CERT_LIST_HEAD(l) ((CERTCertListNode *)PR_LIST_HEAD(&l->list)) +#define CERT_LIST_TAIL(l) ((CERTCertListNode *)PR_LIST_TAIL(&l->list)) +#define CERT_LIST_NEXT(n) ((CERTCertListNode *)n->links.next) +#define CERT_LIST_END(n, l) (((void *)n) == ((void *)&l->list)) +#define CERT_LIST_EMPTY(l) CERT_LIST_END(CERT_LIST_HEAD(l), l) + +struct CERTCrlEntryStr { + SECItem serialNumber; + SECItem revocationDate; + CERTCertExtension **extensions; +}; + +struct CERTCrlStr { + PLArenaPool *arena; + SECItem version; + SECAlgorithmID signatureAlg; + SECItem derName; + CERTName name; + SECItem lastUpdate; + SECItem nextUpdate; /* optional for x.509 CRL */ + CERTCrlEntry **entries; + CERTCertExtension **extensions; + /* can't add anything there for binary backwards compatibility reasons */ +}; + +struct CERTCrlKeyStr { + SECItem derName; + SECItem dummy; /* The decoder can not skip a primitive, + this serves as a place holder for the + decoder to finish its task only + */ +}; + +struct CERTSignedCrlStr { + PLArenaPool *arena; + CERTCrl crl; + void *reserved1; + PRBool reserved2; + PRBool isperm; + PRBool istemp; + int referenceCount; + CERTCertDBHandle *dbhandle; + CERTSignedData signatureWrap; /* XXX */ + char *url; + SECItem *derCrl; + PK11SlotInfo *slot; + CK_OBJECT_HANDLE pkcs11ID; + void *opaque; /* do not touch */ +}; + +struct CERTCrlHeadNodeStr { + PLArenaPool *arena; + CERTCertDBHandle *dbhandle; + CERTCrlNode *first; + CERTCrlNode *last; +}; + +struct CERTCrlNodeStr { + CERTCrlNode *next; + int type; + CERTSignedCrl *crl; +}; + +/* + * Array of X.500 Distinguished Names + */ +struct CERTDistNamesStr { + PLArenaPool *arena; + int nnames; + SECItem *names; + void *head; /* private */ +}; + +/* + * NS_CERT_TYPE defines are used in two areas: + * 1) The old NSS Cert Type Extension, which is a certificate extension in the + * actual cert. It was created before the x509 Extended Key Usage Extension, + * which has now taken over it's function. This field is only 8 bits wide + * 2) The nsCertType entry in the CERTCertificate structure. This field is + * 32 bits wide. + * Any entries in this table greater than 0x80 will not be able to be encoded + * in an NSS Cert Type Extension, but can still be represented internally in + * the nsCertType field. + */ +#define NS_CERT_TYPE_IPSEC_CA (0x200) /* outside the NS Cert Type Extenstion */ +#define NS_CERT_TYPE_IPSEC (0x100) /* outside the NS Cert Type Extenstion */ +#define NS_CERT_TYPE_SSL_CLIENT (0x80) /* bit 0 */ +#define NS_CERT_TYPE_SSL_SERVER (0x40) /* bit 1 */ +#define NS_CERT_TYPE_EMAIL (0x20) /* bit 2 */ +#define NS_CERT_TYPE_OBJECT_SIGNING (0x10) /* bit 3 */ +#define NS_CERT_TYPE_RESERVED (0x08) /* bit 4 */ +#define NS_CERT_TYPE_SSL_CA (0x04) /* bit 5 */ +#define NS_CERT_TYPE_EMAIL_CA (0x02) /* bit 6 */ +#define NS_CERT_TYPE_OBJECT_SIGNING_CA (0x01) /* bit 7 */ + +#define EXT_KEY_USAGE_TIME_STAMP (0x8000) +#define EXT_KEY_USAGE_STATUS_RESPONDER (0x4000) + +#define NS_CERT_TYPE_APP \ + (NS_CERT_TYPE_SSL_CLIENT | NS_CERT_TYPE_SSL_SERVER | NS_CERT_TYPE_EMAIL | \ + NS_CERT_TYPE_IPSEC | NS_CERT_TYPE_OBJECT_SIGNING) + +#define NS_CERT_TYPE_CA \ + (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA | \ + NS_CERT_TYPE_OBJECT_SIGNING_CA | EXT_KEY_USAGE_STATUS_RESPONDER | \ + NS_CERT_TYPE_IPSEC_CA) +typedef enum SECCertUsageEnum { + certUsageSSLClient = 0, + certUsageSSLServer = 1, + certUsageSSLServerWithStepUp = 2, + certUsageSSLCA = 3, + certUsageEmailSigner = 4, + certUsageEmailRecipient = 5, + certUsageObjectSigner = 6, + certUsageUserCertImport = 7, + certUsageVerifyCA = 8, + certUsageProtectedObjectSigner = 9, + certUsageStatusResponder = 10, + certUsageAnyCA = 11, + certUsageIPsec = 12 +} SECCertUsage; + +typedef PRInt64 SECCertificateUsage; + +#define certificateUsageCheckAllUsages (0x0000) +#define certificateUsageSSLClient (0x0001) +#define certificateUsageSSLServer (0x0002) +#define certificateUsageSSLServerWithStepUp (0x0004) +#define certificateUsageSSLCA (0x0008) +#define certificateUsageEmailSigner (0x0010) +#define certificateUsageEmailRecipient (0x0020) +#define certificateUsageObjectSigner (0x0040) +#define certificateUsageUserCertImport (0x0080) +#define certificateUsageVerifyCA (0x0100) +#define certificateUsageProtectedObjectSigner (0x0200) +#define certificateUsageStatusResponder (0x0400) +#define certificateUsageAnyCA (0x0800) +#define certificateUsageIPsec (0x1000) + +#define certificateUsageHighest certificateUsageIPsec + +/* + * Does the cert belong to the user, a peer, or a CA. + */ +typedef enum CERTCertOwnerEnum { + certOwnerUser = 0, + certOwnerPeer = 1, + certOwnerCA = 2 +} CERTCertOwner; + +/* + * This enum represents the state of validity times of a certificate + */ +typedef enum SECCertTimeValidityEnum { + secCertTimeValid = 0, + secCertTimeExpired = 1, + secCertTimeNotValidYet = 2, + secCertTimeUndetermined = 3 /* validity could not be decoded from the + cert, most likely because it was NULL */ +} SECCertTimeValidity; + +/* + * This is used as return status in functions that compare the validity + * periods of two certificates A and B, currently only + * CERT_CompareValidityTimes. + */ + +typedef enum CERTCompareValidityStatusEnum { + certValidityUndetermined = 0, /* the function is unable to select one cert + over another */ + certValidityChooseB = 1, /* cert B should be preferred */ + certValidityEqual = 2, /* both certs have the same validity period */ + certValidityChooseA = 3 /* cert A should be preferred */ +} CERTCompareValidityStatus; + +/* + * Interface for getting certificate nickname strings out of the database + */ + +/* these are values for the what argument below */ +#define SEC_CERT_NICKNAMES_ALL 1 +#define SEC_CERT_NICKNAMES_USER 2 +#define SEC_CERT_NICKNAMES_SERVER 3 +#define SEC_CERT_NICKNAMES_CA 4 + +struct CERTCertNicknamesStr { + PLArenaPool *arena; + void *head; + int numnicknames; + char **nicknames; + int what; + int totallen; +}; + +struct CERTIssuerAndSNStr { + SECItem derIssuer; + CERTName issuer; + SECItem serialNumber; +}; + +/* X.509 v3 Key Usage Extension flags */ +#define KU_DIGITAL_SIGNATURE (0x80) /* bit 0 */ +#define KU_NON_REPUDIATION (0x40) /* bit 1 */ +#define KU_KEY_ENCIPHERMENT (0x20) /* bit 2 */ +#define KU_DATA_ENCIPHERMENT (0x10) /* bit 3 */ +#define KU_KEY_AGREEMENT (0x08) /* bit 4 */ +#define KU_KEY_CERT_SIGN (0x04) /* bit 5 */ +#define KU_CRL_SIGN (0x02) /* bit 6 */ +#define KU_ENCIPHER_ONLY (0x01) /* bit 7 */ +#define KU_ALL \ + (KU_DIGITAL_SIGNATURE | KU_NON_REPUDIATION | KU_KEY_ENCIPHERMENT | \ + KU_DATA_ENCIPHERMENT | KU_KEY_AGREEMENT | KU_KEY_CERT_SIGN | \ + KU_CRL_SIGN | KU_ENCIPHER_ONLY) + +/* This value will not occur in certs. It is used internally for the case + * when either digital signature or non-repudiation is the correct value. + */ +#define KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION (0x2000) + +/* This value will not occur in certs. It is used internally for the case + * when the key type is not know ahead of time and either key agreement or + * key encipherment are the correct value based on key type + */ +#define KU_KEY_AGREEMENT_OR_ENCIPHERMENT (0x4000) + +/* internal bits that do not match bits in the x509v3 spec, but are used + * for similar purposes + */ +#define KU_NS_GOVT_APPROVED (0x8000) /*don't make part of KU_ALL!*/ +/* +* x.509 v3 Basic Constraints Extension +* If isCA is false, the pathLenConstraint is ignored. +* Otherwise, the following pathLenConstraint values will apply: +* < 0 - there is no limit to the certificate path +* 0 - CA can issues end-entity certificates only +* > 0 - the number of certificates in the certificate path is +* limited to this number +*/ +#define CERT_UNLIMITED_PATH_CONSTRAINT -2 + +struct CERTBasicConstraintsStr { + PRBool isCA; /* on if is CA */ + int pathLenConstraint; /* maximum number of certificates that can be + in the cert path. Only applies to a CA + certificate; otherwise, it's ignored. + */ +}; + +/* Maximum length of a certificate chain */ +#define CERT_MAX_CERT_CHAIN 20 + +#define CERT_MAX_SERIAL_NUMBER_BYTES 20 /* from RFC 3280 */ +#define CERT_MAX_DN_BYTES 4096 /* arbitrary */ + +/* x.509 v3 Reason Flags, used in CRLDistributionPoint Extension */ +#define RF_UNUSED (0x80) /* bit 0 */ +#define RF_KEY_COMPROMISE (0x40) /* bit 1 */ +#define RF_CA_COMPROMISE (0x20) /* bit 2 */ +#define RF_AFFILIATION_CHANGED (0x10) /* bit 3 */ +#define RF_SUPERSEDED (0x08) /* bit 4 */ +#define RF_CESSATION_OF_OPERATION (0x04) /* bit 5 */ +#define RF_CERTIFICATE_HOLD (0x02) /* bit 6 */ + +/* enum for CRL Entry Reason Code */ +typedef enum CERTCRLEntryReasonCodeEnum { + crlEntryReasonUnspecified = 0, + crlEntryReasonKeyCompromise = 1, + crlEntryReasonCaCompromise = 2, + crlEntryReasonAffiliationChanged = 3, + crlEntryReasonSuperseded = 4, + crlEntryReasonCessationOfOperation = 5, + crlEntryReasoncertificatedHold = 6, + crlEntryReasonRemoveFromCRL = 8, + crlEntryReasonPrivilegeWithdrawn = 9, + crlEntryReasonAaCompromise = 10 +} CERTCRLEntryReasonCode; + +/* If we needed to extract the general name field, use this */ +/* General Name types */ +typedef enum CERTGeneralNameTypeEnum { + certOtherName = 1, + certRFC822Name = 2, + certDNSName = 3, + certX400Address = 4, + certDirectoryName = 5, + certEDIPartyName = 6, + certURI = 7, + certIPAddress = 8, + certRegisterID = 9 +} CERTGeneralNameType; + +typedef struct OtherNameStr { + SECItem name; + SECItem oid; +} OtherName; + +struct CERTGeneralNameStr { + CERTGeneralNameType type; /* name type */ + union { + CERTName directoryName; /* distinguish name */ + OtherName OthName; /* Other Name */ + SECItem other; /* the rest of the name forms */ + } name; + SECItem derDirectoryName; /* this is saved to simplify directory name + comparison */ + PRCList l; +}; + +struct CERTGeneralNameListStr { + PLArenaPool *arena; + CERTGeneralName *name; + int refCount; + int len; + PZLock *lock; +}; + +struct CERTNameConstraintStr { + CERTGeneralName name; + SECItem DERName; + SECItem min; + SECItem max; + PRCList l; +}; + +struct CERTNameConstraintsStr { + CERTNameConstraint *permited; + CERTNameConstraint *excluded; + SECItem **DERPermited; + SECItem **DERExcluded; +}; + +/* Private Key Usage Period extension struct. */ +struct CERTPrivKeyUsagePeriodStr { + SECItem notBefore; + SECItem notAfter; + PLArenaPool *arena; +}; + +/* X.509 v3 Authority Key Identifier extension. For the authority certificate + issuer field, we only support URI now. + */ +struct CERTAuthKeyIDStr { + SECItem keyID; /* unique key identifier */ + CERTGeneralName *authCertIssuer; /* CA's issuer name. End with a NULL */ + SECItem authCertSerialNumber; /* CA's certificate serial number */ + SECItem **DERAuthCertIssuer; /* This holds the DER encoded format of + the authCertIssuer field. It is used + by the encoding engine. It should be + used as a read only field by the caller. + */ +}; + +/* x.509 v3 CRL Distributeion Point */ + +/* + * defined the types of CRL Distribution points + */ +typedef enum DistributionPointTypesEnum { + generalName = 1, /* only support this for now */ + relativeDistinguishedName = 2 +} DistributionPointTypes; + +struct CRLDistributionPointStr { + DistributionPointTypes distPointType; + union { + CERTGeneralName *fullName; + CERTRDN relativeName; + } distPoint; + SECItem reasons; + CERTGeneralName *crlIssuer; + + /* Reserved for internal use only*/ + SECItem derDistPoint; + SECItem derRelativeName; + SECItem **derCrlIssuer; + SECItem **derFullName; + SECItem bitsmap; +}; + +struct CERTCrlDistributionPointsStr { + CRLDistributionPoint **distPoints; +}; + +/* + * This structure is used to keep a log of errors when verifying + * a cert chain. This allows multiple errors to be reported all at + * once. + */ +struct CERTVerifyLogNodeStr { + CERTCertificate *cert; /* what cert had the error */ + long error; /* what error was it? */ + unsigned int depth; /* how far up the chain are we */ + void *arg; /* error specific argument */ + struct CERTVerifyLogNodeStr *next; /* next in the list */ + struct CERTVerifyLogNodeStr *prev; /* next in the list */ +}; + +struct CERTVerifyLogStr { + PLArenaPool *arena; + unsigned int count; + struct CERTVerifyLogNodeStr *head; + struct CERTVerifyLogNodeStr *tail; +}; + +struct CERTOKDomainNameStr { + CERTOKDomainName *next; + char *name; +}; + +typedef SECStatus(PR_CALLBACK *CERTStatusChecker)(CERTCertDBHandle *handle, + CERTCertificate *cert, + PRTime time, void *pwArg); + +typedef SECStatus(PR_CALLBACK *CERTStatusDestroy)(CERTStatusConfig *handle); + +struct CERTStatusConfigStr { + CERTStatusChecker statusChecker; /* NULL means no checking enabled */ + CERTStatusDestroy statusDestroy; /* enabled or no, will clean up */ + void *statusContext; /* cx specific to checking protocol */ +}; + +struct CERTAuthInfoAccessStr { + SECItem method; + SECItem derLocation; + CERTGeneralName *location; /* decoded location */ +}; + +/* This is the typedef for the callback passed to CERT_OpenCertDB() */ +/* callback to return database name based on version number */ +typedef char *(*CERTDBNameFunc)(void *arg, int dbVersion); + +/* + * types of cert packages that we can decode + */ +typedef enum CERTPackageTypeEnum { + certPackageNone = 0, + certPackageCert = 1, + certPackagePKCS7 = 2, + certPackageNSCertSeq = 3, + certPackageNSCertWrap = 4 +} CERTPackageType; + +/* + * these types are for the PKIX Certificate Policies extension + */ +typedef struct { + SECOidTag oid; + SECItem qualifierID; + SECItem qualifierValue; +} CERTPolicyQualifier; + +typedef struct { + SECOidTag oid; + SECItem policyID; + CERTPolicyQualifier **policyQualifiers; +} CERTPolicyInfo; + +typedef struct { + PLArenaPool *arena; + CERTPolicyInfo **policyInfos; +} CERTCertificatePolicies; + +typedef struct { + SECItem organization; + SECItem **noticeNumbers; +} CERTNoticeReference; + +typedef struct { + PLArenaPool *arena; + CERTNoticeReference noticeReference; + SECItem derNoticeReference; + SECItem displayText; +} CERTUserNotice; + +typedef struct { + PLArenaPool *arena; + SECItem **oids; +} CERTOidSequence; + +/* + * these types are for the PKIX Policy Mappings extension + */ +typedef struct { + SECItem issuerDomainPolicy; + SECItem subjectDomainPolicy; +} CERTPolicyMap; + +typedef struct { + PLArenaPool *arena; + CERTPolicyMap **policyMaps; +} CERTCertificatePolicyMappings; + +/* + * these types are for the PKIX inhibitAnyPolicy extension + */ +typedef struct { + SECItem inhibitAnySkipCerts; +} CERTCertificateInhibitAny; + +/* + * these types are for the PKIX Policy Constraints extension + */ +typedef struct { + SECItem explicitPolicySkipCerts; + SECItem inhibitMappingSkipCerts; +} CERTCertificatePolicyConstraints; + +/* + * These types are for the validate chain callback param. + * + * CERTChainVerifyCallback is an application-supplied callback that can be used + * to augment libpkix's certificate chain validation with additional + * application-specific checks. It may be called multiple times if there are + * multiple potentially-valid paths for the certificate being validated. This + * callback is called before revocation checking is done on the certificates in + * the given chain. + * + * - isValidChainArg contains the application-provided opaque argument + * - currentChain is the currently validated chain. It is ordered with the leaf + * certificate at the head and the trust anchor at the tail. + * + * The callback should set *chainOK = PR_TRUE and return SECSuccess if the + * certificate chain is acceptable. It should set *chainOK = PR_FALSE and + * return SECSuccess if the chain is unacceptable, to indicate that the given + * chain is bad and path building should continue. It should return SECFailure + * to indicate an fatal error that will cause path validation to fail + * immediately. + */ +typedef SECStatus (*CERTChainVerifyCallbackFunc)( + void *isChainValidArg, const CERTCertList *currentChain, PRBool *chainOK); + +/* + * Note: If extending this structure, it will be necessary to change the + * associated CERTValParamInType + */ +typedef struct { + CERTChainVerifyCallbackFunc isChainValid; + void *isChainValidArg; +} CERTChainVerifyCallback; + +/* + * these types are for the CERT_PKIX* Verification functions + * These are all optional parameters. + */ + +typedef enum { + cert_pi_end = 0, /* SPECIAL: signifies end of array of + * CERTValParam* */ + cert_pi_nbioContext = 1, /* specify a non-blocking IO context used to + * resume a session. If this argument is + * specified, no other arguments should be. + * Specified in value.pointer.p. If the + * operation completes the context will be + * freed. */ + cert_pi_nbioAbort = 2, /* specify a non-blocking IO context for an + * existing operation which the caller wants + * to abort. If this argument is + * specified, no other arguments should be. + * Specified in value.pointer.p. If the + * operation succeeds the context will be + * freed. */ + cert_pi_certList = 3, /* specify the chain to validate against. If + * this value is given, then the path + * construction step in the validation is + * skipped. Specified in value.pointer.chain */ + cert_pi_policyOID = 4, /* validate certificate for policy OID. + * Specified in value.array.oids. Cert must + * be good for at least one OID in order + * to validate. Default is that the user is not + * concerned about certificate policy. */ + cert_pi_policyFlags = 5, /* flags for each policy specified in policyOID. + * Specified in value.scalar.ul. Policy flags + * apply to all specified oids. + * Use CERT_POLICY_FLAG_* macros below. If not + * specified policy flags default to 0 */ + cert_pi_keyusage = 6, /* specify what the keyusages the certificate + * will be evaluated against, specified in + * value.scalar.ui. The cert must validate for + * at least one of the specified key usages. + * Values match the KU_ bit flags defined + * in this file. Default is derived from + * the 'usages' function argument */ + cert_pi_extendedKeyusage = 7, /* specify what the required extended key + * usage of the certificate. Specified as + * an array of oidTags in value.array.oids. + * The cert must validate for at least one + * of the specified extended key usages. + * If not specified, no extended key usages + * will be checked. */ + cert_pi_date = 8, /* validate certificate is valid as of date + * specified in value.scalar.time. A special + * value '0' indicates 'now'. default is '0' */ + cert_pi_revocationFlags = 9, /* Specify what revocation checking to do. + * See CERT_REV_FLAG_* macros below + * Set in value.pointer.revocation */ + cert_pi_certStores = 10, /* Bitmask of Cert Store flags (see below) + * Set in value.scalar.ui */ + cert_pi_trustAnchors = + 11, /* Specify the list of trusted roots to + * validate against. + * The default set of trusted roots, these are + * root CA certs from libnssckbi.so or CA + * certs trusted by user, are used in any of + * the following cases: + * * when the parameter is not set. + * * when the list of trust anchors is + * empty. + * Note that this handling can be further + * altered by altering the + * cert_pi_useOnlyTrustAnchors flag + * Specified in value.pointer.chain */ + cert_pi_useAIACertFetch = 12, /* Enables cert fetching using AIA extension. + * In NSS 3.12.1 or later. Default is off. + * Value is in value.scalar.b */ + cert_pi_chainVerifyCallback = 13, + /* The callback container for doing extra + * validation on the currently calculated chain. + * Value is in value.pointer.chainVerifyCallback */ + cert_pi_useOnlyTrustAnchors = 14, + /* If true, disables trusting any + * certificates other than the ones passed in via cert_pi_trustAnchors. + * If false, then the certificates specified via cert_pi_trustAnchors + * will be combined with the pre-existing trusted roots, but only + * for the certificate validation being performed. + * If no value has been supplied via cert_pi_trustAnchors, this has + * no effect. + * The default value is true, meaning if this is not supplied, only + * trust anchors supplied via cert_pi_trustAnchors are trusted. + * Specified in value.scalar.b */ + cert_pi_max /* SPECIAL: signifies maximum allowed value, + * can increase in future releases */ +} CERTValParamInType; + +/* + * for all out parameters: + * out parameters are only returned if the caller asks for them in + * the CERTValOutParam array. Caller is responsible for the CERTValOutParam + * array itself. The pkix verify function will allocate and other arrays + * pointers, or objects. The Caller is responsible for freeing those results. + * If SECWouldBlock is returned, only cert_pi_nbioContext is returned. + */ +typedef enum { + cert_po_end = 0, /* SPECIAL: signifies end of array of + * CERTValParam* */ + cert_po_nbioContext = 1, /* Return a nonblocking context. If no + * non-blocking context is specified, then + * blocking IO will be used. + * Returned in value.pointer.p. The context is + * freed after an abort or a complete operation. + * This value is only returned on SECWouldBlock. + */ + cert_po_trustAnchor = 2, /* Return the trust anchor for the chain that + * was validated. Returned in + * value.pointer.cert, this value is only + * returned on SECSuccess. */ + cert_po_certList = 3, /* Return the entire chain that was validated. + * Returned in value.pointer.certList. If no + * chain could be constructed, this value + * would be NULL. */ + cert_po_policyOID = 4, /* Return the policies that were found to be + * valid. Returned in value.array.oids as an + * array. This is only returned on + * SECSuccess. */ + cert_po_errorLog = 5, /* Return a log of problems with the chain. + * Returned in value.pointer.log */ + cert_po_usages = 6, /* Return what usages the certificate is valid + for. Returned in value.scalar.usages */ + cert_po_keyUsage = 7, /* Return what key usages the certificate + * is valid for. + * Returned in value.scalar.usage */ + cert_po_extendedKeyusage = 8, /* Return what extended key usages the + * certificate is valid for. + * Returned in value.array.oids */ + cert_po_max /* SPECIAL: signifies maximum allowed value, + * can increase in future releases */ + +} CERTValParamOutType; + +typedef enum { + cert_revocation_method_crl = 0, + cert_revocation_method_ocsp, + cert_revocation_method_count +} CERTRevocationMethodIndex; + +/* + * The following flags are supposed to be used to control bits in + * each integer contained in the array pointed to be: + * CERTRevocationTests.cert_rev_flags_per_method + * All Flags are prefixed by CERT_REV_M_, where _M_ indicates + * this is a method dependent flag. + */ + +/* + * Whether or not to use a method for revocation testing. + * If set to "do not test", then all other flags are ignored. + */ +#define CERT_REV_M_DO_NOT_TEST_USING_THIS_METHOD 0UL +#define CERT_REV_M_TEST_USING_THIS_METHOD 1UL + +/* + * Whether or not NSS is allowed to attempt to fetch fresh information + * from the network. + * (Although fetching will never happen if fresh information for the + * method is already locally available.) + */ +#define CERT_REV_M_ALLOW_NETWORK_FETCHING 0UL +#define CERT_REV_M_FORBID_NETWORK_FETCHING 2UL + +/* + * Example for an implicit default source: + * The globally configured default OCSP responder. + * IGNORE means: + * ignore the implicit default source, whether it's configured or not. + * ALLOW means: + * if an implicit default source is configured, + * then it overrides any available or missing source in the cert. + * if no implicit default source is configured, + * then we continue to use what's available (or not available) + * in the certs. + */ +#define CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE 0UL +#define CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE 4UL + +/* + * Defines the behavior if no fresh information is available, + * fetching from the network is allowed, but the source of revocation + * information is unknown (even after considering implicit sources, + * if allowed by other flags). + * SKIPT_TEST means: + * We ignore that no fresh information is available and + * skip this test. + * REQUIRE_INFO means: + * We still require that fresh information is available. + * Other flags define what happens on missing fresh info. + */ +#define CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE 0UL +#define CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE 8UL + +/* + * Defines the behavior if we are unable to obtain fresh information. + * INGORE means: + * Return "cert status unknown" + * FAIL means: + * Return "cert revoked". + */ +#define CERT_REV_M_IGNORE_MISSING_FRESH_INFO 0UL +#define CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO 16UL + +/* + * What should happen if we were able to find fresh information using + * this method, and the data indicated the cert is good? + * STOP_TESTING means: + * Our success is sufficient, do not continue testing + * other methods. + * CONTINUE_TESTING means: + * We will continue and test the next allowed + * specified method. + */ +#define CERT_REV_M_STOP_TESTING_ON_FRESH_INFO 0UL +#define CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO 32UL + +/* When this flag is used, libpkix will never attempt to use the GET HTTP + * method for OCSP requests; it will always use POST. + */ +#define CERT_REV_M_FORCE_POST_METHOD_FOR_OCSP 64UL + +/* + * The following flags are supposed to be used to control bits in + * CERTRevocationTests.cert_rev_method_independent_flags + * All Flags are prefixed by CERT_REV_M_, where _M_ indicates + * this is a method independent flag. + */ + +/* + * This defines the order to checking. + * EACH_METHOD_SEPARATELY means: + * Do all tests related to a particular allowed method + * (both local information and network fetching) in a single step. + * Only after testing for a particular method is done, + * then switching to the next method will happen. + * ALL_LOCAL_INFORMATION_FIRST means: + * Start by testing the information for all allowed methods + * which are already locally available. Only after that is done + * consider to fetch from the network (as allowed by other flags). + */ +#define CERT_REV_MI_TEST_EACH_METHOD_SEPARATELY 0UL +#define CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST 1UL + +/* + * Use this flag to specify that it's necessary that fresh information + * is available for at least one of the allowed methods, but it's + * irrelevant which of the mechanisms succeeded. + * NO_OVERALL_INFO_REQUIREMENT means: + * We strictly follow the requirements for each individual method. + * REQUIRE_SOME_FRESH_INFO_AVAILABLE means: + * After the individual tests have been executed, we must have + * been able to find fresh information using at least one method. + * If we were unable to find fresh info, it's a failure. + * This setting overrides the CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO + * flag on all methods. + */ +#define CERT_REV_MI_NO_OVERALL_INFO_REQUIREMENT 0UL +#define CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE 2UL + +typedef struct { + /* + * The size of the array that cert_rev_flags_per_method points to, + * meaning, the number of methods that are known and defined + * by the caller. + */ + PRUint32 number_of_defined_methods; + + /* + * A pointer to an array of integers. + * Each integer defines revocation checking for a single method, + * by having individual CERT_REV_M_* bits set or not set. + * The meaning of index numbers into this array are defined by + * enum CERTRevocationMethodIndex + * The size of the array must be specified by the caller in the separate + * variable number_of_defined_methods. + * The size of the array may be smaller than + * cert_revocation_method_count, it can happen if a caller + * is not yet aware of the latest revocation methods + * (or does not want to use them). + */ + PRUint64 *cert_rev_flags_per_method; + + /* + * How many preferred methods are specified? + * This is equivalent to the size of the array that + * preferred_methods points to. + * It's allowed to set this value to zero, + * then NSS will decide which methods to prefer. + */ + PRUint32 number_of_preferred_methods; + + /* Array that may specify an optional order of preferred methods. + * Each array entry shall contain a method identifier as defined + * by CERTRevocationMethodIndex. + * The entry at index [0] specifies the method with highest preference. + * These methods will be tested first for locally available information. + * Methods allowed for downloading will be attempted in the same order. + */ + CERTRevocationMethodIndex *preferred_methods; + + /* + * An integer which defines certain aspects of revocation checking + * (independent of individual methods) by having individual + * CERT_REV_MI_* bits set or not set. + */ + PRUint64 cert_rev_method_independent_flags; +} CERTRevocationTests; + +typedef struct { + CERTRevocationTests leafTests; + CERTRevocationTests chainTests; +} CERTRevocationFlags; + +typedef struct CERTValParamInValueStr { + union { + PRBool b; + PRInt32 i; + PRUint32 ui; + PRInt64 l; + PRUint64 ul; + PRTime time; + } scalar; + union { + const void *p; + const char *s; + const CERTCertificate *cert; + const CERTCertList *chain; + const CERTRevocationFlags *revocation; + const CERTChainVerifyCallback *chainVerifyCallback; + } pointer; + union { + const PRInt32 *pi; + const PRUint32 *pui; + const PRInt64 *pl; + const PRUint64 *pul; + const SECOidTag *oids; + } array; + int arraySize; +} CERTValParamInValue; + +typedef struct CERTValParamOutValueStr { + union { + PRBool b; + PRInt32 i; + PRUint32 ui; + PRInt64 l; + PRUint64 ul; + SECCertificateUsage usages; + } scalar; + union { + void *p; + char *s; + CERTVerifyLog *log; + CERTCertificate *cert; + CERTCertList *chain; + } pointer; + union { + void *p; + SECOidTag *oids; + } array; + int arraySize; +} CERTValParamOutValue; + +typedef struct { + CERTValParamInType type; + CERTValParamInValue value; +} CERTValInParam; + +typedef struct { + CERTValParamOutType type; + CERTValParamOutValue value; +} CERTValOutParam; + +/* + * Levels of standards conformance strictness for CERT_NameToAsciiInvertible + */ +typedef enum CertStrictnessLevels { + CERT_N2A_READABLE = 0, /* maximum human readability */ + CERT_N2A_STRICT = 10, /* strict RFC compliance */ + CERT_N2A_INVERTIBLE = 20 /* maximum invertibility, + all DirectoryStrings encoded in hex */ +} CertStrictnessLevel; + +/* + * policy flag defines + */ +#define CERT_POLICY_FLAG_NO_MAPPING 1 +#define CERT_POLICY_FLAG_EXPLICIT 2 +#define CERT_POLICY_FLAG_NO_ANY 4 + +/* + * CertStore flags + */ +#define CERT_ENABLE_LDAP_FETCH 1 +#define CERT_ENABLE_HTTP_FETCH 2 + +/* This functin pointer type may be used for any function that takes + * a CERTCertificate * and returns an allocated string, which must be + * freed by a call to PORT_Free. + */ +typedef char *(*CERT_StringFromCertFcn)(CERTCertificate *cert); + +/* XXX Lisa thinks the template declarations belong in cert.h, not here? */ + +#include "secasn1t.h" /* way down here because I expect template stuff to + * move out of here anyway */ + +SEC_BEGIN_PROTOS + +extern const SEC_ASN1Template CERT_CertificateRequestTemplate[]; +extern const SEC_ASN1Template CERT_CertificateTemplate[]; +extern const SEC_ASN1Template SEC_SignedCertificateTemplate[]; +extern const SEC_ASN1Template CERT_CertExtensionTemplate[]; +extern const SEC_ASN1Template CERT_SequenceOfCertExtensionTemplate[]; +extern const SEC_ASN1Template SECKEY_PublicKeyTemplate[]; +extern const SEC_ASN1Template CERT_SubjectPublicKeyInfoTemplate[]; +extern const SEC_ASN1Template CERT_TimeChoiceTemplate[]; +extern const SEC_ASN1Template CERT_ValidityTemplate[]; +extern const SEC_ASN1Template CERT_PublicKeyAndChallengeTemplate[]; +extern const SEC_ASN1Template SEC_CertSequenceTemplate[]; + +extern const SEC_ASN1Template CERT_IssuerAndSNTemplate[]; +extern const SEC_ASN1Template CERT_NameTemplate[]; +extern const SEC_ASN1Template CERT_SetOfSignedCrlTemplate[]; +extern const SEC_ASN1Template CERT_RDNTemplate[]; +extern const SEC_ASN1Template CERT_SignedDataTemplate[]; +extern const SEC_ASN1Template CERT_CrlTemplate[]; +extern const SEC_ASN1Template CERT_SignedCrlTemplate[]; + +/* +** XXX should the attribute stuff be centralized for all of ns/security? +*/ +extern const SEC_ASN1Template CERT_AttributeTemplate[]; +extern const SEC_ASN1Template CERT_SetOfAttributeTemplate[]; + +/* These functions simply return the address of the above-declared templates. +** This is necessary for Windows DLLs. Sigh. +*/ +SEC_ASN1_CHOOSER_DECLARE(CERT_CertificateRequestTemplate) +SEC_ASN1_CHOOSER_DECLARE(CERT_CertificateTemplate) +SEC_ASN1_CHOOSER_DECLARE(CERT_CrlTemplate) +SEC_ASN1_CHOOSER_DECLARE(CERT_IssuerAndSNTemplate) +SEC_ASN1_CHOOSER_DECLARE(CERT_NameTemplate) +SEC_ASN1_CHOOSER_DECLARE(CERT_SequenceOfCertExtensionTemplate) +SEC_ASN1_CHOOSER_DECLARE(CERT_SetOfSignedCrlTemplate) +SEC_ASN1_CHOOSER_DECLARE(CERT_SignedDataTemplate) +SEC_ASN1_CHOOSER_DECLARE(CERT_SubjectPublicKeyInfoTemplate) +SEC_ASN1_CHOOSER_DECLARE(SEC_SignedCertificateTemplate) +SEC_ASN1_CHOOSER_DECLARE(CERT_SignedCrlTemplate) +SEC_ASN1_CHOOSER_DECLARE(CERT_TimeChoiceTemplate) + +SEC_END_PROTOS + +#endif /* _CERTT_H_ */ diff --git a/security/nss/lib/certdb/certv3.c b/security/nss/lib/certdb/certv3.c new file mode 100644 index 0000000000..f00b88f1d7 --- /dev/null +++ b/security/nss/lib/certdb/certv3.c @@ -0,0 +1,222 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Code for dealing with X509.V3 extensions. + */ + +#include "cert.h" +#include "secitem.h" +#include "secoid.h" +#include "secder.h" +#include "secasn1.h" +#include "certxutl.h" +#include "secerr.h" + +SECStatus +CERT_FindCertExtensionByOID(CERTCertificate *cert, SECItem *oid, SECItem *value) +{ + return (cert_FindExtensionByOID(cert->extensions, oid, value)); +} + +SECStatus +CERT_FindCertExtension(const CERTCertificate *cert, int tag, SECItem *value) +{ + return (cert_FindExtension(cert->extensions, tag, value)); +} + +static void +SetExts(void *object, CERTCertExtension **exts) +{ + CERTCertificate *cert = (CERTCertificate *)object; + + cert->extensions = exts; + DER_SetUInteger(cert->arena, &(cert->version), SEC_CERTIFICATE_VERSION_3); +} + +void * +CERT_StartCertExtensions(CERTCertificate *cert) +{ + return (cert_StartExtensions((void *)cert, cert->arena, SetExts)); +} + +/* + * get the value of the Netscape Certificate Type Extension + */ +SECStatus +CERT_FindNSCertTypeExtension(CERTCertificate *cert, SECItem *retItem) +{ + + return (CERT_FindBitStringExtension( + cert->extensions, SEC_OID_NS_CERT_EXT_CERT_TYPE, retItem)); +} + +/* + * get the value of a string type extension + */ +char * +CERT_FindNSStringExtension(CERTCertificate *cert, int oidtag) +{ + SECItem wrapperItem, tmpItem = { siBuffer, 0 }; + SECStatus rv; + PLArenaPool *arena = NULL; + char *retstring = NULL; + + wrapperItem.data = NULL; + tmpItem.data = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if (!arena) { + goto loser; + } + + rv = cert_FindExtension(cert->extensions, oidtag, &wrapperItem); + if (rv != SECSuccess) { + goto loser; + } + + rv = SEC_QuickDERDecodeItem( + arena, &tmpItem, SEC_ASN1_GET(SEC_IA5StringTemplate), &wrapperItem); + + if (rv != SECSuccess) { + goto loser; + } + + retstring = (char *)PORT_Alloc(tmpItem.len + 1); + if (retstring == NULL) { + goto loser; + } + + PORT_Memcpy(retstring, tmpItem.data, tmpItem.len); + retstring[tmpItem.len] = '\0'; + +loser: + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + + if (wrapperItem.data) { + PORT_Free(wrapperItem.data); + } + + return (retstring); +} + +/* + * get the value of the X.509 v3 Key Usage Extension + */ +SECStatus +CERT_FindKeyUsageExtension(CERTCertificate *cert, SECItem *retItem) +{ + + return (CERT_FindBitStringExtension(cert->extensions, + SEC_OID_X509_KEY_USAGE, retItem)); +} + +/* + * get the value of the X.509 v3 Key Usage Extension + */ +SECStatus +CERT_FindSubjectKeyIDExtension(CERTCertificate *cert, SECItem *retItem) +{ + + SECStatus rv; + SECItem encodedValue = { siBuffer, NULL, 0 }; + SECItem decodedValue = { siBuffer, NULL, 0 }; + + rv = cert_FindExtension(cert->extensions, SEC_OID_X509_SUBJECT_KEY_ID, + &encodedValue); + if (rv == SECSuccess) { + PORTCheapArenaPool tmpArena; + PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); + rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &decodedValue, + SEC_ASN1_GET(SEC_OctetStringTemplate), + &encodedValue); + if (rv == SECSuccess) { + rv = SECITEM_CopyItem(NULL, retItem, &decodedValue); + } + PORT_DestroyCheapArena(&tmpArena); + } + SECITEM_FreeItem(&encodedValue, PR_FALSE); + return rv; +} + +SECStatus +CERT_FindBasicConstraintExten(CERTCertificate *cert, + CERTBasicConstraints *value) +{ + SECItem encodedExtenValue; + SECStatus rv; + + encodedExtenValue.data = NULL; + encodedExtenValue.len = 0; + + rv = cert_FindExtension(cert->extensions, SEC_OID_X509_BASIC_CONSTRAINTS, + &encodedExtenValue); + if (rv != SECSuccess) { + return (rv); + } + + rv = CERT_DecodeBasicConstraintValue(value, &encodedExtenValue); + + /* free the raw extension data */ + PORT_Free(encodedExtenValue.data); + encodedExtenValue.data = NULL; + + return (rv); +} + +CERTAuthKeyID * +CERT_FindAuthKeyIDExten(PLArenaPool *arena, CERTCertificate *cert) +{ + SECItem encodedExtenValue; + SECStatus rv; + CERTAuthKeyID *ret; + + encodedExtenValue.data = NULL; + encodedExtenValue.len = 0; + + rv = cert_FindExtension(cert->extensions, SEC_OID_X509_AUTH_KEY_ID, + &encodedExtenValue); + if (rv != SECSuccess) { + return (NULL); + } + + ret = CERT_DecodeAuthKeyID(arena, &encodedExtenValue); + + PORT_Free(encodedExtenValue.data); + encodedExtenValue.data = NULL; + + return (ret); +} + +SECStatus +CERT_CheckCertUsage(CERTCertificate *cert, unsigned char usage) +{ + SECItem keyUsage; + SECStatus rv; + + /* There is no extension, v1 or v2 certificate */ + if (cert->extensions == NULL) { + return (SECSuccess); + } + + keyUsage.data = NULL; + + /* This code formerly ignored the Key Usage extension if it was + ** marked non-critical. That was wrong. Since we do understand it, + ** we are obligated to honor it, whether or not it is critical. + */ + rv = CERT_FindKeyUsageExtension(cert, &keyUsage); + if (rv == SECFailure) { + rv = (PORT_GetError() == SEC_ERROR_EXTENSION_NOT_FOUND) ? SECSuccess + : SECFailure; + } else if (!keyUsage.data || !keyUsage.len || !(keyUsage.data[0] & usage)) { + PORT_SetError(SEC_ERROR_CERT_USAGES_INVALID); + rv = SECFailure; + } + PORT_Free(keyUsage.data); + return (rv); +} diff --git a/security/nss/lib/certdb/certxutl.c b/security/nss/lib/certdb/certxutl.c new file mode 100644 index 0000000000..5db2eb9ef0 --- /dev/null +++ b/security/nss/lib/certdb/certxutl.c @@ -0,0 +1,496 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Certificate Extensions handling code + * + */ + +#include "cert.h" +#include "secitem.h" +#include "secoid.h" +#include "secder.h" +#include "secasn1.h" +#include "certxutl.h" +#include "secerr.h" + +#ifdef OLD +#include "ocspti.h" /* XXX a better extensions interface would not + * require knowledge of data structures of callers */ +#endif + +static CERTCertExtension * +GetExtension(CERTCertExtension **extensions, SECItem *oid) +{ + CERTCertExtension **exts; + CERTCertExtension *ext = NULL; + SECComparison comp; + + exts = extensions; + + if (exts) { + while (*exts) { + ext = *exts; + comp = SECITEM_CompareItem(oid, &ext->id); + if (comp == SECEqual) + break; + + exts++; + } + return (*exts ? ext : NULL); + } + return (NULL); +} + +SECStatus +cert_FindExtensionByOID(CERTCertExtension **extensions, SECItem *oid, + SECItem *value) +{ + CERTCertExtension *ext; + SECStatus rv = SECSuccess; + + ext = GetExtension(extensions, oid); + if (ext == NULL) { + PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND); + return (SECFailure); + } + if (value) + rv = SECITEM_CopyItem(NULL, value, &ext->value); + return (rv); +} + +SECStatus +CERT_GetExtenCriticality(CERTCertExtension **extensions, int tag, + PRBool *isCritical) +{ + CERTCertExtension *ext; + SECOidData *oid; + + if (!isCritical) + return (SECSuccess); + + /* find the extension in the extensions list */ + oid = SECOID_FindOIDByTag((SECOidTag)tag); + if (!oid) { + return (SECFailure); + } + ext = GetExtension(extensions, &oid->oid); + if (ext == NULL) { + PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND); + return (SECFailure); + } + + /* If the criticality is omitted, then it is false by default. + ex->critical.data is NULL */ + if (ext->critical.data == NULL) + *isCritical = PR_FALSE; + else + *isCritical = (ext->critical.data[0] == 0xff) ? PR_TRUE : PR_FALSE; + return (SECSuccess); +} + +SECStatus +cert_FindExtension(CERTCertExtension **extensions, int tag, SECItem *value) +{ + SECOidData *oid; + + oid = SECOID_FindOIDByTag((SECOidTag)tag); + if (!oid) { + return (SECFailure); + } + + return (cert_FindExtensionByOID(extensions, &oid->oid, value)); +} + +typedef struct _extNode { + struct _extNode *next; + CERTCertExtension *ext; +} extNode; + +typedef struct { + void (*setExts)(void *object, CERTCertExtension **exts); + void *object; + PLArenaPool *ownerArena; + PLArenaPool *arena; + extNode *head; + int count; +} extRec; + +/* + * cert_StartExtensions + * + * NOTE: This interface changed significantly to remove knowledge + * about callers data structures (owner objects) + */ +void * +cert_StartExtensions(void *owner, PLArenaPool *ownerArena, + void (*setExts)(void *object, CERTCertExtension **exts)) +{ + PLArenaPool *arena; + extRec *handle; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + return (0); + } + + handle = (extRec *)PORT_ArenaAlloc(arena, sizeof(extRec)); + if (!handle) { + PORT_FreeArena(arena, PR_FALSE); + return (0); + } + + handle->object = owner; + handle->ownerArena = ownerArena; + handle->setExts = setExts; + + handle->arena = arena; + handle->head = 0; + handle->count = 0; + + return (handle); +} + +static unsigned char hextrue = 0xff; + +/* + * Note - assumes that data pointed to by oid->data will not move + */ +SECStatus +CERT_AddExtensionByOID(void *exthandle, SECItem *oid, SECItem *value, + PRBool critical, PRBool copyData) +{ + CERTCertExtension *ext; + SECStatus rv; + extNode *node; + extRec *handle; + + handle = (extRec *)exthandle; + + /* allocate space for extension and list node */ + ext = (CERTCertExtension *)PORT_ArenaZAlloc(handle->ownerArena, + sizeof(CERTCertExtension)); + if (!ext) { + return (SECFailure); + } + + node = (extNode *)PORT_ArenaAlloc(handle->arena, sizeof(extNode)); + if (!node) { + return (SECFailure); + } + + /* add to list */ + node->next = handle->head; + handle->head = node; + + /* point to ext struct */ + node->ext = ext; + + /* set critical field */ + if (critical) { + ext->critical.data = (unsigned char *)&hextrue; + ext->critical.len = 1; + } + + /* set object ID of the extension and its value */ + if (copyData) { + rv = SECITEM_CopyItem(handle->ownerArena, &ext->id, oid); + if (rv) { + return (SECFailure); + } + + rv = SECITEM_CopyItem(handle->ownerArena, &ext->value, value); + if (rv) { + return (SECFailure); + } + } else { + ext->id = *oid; + ext->value = *value; + } + + handle->count++; + + return (SECSuccess); +} + +SECStatus +CERT_AddExtension(void *exthandle, int idtag, SECItem *value, PRBool critical, + PRBool copyData) +{ + SECOidData *oid; + + oid = SECOID_FindOIDByTag((SECOidTag)idtag); + if (!oid) { + return (SECFailure); + } + + return (CERT_AddExtensionByOID(exthandle, &oid->oid, value, critical, + copyData)); +} + +SECStatus +CERT_EncodeAndAddExtension(void *exthandle, int idtag, void *value, + PRBool critical, const SEC_ASN1Template *atemplate) +{ + extRec *handle; + SECItem *encitem; + + handle = (extRec *)exthandle; + + encitem = SEC_ASN1EncodeItem(handle->ownerArena, NULL, value, atemplate); + if (encitem == NULL) { + return (SECFailure); + } + + return CERT_AddExtension(exthandle, idtag, encitem, critical, PR_FALSE); +} + +void +PrepareBitStringForEncoding(SECItem *bitsmap, SECItem *value) +{ + unsigned char onebyte; + unsigned int i, len = 0; + + /* to prevent warning on some platform at compile time */ + onebyte = '\0'; + /* Get the position of the right-most turn-on bit */ + for (i = 0; i < (value->len) * 8; ++i) { + if (i % 8 == 0) + onebyte = value->data[i / 8]; + if (onebyte & 0x80) + len = i; + onebyte <<= 1; + } + bitsmap->data = value->data; + /* Add one here since we work with base 1 */ + bitsmap->len = len + 1; +} + +SECStatus +CERT_EncodeAndAddBitStrExtension(void *exthandle, int idtag, SECItem *value, + PRBool critical) +{ + SECItem bitsmap; + + PrepareBitStringForEncoding(&bitsmap, value); + return (CERT_EncodeAndAddExtension(exthandle, idtag, &bitsmap, critical, + SEC_ASN1_GET(SEC_BitStringTemplate))); +} + +SECStatus +CERT_FinishExtensions(void *exthandle) +{ + extRec *handle; + extNode *node; + CERTCertExtension **exts; + SECStatus rv = SECFailure; + + handle = (extRec *)exthandle; + + /* allocate space for extensions array */ + exts = PORT_ArenaNewArray(handle->ownerArena, CERTCertExtension *, + handle->count + 1); + if (exts == NULL) { + goto loser; + } + + /* put extensions in owner object and update its version number */ + +#ifdef OLD + switch (handle->type) { + case CertificateExtensions: + handle->owner.cert->extensions = exts; + DER_SetUInteger(ownerArena, &(handle->owner.cert->version), + SEC_CERTIFICATE_VERSION_3); + break; + case CrlExtensions: + handle->owner.crl->extensions = exts; + DER_SetUInteger(ownerArena, &(handle->owner.crl->version), + SEC_CRL_VERSION_2); + break; + case OCSPRequestExtensions: + handle->owner.request->tbsRequest->requestExtensions = exts; + break; + case OCSPSingleRequestExtensions: + handle->owner.singleRequest->singleRequestExtensions = exts; + break; + case OCSPResponseSingleExtensions: + handle->owner.singleResponse->singleExtensions = exts; + break; + } +#endif + + handle->setExts(handle->object, exts); + + /* update the version number */ + + /* copy each extension pointer */ + node = handle->head; + while (node) { + *exts = node->ext; + + node = node->next; + exts++; + } + + /* terminate the array of extensions */ + *exts = 0; + + rv = SECSuccess; + +loser: + /* free working arena */ + PORT_FreeArena(handle->arena, PR_FALSE); + return rv; +} + +SECStatus +CERT_MergeExtensions(void *exthandle, CERTCertExtension **extensions) +{ + CERTCertExtension *ext; + SECStatus rv = SECSuccess; + SECOidTag tag; + extNode *node; + extRec *handle = exthandle; + + if (!exthandle || !extensions) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + while ((ext = *extensions++) != NULL) { + tag = SECOID_FindOIDTag(&ext->id); + for (node = handle->head; node != NULL; node = node->next) { + if (tag == 0) { + if (SECITEM_ItemsAreEqual(&ext->id, &node->ext->id)) + break; + } else { + if (SECOID_FindOIDTag(&node->ext->id) == tag) { + break; + } + } + } + if (node == NULL) { + PRBool critical = (ext->critical.len != 0 && + ext->critical.data[ext->critical.len - 1] != 0); + if (critical && tag == SEC_OID_UNKNOWN) { + PORT_SetError(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION); + rv = SECFailure; + break; + } + /* add to list */ + rv = CERT_AddExtensionByOID(exthandle, &ext->id, &ext->value, + critical, PR_TRUE); + if (rv != SECSuccess) + break; + } + } + return rv; +} + +/* + * get the value of the Netscape Certificate Type Extension + */ +SECStatus +CERT_FindBitStringExtension(CERTCertExtension **extensions, int tag, + SECItem *retItem) +{ + SECItem wrapperItem, tmpItem = { siBuffer, 0 }; + SECStatus rv; + PORTCheapArenaPool tmpArena; + + wrapperItem.data = NULL; + tmpItem.data = NULL; + + PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); + + rv = cert_FindExtension(extensions, tag, &wrapperItem); + if (rv != SECSuccess) { + goto loser; + } + + rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &tmpItem, + SEC_ASN1_GET(SEC_BitStringTemplate), + &wrapperItem); + + if (rv != SECSuccess) { + goto loser; + } + + retItem->data = (unsigned char *)PORT_ZAlloc((tmpItem.len + 7) >> 3); + if (retItem->data == NULL) { + goto loser; + } + + if (tmpItem.len > 0) { + PORT_Memcpy(retItem->data, tmpItem.data, (tmpItem.len + 7) >> 3); + } + + retItem->len = tmpItem.len; + + rv = SECSuccess; + goto done; + +loser: + rv = SECFailure; + +done: + PORT_DestroyCheapArena(&tmpArena); + + if (wrapperItem.data) { + PORT_Free(wrapperItem.data); + } + + return (rv); +} + +PRBool +cert_HasCriticalExtension(CERTCertExtension **extensions) +{ + CERTCertExtension **exts; + CERTCertExtension *ext = NULL; + PRBool hasCriticalExten = PR_FALSE; + + exts = extensions; + + if (exts) { + while (*exts) { + ext = *exts; + /* If the criticality is omitted, it's non-critical */ + if (ext->critical.data && ext->critical.data[0] == 0xff) { + hasCriticalExten = PR_TRUE; + break; + } + exts++; + } + } + return (hasCriticalExten); +} + +PRBool +cert_HasUnknownCriticalExten(CERTCertExtension **extensions) +{ + CERTCertExtension **exts; + CERTCertExtension *ext = NULL; + PRBool hasUnknownCriticalExten = PR_FALSE; + + exts = extensions; + + if (exts) { + while (*exts) { + ext = *exts; + /* If the criticality is omitted, it's non-critical. + If an extension is critical, make sure that we know + how to process the extension. + */ + if (ext->critical.data && ext->critical.data[0] == 0xff) { + if (SECOID_KnownCertExtenOID(&ext->id) == PR_FALSE) { + hasUnknownCriticalExten = PR_TRUE; + break; + } + } + exts++; + } + } + return (hasUnknownCriticalExten); +} diff --git a/security/nss/lib/certdb/certxutl.h b/security/nss/lib/certdb/certxutl.h new file mode 100644 index 0000000000..a8c76b5cfa --- /dev/null +++ b/security/nss/lib/certdb/certxutl.h @@ -0,0 +1,44 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * x.509 v3 certificate extension helper routines + * + */ + +#ifndef _CERTXUTL_H_ +#define _CERTXUTL_H_ + +#include "nspr.h" + +#ifdef OLD +typedef enum { + CertificateExtensions, + CrlExtensions, + OCSPRequestExtensions, + OCSPSingleRequestExtensions, + OCSPResponseSingleExtensions +} ExtensionsType; +#endif + +extern PRBool cert_HasCriticalExtension(CERTCertExtension **extensions); + +extern SECStatus CERT_FindBitStringExtension(CERTCertExtension **extensions, + int tag, SECItem *retItem); +extern void *cert_StartExtensions(void *owner, PLArenaPool *arena, + void (*setExts)(void *object, + CERTCertExtension **exts)); + +extern SECStatus cert_FindExtension(CERTCertExtension **extensions, int tag, + SECItem *value); + +extern SECStatus cert_FindExtensionByOID(CERTCertExtension **extensions, + SECItem *oid, SECItem *value); + +extern SECStatus cert_GetExtenCriticality(CERTCertExtension **extensions, + int tag, PRBool *isCritical); + +extern PRBool cert_HasUnknownCriticalExten(CERTCertExtension **extensions); + +#endif diff --git a/security/nss/lib/certdb/crl.c b/security/nss/lib/certdb/crl.c new file mode 100644 index 0000000000..d110841241 --- /dev/null +++ b/security/nss/lib/certdb/crl.c @@ -0,0 +1,3046 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Moved from secpkcs7.c + */ + +#include "cert.h" +#include "certi.h" +#include "secder.h" +#include "secasn1.h" +#include "secoid.h" +#include "certdb.h" +#include "certxutl.h" +#include "prtime.h" +#include "secerr.h" +#include "pk11func.h" +#include "dev.h" +#include "dev3hack.h" +#include "nssbase.h" +#if defined(DPC_RWLOCK) || defined(GLOBAL_RWLOCK) +#include "nssrwlk.h" +#endif +#include "pk11priv.h" + +const SEC_ASN1Template SEC_CERTExtensionTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCertExtension) }, + { SEC_ASN1_OBJECT_ID, offsetof(CERTCertExtension, id) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN, /* XXX DER_DEFAULT */ + offsetof(CERTCertExtension, critical) }, + { SEC_ASN1_OCTET_STRING, offsetof(CERTCertExtension, value) }, + { 0 } +}; + +static const SEC_ASN1Template SEC_CERTExtensionsTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, 0, SEC_CERTExtensionTemplate } +}; + +/* + * XXX Also, these templates need to be tested; Lisa did the obvious + * translation but they still should be verified. + */ + +const SEC_ASN1Template CERT_IssuerAndSNTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTIssuerAndSN) }, + { SEC_ASN1_SAVE, offsetof(CERTIssuerAndSN, derIssuer) }, + { SEC_ASN1_INLINE, offsetof(CERTIssuerAndSN, issuer), CERT_NameTemplate }, + { SEC_ASN1_INTEGER, offsetof(CERTIssuerAndSN, serialNumber) }, + { 0 } +}; + +SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate) +SEC_ASN1_MKSUB(CERT_TimeChoiceTemplate) + +static const SEC_ASN1Template cert_CrlKeyTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCrlKey) }, + { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof(CERTCrlKey, dummy) }, + { SEC_ASN1_SKIP }, + { SEC_ASN1_ANY, offsetof(CERTCrlKey, derName) }, + { SEC_ASN1_SKIP_REST }, + { 0 } +}; + +static const SEC_ASN1Template cert_CrlEntryTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCrlEntry) }, + { SEC_ASN1_INTEGER, offsetof(CERTCrlEntry, serialNumber) }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTCrlEntry, revocationDate), + SEC_ASN1_SUB(CERT_TimeChoiceTemplate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE_OF, + offsetof(CERTCrlEntry, extensions), SEC_CERTExtensionTemplate }, + { 0 } +}; + +const SEC_ASN1Template CERT_CrlTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCrl) }, + { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof(CERTCrl, version) }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTCrl, signatureAlg), + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, + { SEC_ASN1_SAVE, offsetof(CERTCrl, derName) }, + { SEC_ASN1_INLINE, offsetof(CERTCrl, name), CERT_NameTemplate }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTCrl, lastUpdate), + SEC_ASN1_SUB(CERT_TimeChoiceTemplate) }, + { SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL | SEC_ASN1_XTRN, + offsetof(CERTCrl, nextUpdate), SEC_ASN1_SUB(CERT_TimeChoiceTemplate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE_OF, offsetof(CERTCrl, entries), + cert_CrlEntryTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | + SEC_ASN1_EXPLICIT | 0, + offsetof(CERTCrl, extensions), SEC_CERTExtensionsTemplate }, + { 0 } +}; + +const SEC_ASN1Template CERT_CrlTemplateNoEntries[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCrl) }, + { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof(CERTCrl, version) }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTCrl, signatureAlg), + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, + { SEC_ASN1_SAVE, offsetof(CERTCrl, derName) }, + { SEC_ASN1_INLINE, offsetof(CERTCrl, name), CERT_NameTemplate }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTCrl, lastUpdate), + SEC_ASN1_SUB(CERT_TimeChoiceTemplate) }, + { SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL | SEC_ASN1_XTRN, + offsetof(CERTCrl, nextUpdate), SEC_ASN1_SUB(CERT_TimeChoiceTemplate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE_OF | + SEC_ASN1_SKIP }, /* skip entries */ + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | + SEC_ASN1_EXPLICIT | 0, + offsetof(CERTCrl, extensions), SEC_CERTExtensionsTemplate }, + { 0 } +}; + +const SEC_ASN1Template CERT_CrlTemplateEntriesOnly[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCrl) }, + { SEC_ASN1_SKIP | SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL }, + { SEC_ASN1_SKIP }, + { SEC_ASN1_SKIP }, + { SEC_ASN1_SKIP | SEC_ASN1_INLINE | SEC_ASN1_XTRN, + offsetof(CERTCrl, lastUpdate), SEC_ASN1_SUB(CERT_TimeChoiceTemplate) }, + { SEC_ASN1_SKIP | SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL | SEC_ASN1_XTRN, + offsetof(CERTCrl, nextUpdate), SEC_ASN1_SUB(CERT_TimeChoiceTemplate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE_OF, offsetof(CERTCrl, entries), + cert_CrlEntryTemplate }, /* decode entries */ + { SEC_ASN1_SKIP_REST }, + { 0 } +}; + +const SEC_ASN1Template CERT_SignedCrlTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTSignedCrl) }, + { SEC_ASN1_SAVE, offsetof(CERTSignedCrl, signatureWrap.data) }, + { SEC_ASN1_INLINE, offsetof(CERTSignedCrl, crl), CERT_CrlTemplate }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, + offsetof(CERTSignedCrl, signatureWrap.signatureAlgorithm), + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, + { SEC_ASN1_BIT_STRING, offsetof(CERTSignedCrl, signatureWrap.signature) }, + { 0 } +}; + +static const SEC_ASN1Template cert_SignedCrlTemplateNoEntries[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTSignedCrl) }, + { SEC_ASN1_SAVE, offsetof(CERTSignedCrl, signatureWrap.data) }, + { SEC_ASN1_INLINE, offsetof(CERTSignedCrl, crl), + CERT_CrlTemplateNoEntries }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, + offsetof(CERTSignedCrl, signatureWrap.signatureAlgorithm), + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, + { SEC_ASN1_BIT_STRING, offsetof(CERTSignedCrl, signatureWrap.signature) }, + { 0 } +}; + +const SEC_ASN1Template CERT_SetOfSignedCrlTemplate[] = { + { SEC_ASN1_SET_OF, 0, CERT_SignedCrlTemplate }, +}; + +/* get CRL version */ +int +cert_get_crl_version(CERTCrl* crl) +{ + /* CRL version is defaulted to v1 */ + int version = SEC_CRL_VERSION_1; + if (crl && crl->version.data != 0) { + version = (int)DER_GetUInteger(&crl->version); + } + return version; +} + +/* check the entries in the CRL */ +SECStatus +cert_check_crl_entries(CERTCrl* crl) +{ + CERTCrlEntry** entries; + CERTCrlEntry* entry; + PRBool hasCriticalExten = PR_FALSE; + SECStatus rv = SECSuccess; + + if (!crl) { + return SECFailure; + } + + if (crl->entries == NULL) { + /* CRLs with no entries are valid */ + return (SECSuccess); + } + + /* Look in the crl entry extensions. If there is a critical extension, + then the crl version must be v2; otherwise, it should be v1. + */ + entries = crl->entries; + while (*entries) { + entry = *entries; + if (entry->extensions) { + /* If there is a critical extension in the entries, then the + CRL must be of version 2. If we already saw a critical + extension, + there is no need to check the version again. + */ + if (hasCriticalExten == PR_FALSE) { + hasCriticalExten = cert_HasCriticalExtension(entry->extensions); + if (hasCriticalExten) { + if (cert_get_crl_version(crl) != SEC_CRL_VERSION_2) { + /* only CRL v2 critical extensions are supported */ + PORT_SetError(SEC_ERROR_CRL_V1_CRITICAL_EXTENSION); + rv = SECFailure; + break; + } + } + } + + /* For each entry, make sure that it does not contain an unknown + critical extension. If it does, we must reject the CRL since + we don't know how to process the extension. + */ + if (cert_HasUnknownCriticalExten(entry->extensions) == PR_TRUE) { + PORT_SetError(SEC_ERROR_CRL_UNKNOWN_CRITICAL_EXTENSION); + rv = SECFailure; + break; + } + } + ++entries; + } + return (rv); +} + +/* Check the version of the CRL. If there is a critical extension in the crl + or crl entry, then the version must be v2. Otherwise, it should be v1. If + the crl contains critical extension(s), then we must recognized the + extension's OID. + */ +SECStatus +cert_check_crl_version(CERTCrl* crl) +{ + PRBool hasCriticalExten = PR_FALSE; + int version = cert_get_crl_version(crl); + + if (version > SEC_CRL_VERSION_2) { + PORT_SetError(SEC_ERROR_CRL_INVALID_VERSION); + return (SECFailure); + } + + /* Check the crl extensions for a critial extension. If one is found, + and the version is not v2, then we are done. + */ + if (crl->extensions) { + hasCriticalExten = cert_HasCriticalExtension(crl->extensions); + if (hasCriticalExten) { + if (version != SEC_CRL_VERSION_2) { + /* only CRL v2 critical extensions are supported */ + PORT_SetError(SEC_ERROR_CRL_V1_CRITICAL_EXTENSION); + return (SECFailure); + } + /* make sure that there is no unknown critical extension */ + if (cert_HasUnknownCriticalExten(crl->extensions) == PR_TRUE) { + PORT_SetError(SEC_ERROR_CRL_UNKNOWN_CRITICAL_EXTENSION); + return (SECFailure); + } + } + } + + return (SECSuccess); +} + +/* + * Generate a database key, based on the issuer name from a + * DER crl. + */ +SECStatus +CERT_KeyFromDERCrl(PLArenaPool* arena, SECItem* derCrl, SECItem* key) +{ + SECStatus rv; + CERTSignedData sd; + CERTCrlKey crlkey; + PLArenaPool* myArena; + + if (!arena) { + /* arena needed for QuickDER */ + myArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + } else { + myArena = arena; + } + PORT_Memset(&sd, 0, sizeof(sd)); + rv = SEC_QuickDERDecodeItem(myArena, &sd, CERT_SignedDataTemplate, derCrl); + if (SECSuccess == rv) { + PORT_Memset(&crlkey, 0, sizeof(crlkey)); + rv = SEC_QuickDERDecodeItem(myArena, &crlkey, cert_CrlKeyTemplate, + &sd.data); + } + + /* make a copy so the data doesn't point to memory inside derCrl, which + may be temporary */ + if (SECSuccess == rv) { + rv = SECITEM_CopyItem(arena, key, &crlkey.derName); + } + + if (myArena != arena) { + PORT_FreeArena(myArena, PR_FALSE); + } + + return rv; +} + +#define GetOpaqueCRLFields(x) ((OpaqueCRLFields*)x->opaque) + +SECStatus +CERT_CompleteCRLDecodeEntries(CERTSignedCrl* crl) +{ + SECStatus rv = SECSuccess; + SECItem* crldata = NULL; + OpaqueCRLFields* extended = NULL; + + if ((!crl) || (!(extended = (OpaqueCRLFields*)crl->opaque)) || + (PR_TRUE == extended->decodingError)) { + rv = SECFailure; + } else { + if (PR_FALSE == extended->partial) { + /* the CRL has already been fully decoded */ + return SECSuccess; + } + if (PR_TRUE == extended->badEntries) { + /* the entries decoding already failed */ + return SECFailure; + } + crldata = &crl->signatureWrap.data; + if (!crldata) { + rv = SECFailure; + } + } + + if (SECSuccess == rv) { + rv = SEC_QuickDERDecodeItem(crl->arena, &crl->crl, + CERT_CrlTemplateEntriesOnly, crldata); + if (SECSuccess == rv) { + extended->partial = PR_FALSE; /* successful decode, avoid + decoding again */ + } else { + extended->decodingError = PR_TRUE; + extended->badEntries = PR_TRUE; + /* cache the decoding failure. If it fails the first time, + it will fail again, which will grow the arena and leak + memory, so we want to avoid it */ + } + rv = cert_check_crl_entries(&crl->crl); + if (rv != SECSuccess) { + extended->badExtensions = PR_TRUE; + } + } + return rv; +} + +/* + * take a DER CRL and decode it into a CRL structure + * allow reusing the input DER without making a copy + */ +CERTSignedCrl* +CERT_DecodeDERCrlWithFlags(PLArenaPool* narena, SECItem* derSignedCrl, int type, + PRInt32 options) +{ + PLArenaPool* arena; + CERTSignedCrl* crl; + SECStatus rv; + OpaqueCRLFields* extended = NULL; + const SEC_ASN1Template* crlTemplate = CERT_SignedCrlTemplate; + PRInt32 testOptions = options; + + PORT_Assert(derSignedCrl); + if (!derSignedCrl) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + /* Adopting DER requires not copying it. Code that sets ADOPT flag + * but doesn't set DONT_COPY probably doesn't know What it is doing. + * That condition is a programming error in the caller. + */ + testOptions &= (CRL_DECODE_ADOPT_HEAP_DER | CRL_DECODE_DONT_COPY_DER); + PORT_Assert(testOptions != CRL_DECODE_ADOPT_HEAP_DER); + if (testOptions == CRL_DECODE_ADOPT_HEAP_DER) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + /* make a new arena if needed */ + if (narena == NULL) { + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + return NULL; + } + } else { + arena = narena; + } + + /* allocate the CRL structure */ + crl = (CERTSignedCrl*)PORT_ArenaZAlloc(arena, sizeof(CERTSignedCrl)); + if (!crl) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + crl->arena = arena; + + /* allocate opaque fields */ + crl->opaque = (void*)PORT_ArenaZAlloc(arena, sizeof(OpaqueCRLFields)); + if (!crl->opaque) { + goto loser; + } + extended = (OpaqueCRLFields*)crl->opaque; + if (options & CRL_DECODE_ADOPT_HEAP_DER) { + extended->heapDER = PR_TRUE; + } + if (options & CRL_DECODE_DONT_COPY_DER) { + crl->derCrl = derSignedCrl; /* DER is not copied . The application + must keep derSignedCrl until it + destroys the CRL */ + } else { + crl->derCrl = (SECItem*)PORT_ArenaZAlloc(arena, sizeof(SECItem)); + if (crl->derCrl == NULL) { + goto loser; + } + rv = SECITEM_CopyItem(arena, crl->derCrl, derSignedCrl); + if (rv != SECSuccess) { + goto loser; + } + } + + /* Save the arena in the inner crl for CRL extensions support */ + crl->crl.arena = arena; + if (options & CRL_DECODE_SKIP_ENTRIES) { + crlTemplate = cert_SignedCrlTemplateNoEntries; + extended->partial = PR_TRUE; + } + + /* decode the CRL info */ + switch (type) { + case SEC_CRL_TYPE: + rv = SEC_QuickDERDecodeItem(arena, crl, crlTemplate, crl->derCrl); + if (rv != SECSuccess) { + extended->badDER = PR_TRUE; + break; + } + /* check for critical extensions */ + rv = cert_check_crl_version(&crl->crl); + if (rv != SECSuccess) { + extended->badExtensions = PR_TRUE; + break; + } + + if (PR_TRUE == extended->partial) { + /* partial decoding, don't verify entries */ + break; + } + + rv = cert_check_crl_entries(&crl->crl); + if (rv != SECSuccess) { + extended->badExtensions = PR_TRUE; + } + + break; + + default: + PORT_SetError(SEC_ERROR_INVALID_ARGS); + rv = SECFailure; + break; + } + + if (rv != SECSuccess) { + goto loser; + } + + crl->referenceCount = 1; + + return (crl); + +loser: + if (options & CRL_DECODE_KEEP_BAD_CRL) { + if (extended) { + extended->decodingError = PR_TRUE; + } + if (crl) { + crl->referenceCount = 1; + return (crl); + } + } + + if ((narena == NULL) && arena) { + PORT_FreeArena(arena, PR_FALSE); + } + + return (0); +} + +/* + * take a DER CRL and decode it into a CRL structure + */ +CERTSignedCrl* +CERT_DecodeDERCrl(PLArenaPool* narena, SECItem* derSignedCrl, int type) +{ + return CERT_DecodeDERCrlWithFlags(narena, derSignedCrl, type, + CRL_DECODE_DEFAULT_OPTIONS); +} + +/* + * Lookup a CRL in the databases. We mirror the same fast caching data base + * caching stuff used by certificates....? + * return values : + * + * SECSuccess means we got a valid decodable DER CRL, or no CRL at all. + * Caller may distinguish those cases by the value returned in "decoded". + * When DER CRL is not found, error code will be SEC_ERROR_CRL_NOT_FOUND. + * + * SECFailure means we got a fatal error - most likely, we found a CRL, + * and it failed decoding, or there was an out of memory error. Do NOT ignore + * it and specifically do NOT treat it the same as having no CRL, as this + * can compromise security !!! Ideally, you should treat this case as if you + * received a "catch-all" CRL where all certs you were looking up are + * considered to be revoked + */ +static SECStatus +SEC_FindCrlByKeyOnSlot(PK11SlotInfo* slot, SECItem* crlKey, int type, + CERTSignedCrl** decoded, PRInt32 decodeoptions) +{ + SECStatus rv = SECSuccess; + CERTSignedCrl* crl = NULL; + SECItem* derCrl = NULL; + CK_OBJECT_HANDLE crlHandle = 0; + char* url = NULL; + + PORT_Assert(decoded); + if (!decoded) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + derCrl = PK11_FindCrlByName(&slot, &crlHandle, crlKey, type, &url); + if (derCrl == NULL) { + /* if we had a problem other than the CRL just didn't exist, return + * a failure to the upper level */ + int nsserror = PORT_GetError(); + if (nsserror != SEC_ERROR_CRL_NOT_FOUND) { + rv = SECFailure; + } + goto loser; + } + PORT_Assert(crlHandle != CK_INVALID_HANDLE); + /* PK11_FindCrlByName obtained a slot reference. */ + + /* derCRL is a fresh HEAP copy made for us by PK11_FindCrlByName. + Force adoption of the DER CRL from the heap - this will cause it + to be automatically freed when SEC_DestroyCrl is invoked */ + decodeoptions |= (CRL_DECODE_ADOPT_HEAP_DER | CRL_DECODE_DONT_COPY_DER); + + crl = CERT_DecodeDERCrlWithFlags(NULL, derCrl, type, decodeoptions); + if (crl) { + crl->slot = slot; + slot = NULL; /* adopt it */ + derCrl = NULL; /* adopted by the crl struct */ + crl->pkcs11ID = crlHandle; + if (url) { + crl->url = PORT_ArenaStrdup(crl->arena, url); + } + } else { + rv = SECFailure; + } + + if (url) { + PORT_Free(url); + } + + if (slot) { + PK11_FreeSlot(slot); + } + +loser: + if (derCrl) { + SECITEM_FreeItem(derCrl, PR_TRUE); + } + + *decoded = crl; + + return rv; +} + +CERTSignedCrl* +crl_storeCRL(PK11SlotInfo* slot, char* url, CERTSignedCrl* newCrl, + SECItem* derCrl, int type) +{ + CERTSignedCrl *oldCrl = NULL, *crl = NULL; + PRBool deleteOldCrl = PR_FALSE; + CK_OBJECT_HANDLE crlHandle = CK_INVALID_HANDLE; + + PORT_Assert(newCrl); + PORT_Assert(derCrl); + PORT_Assert(type == SEC_CRL_TYPE); + + if (type != SEC_CRL_TYPE) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + /* we can't use the cache here because we must look in the same + token */ + (void)SEC_FindCrlByKeyOnSlot(slot, &newCrl->crl.derName, type, &oldCrl, + CRL_DECODE_SKIP_ENTRIES); + /* if there is an old crl on the token, make sure the one we are + installing is newer. If not, exit out, otherwise delete the + old crl. + */ + if (oldCrl != NULL) { + /* if it's already there, quietly continue */ + if (SECITEM_CompareItem(newCrl->derCrl, oldCrl->derCrl) == SECEqual) { + crl = newCrl; + crl->slot = PK11_ReferenceSlot(slot); + crl->pkcs11ID = oldCrl->pkcs11ID; + if (oldCrl->url && !url) + url = oldCrl->url; + if (url) + crl->url = PORT_ArenaStrdup(crl->arena, url); + goto done; + } + if (!SEC_CrlIsNewer(&newCrl->crl, &oldCrl->crl)) { + PORT_SetError(SEC_ERROR_OLD_CRL); + goto done; + } + + /* if we have a url in the database, use that one */ + if (oldCrl->url && !url) { + url = oldCrl->url; + } + + /* really destroy this crl */ + /* first drum it out of the permanment Data base */ + deleteOldCrl = PR_TRUE; + } + + /* invalidate CRL cache for this issuer */ + CERT_CRLCacheRefreshIssuer(NULL, &newCrl->crl.derName); + /* Write the new entry into the data base */ + crlHandle = PK11_PutCrl(slot, derCrl, &newCrl->crl.derName, url, type); + if (crlHandle != CK_INVALID_HANDLE) { + crl = newCrl; + crl->slot = PK11_ReferenceSlot(slot); + crl->pkcs11ID = crlHandle; + if (url) { + crl->url = PORT_ArenaStrdup(crl->arena, url); + } + } + +done: + if (oldCrl) { + if (deleteOldCrl && crlHandle != CK_INVALID_HANDLE) { + SEC_DeletePermCRL(oldCrl); + } + SEC_DestroyCrl(oldCrl); + } + + return crl; +} + +/* + * + * create a new CRL from DER material. + * + * The signature on this CRL must be checked before you + * load it. ??? + */ +CERTSignedCrl* +SEC_NewCrl(CERTCertDBHandle* handle, char* url, SECItem* derCrl, int type) +{ + CERTSignedCrl* retCrl = NULL; + PK11SlotInfo* slot = PK11_GetInternalKeySlot(); + retCrl = + PK11_ImportCRL(slot, derCrl, url, type, NULL, CRL_IMPORT_BYPASS_CHECKS, + NULL, CRL_DECODE_DEFAULT_OPTIONS); + PK11_FreeSlot(slot); + + return retCrl; +} + +CERTSignedCrl* +SEC_FindCrlByDERCert(CERTCertDBHandle* handle, SECItem* derCrl, int type) +{ + PLArenaPool* arena; + SECItem crlKey; + SECStatus rv; + CERTSignedCrl* crl = NULL; + + /* create a scratch arena */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + return (NULL); + } + + /* extract the database key from the cert */ + rv = CERT_KeyFromDERCrl(arena, derCrl, &crlKey); + if (rv != SECSuccess) { + goto loser; + } + + /* find the crl */ + crl = SEC_FindCrlByName(handle, &crlKey, type); + +loser: + PORT_FreeArena(arena, PR_FALSE); + return (crl); +} + +CERTSignedCrl* +SEC_DupCrl(CERTSignedCrl* acrl) +{ + if (acrl) { + PR_ATOMIC_INCREMENT(&acrl->referenceCount); + return acrl; + } + return NULL; +} + +SECStatus +SEC_DestroyCrl(CERTSignedCrl* crl) +{ + if (crl) { + if (PR_ATOMIC_DECREMENT(&crl->referenceCount) < 1) { + if (crl->slot) { + PK11_FreeSlot(crl->slot); + } + if (GetOpaqueCRLFields(crl) && + PR_TRUE == GetOpaqueCRLFields(crl)->heapDER) { + SECITEM_FreeItem(crl->derCrl, PR_TRUE); + } + if (crl->arena) { + PORT_FreeArena(crl->arena, PR_FALSE); + } + } + return SECSuccess; + } else { + return SECFailure; + } +} + +SECStatus +SEC_LookupCrls(CERTCertDBHandle* handle, CERTCrlHeadNode** nodes, int type) +{ + CERTCrlHeadNode* head; + PLArenaPool* arena = NULL; + SECStatus rv; + + *nodes = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + return SECFailure; + } + + /* build a head structure */ + head = (CERTCrlHeadNode*)PORT_ArenaAlloc(arena, sizeof(CERTCrlHeadNode)); + head->arena = arena; + head->first = NULL; + head->last = NULL; + head->dbhandle = handle; + + /* Look up the proper crl types */ + *nodes = head; + + rv = PK11_LookupCrls(head, type, NULL); + + if (rv != SECSuccess) { + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + *nodes = NULL; + } + } + + return rv; +} + +/* These functions simply return the address of the above-declared templates. +** This is necessary for Windows DLLs. Sigh. +*/ +SEC_ASN1_CHOOSER_IMPLEMENT(CERT_IssuerAndSNTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(CERT_CrlTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SignedCrlTemplate) +SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SetOfSignedCrlTemplate) + +/* CRL cache code starts here */ + +/* constructor */ +static SECStatus CachedCrl_Create(CachedCrl** returned, CERTSignedCrl* crl, + CRLOrigin origin); +/* destructor */ +static SECStatus CachedCrl_Destroy(CachedCrl* crl); + +/* create hash table of CRL entries */ +static SECStatus CachedCrl_Populate(CachedCrl* crlobject); + +/* empty the cache content */ +static SECStatus CachedCrl_Depopulate(CachedCrl* crl); + +/* are these CRLs the same, as far as the cache is concerned ? + Or are they the same token object, but with different DER ? */ + +static SECStatus CachedCrl_Compare(CachedCrl* a, CachedCrl* b, PRBool* isDupe, + PRBool* isUpdated); + +/* create a DPCache object */ +static SECStatus DPCache_Create(CRLDPCache** returned, CERTCertificate* issuer, + const SECItem* subject, SECItem* dp); + +/* destructor for CRL DPCache object */ +static SECStatus DPCache_Destroy(CRLDPCache* cache); + +/* add a new CRL object to the dynamic array of CRLs of the DPCache, and + returns the cached CRL object . Needs write access to DPCache. */ +static SECStatus DPCache_AddCRL(CRLDPCache* cache, CachedCrl* crl, + PRBool* added); + +/* fetch the CRL for this DP from the PKCS#11 tokens */ +static SECStatus DPCache_FetchFromTokens(CRLDPCache* cache, PRTime vfdate, + void* wincx); + +/* update the content of the CRL cache, including fetching of CRLs, and + reprocessing with specified issuer and date */ +static SECStatus DPCache_GetUpToDate(CRLDPCache* cache, CERTCertificate* issuer, + PRBool readlocked, PRTime vfdate, + void* wincx); + +/* returns true if there are CRLs from PKCS#11 slots */ +static PRBool DPCache_HasTokenCRLs(CRLDPCache* cache); + +/* remove CRL at offset specified */ +static SECStatus DPCache_RemoveCRL(CRLDPCache* cache, PRUint32 offset); + +/* Pick best CRL to use . needs write access */ +static SECStatus DPCache_SelectCRL(CRLDPCache* cache); + +/* create an issuer cache object (per CA subject ) */ +static SECStatus IssuerCache_Create(CRLIssuerCache** returned, + CERTCertificate* issuer, + const SECItem* subject, const SECItem* dp); + +/* destructor for CRL IssuerCache object */ +SECStatus IssuerCache_Destroy(CRLIssuerCache* cache); + +/* add a DPCache to the issuer cache */ +static SECStatus IssuerCache_AddDP(CRLIssuerCache* cache, + CERTCertificate* issuer, + const SECItem* subject, const SECItem* dp, + CRLDPCache** newdpc); + +/* get a particular DPCache object from an IssuerCache */ +static CRLDPCache* IssuerCache_GetDPCache(CRLIssuerCache* cache, + const SECItem* dp); + +/* +** Pre-allocator hash allocator ops. +*/ + +/* allocate memory for hash table */ +static void* PR_CALLBACK +PreAllocTable(void* pool, PRSize size) +{ + PreAllocator* alloc = (PreAllocator*)pool; + PORT_Assert(alloc); + if (!alloc) { + /* no allocator, or buffer full */ + return NULL; + } + if (size > (alloc->len - alloc->used)) { + /* initial buffer full, let's use the arena */ + alloc->extra += size; + return PORT_ArenaAlloc(alloc->arena, size); + } + /* use the initial buffer */ + alloc->used += size; + return (char*)alloc->data + alloc->used - size; +} + +/* free hash table memory. + Individual PreAllocator elements cannot be freed, so this is a no-op. */ +static void PR_CALLBACK +PreFreeTable(void* pool, void* item) +{ +} + +/* allocate memory for hash table */ +static PLHashEntry* PR_CALLBACK +PreAllocEntry(void* pool, const void* key) +{ + return PreAllocTable(pool, sizeof(PLHashEntry)); +} + +/* free hash table entry. + Individual PreAllocator elements cannot be freed, so this is a no-op. */ +static void PR_CALLBACK +PreFreeEntry(void* pool, PLHashEntry* he, PRUintn flag) +{ +} + +/* methods required for PL hash table functions */ +static PLHashAllocOps preAllocOps = { PreAllocTable, PreFreeTable, + PreAllocEntry, PreFreeEntry }; + +/* destructor for PreAllocator object */ +void +PreAllocator_Destroy(PreAllocator* allocator) +{ + if (!allocator) { + return; + } + if (allocator->arena) { + PORT_FreeArena(allocator->arena, PR_TRUE); + } +} + +/* constructor for PreAllocator object */ +PreAllocator* +PreAllocator_Create(PRSize size) +{ + PLArenaPool* arena = NULL; + PreAllocator* prebuffer = NULL; + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + return NULL; + } + prebuffer = (PreAllocator*)PORT_ArenaZAlloc(arena, sizeof(PreAllocator)); + if (!prebuffer) { + PORT_FreeArena(arena, PR_TRUE); + return NULL; + } + prebuffer->arena = arena; + + if (size) { + prebuffer->len = size; + prebuffer->data = PORT_ArenaAlloc(arena, size); + if (!prebuffer->data) { + PORT_FreeArena(arena, PR_TRUE); + return NULL; + } + } + return prebuffer; +} + +/* global Named CRL cache object */ +static NamedCRLCache namedCRLCache = { NULL, NULL }; + +/* global CRL cache object */ +static CRLCache crlcache = { NULL, NULL }; + +/* initial state is off */ +static PRBool crlcache_initialized = PR_FALSE; + +PRTime CRLCache_Empty_TokenFetch_Interval = 60 * 1000000; /* how often + to query the tokens for CRL objects, in order to discover new objects, if + the cache does not contain any token CRLs . In microseconds */ + +PRTime CRLCache_TokenRefetch_Interval = 600 * 1000000; /* how often + to query the tokens for CRL objects, in order to discover new objects, if + the cache already contains token CRLs In microseconds */ + +PRTime CRLCache_ExistenceCheck_Interval = 60 * 1000000; /* how often to check + if a token CRL object still exists. In microseconds */ + +/* this function is called at NSS initialization time */ +SECStatus +InitCRLCache(void) +{ + if (PR_FALSE == crlcache_initialized) { + PORT_Assert(NULL == crlcache.lock); + PORT_Assert(NULL == crlcache.issuers); + PORT_Assert(NULL == namedCRLCache.lock); + PORT_Assert(NULL == namedCRLCache.entries); + if (crlcache.lock || crlcache.issuers || namedCRLCache.lock || + namedCRLCache.entries) { + /* CRL cache already partially initialized */ + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } +#ifdef GLOBAL_RWLOCK + crlcache.lock = NSSRWLock_New(NSS_RWLOCK_RANK_NONE, NULL); +#else + crlcache.lock = PR_NewLock(); +#endif + namedCRLCache.lock = PR_NewLock(); + crlcache.issuers = PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare, + PL_CompareValues, NULL, NULL); + namedCRLCache.entries = PL_NewHashTable( + 0, SECITEM_Hash, SECITEM_HashCompare, PL_CompareValues, NULL, NULL); + if (!crlcache.lock || !namedCRLCache.lock || !crlcache.issuers || + !namedCRLCache.entries) { + if (crlcache.lock) { +#ifdef GLOBAL_RWLOCK + NSSRWLock_Destroy(crlcache.lock); +#else + PR_DestroyLock(crlcache.lock); +#endif + crlcache.lock = NULL; + } + if (namedCRLCache.lock) { + PR_DestroyLock(namedCRLCache.lock); + namedCRLCache.lock = NULL; + } + if (crlcache.issuers) { + PL_HashTableDestroy(crlcache.issuers); + crlcache.issuers = NULL; + } + if (namedCRLCache.entries) { + PL_HashTableDestroy(namedCRLCache.entries); + namedCRLCache.entries = NULL; + } + + return SECFailure; + } + crlcache_initialized = PR_TRUE; + return SECSuccess; + } else { + PORT_Assert(crlcache.lock); + PORT_Assert(crlcache.issuers); + if ((NULL == crlcache.lock) || (NULL == crlcache.issuers)) { + /* CRL cache not fully initialized */ + return SECFailure; + } else { + /* CRL cache already initialized */ + return SECSuccess; + } + } +} + +/* destructor for CRL DPCache object */ +static SECStatus +DPCache_Destroy(CRLDPCache* cache) +{ + PRUint32 i = 0; + PORT_Assert(cache); + if (!cache) { + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + if (cache->lock) { +#ifdef DPC_RWLOCK + NSSRWLock_Destroy(cache->lock); +#else + PR_DestroyLock(cache->lock); +#endif + } else { + PORT_Assert(0); + return SECFailure; + } + /* destroy all our CRL objects */ + for (i = 0; i < cache->ncrls; i++) { + if (!cache->crls || !cache->crls[i] || + SECSuccess != CachedCrl_Destroy(cache->crls[i])) { + return SECFailure; + } + } + /* free the array of CRLs */ + if (cache->crls) { + PORT_Free(cache->crls); + } + /* destroy the cert */ + if (cache->issuerDERCert) { + SECITEM_FreeItem(cache->issuerDERCert, PR_TRUE); + } + /* free the subject */ + if (cache->subject) { + SECITEM_FreeItem(cache->subject, PR_TRUE); + } + /* free the distribution points */ + if (cache->distributionPoint) { + SECITEM_FreeItem(cache->distributionPoint, PR_TRUE); + } + PORT_Free(cache); + return SECSuccess; +} + +/* destructor for CRL IssuerCache object */ +SECStatus +IssuerCache_Destroy(CRLIssuerCache* cache) +{ + PORT_Assert(cache); + if (!cache) { + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } +#ifdef XCRL + if (cache->lock) { + NSSRWLock_Destroy(cache->lock); + } else { + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + if (cache->issuer) { + CERT_DestroyCertificate(cache->issuer); + } +#endif + /* free the subject */ + if (cache->subject) { + SECITEM_FreeItem(cache->subject, PR_TRUE); + } + if (SECSuccess != DPCache_Destroy(cache->dpp)) { + PORT_Assert(0); + return SECFailure; + } + PORT_Free(cache); + return SECSuccess; +} + +/* create a named CRL entry object */ +static SECStatus +NamedCRLCacheEntry_Create(NamedCRLCacheEntry** returned) +{ + NamedCRLCacheEntry* entry = NULL; + if (!returned) { + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + *returned = NULL; + entry = (NamedCRLCacheEntry*)PORT_ZAlloc(sizeof(NamedCRLCacheEntry)); + if (!entry) { + return SECFailure; + } + *returned = entry; + return SECSuccess; +} + +/* destroy a named CRL entry object */ +static SECStatus +NamedCRLCacheEntry_Destroy(NamedCRLCacheEntry* entry) +{ + if (!entry) { + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + if (entry->crl) { + /* named CRL cache owns DER memory */ + SECITEM_ZfreeItem(entry->crl, PR_TRUE); + } + if (entry->canonicalizedName) { + SECITEM_FreeItem(entry->canonicalizedName, PR_TRUE); + } + PORT_Free(entry); + return SECSuccess; +} + +/* callback function used in hash table destructor */ +static PRIntn PR_CALLBACK +FreeIssuer(PLHashEntry* he, PRIntn i, void* arg) +{ + CRLIssuerCache* issuer = NULL; + SECStatus* rv = (SECStatus*)arg; + + PORT_Assert(he); + if (!he) { + return HT_ENUMERATE_NEXT; + } + issuer = (CRLIssuerCache*)he->value; + PORT_Assert(issuer); + if (issuer) { + if (SECSuccess != IssuerCache_Destroy(issuer)) { + PORT_Assert(rv); + if (rv) { + *rv = SECFailure; + } + return HT_ENUMERATE_NEXT; + } + } + return HT_ENUMERATE_NEXT; +} + +/* callback function used in hash table destructor */ +static PRIntn PR_CALLBACK +FreeNamedEntries(PLHashEntry* he, PRIntn i, void* arg) +{ + NamedCRLCacheEntry* entry = NULL; + SECStatus* rv = (SECStatus*)arg; + + PORT_Assert(he); + if (!he) { + return HT_ENUMERATE_NEXT; + } + entry = (NamedCRLCacheEntry*)he->value; + PORT_Assert(entry); + if (entry) { + if (SECSuccess != NamedCRLCacheEntry_Destroy(entry)) { + PORT_Assert(rv); + if (rv) { + *rv = SECFailure; + } + return HT_ENUMERATE_NEXT; + } + } + return HT_ENUMERATE_NEXT; +} + +/* needs to be called at NSS shutdown time + This will destroy the global CRL cache, including + - the hash table of issuer cache objects + - the issuer cache objects + - DPCache objects in issuer cache objects */ +SECStatus +ShutdownCRLCache(void) +{ + SECStatus rv = SECSuccess; + if (PR_FALSE == crlcache_initialized && !crlcache.lock && + !crlcache.issuers) { + /* CRL cache has already been shut down */ + return SECSuccess; + } + if (PR_TRUE == crlcache_initialized && + (!crlcache.lock || !crlcache.issuers || !namedCRLCache.lock || + !namedCRLCache.entries)) { + /* CRL cache has partially been shut down */ + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + /* empty the CRL cache */ + /* free the issuers */ + PL_HashTableEnumerateEntries(crlcache.issuers, &FreeIssuer, &rv); + /* free the hash table of issuers */ + PL_HashTableDestroy(crlcache.issuers); + crlcache.issuers = NULL; +/* free the global lock */ +#ifdef GLOBAL_RWLOCK + NSSRWLock_Destroy(crlcache.lock); +#else + PR_DestroyLock(crlcache.lock); +#endif + crlcache.lock = NULL; + + /* empty the named CRL cache. This must be done after freeing the CRL + * cache, since some CRLs in this cache are in the memory for the other */ + /* free the entries */ + PL_HashTableEnumerateEntries(namedCRLCache.entries, &FreeNamedEntries, &rv); + /* free the hash table of issuers */ + PL_HashTableDestroy(namedCRLCache.entries); + namedCRLCache.entries = NULL; + /* free the global lock */ + PR_DestroyLock(namedCRLCache.lock); + namedCRLCache.lock = NULL; + + crlcache_initialized = PR_FALSE; + return rv; +} + +/* add a new CRL object to the dynamic array of CRLs of the DPCache, and + returns the cached CRL object . Needs write access to DPCache. */ +static SECStatus +DPCache_AddCRL(CRLDPCache* cache, CachedCrl* newcrl, PRBool* added) +{ + CachedCrl** newcrls = NULL; + PRUint32 i = 0; + PORT_Assert(cache); + PORT_Assert(newcrl); + PORT_Assert(added); + if (!cache || !newcrl || !added) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + *added = PR_FALSE; + /* before adding a new CRL, check if it is a duplicate */ + for (i = 0; i < cache->ncrls; i++) { + CachedCrl* existing = NULL; + SECStatus rv = SECSuccess; + PRBool dupe = PR_FALSE, updated = PR_FALSE; + if (!cache->crls) { + PORT_Assert(0); + return SECFailure; + } + existing = cache->crls[i]; + if (!existing) { + PORT_Assert(0); + return SECFailure; + } + rv = CachedCrl_Compare(existing, newcrl, &dupe, &updated); + if (SECSuccess != rv) { + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + if (PR_TRUE == dupe) { + /* dupe */ + PORT_SetError(SEC_ERROR_CRL_ALREADY_EXISTS); + return SECSuccess; + } + if (PR_TRUE == updated) { + /* this token CRL is in the same slot and has the same object ID, + but different content. We need to remove the old object */ + if (SECSuccess != DPCache_RemoveCRL(cache, i)) { + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return PR_FALSE; + } + } + } + + newcrls = (CachedCrl**)PORT_Realloc(cache->crls, (cache->ncrls + 1) * sizeof(CachedCrl*)); + if (!newcrls) { + return SECFailure; + } + cache->crls = newcrls; + cache->ncrls++; + cache->crls[cache->ncrls - 1] = newcrl; + *added = PR_TRUE; + return SECSuccess; +} + +/* remove CRL at offset specified */ +static SECStatus +DPCache_RemoveCRL(CRLDPCache* cache, PRUint32 offset) +{ + CachedCrl* acrl = NULL; + PORT_Assert(cache); + if (!cache || (!cache->crls) || (!(offset < cache->ncrls))) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + acrl = cache->crls[offset]; + PORT_Assert(acrl); + if (!acrl) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + cache->crls[offset] = cache->crls[cache->ncrls - 1]; + cache->crls[cache->ncrls - 1] = NULL; + cache->ncrls--; + if (cache->selected == acrl) { + cache->selected = NULL; + } + if (SECSuccess != CachedCrl_Destroy(acrl)) { + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + return SECSuccess; +} + +/* check whether a CRL object stored in a PKCS#11 token still exists in + that token . This has to be efficient (the entire CRL value cannot be + transferred accross the token boundaries), so this is accomplished by + simply fetching the subject attribute and making sure it hasn't changed . + Note that technically, the CRL object could have been replaced with a new + PKCS#11 object of the same ID and subject (which actually happens in + softoken), but this function has no way of knowing that the object + value changed, since CKA_VALUE isn't checked. */ +static PRBool +TokenCRLStillExists(CERTSignedCrl* crl) +{ + NSSItem newsubject; + SECItem subject; + CK_ULONG crl_class; + PRStatus status; + PK11SlotInfo* slot = NULL; + nssCryptokiObject instance; + NSSArena* arena; + PRBool xstatus = PR_TRUE; + SECItem* oldSubject = NULL; + + PORT_Assert(crl); + if (!crl) { + return PR_FALSE; + } + slot = crl->slot; + PORT_Assert(crl->slot); + if (!slot) { + return PR_FALSE; + } + oldSubject = &crl->crl.derName; + PORT_Assert(oldSubject); + if (!oldSubject) { + return PR_FALSE; + } + + /* query subject and type attributes in order to determine if the + object has been deleted */ + + /* first, make an nssCryptokiObject */ + instance.handle = crl->pkcs11ID; + PORT_Assert(instance.handle); + if (!instance.handle) { + return PR_FALSE; + } + instance.token = PK11Slot_GetNSSToken(slot); + PORT_Assert(instance.token); + if (!instance.token) { + return PR_FALSE; + } + instance.isTokenObject = PR_TRUE; + instance.label = NULL; + + arena = NSSArena_Create(); + PORT_Assert(arena); + if (!arena) { + (void)nssToken_Destroy(instance.token); + return PR_FALSE; + } + + status = + nssCryptokiCRL_GetAttributes(&instance, NULL, /* XXX sessionOpt */ + arena, NULL, &newsubject, /* subject */ + &crl_class, /* class */ + NULL, NULL); + if (PR_SUCCESS == status) { + subject.data = newsubject.data; + subject.len = newsubject.size; + if (SECITEM_CompareItem(oldSubject, &subject) != SECEqual) { + xstatus = PR_FALSE; + } + if (CKO_NSS_CRL != crl_class) { + xstatus = PR_FALSE; + } + } else { + xstatus = PR_FALSE; + } + NSSArena_Destroy(arena); + (void)nssToken_Destroy(instance.token); + return xstatus; +} + +/* verify the signature of a CRL against its issuer at a given date */ +static SECStatus +CERT_VerifyCRL(CERTSignedCrl* crlobject, CERTCertificate* issuer, PRTime vfdate, + void* wincx) +{ + return CERT_VerifySignedData(&crlobject->signatureWrap, issuer, vfdate, + wincx); +} + +/* verify a CRL and update cache state */ +static SECStatus +CachedCrl_Verify(CRLDPCache* cache, CachedCrl* crlobject, PRTime vfdate, + void* wincx) +{ + /* Check if it is an invalid CRL + if we got a bad CRL, we want to cache it in order to avoid + subsequent fetches of this same identical bad CRL. We set + the cache to the invalid state to ensure that all certs on this + DP are considered to have unknown status from now on. The cache + object will remain in this state until the bad CRL object + is removed from the token it was fetched from. If the cause + of the failure is that we didn't have the issuer cert to + verify the signature, this state can be cleared when + the issuer certificate becomes available if that causes the + signature to verify */ + + if (!cache || !crlobject) { + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + if (PR_TRUE == GetOpaqueCRLFields(crlobject->crl)->decodingError) { + crlobject->sigChecked = PR_TRUE; /* we can never verify a CRL + with bogus DER. Mark it checked so we won't try again */ + PORT_SetError(SEC_ERROR_BAD_DER); + return SECSuccess; + } else { + SECStatus signstatus = SECFailure; + if (cache->issuerDERCert) { + CERTCertificate* issuer = CERT_NewTempCertificate( + cache->dbHandle, cache->issuerDERCert, NULL, PR_FALSE, PR_TRUE); + + if (issuer) { + signstatus = + CERT_VerifyCRL(crlobject->crl, issuer, vfdate, wincx); + CERT_DestroyCertificate(issuer); + } + } + if (SECSuccess != signstatus) { + if (!cache->issuerDERCert) { + /* we tried to verify without an issuer cert . This is + because this CRL came through a call to SEC_FindCrlByName. + So, we don't cache this verification failure. We'll try + to verify the CRL again when a certificate from that issuer + becomes available */ + } else { + crlobject->sigChecked = PR_TRUE; + } + PORT_SetError(SEC_ERROR_CRL_BAD_SIGNATURE); + return SECSuccess; + } else { + crlobject->sigChecked = PR_TRUE; + crlobject->sigValid = PR_TRUE; + } + } + + return SECSuccess; +} + +/* fetch the CRLs for this DP from the PKCS#11 tokens */ +static SECStatus +DPCache_FetchFromTokens(CRLDPCache* cache, PRTime vfdate, void* wincx) +{ + SECStatus rv = SECSuccess; + CERTCrlHeadNode head; + if (!cache) { + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + /* first, initialize list */ + memset(&head, 0, sizeof(head)); + head.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + rv = pk11_RetrieveCrls(&head, cache->subject, wincx); + + /* if this function fails, something very wrong happened, such as an out + of memory error during CRL decoding. We don't want to proceed and must + mark the cache object invalid */ + if (SECFailure == rv) { + /* fetch failed, add error bit */ + cache->invalid |= CRL_CACHE_LAST_FETCH_FAILED; + } else { + /* fetch was successful, clear this error bit */ + cache->invalid &= (~CRL_CACHE_LAST_FETCH_FAILED); + } + + /* add any CRLs found to our array */ + if (SECSuccess == rv) { + CERTCrlNode* crlNode = NULL; + + for (crlNode = head.first; crlNode; crlNode = crlNode->next) { + CachedCrl* returned = NULL; + CERTSignedCrl* crlobject = crlNode->crl; + if (!crlobject) { + PORT_Assert(0); + continue; + } + rv = CachedCrl_Create(&returned, crlobject, CRL_OriginToken); + if (SECSuccess == rv) { + PRBool added = PR_FALSE; + rv = DPCache_AddCRL(cache, returned, &added); + if (PR_TRUE != added) { + rv = CachedCrl_Destroy(returned); + returned = NULL; + } else if (vfdate) { + rv = CachedCrl_Verify(cache, returned, vfdate, wincx); + } + } else { + /* not enough memory to add the CRL to the cache. mark it + invalid so we will try again . */ + cache->invalid |= CRL_CACHE_LAST_FETCH_FAILED; + } + if (SECFailure == rv) { + break; + } + } + } + + if (head.arena) { + CERTCrlNode* crlNode = NULL; + /* clean up the CRL list in case we got a partial one + during a failed fetch */ + for (crlNode = head.first; crlNode; crlNode = crlNode->next) { + if (crlNode->crl) { + SEC_DestroyCrl(crlNode->crl); /* free the CRL. Either it got + added to the cache and the refcount got bumped, or not, and + thus we need to free its RAM */ + } + } + PORT_FreeArena(head.arena, PR_FALSE); /* destroy CRL list */ + } + + return rv; +} + +static SECStatus +CachedCrl_GetEntry(CachedCrl* crl, const SECItem* sn, CERTCrlEntry** returned) +{ + CERTCrlEntry* acrlEntry; + + PORT_Assert(crl); + PORT_Assert(crl->entries); + PORT_Assert(sn); + PORT_Assert(returned); + if (!crl || !sn || !returned || !crl->entries) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + acrlEntry = PL_HashTableLookup(crl->entries, (void*)sn); + if (acrlEntry) { + *returned = acrlEntry; + } else { + *returned = NULL; + } + return SECSuccess; +} + +/* check if a particular SN is in the CRL cache and return its entry */ +dpcacheStatus +DPCache_Lookup(CRLDPCache* cache, const SECItem* sn, CERTCrlEntry** returned) +{ + SECStatus rv; + if (!cache || !sn || !returned) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + /* no cache or SN to look up, or no way to return entry */ + return dpcacheCallerError; + } + *returned = NULL; + if (0 != cache->invalid) { + /* the cache contains a bad CRL, or there was a CRL fetching error. */ + PORT_SetError(SEC_ERROR_CRL_INVALID); + return dpcacheInvalidCacheError; + } + if (!cache->selected) { + /* no CRL means no entry to return. This is OK, except for + * NIST policy */ + return dpcacheEmpty; + } + rv = CachedCrl_GetEntry(cache->selected, sn, returned); + if (SECSuccess != rv) { + return dpcacheLookupError; + } else { + if (*returned) { + return dpcacheFoundEntry; + } else { + return dpcacheNoEntry; + } + } +} + +#if defined(DPC_RWLOCK) + +#define DPCache_LockWrite() \ + { \ + if (readlocked) { \ + NSSRWLock_UnlockRead(cache->lock); \ + } \ + NSSRWLock_LockWrite(cache->lock); \ + } + +#define DPCache_UnlockWrite() \ + { \ + if (readlocked) { \ + NSSRWLock_LockRead(cache->lock); \ + } \ + NSSRWLock_UnlockWrite(cache->lock); \ + } + +#else + +/* with a global lock, we are always locked for read before we need write + access, so do nothing */ + +#define DPCache_LockWrite() \ + { \ + } + +#define DPCache_UnlockWrite() \ + { \ + } + +#endif + +/* update the content of the CRL cache, including fetching of CRLs, and + reprocessing with specified issuer and date . We are always holding + either the read or write lock on DPCache upon entry. */ +static SECStatus +DPCache_GetUpToDate(CRLDPCache* cache, CERTCertificate* issuer, + PRBool readlocked, PRTime vfdate, void* wincx) +{ + /* Update the CRLDPCache now. We don't cache token CRL lookup misses + yet, as we have no way of getting notified of new PKCS#11 object + creation that happens in a token */ + SECStatus rv = SECSuccess; + PRUint32 i = 0; + PRBool forcedrefresh = PR_FALSE; + PRBool dirty = PR_FALSE; /* whether something was changed in the + cache state during this update cycle */ + PRBool hastokenCRLs = PR_FALSE; + PRTime now = 0; + PRTime lastfetch = 0; + PRBool mustunlock = PR_FALSE; + + if (!cache) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + /* first, make sure we have obtained all the CRLs we need. + We do an expensive token fetch in the following cases : + 1) cache is empty because no fetch was ever performed yet + 2) cache is explicitly set to refresh state + 3) cache is in invalid state because last fetch failed + 4) cache contains no token CRLs, and it's been more than one minute + since the last fetch + 5) cache contains token CRLs, and it's been more than 10 minutes since + the last fetch + */ + forcedrefresh = cache->refresh; + lastfetch = cache->lastfetch; + if (PR_TRUE != forcedrefresh && + (!(cache->invalid & CRL_CACHE_LAST_FETCH_FAILED))) { + now = PR_Now(); + hastokenCRLs = DPCache_HasTokenCRLs(cache); + } + if ((0 == lastfetch) || + + (PR_TRUE == forcedrefresh) || + + (cache->invalid & CRL_CACHE_LAST_FETCH_FAILED) || + + ((PR_FALSE == hastokenCRLs) && + ((now - cache->lastfetch > CRLCache_Empty_TokenFetch_Interval) || + (now < cache->lastfetch))) || + + ((PR_TRUE == hastokenCRLs) && + ((now - cache->lastfetch > CRLCache_TokenRefetch_Interval) || + (now < cache->lastfetch)))) { + /* the cache needs to be refreshed, and/or we had zero CRL for this + DP. Try to get one from PKCS#11 tokens */ + DPCache_LockWrite(); + /* check if another thread updated before us, and skip update if so */ + if (lastfetch == cache->lastfetch) { + /* we are the first */ + rv = DPCache_FetchFromTokens(cache, vfdate, wincx); + if (PR_TRUE == cache->refresh) { + cache->refresh = PR_FALSE; /* clear refresh state */ + } + dirty = PR_TRUE; + cache->lastfetch = PR_Now(); + } + DPCache_UnlockWrite(); + } + + /* now, make sure we have no extraneous CRLs (deleted token objects) + we'll do this inexpensive existence check either + 1) if there was a token object fetch + 2) every minute */ + if ((PR_TRUE != dirty) && (!now)) { + now = PR_Now(); + } + if ((PR_TRUE == dirty) || + ((now - cache->lastcheck > CRLCache_ExistenceCheck_Interval) || + (now < cache->lastcheck))) { + PRTime lastcheck = cache->lastcheck; + mustunlock = PR_FALSE; + /* check if all CRLs still exist */ + for (i = 0; (i < cache->ncrls); i++) { + CachedCrl* savcrl = cache->crls[i]; + if ((!savcrl) || (savcrl && CRL_OriginToken != savcrl->origin)) { + /* we only want to check token CRLs */ + continue; + } + if ((PR_TRUE != TokenCRLStillExists(savcrl->crl))) { + + /* this CRL is gone */ + if (PR_TRUE != mustunlock) { + DPCache_LockWrite(); + mustunlock = PR_TRUE; + } + /* first, we need to check if another thread did an update + before we did */ + if (lastcheck == cache->lastcheck) { + /* the CRL is gone. And we are the one to do the update */ + DPCache_RemoveCRL(cache, i); + dirty = PR_TRUE; + } + /* stay locked here intentionally so we do all the other + updates in this thread for the remaining CRLs */ + } + } + if (PR_TRUE == mustunlock) { + cache->lastcheck = PR_Now(); + DPCache_UnlockWrite(); + mustunlock = PR_FALSE; + } + } + + /* add issuer certificate if it was previously unavailable */ + if (issuer && (NULL == cache->issuerDERCert) && + (SECSuccess == CERT_CheckCertUsage(issuer, KU_CRL_SIGN))) { + /* if we didn't have a valid issuer cert yet, but we do now. add it */ + DPCache_LockWrite(); + if (!cache->issuerDERCert) { + dirty = PR_TRUE; + cache->dbHandle = issuer->dbhandle; + cache->issuerDERCert = SECITEM_DupItem(&issuer->derCert); + } + DPCache_UnlockWrite(); + } + + /* verify CRLs that couldn't be checked when inserted into the cache + because the issuer cert or a verification date was unavailable. + These are CRLs that were inserted into the cache through + SEC_FindCrlByName, or through manual insertion, rather than through a + certificate verification (CERT_CheckCRL) */ + + if (cache->issuerDERCert && vfdate) { + mustunlock = PR_FALSE; + /* re-process all unverified CRLs */ + for (i = 0; i < cache->ncrls; i++) { + CachedCrl* savcrl = cache->crls[i]; + if (!savcrl) { + continue; + } + if (PR_TRUE != savcrl->sigChecked) { + if (!mustunlock) { + DPCache_LockWrite(); + mustunlock = PR_TRUE; + } + /* first, we need to check if another thread updated + it before we did, and abort if it has been modified since + we acquired the lock. Make sure first that the CRL is still + in the array at the same position */ + if ((i < cache->ncrls) && (savcrl == cache->crls[i]) && + (PR_TRUE != savcrl->sigChecked)) { + /* the CRL is still there, unverified. Do it */ + CachedCrl_Verify(cache, savcrl, vfdate, wincx); + dirty = PR_TRUE; + } + /* stay locked here intentionally so we do all the other + updates in this thread for the remaining CRLs */ + } + if (mustunlock && !dirty) { + DPCache_UnlockWrite(); + mustunlock = PR_FALSE; + } + } + } + + if (dirty || cache->mustchoose) { + /* changes to the content of the CRL cache necessitate examining all + CRLs for selection of the most appropriate one to cache */ + if (!mustunlock) { + DPCache_LockWrite(); + mustunlock = PR_TRUE; + } + DPCache_SelectCRL(cache); + cache->mustchoose = PR_FALSE; + } + if (mustunlock) + DPCache_UnlockWrite(); + + return rv; +} + +/* callback for qsort to sort by thisUpdate */ +static int +SortCRLsByThisUpdate(const void* arg1, const void* arg2) +{ + PRTime timea, timeb; + SECStatus rv = SECSuccess; + CachedCrl *a, *b; + + a = *(CachedCrl**)arg1; + b = *(CachedCrl**)arg2; + + if (!a || !b) { + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + rv = SECFailure; + } + + if (SECSuccess == rv) { + rv = DER_DecodeTimeChoice(&timea, &a->crl->crl.lastUpdate); + } + if (SECSuccess == rv) { + rv = DER_DecodeTimeChoice(&timeb, &b->crl->crl.lastUpdate); + } + if (SECSuccess == rv) { + if (timea > timeb) { + return 1; /* a is better than b */ + } + if (timea < timeb) { + return -1; /* a is not as good as b */ + } + } + + /* if they are equal, or if all else fails, use pointer differences */ + PORT_Assert(a != b); /* they should never be equal */ + return a > b ? 1 : -1; +} + +/* callback for qsort to sort a set of disparate CRLs, some of which are + invalid DER or failed signature check. + + Validated CRLs are differentiated by thisUpdate . + Validated CRLs are preferred over non-validated CRLs . + Proper DER CRLs are preferred over non-DER data . +*/ +static int +SortImperfectCRLs(const void* arg1, const void* arg2) +{ + CachedCrl *a, *b; + + a = *(CachedCrl**)arg1; + b = *(CachedCrl**)arg2; + + if (!a || !b) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + PORT_Assert(0); + } else { + PRBool aDecoded = PR_FALSE, bDecoded = PR_FALSE; + if ((PR_TRUE == a->sigValid) && (PR_TRUE == b->sigValid)) { + /* both CRLs have been validated, choose the latest one */ + return SortCRLsByThisUpdate(arg1, arg2); + } + if (PR_TRUE == a->sigValid) { + return 1; /* a is greater than b */ + } + if (PR_TRUE == b->sigValid) { + return -1; /* a is not as good as b */ + } + aDecoded = GetOpaqueCRLFields(a->crl)->decodingError; + bDecoded = GetOpaqueCRLFields(b->crl)->decodingError; + /* neither CRL had its signature check pass */ + if ((PR_FALSE == aDecoded) && (PR_FALSE == bDecoded)) { + /* both CRLs are proper DER, choose the latest one */ + return SortCRLsByThisUpdate(arg1, arg2); + } + if (PR_FALSE == aDecoded) { + return 1; /* a is better than b */ + } + if (PR_FALSE == bDecoded) { + return -1; /* a is not as good as b */ + } + /* both are invalid DER. sigh. */ + } + /* if they are equal, or if all else fails, use pointer differences */ + PORT_Assert(a != b); /* they should never be equal */ + return a > b ? 1 : -1; +} + +/* Pick best CRL to use . needs write access */ +static SECStatus +DPCache_SelectCRL(CRLDPCache* cache) +{ + PRUint32 i; + PRBool valid = PR_TRUE; + CachedCrl* selected = NULL; + + PORT_Assert(cache); + if (!cache) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + /* if any invalid CRL is present, then the CRL cache is + considered invalid, for security reasons */ + for (i = 0; i < cache->ncrls; i++) { + if (!cache->crls[i] || !cache->crls[i]->sigChecked || + !cache->crls[i]->sigValid) { + valid = PR_FALSE; + break; + } + } + if (PR_TRUE == valid) { + /* all CRLs are valid, clear this error */ + cache->invalid &= (~CRL_CACHE_INVALID_CRLS); + } else { + /* some CRLs are invalid, set this error */ + cache->invalid |= CRL_CACHE_INVALID_CRLS; + } + + if (cache->invalid) { + /* cache is in an invalid state, so reset it */ + if (cache->selected) { + cache->selected = NULL; + } + /* also sort the CRLs imperfectly */ + qsort(cache->crls, cache->ncrls, sizeof(CachedCrl*), SortImperfectCRLs); + return SECSuccess; + } + + if (cache->ncrls) { + /* all CRLs are good, sort them by thisUpdate */ + qsort(cache->crls, cache->ncrls, sizeof(CachedCrl*), SortCRLsByThisUpdate); + + /* pick the newest CRL */ + selected = cache->crls[cache->ncrls - 1]; + + /* and populate the cache */ + if (SECSuccess != CachedCrl_Populate(selected)) { + return SECFailure; + } + } + + cache->selected = selected; + + return SECSuccess; +} + +/* initialize a DPCache object */ +static SECStatus +DPCache_Create(CRLDPCache** returned, CERTCertificate* issuer, + const SECItem* subject, SECItem* dp) +{ + CRLDPCache* cache = NULL; + PORT_Assert(returned); + /* issuer and dp are allowed to be NULL */ + if (!returned || !subject) { + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + *returned = NULL; + cache = PORT_ZAlloc(sizeof(CRLDPCache)); + if (!cache) { + return SECFailure; + } +#ifdef DPC_RWLOCK + cache->lock = NSSRWLock_New(NSS_RWLOCK_RANK_NONE, NULL); +#else + cache->lock = PR_NewLock(); +#endif + if (!cache->lock) { + PORT_Free(cache); + return SECFailure; + } + if (issuer) { + cache->dbHandle = issuer->dbhandle; + cache->issuerDERCert = SECITEM_DupItem(&issuer->derCert); + } + cache->distributionPoint = SECITEM_DupItem(dp); + cache->subject = SECITEM_DupItem(subject); + cache->lastfetch = 0; + cache->lastcheck = 0; + *returned = cache; + return SECSuccess; +} + +/* create an issuer cache object (per CA subject ) */ +static SECStatus +IssuerCache_Create(CRLIssuerCache** returned, CERTCertificate* issuer, + const SECItem* subject, const SECItem* dp) +{ + SECStatus rv = SECSuccess; + CRLIssuerCache* cache = NULL; + PORT_Assert(returned); + PORT_Assert(subject); + /* issuer and dp are allowed to be NULL */ + if (!returned || !subject) { + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + *returned = NULL; + cache = (CRLIssuerCache*)PORT_ZAlloc(sizeof(CRLIssuerCache)); + if (!cache) { + return SECFailure; + } + cache->subject = SECITEM_DupItem(subject); +#ifdef XCRL + cache->lock = NSSRWLock_New(NSS_RWLOCK_RANK_NONE, NULL); + if (!cache->lock) { + rv = SECFailure; + } + if (SECSuccess == rv && issuer) { + cache->issuer = CERT_DupCertificate(issuer); + if (!cache->issuer) { + rv = SECFailure; + } + } +#endif + if (SECSuccess != rv) { + PORT_Assert(SECSuccess == IssuerCache_Destroy(cache)); + return SECFailure; + } + *returned = cache; + return SECSuccess; +} + +/* add a DPCache to the issuer cache */ +static SECStatus +IssuerCache_AddDP(CRLIssuerCache* cache, CERTCertificate* issuer, + const SECItem* subject, const SECItem* dp, + CRLDPCache** newdpc) +{ + /* now create the required DP cache object */ + if (!cache || !subject || !newdpc) { + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + if (!dp) { + /* default distribution point */ + SECStatus rv = DPCache_Create(&cache->dpp, issuer, subject, NULL); + if (SECSuccess == rv) { + *newdpc = cache->dpp; + return SECSuccess; + } + } else { + /* we should never hit this until we support multiple DPs */ + PORT_Assert(dp); + /* XCRL allocate a new distribution point cache object, initialize it, + and add it to the hash table of DPs */ + } + return SECFailure; +} + +/* add an IssuerCache to the global hash table of issuers */ +static SECStatus +CRLCache_AddIssuer(CRLIssuerCache* issuer) +{ + PORT_Assert(issuer); + PORT_Assert(crlcache.issuers); + if (!issuer || !crlcache.issuers) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + if (NULL == PL_HashTableAdd(crlcache.issuers, (void*)issuer->subject, + (void*)issuer)) { + return SECFailure; + } + return SECSuccess; +} + +/* retrieve the issuer cache object for a given issuer subject */ +static SECStatus +CRLCache_GetIssuerCache(CRLCache* cache, const SECItem* subject, + CRLIssuerCache** returned) +{ + /* we need to look up the issuer in the hash table */ + SECStatus rv = SECSuccess; + PORT_Assert(cache); + PORT_Assert(subject); + PORT_Assert(returned); + PORT_Assert(crlcache.issuers); + if (!cache || !subject || !returned || !crlcache.issuers) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + rv = SECFailure; + } + + if (SECSuccess == rv) { + *returned = (CRLIssuerCache*)PL_HashTableLookup(crlcache.issuers, + (void*)subject); + } + + return rv; +} + +/* retrieve the full CRL object that best matches the content of a DPCache */ +static CERTSignedCrl* +GetBestCRL(CRLDPCache* cache, PRBool entries) +{ + CachedCrl* acrl = NULL; + + PORT_Assert(cache); + if (!cache) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return NULL; + } + + if (0 == cache->ncrls) { + /* empty cache*/ + PORT_SetError(SEC_ERROR_CRL_NOT_FOUND); + return NULL; + } + + /* if we have a valid full CRL selected, return it */ + if (cache->selected) { + return SEC_DupCrl(cache->selected->crl); + } + + /* otherwise, use latest valid DER CRL */ + acrl = cache->crls[cache->ncrls - 1]; + + if (acrl && (PR_FALSE == GetOpaqueCRLFields(acrl->crl)->decodingError)) { + SECStatus rv = SECSuccess; + if (PR_TRUE == entries) { + rv = CERT_CompleteCRLDecodeEntries(acrl->crl); + } + if (SECSuccess == rv) { + return SEC_DupCrl(acrl->crl); + } + } + + PORT_SetError(SEC_ERROR_CRL_NOT_FOUND); + return NULL; +} + +/* get a particular DPCache object from an IssuerCache */ +static CRLDPCache* +IssuerCache_GetDPCache(CRLIssuerCache* cache, const SECItem* dp) +{ + CRLDPCache* dpp = NULL; + PORT_Assert(cache); + /* XCRL for now we only support the "default" DP, ie. the + full CRL. So we can return the global one without locking. In + the future we will have a lock */ + PORT_Assert(NULL == dp); + if (!cache || dp) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return NULL; + } +#ifdef XCRL + NSSRWLock_LockRead(cache->lock); +#endif + dpp = cache->dpp; +#ifdef XCRL + NSSRWLock_UnlockRead(cache->lock); +#endif + return dpp; +} + +/* get a DPCache object for the given issuer subject and dp + Automatically creates the cache object if it doesn't exist yet. + */ +SECStatus +AcquireDPCache(CERTCertificate* issuer, const SECItem* subject, + const SECItem* dp, PRTime t, void* wincx, CRLDPCache** dpcache, + PRBool* writeLocked) +{ + SECStatus rv = SECSuccess; + CRLIssuerCache* issuercache = NULL; +#ifdef GLOBAL_RWLOCK + PRBool globalwrite = PR_FALSE; +#endif + PORT_Assert(crlcache.lock); + if (!crlcache.lock) { + /* CRL cache is not initialized */ + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } +#ifdef GLOBAL_RWLOCK + NSSRWLock_LockRead(crlcache.lock); +#else + PR_Lock(crlcache.lock); +#endif + rv = CRLCache_GetIssuerCache(&crlcache, subject, &issuercache); + if (SECSuccess != rv) { +#ifdef GLOBAL_RWLOCK + NSSRWLock_UnlockRead(crlcache.lock); +#else + PR_Unlock(crlcache.lock); +#endif + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + if (!issuercache) { + /* there is no cache for this issuer yet. This means this is the + first time we look up a cert from that issuer, and we need to + create the cache. */ + + rv = IssuerCache_Create(&issuercache, issuer, subject, dp); + if (SECSuccess == rv && !issuercache) { + PORT_Assert(issuercache); + rv = SECFailure; + } + + if (SECSuccess == rv) { + /* This is the first time we look up a cert of this issuer. + Create the DPCache for this DP . */ + rv = IssuerCache_AddDP(issuercache, issuer, subject, dp, dpcache); + } + + if (SECSuccess == rv) { + /* lock the DPCache for write to ensure the update happens in this + thread */ + *writeLocked = PR_TRUE; +#ifdef DPC_RWLOCK + NSSRWLock_LockWrite((*dpcache)->lock); +#else + PR_Lock((*dpcache)->lock); +#endif + } + + if (SECSuccess == rv) { +/* now add the new issuer cache to the global hash table of + issuers */ +#ifdef GLOBAL_RWLOCK + CRLIssuerCache* existing = NULL; + NSSRWLock_UnlockRead(crlcache.lock); + /* when using a r/w lock for the global cache, check if the issuer + already exists before adding to the hash table */ + NSSRWLock_LockWrite(crlcache.lock); + globalwrite = PR_TRUE; + rv = CRLCache_GetIssuerCache(&crlcache, subject, &existing); + if (!existing) { +#endif + rv = CRLCache_AddIssuer(issuercache); + if (SECSuccess != rv) { + /* failure */ + rv = SECFailure; + } +#ifdef GLOBAL_RWLOCK + } else { + /* somebody else updated before we did */ + IssuerCache_Destroy(issuercache); /* destroy the new object */ + issuercache = existing; /* use the existing one */ + *dpcache = IssuerCache_GetDPCache(issuercache, dp); + } +#endif + } + +/* now unlock the global cache. We only want to lock the issuer hash + table addition. Holding it longer would hurt scalability */ +#ifdef GLOBAL_RWLOCK + if (PR_TRUE == globalwrite) { + NSSRWLock_UnlockWrite(crlcache.lock); + globalwrite = PR_FALSE; + } else { + NSSRWLock_UnlockRead(crlcache.lock); + } +#else + PR_Unlock(crlcache.lock); +#endif + + /* if there was a failure adding an issuer cache object, destroy it */ + if (SECSuccess != rv && issuercache) { + if (PR_TRUE == *writeLocked) { +#ifdef DPC_RWLOCK + NSSRWLock_UnlockWrite((*dpcache)->lock); +#else + PR_Unlock((*dpcache)->lock); +#endif + } + IssuerCache_Destroy(issuercache); + issuercache = NULL; + } + + if (SECSuccess != rv) { + return SECFailure; + } + } else { +#ifdef GLOBAL_RWLOCK + NSSRWLock_UnlockRead(crlcache.lock); +#else + PR_Unlock(crlcache.lock); +#endif + *dpcache = IssuerCache_GetDPCache(issuercache, dp); + } + /* we now have a DPCache that we can use for lookups */ + /* lock it for read, unless we already locked for write */ + if (PR_FALSE == *writeLocked) { +#ifdef DPC_RWLOCK + NSSRWLock_LockRead((*dpcache)->lock); +#else + PR_Lock((*dpcache)->lock); +#endif + } + + if (SECSuccess == rv) { + /* currently there is always one and only one DPCache per issuer */ + PORT_Assert(*dpcache); + if (*dpcache) { + /* make sure the DP cache is up to date before using it */ + rv = DPCache_GetUpToDate(*dpcache, issuer, PR_FALSE == *writeLocked, + t, wincx); + } else { + rv = SECFailure; + } + } + return rv; +} + +/* unlock access to the DPCache */ +void +ReleaseDPCache(CRLDPCache* dpcache, PRBool writeLocked) +{ + if (!dpcache) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return; + } +#ifdef DPC_RWLOCK + if (PR_TRUE == writeLocked) { + NSSRWLock_UnlockWrite(dpcache->lock); + } else { + NSSRWLock_UnlockRead(dpcache->lock); + } +#else + PR_Unlock(dpcache->lock); +#endif +} + +SECStatus +cert_CheckCertRevocationStatus(CERTCertificate* cert, CERTCertificate* issuer, + const SECItem* dp, PRTime t, void* wincx, + CERTRevocationStatus* revStatus, + CERTCRLEntryReasonCode* revReason) +{ + PRBool lockedwrite = PR_FALSE; + SECStatus rv = SECSuccess; + CRLDPCache* dpcache = NULL; + CERTRevocationStatus status = certRevocationStatusRevoked; + CERTCRLEntryReasonCode reason = crlEntryReasonUnspecified; + CERTCrlEntry* entry = NULL; + dpcacheStatus ds; + + if (!cert || !issuer) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + if (revStatus) { + *revStatus = status; + } + if (revReason) { + *revReason = reason; + } + + if (t && + secCertTimeValid != CERT_CheckCertValidTimes(issuer, t, PR_FALSE)) { + /* we won't be able to check the CRL's signature if the issuer cert + is expired as of the time we are verifying. This may cause a valid + CRL to be cached as bad. short-circuit to avoid this case. */ + PORT_SetError(SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE); + return SECFailure; + } + + rv = AcquireDPCache(issuer, &issuer->derSubject, dp, t, wincx, &dpcache, + &lockedwrite); + PORT_Assert(SECSuccess == rv); + if (SECSuccess != rv) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + /* now look up the certificate SN in the DP cache's CRL */ + ds = DPCache_Lookup(dpcache, &cert->serialNumber, &entry); + switch (ds) { + case dpcacheFoundEntry: + PORT_Assert(entry); + /* check the time if we have one */ + if (entry->revocationDate.data && entry->revocationDate.len) { + PRTime revocationDate = 0; + if (SECSuccess == + DER_DecodeTimeChoice(&revocationDate, + &entry->revocationDate)) { + /* we got a good revocation date, only consider the + certificate revoked if the time we are inquiring about + is past the revocation date */ + if (t >= revocationDate) { + rv = SECFailure; + } else { + status = certRevocationStatusValid; + } + } else { + /* invalid revocation date, consider the certificate + permanently revoked */ + rv = SECFailure; + } + } else { + /* no revocation date, certificate is permanently revoked */ + rv = SECFailure; + } + if (SECFailure == rv) { + (void)CERT_FindCRLEntryReasonExten(entry, &reason); + PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE); + } + break; + + case dpcacheEmpty: + /* useful for NIST policy */ + status = certRevocationStatusUnknown; + break; + + case dpcacheNoEntry: + status = certRevocationStatusValid; + break; + + case dpcacheInvalidCacheError: + /* treat it as unknown and let the caller decide based on + the policy */ + status = certRevocationStatusUnknown; + break; + + default: + /* leave status as revoked */ + break; + } + + ReleaseDPCache(dpcache, lockedwrite); + if (revStatus) { + *revStatus = status; + } + if (revReason) { + *revReason = reason; + } + return rv; +} + +/* check CRL revocation status of given certificate and issuer */ +SECStatus +CERT_CheckCRL(CERTCertificate* cert, CERTCertificate* issuer, const SECItem* dp, + PRTime t, void* wincx) +{ + return cert_CheckCertRevocationStatus(cert, issuer, dp, t, wincx, NULL, + NULL); +} + +/* retrieve full CRL object that best matches the cache status */ +CERTSignedCrl* +SEC_FindCrlByName(CERTCertDBHandle* handle, SECItem* crlKey, int type) +{ + CERTSignedCrl* acrl = NULL; + CRLDPCache* dpcache = NULL; + SECStatus rv = SECSuccess; + PRBool writeLocked = PR_FALSE; + + if (!crlKey) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + rv = AcquireDPCache(NULL, crlKey, NULL, 0, NULL, &dpcache, &writeLocked); + if (SECSuccess == rv) { + acrl = GetBestCRL(dpcache, PR_TRUE); /* decode entries, because + SEC_FindCrlByName always returned fully decoded CRLs in the past */ + ReleaseDPCache(dpcache, writeLocked); + } + return acrl; +} + +/* invalidate the CRL cache for a given issuer, which forces a refetch of + CRL objects from PKCS#11 tokens */ +void +CERT_CRLCacheRefreshIssuer(CERTCertDBHandle* dbhandle, SECItem* crlKey) +{ + CRLDPCache* cache = NULL; + SECStatus rv = SECSuccess; + PRBool writeLocked = PR_FALSE; + PRBool readlocked; + + (void)dbhandle; /* silence compiler warnings */ + + /* XCRL we will need to refresh all the DPs of the issuer in the future, + not just the default one */ + rv = AcquireDPCache(NULL, crlKey, NULL, 0, NULL, &cache, &writeLocked); + if (SECSuccess != rv) { + return; + } + /* we need to invalidate the DPCache here */ + readlocked = (writeLocked == PR_TRUE ? PR_FALSE : PR_TRUE); + DPCache_LockWrite(); + cache->refresh = PR_TRUE; + DPCache_UnlockWrite(); + ReleaseDPCache(cache, writeLocked); + return; +} + +/* add the specified RAM CRL object to the cache */ +SECStatus +CERT_CacheCRL(CERTCertDBHandle* dbhandle, SECItem* newdercrl) +{ + CRLDPCache* cache = NULL; + SECStatus rv = SECSuccess; + PRBool writeLocked = PR_FALSE; + PRBool readlocked; + CachedCrl* returned = NULL; + PRBool added = PR_FALSE; + CERTSignedCrl* newcrl = NULL; + int realerror = 0; + + if (!dbhandle || !newdercrl) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* first decode the DER CRL to make sure it's OK */ + newcrl = CERT_DecodeDERCrlWithFlags(NULL, newdercrl, SEC_CRL_TYPE, + CRL_DECODE_DONT_COPY_DER | + CRL_DECODE_SKIP_ENTRIES); + + if (!newcrl) { + return SECFailure; + } + + /* XXX check if it has IDP extension. If so, do not proceed and set error */ + + rv = AcquireDPCache(NULL, &newcrl->crl.derName, NULL, 0, NULL, &cache, + &writeLocked); + if (SECSuccess == rv) { + readlocked = (writeLocked == PR_TRUE ? PR_FALSE : PR_TRUE); + + rv = CachedCrl_Create(&returned, newcrl, CRL_OriginExplicit); + if (SECSuccess == rv && returned) { + DPCache_LockWrite(); + rv = DPCache_AddCRL(cache, returned, &added); + if (PR_TRUE != added) { + realerror = PORT_GetError(); + CachedCrl_Destroy(returned); + returned = NULL; + } + DPCache_UnlockWrite(); + } + + ReleaseDPCache(cache, writeLocked); + + if (!added) { + rv = SECFailure; + } + } + SEC_DestroyCrl(newcrl); /* free the CRL. Either it got added to the cache + and the refcount got bumped, or not, and thus we need to free its + RAM */ + if (realerror) { + PORT_SetError(realerror); + } + return rv; +} + +/* remove the specified RAM CRL object from the cache */ +SECStatus +CERT_UncacheCRL(CERTCertDBHandle* dbhandle, SECItem* olddercrl) +{ + CRLDPCache* cache = NULL; + SECStatus rv = SECSuccess; + PRBool writeLocked = PR_FALSE; + PRBool readlocked; + PRBool removed = PR_FALSE; + PRUint32 i; + CERTSignedCrl* oldcrl = NULL; + + if (!dbhandle || !olddercrl) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* first decode the DER CRL to make sure it's OK */ + oldcrl = CERT_DecodeDERCrlWithFlags(NULL, olddercrl, SEC_CRL_TYPE, + CRL_DECODE_DONT_COPY_DER | + CRL_DECODE_SKIP_ENTRIES); + + if (!oldcrl) { + /* if this DER CRL can't decode, it can't be in the cache */ + return SECFailure; + } + + rv = AcquireDPCache(NULL, &oldcrl->crl.derName, NULL, 0, NULL, &cache, + &writeLocked); + if (SECSuccess == rv) { + CachedCrl* returned = NULL; + + readlocked = (writeLocked == PR_TRUE ? PR_FALSE : PR_TRUE); + + rv = CachedCrl_Create(&returned, oldcrl, CRL_OriginExplicit); + if (SECSuccess == rv && returned) { + DPCache_LockWrite(); + for (i = 0; i < cache->ncrls; i++) { + PRBool dupe = PR_FALSE, updated = PR_FALSE; + rv = CachedCrl_Compare(returned, cache->crls[i], &dupe, + &updated); + if (SECSuccess != rv) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + break; + } + if (PR_TRUE == dupe) { + rv = DPCache_RemoveCRL(cache, i); /* got a match */ + if (SECSuccess == rv) { + cache->mustchoose = PR_TRUE; + removed = PR_TRUE; + } + break; + } + } + + DPCache_UnlockWrite(); + + if (SECSuccess != CachedCrl_Destroy(returned)) { + rv = SECFailure; + } + } + + ReleaseDPCache(cache, writeLocked); + } + if (SECSuccess != SEC_DestroyCrl(oldcrl)) { + /* need to do this because object is refcounted */ + rv = SECFailure; + } + if (SECSuccess == rv && PR_TRUE != removed) { + PORT_SetError(SEC_ERROR_CRL_NOT_FOUND); + } + return rv; +} + +SECStatus +cert_AcquireNamedCRLCache(NamedCRLCache** returned) +{ + PORT_Assert(returned); + if (!namedCRLCache.lock) { + PORT_Assert(0); + return SECFailure; + } + PR_Lock(namedCRLCache.lock); + *returned = &namedCRLCache; + return SECSuccess; +} + +/* This must be called only while cache is acquired, and the entry is only + * valid until cache is released. + */ +SECStatus +cert_FindCRLByGeneralName(NamedCRLCache* ncc, const SECItem* canonicalizedName, + NamedCRLCacheEntry** retEntry) +{ + if (!ncc || !canonicalizedName || !retEntry) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + *retEntry = (NamedCRLCacheEntry*)PL_HashTableLookup( + namedCRLCache.entries, (void*)canonicalizedName); + return SECSuccess; +} + +SECStatus +cert_ReleaseNamedCRLCache(NamedCRLCache* ncc) +{ + if (!ncc) { + return SECFailure; + } + if (!ncc->lock) { + PORT_Assert(0); + return SECFailure; + } + PR_Unlock(namedCRLCache.lock); + return SECSuccess; +} + +/* creates new named cache entry from CRL, and tries to add it to CRL cache */ +static SECStatus +addCRLToCache(CERTCertDBHandle* dbhandle, SECItem* crl, + const SECItem* canonicalizedName, NamedCRLCacheEntry** newEntry) +{ + SECStatus rv = SECSuccess; + NamedCRLCacheEntry* entry = NULL; + + /* create new named entry */ + if (SECSuccess != NamedCRLCacheEntry_Create(newEntry) || !*newEntry) { + /* no need to keep unused CRL around */ + SECITEM_ZfreeItem(crl, PR_TRUE); + return SECFailure; + } + entry = *newEntry; + entry->crl = crl; /* named CRL cache owns DER */ + entry->lastAttemptTime = PR_Now(); + entry->canonicalizedName = SECITEM_DupItem(canonicalizedName); + if (!entry->canonicalizedName) { + rv = NamedCRLCacheEntry_Destroy(entry); /* destroys CRL too */ + PORT_Assert(SECSuccess == rv); + return SECFailure; + } + /* now, attempt to insert CRL into CRL cache */ + if (SECSuccess == CERT_CacheCRL(dbhandle, entry->crl)) { + entry->inCRLCache = PR_TRUE; + entry->successfulInsertionTime = entry->lastAttemptTime; + } else { + switch (PR_GetError()) { + case SEC_ERROR_CRL_ALREADY_EXISTS: + entry->dupe = PR_TRUE; + break; + + case SEC_ERROR_BAD_DER: + entry->badDER = PR_TRUE; + break; + + /* all other reasons */ + default: + entry->unsupported = PR_TRUE; + break; + } + rv = SECFailure; + /* no need to keep unused CRL around */ + SECITEM_ZfreeItem(entry->crl, PR_TRUE); + entry->crl = NULL; + } + return rv; +} + +/* take ownership of CRL, and insert it into the named CRL cache + * and indexed CRL cache + */ +SECStatus +cert_CacheCRLByGeneralName(CERTCertDBHandle* dbhandle, SECItem* crl, + const SECItem* canonicalizedName) +{ + NamedCRLCacheEntry *oldEntry, *newEntry = NULL; + NamedCRLCache* ncc = NULL; + SECStatus rv = SECSuccess; + + PORT_Assert(namedCRLCache.lock); + PORT_Assert(namedCRLCache.entries); + + if (!crl || !canonicalizedName) { + PORT_Assert(0); + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + rv = cert_AcquireNamedCRLCache(&ncc); + PORT_Assert(SECSuccess == rv); + if (SECSuccess != rv) { + SECITEM_ZfreeItem(crl, PR_TRUE); + return SECFailure; + } + rv = cert_FindCRLByGeneralName(ncc, canonicalizedName, &oldEntry); + PORT_Assert(SECSuccess == rv); + if (SECSuccess != rv) { + (void)cert_ReleaseNamedCRLCache(ncc); + SECITEM_ZfreeItem(crl, PR_TRUE); + return SECFailure; + } + if (SECSuccess == + addCRLToCache(dbhandle, crl, canonicalizedName, &newEntry)) { + if (!oldEntry) { + /* add new good entry to the hash table */ + if (NULL == PL_HashTableAdd(namedCRLCache.entries, + (void*)newEntry->canonicalizedName, + (void*)newEntry)) { + PORT_Assert(0); + NamedCRLCacheEntry_Destroy(newEntry); + rv = SECFailure; + } + } else { + PRBool removed; + /* remove the old CRL from the cache if needed */ + if (oldEntry->inCRLCache) { + rv = CERT_UncacheCRL(dbhandle, oldEntry->crl); + PORT_Assert(SECSuccess == rv); + } + removed = PL_HashTableRemove(namedCRLCache.entries, + (void*)oldEntry->canonicalizedName); + PORT_Assert(removed); + if (!removed) { + rv = SECFailure; + /* leak old entry since we couldn't remove it from the hash + * table */ + } else { + PORT_CheckSuccess(NamedCRLCacheEntry_Destroy(oldEntry)); + } + if (NULL == PL_HashTableAdd(namedCRLCache.entries, + (void*)newEntry->canonicalizedName, + (void*)newEntry)) { + PORT_Assert(0); + rv = SECFailure; + } + } + } else { + /* error adding new CRL to cache */ + if (!oldEntry) { + /* no old cache entry, use the new one even though it's bad */ + if (NULL == PL_HashTableAdd(namedCRLCache.entries, + (void*)newEntry->canonicalizedName, + (void*)newEntry)) { + PORT_Assert(0); + rv = SECFailure; + } + } else { + if (oldEntry->inCRLCache) { + /* previous cache entry was good, keep it and update time */ + oldEntry->lastAttemptTime = newEntry->lastAttemptTime; + /* throw away new bad entry */ + rv = NamedCRLCacheEntry_Destroy(newEntry); + PORT_Assert(SECSuccess == rv); + } else { + /* previous cache entry was bad, just replace it */ + PRBool removed = PL_HashTableRemove( + namedCRLCache.entries, (void*)oldEntry->canonicalizedName); + PORT_Assert(removed); + if (!removed) { + /* leak old entry since we couldn't remove it from the hash + * table */ + rv = SECFailure; + } else { + PORT_CheckSuccess(NamedCRLCacheEntry_Destroy(oldEntry)); + } + if (NULL == PL_HashTableAdd(namedCRLCache.entries, + (void*)newEntry->canonicalizedName, + (void*)newEntry)) { + PORT_Assert(0); + rv = SECFailure; + } + } + } + } + PORT_CheckSuccess(cert_ReleaseNamedCRLCache(ncc)); + + return rv; +} + +static SECStatus +CachedCrl_Create(CachedCrl** returned, CERTSignedCrl* crl, CRLOrigin origin) +{ + CachedCrl* newcrl = NULL; + if (!returned) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + newcrl = PORT_ZAlloc(sizeof(CachedCrl)); + if (!newcrl) { + return SECFailure; + } + newcrl->crl = SEC_DupCrl(crl); + newcrl->origin = origin; + *returned = newcrl; + return SECSuccess; +} + +/* empty the cache content */ +static SECStatus +CachedCrl_Depopulate(CachedCrl* crl) +{ + if (!crl) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + /* destroy the hash table */ + if (crl->entries) { + PL_HashTableDestroy(crl->entries); + crl->entries = NULL; + } + + /* free the pre buffer */ + if (crl->prebuffer) { + PreAllocator_Destroy(crl->prebuffer); + crl->prebuffer = NULL; + } + return SECSuccess; +} + +static SECStatus +CachedCrl_Destroy(CachedCrl* crl) +{ + if (!crl) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + CachedCrl_Depopulate(crl); + SEC_DestroyCrl(crl->crl); + PORT_Free(crl); + return SECSuccess; +} + +/* create hash table of CRL entries */ +static SECStatus +CachedCrl_Populate(CachedCrl* crlobject) +{ + SECStatus rv = SECFailure; + CERTCrlEntry** crlEntry = NULL; + PRUint32 numEntries = 0; + + if (!crlobject) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + /* complete the entry decoding . XXX thread-safety of CRL object */ + rv = CERT_CompleteCRLDecodeEntries(crlobject->crl); + if (SECSuccess != rv) { + crlobject->unbuildable = PR_TRUE; /* don't try to build this again */ + return SECFailure; + } + + if (crlobject->entries && crlobject->prebuffer) { + /* cache is already built */ + return SECSuccess; + } + + /* build the hash table from the full CRL */ + /* count CRL entries so we can pre-allocate space for hash table entries */ + for (crlEntry = crlobject->crl->crl.entries; crlEntry && *crlEntry; + crlEntry++) { + numEntries++; + } + crlobject->prebuffer = + PreAllocator_Create(numEntries * sizeof(PLHashEntry)); + PORT_Assert(crlobject->prebuffer); + if (!crlobject->prebuffer) { + return SECFailure; + } + /* create a new hash table */ + crlobject->entries = + PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare, PL_CompareValues, + &preAllocOps, crlobject->prebuffer); + PORT_Assert(crlobject->entries); + if (!crlobject->entries) { + return SECFailure; + } + /* add all serial numbers to the hash table */ + for (crlEntry = crlobject->crl->crl.entries; crlEntry && *crlEntry; + crlEntry++) { + PL_HashTableAdd(crlobject->entries, &(*crlEntry)->serialNumber, + *crlEntry); + } + + return SECSuccess; +} + +/* returns true if there are CRLs from PKCS#11 slots */ +static PRBool +DPCache_HasTokenCRLs(CRLDPCache* cache) +{ + PRBool answer = PR_FALSE; + PRUint32 i; + for (i = 0; i < cache->ncrls; i++) { + if (cache->crls[i] && (CRL_OriginToken == cache->crls[i]->origin)) { + answer = PR_TRUE; + break; + } + } + return answer; +} + +/* are these CRLs the same, as far as the cache is concerned ? */ +/* are these CRLs the same token object but with different DER ? + This can happen if the DER CRL got updated in the token, but the PKCS#11 + object ID did not change. NSS softoken has the unfortunate property to + never change the object ID for CRL objects. */ +static SECStatus +CachedCrl_Compare(CachedCrl* a, CachedCrl* b, PRBool* isDupe, PRBool* isUpdated) +{ + PORT_Assert(a); + PORT_Assert(b); + PORT_Assert(isDupe); + PORT_Assert(isUpdated); + if (!a || !b || !isDupe || !isUpdated || !a->crl || !b->crl) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + *isDupe = *isUpdated = PR_FALSE; + + if (a == b) { + /* dupe */ + *isDupe = PR_TRUE; + *isUpdated = PR_FALSE; + return SECSuccess; + } + if (b->origin != a->origin) { + /* CRLs of different origins are not considered dupes, + and can't be updated either */ + return SECSuccess; + } + if (CRL_OriginToken == b->origin) { + /* for token CRLs, slot and PKCS#11 object handle must match for CRL + to truly be a dupe */ + if ((b->crl->slot == a->crl->slot) && + (b->crl->pkcs11ID == a->crl->pkcs11ID)) { + /* ASN.1 DER needs to match for dupe check */ + /* could optimize by just checking a few fields like thisUpdate */ + if (SECEqual == + SECITEM_CompareItem(b->crl->derCrl, a->crl->derCrl)) { + *isDupe = PR_TRUE; + } else { + *isUpdated = PR_TRUE; + } + } + return SECSuccess; + } + if (CRL_OriginExplicit == b->origin) { + /* We need to make sure this is the same object that the user provided + to CERT_CacheCRL previously. That API takes a SECItem*, thus, we + just do a pointer comparison here. + */ + if (b->crl->derCrl == a->crl->derCrl) { + *isDupe = PR_TRUE; + } + } + return SECSuccess; +} diff --git a/security/nss/lib/certdb/exports.gyp b/security/nss/lib/certdb/exports.gyp new file mode 100644 index 0000000000..359c93041f --- /dev/null +++ b/security/nss/lib/certdb/exports.gyp @@ -0,0 +1,36 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +{ + 'includes': [ + '../../coreconf/config.gypi' + ], + 'targets': [ + { + 'target_name': 'lib_certdb_exports', + 'type': 'none', + 'copies': [ + { + 'files': [ + 'cert.h', + 'certdb.h', + 'certt.h' + ], + 'destination': '<(nss_public_dist_dir)/<(module)' + }, + { + 'files': [ + 'certi.h', + 'certxutl.h', + 'genname.h', + 'xconst.h' + ], + 'destination': '<(nss_private_dist_dir)/<(module)' + } + ] + } + ], + 'variables': { + 'module': 'nss' + } +} diff --git a/security/nss/lib/certdb/genname.c b/security/nss/lib/certdb/genname.c new file mode 100644 index 0000000000..764a5c1cc5 --- /dev/null +++ b/security/nss/lib/certdb/genname.c @@ -0,0 +1,2011 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "plarena.h" +#include "seccomon.h" +#include "secitem.h" +#include "secoidt.h" +#include "secasn1.h" +#include "secder.h" +#include "certt.h" +#include "cert.h" +#include "certi.h" +#include "xconst.h" +#include "secerr.h" +#include "secoid.h" +#include "prprf.h" +#include "genname.h" + +SEC_ASN1_MKSUB(SEC_AnyTemplate) +SEC_ASN1_MKSUB(SEC_IntegerTemplate) +SEC_ASN1_MKSUB(SEC_IA5StringTemplate) +SEC_ASN1_MKSUB(SEC_ObjectIDTemplate) +SEC_ASN1_MKSUB(SEC_OctetStringTemplate) + +static const SEC_ASN1Template CERTNameConstraintTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTNameConstraint) }, + { SEC_ASN1_ANY, offsetof(CERTNameConstraint, DERName) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, + offsetof(CERTNameConstraint, min), SEC_ASN1_SUB(SEC_IntegerTemplate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1, + offsetof(CERTNameConstraint, max), SEC_ASN1_SUB(SEC_IntegerTemplate) }, + { 0 } +}; + +const SEC_ASN1Template CERT_NameConstraintSubtreeSubTemplate[] = { + { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN, 0, SEC_ASN1_SUB(SEC_AnyTemplate) } +}; + +static const SEC_ASN1Template CERTNameConstraintsTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTNameConstraints) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(CERTNameConstraints, DERPermited), + CERT_NameConstraintSubtreeSubTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(CERTNameConstraints, DERExcluded), + CERT_NameConstraintSubtreeSubTemplate }, + { 0 } +}; + +static const SEC_ASN1Template CERTOthNameTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(OtherName) }, + { SEC_ASN1_OBJECT_ID, offsetof(OtherName, oid) }, + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | + SEC_ASN1_XTRN | 0, + offsetof(OtherName, name), SEC_ASN1_SUB(SEC_AnyTemplate) }, + { 0 } +}; + +static const SEC_ASN1Template CERTOtherNameTemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | 0, + offsetof(CERTGeneralName, name.OthName), CERTOthNameTemplate, + sizeof(CERTGeneralName) } +}; + +static const SEC_ASN1Template CERT_RFC822NameTemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1, + offsetof(CERTGeneralName, name.other), + SEC_ASN1_SUB(SEC_IA5StringTemplate), sizeof(CERTGeneralName) } +}; + +static const SEC_ASN1Template CERT_DNSNameTemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2, + offsetof(CERTGeneralName, name.other), + SEC_ASN1_SUB(SEC_IA5StringTemplate), sizeof(CERTGeneralName) } +}; + +static const SEC_ASN1Template CERT_X400AddressTemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_XTRN | 3, + offsetof(CERTGeneralName, name.other), SEC_ASN1_SUB(SEC_AnyTemplate), + sizeof(CERTGeneralName) } +}; + +static const SEC_ASN1Template CERT_DirectoryNameTemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | + SEC_ASN1_XTRN | 4, + offsetof(CERTGeneralName, derDirectoryName), + SEC_ASN1_SUB(SEC_AnyTemplate), sizeof(CERTGeneralName) } +}; + +static const SEC_ASN1Template CERT_EDIPartyNameTemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_XTRN | 5, + offsetof(CERTGeneralName, name.other), SEC_ASN1_SUB(SEC_AnyTemplate), + sizeof(CERTGeneralName) } +}; + +static const SEC_ASN1Template CERT_URITemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 6, + offsetof(CERTGeneralName, name.other), + SEC_ASN1_SUB(SEC_IA5StringTemplate), sizeof(CERTGeneralName) } +}; + +static const SEC_ASN1Template CERT_IPAddressTemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 7, + offsetof(CERTGeneralName, name.other), + SEC_ASN1_SUB(SEC_OctetStringTemplate), sizeof(CERTGeneralName) } +}; + +static const SEC_ASN1Template CERT_RegisteredIDTemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 8, + offsetof(CERTGeneralName, name.other), SEC_ASN1_SUB(SEC_ObjectIDTemplate), + sizeof(CERTGeneralName) } +}; + +const SEC_ASN1Template CERT_GeneralNamesTemplate[] = { + { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN, 0, SEC_ASN1_SUB(SEC_AnyTemplate) } +}; + +static struct { + CERTGeneralNameType type; + char *name; +} typesArray[] = { { certOtherName, "other" }, + { certRFC822Name, "email" }, + { certRFC822Name, "rfc822" }, + { certDNSName, "dns" }, + { certX400Address, "x400" }, + { certX400Address, "x400addr" }, + { certDirectoryName, "directory" }, + { certDirectoryName, "dn" }, + { certEDIPartyName, "edi" }, + { certEDIPartyName, "ediparty" }, + { certURI, "uri" }, + { certIPAddress, "ip" }, + { certIPAddress, "ipaddr" }, + { certRegisterID, "registerid" } }; + +CERTGeneralNameType +CERT_GetGeneralNameTypeFromString(const char *string) +{ + int types_count = sizeof(typesArray) / sizeof(typesArray[0]); + int i; + + for (i = 0; i < types_count; i++) { + if (PORT_Strcasecmp(string, typesArray[i].name) == 0) { + return typesArray[i].type; + } + } + return 0; +} + +CERTGeneralName * +CERT_NewGeneralName(PLArenaPool *arena, CERTGeneralNameType type) +{ + CERTGeneralName *name = arena ? PORT_ArenaZNew(arena, CERTGeneralName) + : PORT_ZNew(CERTGeneralName); + if (name) { + name->type = type; + name->l.prev = name->l.next = &name->l; + } + return name; +} + +/* Copy content of one General Name to another. +** Caller has allocated destination general name. +** This function does not change the destinate's GeneralName's list linkage. +*/ +SECStatus +cert_CopyOneGeneralName(PLArenaPool *arena, CERTGeneralName *dest, + CERTGeneralName *src) +{ + SECStatus rv; + void *mark = NULL; + + PORT_Assert(dest != NULL); + dest->type = src->type; + + mark = PORT_ArenaMark(arena); + + switch (src->type) { + case certDirectoryName: + rv = SECITEM_CopyItem(arena, &dest->derDirectoryName, + &src->derDirectoryName); + if (rv == SECSuccess) + rv = CERT_CopyName(arena, &dest->name.directoryName, + &src->name.directoryName); + break; + + case certOtherName: + rv = SECITEM_CopyItem(arena, &dest->name.OthName.name, + &src->name.OthName.name); + if (rv == SECSuccess) + rv = SECITEM_CopyItem(arena, &dest->name.OthName.oid, + &src->name.OthName.oid); + break; + + default: + rv = SECITEM_CopyItem(arena, &dest->name.other, &src->name.other); + break; + } + if (rv != SECSuccess) { + PORT_ArenaRelease(arena, mark); + } else { + PORT_ArenaUnmark(arena, mark); + } + return rv; +} + +void +CERT_DestroyGeneralNameList(CERTGeneralNameList *list) +{ + PZLock *lock; + + if (list != NULL) { + lock = list->lock; + PZ_Lock(lock); + if (--list->refCount <= 0 && list->arena != NULL) { + PORT_FreeArena(list->arena, PR_FALSE); + PZ_Unlock(lock); + PZ_DestroyLock(lock); + } else { + PZ_Unlock(lock); + } + } + return; +} + +CERTGeneralNameList * +CERT_CreateGeneralNameList(CERTGeneralName *name) +{ + PLArenaPool *arena; + CERTGeneralNameList *list = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + goto done; + } + list = PORT_ArenaZNew(arena, CERTGeneralNameList); + if (!list) + goto loser; + if (name != NULL) { + SECStatus rv; + list->name = CERT_NewGeneralName(arena, (CERTGeneralNameType)0); + if (!list->name) + goto loser; + rv = CERT_CopyGeneralName(arena, list->name, name); + if (rv != SECSuccess) + goto loser; + } + list->lock = PZ_NewLock(nssILockList); + if (!list->lock) + goto loser; + list->arena = arena; + list->refCount = 1; +done: + return list; + +loser: + PORT_FreeArena(arena, PR_FALSE); + return NULL; +} + +CERTGeneralName * +CERT_GetNextGeneralName(CERTGeneralName *current) +{ + PRCList *next; + + next = current->l.next; + return (CERTGeneralName *)(((char *)next) - offsetof(CERTGeneralName, l)); +} + +CERTGeneralName * +CERT_GetPrevGeneralName(CERTGeneralName *current) +{ + PRCList *prev; + prev = current->l.prev; + return (CERTGeneralName *)(((char *)prev) - offsetof(CERTGeneralName, l)); +} + +CERTNameConstraint * +CERT_GetNextNameConstraint(CERTNameConstraint *current) +{ + PRCList *next; + + next = current->l.next; + return (CERTNameConstraint *)(((char *)next) - + offsetof(CERTNameConstraint, l)); +} + +CERTNameConstraint * +CERT_GetPrevNameConstraint(CERTNameConstraint *current) +{ + PRCList *prev; + prev = current->l.prev; + return (CERTNameConstraint *)(((char *)prev) - + offsetof(CERTNameConstraint, l)); +} + +SECItem * +CERT_EncodeGeneralName(CERTGeneralName *genName, SECItem *dest, + PLArenaPool *arena) +{ + + const SEC_ASN1Template *template; + + PORT_Assert(arena); + if (arena == NULL || !genName) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + /* TODO: mark arena */ + if (dest == NULL) { + dest = PORT_ArenaZNew(arena, SECItem); + if (!dest) + goto loser; + } + if (genName->type == certDirectoryName) { + if (genName->derDirectoryName.data == NULL) { + /* The field hasn't been encoded yet. */ + SECItem *pre_dest = SEC_ASN1EncodeItem( + arena, &(genName->derDirectoryName), + &(genName->name.directoryName), CERT_NameTemplate); + if (!pre_dest) + goto loser; + } + if (genName->derDirectoryName.data == NULL) { + goto loser; + } + } + switch (genName->type) { + case certURI: + template = CERT_URITemplate; + break; + case certRFC822Name: + template = CERT_RFC822NameTemplate; + break; + case certDNSName: + template = CERT_DNSNameTemplate; + break; + case certIPAddress: + template = CERT_IPAddressTemplate; + break; + case certOtherName: + template = CERTOtherNameTemplate; + break; + case certRegisterID: + template = CERT_RegisteredIDTemplate; + break; + /* for this type, we expect the value is already encoded */ + case certEDIPartyName: + template = CERT_EDIPartyNameTemplate; + break; + /* for this type, we expect the value is already encoded */ + case certX400Address: + template = CERT_X400AddressTemplate; + break; + case certDirectoryName: + template = CERT_DirectoryNameTemplate; + break; + default: + PORT_Assert(0); + goto loser; + } + dest = SEC_ASN1EncodeItem(arena, dest, genName, template); + if (!dest) { + goto loser; + } + /* TODO: unmark arena */ + return dest; +loser: + /* TODO: release arena back to mark */ + return NULL; +} + +SECItem ** +cert_EncodeGeneralNames(PLArenaPool *arena, CERTGeneralName *names) +{ + CERTGeneralName *current_name; + SECItem **items = NULL; + int count = 1; + int i; + PRCList *head; + + if (!names) { + return NULL; + } + + PORT_Assert(arena); + /* TODO: mark arena */ + current_name = names; + head = &(names->l); + while (current_name->l.next != head) { + current_name = CERT_GetNextGeneralName(current_name); + ++count; + } + current_name = CERT_GetNextGeneralName(current_name); + items = PORT_ArenaNewArray(arena, SECItem *, count + 1); + if (items == NULL) { + goto loser; + } + for (i = 0; i < count; i++) { + items[i] = CERT_EncodeGeneralName(current_name, (SECItem *)NULL, arena); + if (items[i] == NULL) { + goto loser; + } + current_name = CERT_GetNextGeneralName(current_name); + } + items[i] = NULL; + /* TODO: unmark arena */ + return items; +loser: + /* TODO: release arena to mark */ + return NULL; +} + +CERTGeneralName * +CERT_DecodeGeneralName(PLArenaPool *reqArena, SECItem *encodedName, + CERTGeneralName *genName) +{ + const SEC_ASN1Template *template; + CERTGeneralNameType genNameType; + SECStatus rv = SECSuccess; + SECItem *newEncodedName; + + if (!reqArena) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + /* make a copy for decoding so the data decoded with QuickDER doesn't + point to temporary memory */ + newEncodedName = SECITEM_ArenaDupItem(reqArena, encodedName); + if (!newEncodedName) { + return NULL; + } + /* TODO: mark arena */ + genNameType = (CERTGeneralNameType)((*(newEncodedName->data) & 0x0f) + 1); + if (genName == NULL) { + genName = CERT_NewGeneralName(reqArena, genNameType); + if (!genName) + goto loser; + } else { + genName->type = genNameType; + genName->l.prev = genName->l.next = &genName->l; + } + + switch (genNameType) { + case certURI: + template = CERT_URITemplate; + break; + case certRFC822Name: + template = CERT_RFC822NameTemplate; + break; + case certDNSName: + template = CERT_DNSNameTemplate; + break; + case certIPAddress: + template = CERT_IPAddressTemplate; + break; + case certOtherName: + template = CERTOtherNameTemplate; + break; + case certRegisterID: + template = CERT_RegisteredIDTemplate; + break; + case certEDIPartyName: + template = CERT_EDIPartyNameTemplate; + break; + case certX400Address: + template = CERT_X400AddressTemplate; + break; + case certDirectoryName: + template = CERT_DirectoryNameTemplate; + break; + default: + goto loser; + } + rv = SEC_QuickDERDecodeItem(reqArena, genName, template, newEncodedName); + if (rv != SECSuccess) + goto loser; + if (genNameType == certDirectoryName) { + rv = SEC_QuickDERDecodeItem(reqArena, &(genName->name.directoryName), + CERT_NameTemplate, + &(genName->derDirectoryName)); + if (rv != SECSuccess) + goto loser; + } + + /* TODO: unmark arena */ + return genName; +loser: + /* TODO: release arena to mark */ + return NULL; +} + +CERTGeneralName * +cert_DecodeGeneralNames(PLArenaPool *arena, SECItem **encodedGenName) +{ + PRCList *head = NULL; + PRCList *tail = NULL; + CERTGeneralName *currentName = NULL; + + PORT_Assert(arena); + if (!encodedGenName || !arena) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + /* TODO: mark arena */ + while (*encodedGenName != NULL) { + currentName = CERT_DecodeGeneralName(arena, *encodedGenName, NULL); + if (currentName == NULL) + break; + if (head == NULL) { + head = &(currentName->l); + tail = head; + } + currentName->l.next = head; + currentName->l.prev = tail; + tail = head->prev = tail->next = &(currentName->l); + encodedGenName++; + } + if (currentName) { + /* TODO: unmark arena */ + return CERT_GetNextGeneralName(currentName); + } + /* TODO: release arena to mark */ + return NULL; +} + +void +CERT_DestroyGeneralName(CERTGeneralName *name) +{ + cert_DestroyGeneralNames(name); +} + +SECStatus +cert_DestroyGeneralNames(CERTGeneralName *name) +{ + CERTGeneralName *first; + CERTGeneralName *next = NULL; + + first = name; + do { + next = CERT_GetNextGeneralName(name); + PORT_Free(name); + name = next; + } while (name != first); + return SECSuccess; +} + +static SECItem * +cert_EncodeNameConstraint(CERTNameConstraint *constraint, SECItem *dest, + PLArenaPool *arena) +{ + PORT_Assert(arena); + if (dest == NULL) { + dest = PORT_ArenaZNew(arena, SECItem); + if (dest == NULL) { + return NULL; + } + } + CERT_EncodeGeneralName(&(constraint->name), &(constraint->DERName), arena); + + dest = + SEC_ASN1EncodeItem(arena, dest, constraint, CERTNameConstraintTemplate); + return dest; +} + +SECStatus +cert_EncodeNameConstraintSubTree(CERTNameConstraint *constraints, + PLArenaPool *arena, SECItem ***dest, + PRBool permited) +{ + CERTNameConstraint *current_constraint = constraints; + SECItem **items = NULL; + int count = 0; + int i; + PRCList *head; + + PORT_Assert(arena); + /* TODO: mark arena */ + if (constraints != NULL) { + count = 1; + } + head = &constraints->l; + while (current_constraint->l.next != head) { + current_constraint = CERT_GetNextNameConstraint(current_constraint); + ++count; + } + current_constraint = CERT_GetNextNameConstraint(current_constraint); + items = PORT_ArenaZNewArray(arena, SECItem *, count + 1); + if (items == NULL) { + goto loser; + } + for (i = 0; i < count; i++) { + items[i] = cert_EncodeNameConstraint(current_constraint, + (SECItem *)NULL, arena); + if (items[i] == NULL) { + goto loser; + } + current_constraint = CERT_GetNextNameConstraint(current_constraint); + } + *dest = items; + if (*dest == NULL) { + goto loser; + } + /* TODO: unmark arena */ + return SECSuccess; +loser: + /* TODO: release arena to mark */ + return SECFailure; +} + +SECStatus +cert_EncodeNameConstraints(CERTNameConstraints *constraints, PLArenaPool *arena, + SECItem *dest) +{ + SECStatus rv = SECSuccess; + + PORT_Assert(arena); + /* TODO: mark arena */ + if (constraints->permited != NULL) { + rv = cert_EncodeNameConstraintSubTree( + constraints->permited, arena, &constraints->DERPermited, PR_TRUE); + if (rv == SECFailure) { + goto loser; + } + } + if (constraints->excluded != NULL) { + rv = cert_EncodeNameConstraintSubTree( + constraints->excluded, arena, &constraints->DERExcluded, PR_FALSE); + if (rv == SECFailure) { + goto loser; + } + } + dest = SEC_ASN1EncodeItem(arena, dest, constraints, + CERTNameConstraintsTemplate); + if (dest == NULL) { + goto loser; + } + /* TODO: unmark arena */ + return SECSuccess; +loser: + /* TODO: release arena to mark */ + return SECFailure; +} + +CERTNameConstraint * +cert_DecodeNameConstraint(PLArenaPool *reqArena, SECItem *encodedConstraint) +{ + CERTNameConstraint *constraint; + SECStatus rv = SECSuccess; + CERTGeneralName *temp; + SECItem *newEncodedConstraint; + + if (!reqArena) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + newEncodedConstraint = SECITEM_ArenaDupItem(reqArena, encodedConstraint); + if (!newEncodedConstraint) { + return NULL; + } + /* TODO: mark arena */ + constraint = PORT_ArenaZNew(reqArena, CERTNameConstraint); + if (!constraint) + goto loser; + rv = SEC_QuickDERDecodeItem( + reqArena, constraint, CERTNameConstraintTemplate, newEncodedConstraint); + if (rv != SECSuccess) { + goto loser; + } + temp = CERT_DecodeGeneralName(reqArena, &(constraint->DERName), + &(constraint->name)); + if (temp != &(constraint->name)) { + goto loser; + } + + /* ### sjlee: since the name constraint contains only one + * CERTGeneralName, the list within CERTGeneralName shouldn't + * point anywhere else. Otherwise, bad things will happen. + */ + constraint->name.l.prev = constraint->name.l.next = &(constraint->name.l); + /* TODO: unmark arena */ + return constraint; +loser: + /* TODO: release arena back to mark */ + return NULL; +} + +static CERTNameConstraint * +cert_DecodeNameConstraintSubTree(PLArenaPool *arena, SECItem **subTree, + PRBool permited) +{ + CERTNameConstraint *current = NULL; + CERTNameConstraint *first = NULL; + CERTNameConstraint *last = NULL; + int i = 0; + + PORT_Assert(arena); + /* TODO: mark arena */ + while (subTree[i] != NULL) { + current = cert_DecodeNameConstraint(arena, subTree[i]); + if (current == NULL) { + goto loser; + } + if (first == NULL) { + first = current; + } else { + current->l.prev = &(last->l); + last->l.next = &(current->l); + } + last = current; + i++; + } + if (first && last) { + first->l.prev = &(last->l); + last->l.next = &(first->l); + } + /* TODO: unmark arena */ + return first; +loser: + /* TODO: release arena back to mark */ + return NULL; +} + +CERTNameConstraints * +cert_DecodeNameConstraints(PLArenaPool *reqArena, + const SECItem *encodedConstraints) +{ + CERTNameConstraints *constraints; + SECStatus rv; + SECItem *newEncodedConstraints; + + if (!reqArena) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + PORT_Assert(encodedConstraints); + newEncodedConstraints = SECITEM_ArenaDupItem(reqArena, encodedConstraints); + + /* TODO: mark arena */ + constraints = PORT_ArenaZNew(reqArena, CERTNameConstraints); + if (constraints == NULL) { + goto loser; + } + rv = SEC_QuickDERDecodeItem(reqArena, constraints, + CERTNameConstraintsTemplate, + newEncodedConstraints); + if (rv != SECSuccess) { + goto loser; + } + if (constraints->DERPermited != NULL && + constraints->DERPermited[0] != NULL) { + constraints->permited = cert_DecodeNameConstraintSubTree( + reqArena, constraints->DERPermited, PR_TRUE); + if (constraints->permited == NULL) { + goto loser; + } + } + if (constraints->DERExcluded != NULL && + constraints->DERExcluded[0] != NULL) { + constraints->excluded = cert_DecodeNameConstraintSubTree( + reqArena, constraints->DERExcluded, PR_FALSE); + if (constraints->excluded == NULL) { + goto loser; + } + } + /* TODO: unmark arena */ + return constraints; +loser: + /* TODO: release arena back to mark */ + return NULL; +} + +/* Copy a chain of one or more general names to a destination chain. +** Caller has allocated at least the first destination GeneralName struct. +** Both source and destination chains are circular doubly-linked lists. +** The first source struct is copied to the first destination struct. +** If the source chain has more than one member, and the destination chain +** has only one member, then this function allocates new structs for all but +** the first copy from the arena and links them into the destination list. +** If the destination struct is part of a list with more than one member, +** then this function traverses both the source and destination lists, +** copying each source struct to the corresponding dest struct. +** In that case, the destination list MUST contain at least as many +** structs as the source list or some dest entries will be overwritten. +*/ +SECStatus +CERT_CopyGeneralName(PLArenaPool *arena, CERTGeneralName *dest, + CERTGeneralName *src) +{ + SECStatus rv; + CERTGeneralName *destHead = dest; + CERTGeneralName *srcHead = src; + + PORT_Assert(dest != NULL); + if (!dest) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + /* TODO: mark arena */ + do { + rv = cert_CopyOneGeneralName(arena, dest, src); + if (rv != SECSuccess) + goto loser; + src = CERT_GetNextGeneralName(src); + /* if there is only one general name, we shouldn't do this */ + if (src != srcHead) { + if (dest->l.next == &destHead->l) { + CERTGeneralName *temp; + temp = CERT_NewGeneralName(arena, (CERTGeneralNameType)0); + if (!temp) + goto loser; + temp->l.next = &destHead->l; + temp->l.prev = &dest->l; + destHead->l.prev = &temp->l; + dest->l.next = &temp->l; + dest = temp; + } else { + dest = CERT_GetNextGeneralName(dest); + } + } + } while (src != srcHead && rv == SECSuccess); + /* TODO: unmark arena */ + return rv; +loser: + /* TODO: release back to mark */ + return SECFailure; +} + +CERTGeneralNameList * +CERT_DupGeneralNameList(CERTGeneralNameList *list) +{ + if (list != NULL) { + PZ_Lock(list->lock); + list->refCount++; + PZ_Unlock(list->lock); + } + return list; +} + +/* Allocate space and copy CERTNameConstraint from src to dest */ +CERTNameConstraint * +CERT_CopyNameConstraint(PLArenaPool *arena, CERTNameConstraint *dest, + CERTNameConstraint *src) +{ + SECStatus rv; + + /* TODO: mark arena */ + if (dest == NULL) { + dest = PORT_ArenaZNew(arena, CERTNameConstraint); + if (!dest) + goto loser; + /* mark that it is not linked */ + dest->name.l.prev = dest->name.l.next = &(dest->name.l); + } + rv = CERT_CopyGeneralName(arena, &dest->name, &src->name); + if (rv != SECSuccess) { + goto loser; + } + rv = SECITEM_CopyItem(arena, &dest->DERName, &src->DERName); + if (rv != SECSuccess) { + goto loser; + } + rv = SECITEM_CopyItem(arena, &dest->min, &src->min); + if (rv != SECSuccess) { + goto loser; + } + rv = SECITEM_CopyItem(arena, &dest->max, &src->max); + if (rv != SECSuccess) { + goto loser; + } + dest->l.prev = dest->l.next = &dest->l; + /* TODO: unmark arena */ + return dest; +loser: + /* TODO: release arena to mark */ + return NULL; +} + +CERTGeneralName * +cert_CombineNamesLists(CERTGeneralName *list1, CERTGeneralName *list2) +{ + PRCList *begin1; + PRCList *begin2; + PRCList *end1; + PRCList *end2; + + if (list1 == NULL) { + return list2; + } else if (list2 == NULL) { + return list1; + } else { + begin1 = &list1->l; + begin2 = &list2->l; + end1 = list1->l.prev; + end2 = list2->l.prev; + end1->next = begin2; + end2->next = begin1; + begin1->prev = end2; + begin2->prev = end1; + return list1; + } +} + +CERTNameConstraint * +cert_CombineConstraintsLists(CERTNameConstraint *list1, + CERTNameConstraint *list2) +{ + PRCList *begin1; + PRCList *begin2; + PRCList *end1; + PRCList *end2; + + if (list1 == NULL) { + return list2; + } else if (list2 == NULL) { + return list1; + } else { + begin1 = &list1->l; + begin2 = &list2->l; + end1 = list1->l.prev; + end2 = list2->l.prev; + end1->next = begin2; + end2->next = begin1; + begin1->prev = end2; + begin2->prev = end1; + return list1; + } +} + +/* Add a CERTNameConstraint to the CERTNameConstraint list */ +CERTNameConstraint * +CERT_AddNameConstraint(CERTNameConstraint *list, CERTNameConstraint *constraint) +{ + PORT_Assert(constraint != NULL); + constraint->l.next = constraint->l.prev = &constraint->l; + list = cert_CombineConstraintsLists(list, constraint); + return list; +} + +SECStatus +CERT_GetNameConstraintByType(CERTNameConstraint *constraints, + CERTGeneralNameType type, + CERTNameConstraint **returnList, + PLArenaPool *arena) +{ + CERTNameConstraint *current = NULL; + void *mark = NULL; + + *returnList = NULL; + if (!constraints) + return SECSuccess; + + mark = PORT_ArenaMark(arena); + + current = constraints; + do { + PORT_Assert(current->name.type); + if (current->name.type == type) { + CERTNameConstraint *temp; + temp = CERT_CopyNameConstraint(arena, NULL, current); + if (temp == NULL) + goto loser; + *returnList = CERT_AddNameConstraint(*returnList, temp); + } + current = CERT_GetNextNameConstraint(current); + } while (current != constraints); + PORT_ArenaUnmark(arena, mark); + return SECSuccess; + +loser: + PORT_ArenaRelease(arena, mark); + return SECFailure; +} + +void * +CERT_GetGeneralNameByType(CERTGeneralName *genNames, CERTGeneralNameType type, + PRBool derFormat) +{ + CERTGeneralName *current; + + if (!genNames) + return NULL; + current = genNames; + + do { + if (current->type == type) { + switch (type) { + case certDNSName: + case certEDIPartyName: + case certIPAddress: + case certRegisterID: + case certRFC822Name: + case certX400Address: + case certURI: + return (void *)¤t->name.other; /* SECItem * */ + + case certOtherName: + return (void *)¤t->name.OthName; /* OthName * */ + + case certDirectoryName: + return derFormat + ? (void *)¤t + ->derDirectoryName /* SECItem * */ + : (void *)¤t->name + .directoryName; /* CERTName * */ + } + PORT_Assert(0); + return NULL; + } + current = CERT_GetNextGeneralName(current); + } while (current != genNames); + return NULL; +} + +int +CERT_GetNamesLength(CERTGeneralName *names) +{ + int length = 0; + CERTGeneralName *first; + + first = names; + if (names != NULL) { + do { + length++; + names = CERT_GetNextGeneralName(names); + } while (names != first); + } + return length; +} + +/* Creates new GeneralNames for any email addresses found in the +** input DN, and links them onto the list for the DN. +*/ +SECStatus +cert_ExtractDNEmailAddrs(CERTGeneralName *name, PLArenaPool *arena) +{ + CERTGeneralName *nameList = NULL; + const CERTRDN **nRDNs = (const CERTRDN **)(name->name.directoryName.rdns); + SECStatus rv = SECSuccess; + + PORT_Assert(name->type == certDirectoryName); + if (name->type != certDirectoryName) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + /* TODO: mark arena */ + while (nRDNs && *nRDNs) { /* loop over RDNs */ + const CERTRDN *nRDN = *nRDNs++; + CERTAVA **nAVAs = nRDN->avas; + while (nAVAs && *nAVAs) { /* loop over AVAs */ + int tag; + CERTAVA *nAVA = *nAVAs++; + tag = CERT_GetAVATag(nAVA); + if (tag == SEC_OID_PKCS9_EMAIL_ADDRESS || + tag == SEC_OID_RFC1274_MAIL) { /* email AVA */ + CERTGeneralName *newName = NULL; + SECItem *avaValue = CERT_DecodeAVAValue(&nAVA->value); + if (!avaValue) + goto loser; + rv = SECFailure; + newName = CERT_NewGeneralName(arena, certRFC822Name); + if (newName) { + rv = + SECITEM_CopyItem(arena, &newName->name.other, avaValue); + } + SECITEM_FreeItem(avaValue, PR_TRUE); + if (rv != SECSuccess) + goto loser; + nameList = cert_CombineNamesLists(nameList, newName); + } /* handle one email AVA */ + } /* loop over AVAs */ + } /* loop over RDNs */ + /* combine new names with old one. */ + (void)cert_CombineNamesLists(name, nameList); + /* TODO: unmark arena */ + return SECSuccess; + +loser: + /* TODO: release arena back to mark */ + return SECFailure; +} + +/* Extract all names except Subject Common Name from a cert +** in preparation for a name constraints test. +*/ +CERTGeneralName * +CERT_GetCertificateNames(CERTCertificate *cert, PLArenaPool *arena) +{ + return CERT_GetConstrainedCertificateNames(cert, arena, PR_FALSE); +} + +/* This function is called by CERT_VerifyCertChain to extract all +** names from a cert in preparation for a name constraints test. +*/ +CERTGeneralName * +CERT_GetConstrainedCertificateNames(const CERTCertificate *cert, + PLArenaPool *arena, + PRBool includeSubjectCommonName) +{ + CERTGeneralName *DN; + CERTGeneralName *SAN; + PRUint32 numDNSNames = 0; + SECStatus rv; + + if (!arena) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + /* TODO: mark arena */ + DN = CERT_NewGeneralName(arena, certDirectoryName); + if (DN == NULL) { + goto loser; + } + rv = CERT_CopyName(arena, &DN->name.directoryName, &cert->subject); + if (rv != SECSuccess) { + goto loser; + } + rv = SECITEM_CopyItem(arena, &DN->derDirectoryName, &cert->derSubject); + if (rv != SECSuccess) { + goto loser; + } + /* Extract email addresses from DN, construct CERTGeneralName structs + ** for them, add them to the name list + */ + rv = cert_ExtractDNEmailAddrs(DN, arena); + if (rv != SECSuccess) + goto loser; + + /* Now extract any GeneralNames from the subject name names extension. */ + SAN = cert_GetSubjectAltNameList(cert, arena); + if (SAN) { + numDNSNames = cert_CountDNSPatterns(SAN); + DN = cert_CombineNamesLists(DN, SAN); + } + if (!numDNSNames && includeSubjectCommonName) { + char *cn = CERT_GetCommonName(&cert->subject); + if (cn) { + CERTGeneralName *CN = CERT_NewGeneralName(arena, certDNSName); + if (CN) { + SECItem cnItem = { siBuffer, NULL, 0 }; + cnItem.data = (unsigned char *)cn; + cnItem.len = strlen(cn); + rv = SECITEM_CopyItem(arena, &CN->name.other, &cnItem); + if (rv == SECSuccess) { + DN = cert_CombineNamesLists(DN, CN); + } + } + PORT_Free(cn); + } + } + if (rv == SECSuccess) { + /* TODO: unmark arena */ + return DN; + } +loser: + /* TODO: release arena to mark */ + return NULL; +} + +/* Returns SECSuccess if name matches constraint per RFC 3280 rules for +** URI name constraints. SECFailure otherwise. +** If the constraint begins with a dot, it is a domain name, otherwise +** It is a host name. Examples: +** Constraint Name Result +** ------------ --------------- -------- +** foo.bar.com foo.bar.com matches +** foo.bar.com FoO.bAr.CoM matches +** foo.bar.com www.foo.bar.com no match +** foo.bar.com nofoo.bar.com no match +** .foo.bar.com www.foo.bar.com matches +** .foo.bar.com nofoo.bar.com no match +** .foo.bar.com foo.bar.com no match +** .foo.bar.com www..foo.bar.com no match +*/ +static SECStatus +compareURIN2C(const SECItem *name, const SECItem *constraint) +{ + int offset; + /* The spec is silent on intepreting zero-length constraints. + ** We interpret them as matching no URI names. + */ + if (!constraint->len) + return SECFailure; + if (constraint->data[0] != '.') { + /* constraint is a host name. */ + if (name->len != constraint->len || + PL_strncasecmp((char *)name->data, (char *)constraint->data, + constraint->len)) + return SECFailure; + return SECSuccess; + } + /* constraint is a domain name. */ + if (name->len < constraint->len) + return SECFailure; + offset = name->len - constraint->len; + if (PL_strncasecmp((char *)(name->data + offset), (char *)constraint->data, + constraint->len)) + return SECFailure; + if (!offset || + (name->data[offset - 1] == '.') + (constraint->data[0] == '.') == 1) + return SECSuccess; + return SECFailure; +} + +/* for DNSname constraints, RFC 3280 says, (section 4.2.1.11, page 38) +** +** DNS name restrictions are expressed as foo.bar.com. Any DNS name +** that can be constructed by simply adding to the left hand side of the +** name satisfies the name constraint. For example, www.foo.bar.com +** would satisfy the constraint but foo1.bar.com would not. +** +** But NIST's PKITS test suite requires that the constraint be treated +** as a domain name, and requires that any name added to the left hand +** side end in a dot ".". Sensible, but not strictly following the RFC. +** +** Constraint Name RFC 3280 NIST PKITS +** ------------ --------------- -------- ---------- +** foo.bar.com foo.bar.com matches matches +** foo.bar.com FoO.bAr.CoM matches matches +** foo.bar.com www.foo.bar.com matches matches +** foo.bar.com nofoo.bar.com MATCHES NO MATCH +** .foo.bar.com www.foo.bar.com matches matches? disallowed? +** .foo.bar.com foo.bar.com no match no match +** .foo.bar.com www..foo.bar.com matches probably not +** +** We will try to conform to NIST's PKITS tests, and the unstated +** rules they imply. +*/ +static SECStatus +compareDNSN2C(const SECItem *name, const SECItem *constraint) +{ + int offset; + /* The spec is silent on intepreting zero-length constraints. + ** We interpret them as matching all DNSnames. + */ + if (!constraint->len) + return SECSuccess; + if (name->len < constraint->len) + return SECFailure; + offset = name->len - constraint->len; + if (PL_strncasecmp((char *)(name->data + offset), (char *)constraint->data, + constraint->len)) + return SECFailure; + if (!offset || + (name->data[offset - 1] == '.') + (constraint->data[0] == '.') == 1) + return SECSuccess; + return SECFailure; +} + +/* Returns SECSuccess if name matches constraint per RFC 3280 rules for +** internet email addresses. SECFailure otherwise. +** If constraint contains a '@' then the two strings much match exactly. +** Else if constraint starts with a '.'. then it must match the right-most +** substring of the name, +** else constraint string must match entire name after the name's '@'. +** Empty constraint string matches all names. All comparisons case insensitive. +*/ +static SECStatus +compareRFC822N2C(const SECItem *name, const SECItem *constraint) +{ + int offset; + if (!constraint->len) + return SECSuccess; + if (name->len < constraint->len) + return SECFailure; + if (constraint->len == 1 && constraint->data[0] == '.') + return SECSuccess; + for (offset = constraint->len - 1; offset >= 0; --offset) { + if (constraint->data[offset] == '@') { + return (name->len == constraint->len && + !PL_strncasecmp((char *)name->data, + (char *)constraint->data, constraint->len)) + ? SECSuccess + : SECFailure; + } + } + offset = name->len - constraint->len; + if (PL_strncasecmp((char *)(name->data + offset), (char *)constraint->data, + constraint->len)) + return SECFailure; + if (constraint->data[0] == '.') + return SECSuccess; + if (offset > 0 && name->data[offset - 1] == '@') + return SECSuccess; + return SECFailure; +} + +/* name contains either a 4 byte IPv4 address or a 16 byte IPv6 address. +** constraint contains an address of the same length, and a subnet mask +** of the same length. Compare name's address to the constraint's +** address, subject to the mask. +** Return SECSuccess if they match, SECFailure if they don't. +*/ +static SECStatus +compareIPaddrN2C(const SECItem *name, const SECItem *constraint) +{ + int i; + if (name->len == 4 && constraint->len == 8) { /* ipv4 addr */ + for (i = 0; i < 4; i++) { + if ((name->data[i] ^ constraint->data[i]) & constraint->data[i + 4]) + goto loser; + } + return SECSuccess; + } + if (name->len == 16 && constraint->len == 32) { /* ipv6 addr */ + for (i = 0; i < 16; i++) { + if ((name->data[i] ^ constraint->data[i]) & + constraint->data[i + 16]) + goto loser; + } + return SECSuccess; + } +loser: + return SECFailure; +} + +/* start with a SECItem that points to a URI. Parse it lookingg for +** a hostname. Modify item->data and item->len to define the hostname, +** but do not modify and data at item->data. +** If anything goes wrong, the contents of *item are undefined. +*/ +static SECStatus +parseUriHostname(SECItem *item) +{ + int i; + PRBool found = PR_FALSE; + for (i = 0; (unsigned)(i + 2) < item->len; ++i) { + if (item->data[i] == ':' && item->data[i + 1] == '/' && + item->data[i + 2] == '/') { + i += 3; + item->data += i; + item->len -= i; + found = PR_TRUE; + break; + } + } + if (!found) + return SECFailure; + /* now look for a '/', which is an upper bound in the end of the name */ + for (i = 0; (unsigned)i < item->len; ++i) { + if (item->data[i] == '/') { + item->len = i; + break; + } + } + /* now look for a ':', which marks the end of the name */ + for (i = item->len; --i >= 0;) { + if (item->data[i] == ':') { + item->len = i; + break; + } + } + /* now look for an '@', which marks the beginning of the hostname */ + for (i = 0; (unsigned)i < item->len; ++i) { + if (item->data[i] == '@') { + ++i; + item->data += i; + item->len -= i; + break; + } + } + return item->len ? SECSuccess : SECFailure; +} + +/* This function takes one name, and a list of constraints. +** It searches the constraints looking for a match. +** It returns SECSuccess if the name satisfies the constraints, i.e., +** if excluded, then the name does not match any constraint, +** if permitted, then the name matches at least one constraint. +** It returns SECFailure if the name fails to satisfy the constraints, +** or if some code fails (e.g. out of memory, or invalid constraint) +*/ +SECStatus +cert_CompareNameWithConstraints(const CERTGeneralName *name, + const CERTNameConstraint *constraints, + PRBool excluded) +{ + SECStatus rv = SECSuccess; + SECStatus matched = SECFailure; + const CERTNameConstraint *current; + + PORT_Assert(constraints); /* caller should not call with NULL */ + if (!constraints) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + current = constraints; + do { + rv = SECSuccess; + matched = SECFailure; + PORT_Assert(name->type == current->name.type); + switch (name->type) { + + case certDNSName: + matched = + compareDNSN2C(&name->name.other, ¤t->name.name.other); + break; + + case certRFC822Name: + matched = compareRFC822N2C(&name->name.other, + ¤t->name.name.other); + break; + + case certURI: { + /* make a modifiable copy of the URI SECItem. */ + SECItem uri = name->name.other; + /* find the hostname in the URI */ + rv = parseUriHostname(&uri); + if (rv == SECSuccess) { + /* does our hostname meet the constraint? */ + matched = compareURIN2C(&uri, ¤t->name.name.other); + } + } break; + + case certDirectoryName: + /* Determine if the constraint directory name is a "prefix" + ** for the directory name being tested. + */ + { + /* status defaults to SECEqual, so that a constraint with + ** no AVAs will be a wildcard, matching all directory names. + */ + SECComparison status = SECEqual; + const CERTRDN **cRDNs = + (const CERTRDN **)current->name.name.directoryName.rdns; + const CERTRDN **nRDNs = + (const CERTRDN **)name->name.directoryName.rdns; + while (cRDNs && *cRDNs && nRDNs && *nRDNs) { + /* loop over name RDNs and constraint RDNs in lock step + */ + const CERTRDN *cRDN = *cRDNs++; + const CERTRDN *nRDN = *nRDNs++; + CERTAVA **cAVAs = cRDN->avas; + while (cAVAs && + *cAVAs) { /* loop over constraint AVAs */ + CERTAVA *cAVA = *cAVAs++; + CERTAVA **nAVAs = nRDN->avas; + while (nAVAs && *nAVAs) { /* loop over name AVAs */ + CERTAVA *nAVA = *nAVAs++; + status = CERT_CompareAVA(cAVA, nAVA); + if (status == SECEqual) + break; + } /* loop over name AVAs */ + if (status != SECEqual) + break; + } /* loop over constraint AVAs */ + if (status != SECEqual) + break; + } /* loop over name RDNs and constraint RDNs */ + matched = (status == SECEqual) ? SECSuccess : SECFailure; + break; + } + + case certIPAddress: /* type 8 */ + matched = compareIPaddrN2C(&name->name.other, + ¤t->name.name.other); + break; + + /* NSS does not know how to compare these "Other" type names with + ** their respective constraints. But it does know how to tell + ** if the constraint applies to the type of name (by comparing + ** the constraint OID to the name OID). NSS makes no use of "Other" + ** type names at all, so NSS errs on the side of leniency for these + ** types, provided that their OIDs match. So, when an "Other" + ** name constraint appears in an excluded subtree, it never causes + ** a name to fail. When an "Other" name constraint appears in a + ** permitted subtree, AND the constraint's OID matches the name's + ** OID, then name is treated as if it matches the constraint. + */ + case certOtherName: /* type 1 */ + matched = + (!excluded && name->type == current->name.type && + SECITEM_ItemsAreEqual(&name->name.OthName.oid, + ¤t->name.name.OthName.oid)) + ? SECSuccess + : SECFailure; + break; + + /* NSS does not know how to compare these types of names with their + ** respective constraints. But NSS makes no use of these types of + ** names at all, so it errs on the side of leniency for these types. + ** Constraints for these types of names never cause the name to + ** fail the constraints test. NSS behaves as if the name matched + ** for permitted constraints, and did not match for excluded ones. + */ + case certX400Address: /* type 4 */ + case certEDIPartyName: /* type 6 */ + case certRegisterID: /* type 9 */ + matched = excluded ? SECFailure : SECSuccess; + break; + + default: /* non-standard types are not supported */ + rv = SECFailure; + break; + } + if (matched == SECSuccess || rv != SECSuccess) + break; + current = CERT_GetNextNameConstraint((CERTNameConstraint *)current); + } while (current != constraints); + if (rv == SECSuccess) { + if (matched == SECSuccess) + rv = excluded ? SECFailure : SECSuccess; + else + rv = excluded ? SECSuccess : SECFailure; + return rv; + } + + return SECFailure; +} + +/* Add and link a CERTGeneralName to a CERTNameConstraint list. Most +** likely the CERTNameConstraint passed in is either the permitted +** list or the excluded list of a CERTNameConstraints. +*/ +SECStatus +CERT_AddNameConstraintByGeneralName(PLArenaPool *arena, + CERTNameConstraint **constraints, + CERTGeneralName *name) +{ + SECStatus rv; + CERTNameConstraint *current = NULL; + CERTNameConstraint *first = *constraints; + void *mark = NULL; + + mark = PORT_ArenaMark(arena); + + current = PORT_ArenaZNew(arena, CERTNameConstraint); + if (current == NULL) { + rv = SECFailure; + goto done; + } + + rv = cert_CopyOneGeneralName(arena, ¤t->name, name); + if (rv != SECSuccess) { + goto done; + } + + current->name.l.prev = current->name.l.next = &(current->name.l); + + if (first == NULL) { + *constraints = current; + PR_INIT_CLIST(¤t->l); + } else { + PR_INSERT_BEFORE(¤t->l, &first->l); + } + +done: + if (rv == SECFailure) { + PORT_ArenaRelease(arena, mark); + } else { + PORT_ArenaUnmark(arena, mark); + } + return rv; +} + +/* + * Here we define a list of name constraints to be imposed on + * certain certificates, most importantly root certificates. + * + * Each entry in the name constraints list is constructed with this + * macro. An entry contains two SECItems, which have names in + * specific forms to make the macro work: + * + * * ${CA}_SUBJECT_DN - The subject DN for which the constraints + * should be applied + * * ${CA}_NAME_CONSTRAINTS - The name constraints extension + * + * Entities subject to name constraints are identified by subject name + * so that we can cover all certificates for that entity, including, e.g., + * cross-certificates. We use subject rather than public key because + * calling methods often have easy access to that field (vs., say, a key ID), + * and in practice, subject names and public keys are usually in one-to-one + * correspondence anyway. + * + */ + +#define STRING_TO_SECITEM(str) \ + { \ + siBuffer, (unsigned char *)str, sizeof(str) - 1 \ + } + +#define NAME_CONSTRAINTS_ENTRY(CA) \ + { \ + STRING_TO_SECITEM(CA##_SUBJECT_DN) \ + , \ + STRING_TO_SECITEM(CA##_NAME_CONSTRAINTS) \ + } + +/* clang-format off */ + +/* Agence Nationale de la Securite des Systemes d'Information (ANSSI) */ + +#define ANSSI_SUBJECT_DN \ + "\x30\x81\x85" \ + "\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02" "FR" /* C */ \ + "\x31\x0F\x30\x0D\x06\x03\x55\x04\x08\x13\x06" "France" /* ST */ \ + "\x31\x0E\x30\x0C\x06\x03\x55\x04\x07\x13\x05" "Paris" /* L */ \ + "\x31\x10\x30\x0E\x06\x03\x55\x04\x0A\x13\x07" "PM/SGDN" /* O */ \ + "\x31\x0E\x30\x0C\x06\x03\x55\x04\x0B\x13\x05" "DCSSI" /* OU */ \ + "\x31\x0E\x30\x0C\x06\x03\x55\x04\x03\x13\x05" "IGC/A" /* CN */ \ + "\x31\x23\x30\x21\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x09\x01" \ + "\x16\x14" "igca@sgdn.pm.gouv.fr" /* emailAddress */ \ + +#define ANSSI_NAME_CONSTRAINTS \ + "\x30\x5D\xA0\x5B" \ + "\x30\x05\x82\x03" ".fr" \ + "\x30\x05\x82\x03" ".gp" \ + "\x30\x05\x82\x03" ".gf" \ + "\x30\x05\x82\x03" ".mq" \ + "\x30\x05\x82\x03" ".re" \ + "\x30\x05\x82\x03" ".yt" \ + "\x30\x05\x82\x03" ".pm" \ + "\x30\x05\x82\x03" ".bl" \ + "\x30\x05\x82\x03" ".mf" \ + "\x30\x05\x82\x03" ".wf" \ + "\x30\x05\x82\x03" ".pf" \ + "\x30\x05\x82\x03" ".nc" \ + "\x30\x05\x82\x03" ".tf" + +/* TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 */ + +#define TUBITAK1_SUBJECT_DN \ + "\x30\x81\xd2" \ + "\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02" \ + /* C */ "TR" \ + "\x31\x18\x30\x16\x06\x03\x55\x04\x07\x13\x0f" \ + /* L */ "Gebze - Kocaeli" \ + "\x31\x42\x30\x40\x06\x03\x55\x04\x0a\x13\x39" \ + /* O */ "Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK" \ + "\x31\x2d\x30\x2b\x06\x03\x55\x04\x0b\x13\x24" \ + /* OU */ "Kamu Sertifikasyon Merkezi - Kamu SM" \ + "\x31\x36\x30\x34\x06\x03\x55\x04\x03\x13\x2d" \ + /* CN */ "TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1" + +#define TUBITAK1_NAME_CONSTRAINTS \ + "\x30\x09\xa0\x07" \ + "\x30\x05\x82\x03" ".tr" + +/* clang-format on */ + +static const SECItem builtInNameConstraints[][2] = { + NAME_CONSTRAINTS_ENTRY(ANSSI), + NAME_CONSTRAINTS_ENTRY(TUBITAK1) +}; + +SECStatus +CERT_GetImposedNameConstraints(const SECItem *derSubject, SECItem *extensions) +{ + size_t i; + + if (!extensions) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + for (i = 0; i < PR_ARRAY_SIZE(builtInNameConstraints); ++i) { + if (SECITEM_ItemsAreEqual(derSubject, &builtInNameConstraints[i][0])) { + return SECITEM_CopyItem(NULL, extensions, + &builtInNameConstraints[i][1]); + } + } + + PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND); + return SECFailure; +} + +/* + * Extract the name constraints extension from the CA cert. + * If the certificate contains no name constraints extension, but + * CERT_GetImposedNameConstraints returns a name constraints extension + * for the subject of the certificate, then that extension will be returned. + */ +SECStatus +CERT_FindNameConstraintsExten(PLArenaPool *arena, CERTCertificate *cert, + CERTNameConstraints **constraints) +{ + SECStatus rv = SECSuccess; + SECItem constraintsExtension; + void *mark = NULL; + + *constraints = NULL; + + rv = CERT_FindCertExtension(cert, SEC_OID_X509_NAME_CONSTRAINTS, + &constraintsExtension); + if (rv != SECSuccess) { + if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) { + return rv; + } + rv = CERT_GetImposedNameConstraints(&cert->derSubject, + &constraintsExtension); + if (rv != SECSuccess) { + if (PORT_GetError() == SEC_ERROR_EXTENSION_NOT_FOUND) { + return SECSuccess; + } + return rv; + } + } + + mark = PORT_ArenaMark(arena); + + *constraints = cert_DecodeNameConstraints(arena, &constraintsExtension); + if (*constraints == NULL) { /* decode failed */ + rv = SECFailure; + } + PORT_Free(constraintsExtension.data); + + if (rv == SECFailure) { + PORT_ArenaRelease(arena, mark); + } else { + PORT_ArenaUnmark(arena, mark); + } + + return rv; +} + +/* Verify name against all the constraints relevant to that type of +** the name. +*/ +SECStatus +CERT_CheckNameSpace(PLArenaPool *arena, const CERTNameConstraints *constraints, + const CERTGeneralName *currentName) +{ + CERTNameConstraint *matchingConstraints; + SECStatus rv = SECSuccess; + + if (constraints->excluded != NULL) { + rv = CERT_GetNameConstraintByType(constraints->excluded, + currentName->type, + &matchingConstraints, arena); + if (rv == SECSuccess && matchingConstraints != NULL) { + rv = cert_CompareNameWithConstraints(currentName, + matchingConstraints, PR_TRUE); + } + if (rv != SECSuccess) { + return (rv); + } + } + + if (constraints->permited != NULL) { + rv = CERT_GetNameConstraintByType(constraints->permited, + currentName->type, + &matchingConstraints, arena); + if (rv == SECSuccess && matchingConstraints != NULL) { + rv = cert_CompareNameWithConstraints(currentName, + matchingConstraints, PR_FALSE); + } + if (rv != SECSuccess) { + return (rv); + } + } + + return (SECSuccess); +} + +/* Extract the name constraints extension from the CA cert. +** Test each and every name in namesList against all the constraints +** relevant to that type of name. +** Returns NULL in pBadCert for success, if all names are acceptable. +** If some name is not acceptable, returns a pointer to the cert that +** contained that name. +*/ +SECStatus +CERT_CompareNameSpace(CERTCertificate *cert, CERTGeneralName *namesList, + CERTCertificate **certsList, PLArenaPool *reqArena, + CERTCertificate **pBadCert) +{ + SECStatus rv = SECSuccess; + CERTNameConstraints *constraints; + CERTGeneralName *currentName; + int count = 0; + CERTCertificate *badCert = NULL; + + /* If no names to check, then no names can be bad. */ + if (!namesList) + goto done; + rv = CERT_FindNameConstraintsExten(reqArena, cert, &constraints); + if (rv != SECSuccess) { + count = -1; + goto done; + } + + currentName = namesList; + do { + if (constraints) { + rv = CERT_CheckNameSpace(reqArena, constraints, currentName); + if (rv != SECSuccess) { + break; + } + } + currentName = CERT_GetNextGeneralName(currentName); + count++; + } while (currentName != namesList); + +done: + if (rv != SECSuccess) { + badCert = (count >= 0) ? certsList[count] : cert; + } + if (pBadCert) + *pBadCert = badCert; + + return rv; +} + +#if 0 +/* not exported from shared libs, not used. Turn on if we ever need it. */ +SECStatus +CERT_CompareGeneralName(CERTGeneralName *a, CERTGeneralName *b) +{ + CERTGeneralName *currentA; + CERTGeneralName *currentB; + PRBool found; + + currentA = a; + currentB = b; + if (a != NULL) { + do { + if (currentB == NULL) { + return SECFailure; + } + currentB = CERT_GetNextGeneralName(currentB); + currentA = CERT_GetNextGeneralName(currentA); + } while (currentA != a); + } + if (currentB != b) { + return SECFailure; + } + currentA = a; + do { + currentB = b; + found = PR_FALSE; + do { + if (currentB->type == currentA->type) { + switch (currentB->type) { + case certDNSName: + case certEDIPartyName: + case certIPAddress: + case certRegisterID: + case certRFC822Name: + case certX400Address: + case certURI: + if (SECITEM_CompareItem(¤tA->name.other, + ¤tB->name.other) + == SECEqual) { + found = PR_TRUE; + } + break; + case certOtherName: + if (SECITEM_CompareItem(¤tA->name.OthName.oid, + ¤tB->name.OthName.oid) + == SECEqual && + SECITEM_CompareItem(¤tA->name.OthName.name, + ¤tB->name.OthName.name) + == SECEqual) { + found = PR_TRUE; + } + break; + case certDirectoryName: + if (CERT_CompareName(¤tA->name.directoryName, + ¤tB->name.directoryName) + == SECEqual) { + found = PR_TRUE; + } + } + + } + currentB = CERT_GetNextGeneralName(currentB); + } while (currentB != b && found != PR_TRUE); + if (found != PR_TRUE) { + return SECFailure; + } + currentA = CERT_GetNextGeneralName(currentA); + } while (currentA != a); + return SECSuccess; +} + +SECStatus +CERT_CompareGeneralNameLists(CERTGeneralNameList *a, CERTGeneralNameList *b) +{ + SECStatus rv; + + if (a == b) { + return SECSuccess; + } + if (a != NULL && b != NULL) { + PZ_Lock(a->lock); + PZ_Lock(b->lock); + rv = CERT_CompareGeneralName(a->name, b->name); + PZ_Unlock(a->lock); + PZ_Unlock(b->lock); + } else { + rv = SECFailure; + } + return rv; +} +#endif + +#if 0 +/* This function is not exported from NSS shared libraries, and is not +** used inside of NSS. +** XXX it doesn't check for failed allocations. :-( +*/ +void * +CERT_GetGeneralNameFromListByType(CERTGeneralNameList *list, + CERTGeneralNameType type, + PLArenaPool *arena) +{ + CERTName *name = NULL; + SECItem *item = NULL; + OtherName *other = NULL; + OtherName *tmpOther = NULL; + void *data; + + PZ_Lock(list->lock); + data = CERT_GetGeneralNameByType(list->name, type, PR_FALSE); + if (data != NULL) { + switch (type) { + case certDNSName: + case certEDIPartyName: + case certIPAddress: + case certRegisterID: + case certRFC822Name: + case certX400Address: + case certURI: + if (arena != NULL) { + item = PORT_ArenaNew(arena, SECItem); + if (item != NULL) { +XXX SECITEM_CopyItem(arena, item, (SECItem *) data); + } + } else { + item = SECITEM_DupItem((SECItem *) data); + } + PZ_Unlock(list->lock); + return item; + case certOtherName: + other = (OtherName *) data; + if (arena != NULL) { + tmpOther = PORT_ArenaNew(arena, OtherName); + } else { + tmpOther = PORT_New(OtherName); + } + if (tmpOther != NULL) { +XXX SECITEM_CopyItem(arena, &tmpOther->oid, &other->oid); +XXX SECITEM_CopyItem(arena, &tmpOther->name, &other->name); + } + PZ_Unlock(list->lock); + return tmpOther; + case certDirectoryName: + if (arena) { + name = PORT_ArenaZNew(list->arena, CERTName); + if (name) { +XXX CERT_CopyName(arena, name, (CERTName *) data); + } + } + PZ_Unlock(list->lock); + return name; + } + } + PZ_Unlock(list->lock); + return NULL; +} +#endif + +#if 0 +/* This function is not exported from NSS shared libraries, and is not +** used inside of NSS. +** XXX it should NOT be a void function, since it does allocations +** that can fail. +*/ +void +CERT_AddGeneralNameToList(CERTGeneralNameList *list, + CERTGeneralNameType type, + void *data, SECItem *oid) +{ + CERTGeneralName *name; + + if (list != NULL && data != NULL) { + PZ_Lock(list->lock); + name = CERT_NewGeneralName(list->arena, type); + if (!name) + goto done; + switch (type) { + case certDNSName: + case certEDIPartyName: + case certIPAddress: + case certRegisterID: + case certRFC822Name: + case certX400Address: + case certURI: +XXX SECITEM_CopyItem(list->arena, &name->name.other, (SECItem *)data); + break; + case certOtherName: +XXX SECITEM_CopyItem(list->arena, &name->name.OthName.name, + (SECItem *) data); +XXX SECITEM_CopyItem(list->arena, &name->name.OthName.oid, + oid); + break; + case certDirectoryName: +XXX CERT_CopyName(list->arena, &name->name.directoryName, + (CERTName *) data); + break; + } + list->name = cert_CombineNamesLists(list->name, name); + list->len++; +done: + PZ_Unlock(list->lock); + } + return; +} +#endif diff --git a/security/nss/lib/certdb/genname.h b/security/nss/lib/certdb/genname.h new file mode 100644 index 0000000000..5824157108 --- /dev/null +++ b/security/nss/lib/certdb/genname.h @@ -0,0 +1,93 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _GENAME_H_ +#define _GENAME_H_ + +#include "plarena.h" +#include "seccomon.h" +#include "secoidt.h" +#include "secasn1.h" +#include "secder.h" +#include "certt.h" + +/************************************************************************/ +SEC_BEGIN_PROTOS + +extern const SEC_ASN1Template CERT_GeneralNamesTemplate[]; + +extern SECItem **cert_EncodeGeneralNames(PLArenaPool *arena, + CERTGeneralName *names); + +extern CERTGeneralName *cert_DecodeGeneralNames(PLArenaPool *arena, + SECItem **encodedGenName); + +extern SECStatus cert_DestroyGeneralNames(CERTGeneralName *name); + +extern SECStatus cert_EncodeNameConstraints(CERTNameConstraints *constraints, + PLArenaPool *arena, SECItem *dest); + +extern CERTNameConstraints *cert_DecodeNameConstraints( + PLArenaPool *arena, const SECItem *encodedConstraints); + +extern CERTGeneralName *cert_CombineNamesLists(CERTGeneralName *list1, + CERTGeneralName *list2); + +extern CERTNameConstraint *cert_CombineConstraintsLists( + CERTNameConstraint *list1, CERTNameConstraint *list2); + +/*********************************************************************/ +/* A thread safe implementation of General Names */ +/*********************************************************************/ + +/* Destroy a Single CERTGeneralName */ +void CERT_DestroyGeneralName(CERTGeneralName *name); + +SECStatus CERT_CompareGeneralName(CERTGeneralName *a, CERTGeneralName *b); + +SECStatus CERT_CopyGeneralName(PLArenaPool *arena, CERTGeneralName *dest, + CERTGeneralName *src); + +/* General Name Lists are a thread safe, reference counting layer to + * general names */ + +/* Destroys a CERTGeneralNameList */ +void CERT_DestroyGeneralNameList(CERTGeneralNameList *list); + +/* Creates a CERTGeneralNameList */ +CERTGeneralNameList *CERT_CreateGeneralNameList(CERTGeneralName *name); + +/* Compares two CERTGeneralNameList */ +SECStatus CERT_CompareGeneralNameLists(CERTGeneralNameList *a, + CERTGeneralNameList *b); + +/* returns a copy of the first name of the type requested */ +void *CERT_GetGeneralNameFromListByType(CERTGeneralNameList *list, + CERTGeneralNameType type, + PLArenaPool *arena); + +/* Adds a name to the tail of the list */ +void CERT_AddGeneralNameToList(CERTGeneralNameList *list, + CERTGeneralNameType type, void *data, + SECItem *oid); + +/* returns a duplicate of the CERTGeneralNameList */ +CERTGeneralNameList *CERT_DupGeneralNameList(CERTGeneralNameList *list); + +/* returns the number of CERTGeneralName objects in the doubly linked +** list of which *names is a member. +*/ +extern int CERT_GetNamesLength(CERTGeneralName *names); + +/************************************************************************/ + +SECStatus CERT_CompareNameSpace(CERTCertificate *cert, + CERTGeneralName *namesList, + CERTCertificate **certsList, + PLArenaPool *reqArena, + CERTCertificate **pBadCert); + +SEC_END_PROTOS + +#endif diff --git a/security/nss/lib/certdb/manifest.mn b/security/nss/lib/certdb/manifest.mn new file mode 100644 index 0000000000..4a8c5c40ad --- /dev/null +++ b/security/nss/lib/certdb/manifest.mn @@ -0,0 +1,41 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +CORE_DEPTH = ../.. + +EXPORTS = \ + cert.h \ + certt.h \ + certdb.h \ + $(NULL) + +PRIVATE_EXPORTS = \ + genname.h \ + xconst.h \ + certxutl.h \ + certi.h \ + $(NULL) + +MODULE = nss + +CSRCS = \ + alg1485.c \ + certdb.c \ + certv3.c \ + certxutl.c \ + crl.c \ + genname.c \ + stanpcertdb.c \ + polcyxtn.c \ + secname.c \ + xauthkid.c \ + xbsconst.c \ + xconst.c \ + $(NULL) + +LIBRARY_NAME = certdb +SHARED_LIBRARY = $(NULL) + +# This part of the code, including all sub-dirs, can be optimized for size +export ALLOW_OPT_CODE_SIZE = 1 diff --git a/security/nss/lib/certdb/polcyxtn.c b/security/nss/lib/certdb/polcyxtn.c new file mode 100644 index 0000000000..aae34e2433 --- /dev/null +++ b/security/nss/lib/certdb/polcyxtn.c @@ -0,0 +1,806 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Support for various policy related extensions + */ + +#include "seccomon.h" +#include "secport.h" +#include "secder.h" +#include "cert.h" +#include "secoid.h" +#include "secasn1.h" +#include "secerr.h" +#include "nspr.h" + +SEC_ASN1_MKSUB(SEC_IntegerTemplate) +SEC_ASN1_MKSUB(SEC_ObjectIDTemplate) + +const SEC_ASN1Template CERT_DisplayTextTypeTemplate[] = { + { SEC_ASN1_CHOICE, offsetof(SECItem, type), 0, sizeof(SECItem) }, + { SEC_ASN1_IA5_STRING, 0, 0, siAsciiString }, + { SEC_ASN1_VISIBLE_STRING, 0, 0, siVisibleString }, + { SEC_ASN1_BMP_STRING, 0, 0, siBMPString }, + { SEC_ASN1_UTF8_STRING, 0, 0, siUTF8String }, + { 0 } +}; + +const SEC_ASN1Template CERT_NoticeReferenceTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTNoticeReference) }, + { SEC_ASN1_INLINE, offsetof(CERTNoticeReference, organization), + CERT_DisplayTextTypeTemplate, 0 }, + { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN, + offsetof(CERTNoticeReference, noticeNumbers), + SEC_ASN1_SUB(SEC_IntegerTemplate) }, + { 0 } +}; + +const SEC_ASN1Template CERT_UserNoticeTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTUserNotice) }, + { SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL, + offsetof(CERTUserNotice, noticeReference), CERT_NoticeReferenceTemplate, + 0 }, + { SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL, + offsetof(CERTUserNotice, displayText), CERT_DisplayTextTypeTemplate, 0 }, + { 0 } +}; + +const SEC_ASN1Template CERT_PolicyQualifierTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTPolicyQualifier) }, + { SEC_ASN1_OBJECT_ID, offsetof(CERTPolicyQualifier, qualifierID) }, + { SEC_ASN1_ANY, offsetof(CERTPolicyQualifier, qualifierValue) }, + { 0 } +}; + +const SEC_ASN1Template CERT_PolicyInfoTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTPolicyInfo) }, + { SEC_ASN1_OBJECT_ID, offsetof(CERTPolicyInfo, policyID) }, + { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_OPTIONAL, + offsetof(CERTPolicyInfo, policyQualifiers), + CERT_PolicyQualifierTemplate }, + { 0 } +}; + +const SEC_ASN1Template CERT_CertificatePoliciesTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, offsetof(CERTCertificatePolicies, policyInfos), + CERT_PolicyInfoTemplate, sizeof(CERTCertificatePolicies) } +}; + +const SEC_ASN1Template CERT_PolicyMapTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTPolicyMap) }, + { SEC_ASN1_OBJECT_ID, offsetof(CERTPolicyMap, issuerDomainPolicy) }, + { SEC_ASN1_OBJECT_ID, offsetof(CERTPolicyMap, subjectDomainPolicy) }, + { 0 } +}; + +const SEC_ASN1Template CERT_PolicyMappingsTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, offsetof(CERTCertificatePolicyMappings, policyMaps), + CERT_PolicyMapTemplate, sizeof(CERTPolicyMap) } +}; + +const SEC_ASN1Template CERT_PolicyConstraintsTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCertificatePolicyConstraints) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, + offsetof(CERTCertificatePolicyConstraints, explicitPolicySkipCerts), + SEC_ASN1_SUB(SEC_IntegerTemplate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1, + offsetof(CERTCertificatePolicyConstraints, inhibitMappingSkipCerts), + SEC_ASN1_SUB(SEC_IntegerTemplate) }, + { 0 } +}; + +const SEC_ASN1Template CERT_InhibitAnyTemplate[] = { + { SEC_ASN1_INTEGER, + offsetof(CERTCertificateInhibitAny, inhibitAnySkipCerts), NULL, + sizeof(CERTCertificateInhibitAny) } +}; + +static void +breakLines(char *string) +{ + char *tmpstr; + char *lastspace = NULL; + int curlen = 0; + int c; + + tmpstr = string; + + while ((c = *tmpstr) != '\0') { + switch (c) { + case ' ': + lastspace = tmpstr; + break; + case '\n': + lastspace = NULL; + curlen = 0; + break; + } + + if ((curlen >= 55) && (lastspace != NULL)) { + *lastspace = '\n'; + curlen = (tmpstr - lastspace); + lastspace = NULL; + } + + curlen++; + tmpstr++; + } + + return; +} + +CERTCertificatePolicies * +CERT_DecodeCertificatePoliciesExtension(const SECItem *extnValue) +{ + PLArenaPool *arena = NULL; + SECStatus rv; + CERTCertificatePolicies *policies; + CERTPolicyInfo **policyInfos, *policyInfo; + CERTPolicyQualifier **policyQualifiers, *policyQualifier; + SECItem newExtnValue; + + /* make a new arena */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if (!arena) { + goto loser; + } + + /* allocate the certificate policies structure */ + policies = (CERTCertificatePolicies *)PORT_ArenaZAlloc( + arena, sizeof(CERTCertificatePolicies)); + + if (policies == NULL) { + goto loser; + } + + policies->arena = arena; + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newExtnValue, extnValue); + if (rv != SECSuccess) { + goto loser; + } + + /* decode the policy info */ + rv = SEC_QuickDERDecodeItem( + arena, policies, CERT_CertificatePoliciesTemplate, &newExtnValue); + + if (rv != SECSuccess) { + goto loser; + } + + /* initialize the oid tags */ + policyInfos = policies->policyInfos; + while (*policyInfos != NULL) { + policyInfo = *policyInfos; + policyInfo->oid = SECOID_FindOIDTag(&policyInfo->policyID); + policyQualifiers = policyInfo->policyQualifiers; + while (policyQualifiers != NULL && *policyQualifiers != NULL) { + policyQualifier = *policyQualifiers; + policyQualifier->oid = + SECOID_FindOIDTag(&policyQualifier->qualifierID); + policyQualifiers++; + } + policyInfos++; + } + + return (policies); + +loser: + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + + return (NULL); +} + +void +CERT_DestroyCertificatePoliciesExtension(CERTCertificatePolicies *policies) +{ + if (policies != NULL) { + PORT_FreeArena(policies->arena, PR_FALSE); + } + return; +} + +CERTCertificatePolicyMappings * +CERT_DecodePolicyMappingsExtension(SECItem *extnValue) +{ + PLArenaPool *arena = NULL; + SECStatus rv; + CERTCertificatePolicyMappings *mappings; + SECItem newExtnValue; + + /* make a new arena */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + goto loser; + } + + /* allocate the policy mappings structure */ + mappings = (CERTCertificatePolicyMappings *)PORT_ArenaZAlloc( + arena, sizeof(CERTCertificatePolicyMappings)); + if (mappings == NULL) { + goto loser; + } + mappings->arena = arena; + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newExtnValue, extnValue); + if (rv != SECSuccess) { + goto loser; + } + + /* decode the policy mappings */ + rv = SEC_QuickDERDecodeItem(arena, mappings, CERT_PolicyMappingsTemplate, + &newExtnValue); + if (rv != SECSuccess) { + goto loser; + } + + return (mappings); + +loser: + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + + return (NULL); +} + +SECStatus +CERT_DestroyPolicyMappingsExtension(CERTCertificatePolicyMappings *mappings) +{ + if (mappings != NULL) { + PORT_FreeArena(mappings->arena, PR_FALSE); + } + return SECSuccess; +} + +SECStatus +CERT_DecodePolicyConstraintsExtension( + CERTCertificatePolicyConstraints *decodedValue, const SECItem *encodedValue) +{ + CERTCertificatePolicyConstraints decodeContext; + PLArenaPool *arena = NULL; + SECStatus rv = SECSuccess; + + /* initialize so we can tell when an optional component is omitted */ + PORT_Memset(&decodeContext, 0, sizeof(decodeContext)); + + /* make a new arena */ + arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if (!arena) { + return SECFailure; + } + + do { + /* decode the policy constraints */ + rv = SEC_QuickDERDecodeItem(arena, &decodeContext, + CERT_PolicyConstraintsTemplate, + encodedValue); + + if (rv != SECSuccess) { + break; + } + + if (decodeContext.explicitPolicySkipCerts.len == 0) { + *(PRInt32 *)decodedValue->explicitPolicySkipCerts.data = -1; + } else { + *(PRInt32 *)decodedValue->explicitPolicySkipCerts.data = + DER_GetInteger(&decodeContext.explicitPolicySkipCerts); + } + + if (decodeContext.inhibitMappingSkipCerts.len == 0) { + *(PRInt32 *)decodedValue->inhibitMappingSkipCerts.data = -1; + } else { + *(PRInt32 *)decodedValue->inhibitMappingSkipCerts.data = + DER_GetInteger(&decodeContext.inhibitMappingSkipCerts); + } + + if ((*(PRInt32 *)decodedValue->explicitPolicySkipCerts.data == + PR_INT32_MIN) || + (*(PRInt32 *)decodedValue->explicitPolicySkipCerts.data == + PR_INT32_MAX) || + (*(PRInt32 *)decodedValue->inhibitMappingSkipCerts.data == + PR_INT32_MIN) || + (*(PRInt32 *)decodedValue->inhibitMappingSkipCerts.data == + PR_INT32_MAX)) { + rv = SECFailure; + } + + } while (0); + + PORT_FreeArena(arena, PR_FALSE); + return (rv); +} + +SECStatus +CERT_DecodeInhibitAnyExtension(CERTCertificateInhibitAny *decodedValue, + SECItem *encodedValue) +{ + CERTCertificateInhibitAny decodeContext; + PLArenaPool *arena = NULL; + SECStatus rv = SECSuccess; + + /* make a new arena */ + arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if (!arena) { + return SECFailure; + } + + do { + + /* decode the policy mappings */ + decodeContext.inhibitAnySkipCerts.type = siUnsignedInteger; + rv = SEC_QuickDERDecodeItem(arena, &decodeContext, + CERT_InhibitAnyTemplate, encodedValue); + + if (rv != SECSuccess) { + break; + } + + *(PRInt32 *)decodedValue->inhibitAnySkipCerts.data = + DER_GetInteger(&decodeContext.inhibitAnySkipCerts); + + } while (0); + + PORT_FreeArena(arena, PR_FALSE); + return (rv); +} + +CERTUserNotice * +CERT_DecodeUserNotice(SECItem *noticeItem) +{ + PLArenaPool *arena = NULL; + SECStatus rv; + CERTUserNotice *userNotice; + SECItem newNoticeItem; + + /* make a new arena */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if (!arena) { + goto loser; + } + + /* allocate the userNotice structure */ + userNotice = + (CERTUserNotice *)PORT_ArenaZAlloc(arena, sizeof(CERTUserNotice)); + + if (userNotice == NULL) { + goto loser; + } + + userNotice->arena = arena; + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newNoticeItem, noticeItem); + if (rv != SECSuccess) { + goto loser; + } + + /* decode the user notice */ + rv = SEC_QuickDERDecodeItem(arena, userNotice, CERT_UserNoticeTemplate, + &newNoticeItem); + + if (rv != SECSuccess) { + goto loser; + } + + if (userNotice->derNoticeReference.data != NULL) { + + rv = SEC_QuickDERDecodeItem(arena, &userNotice->noticeReference, + CERT_NoticeReferenceTemplate, + &userNotice->derNoticeReference); + if (rv == SECFailure) { + goto loser; + } + } + + return (userNotice); + +loser: + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + + return (NULL); +} + +void +CERT_DestroyUserNotice(CERTUserNotice *userNotice) +{ + if (userNotice != NULL) { + PORT_FreeArena(userNotice->arena, PR_FALSE); + } + return; +} + +static CERTPolicyStringCallback policyStringCB = NULL; +static void *policyStringCBArg = NULL; + +void +CERT_SetCAPolicyStringCallback(CERTPolicyStringCallback cb, void *cbarg) +{ + policyStringCB = cb; + policyStringCBArg = cbarg; + return; +} + +char * +stringFromUserNotice(SECItem *noticeItem) +{ + SECItem *org; + unsigned int len, headerlen; + char *stringbuf; + CERTUserNotice *userNotice; + char *policystr; + char *retstr = NULL; + SECItem *displayText; + SECItem **noticeNumbers; + unsigned int strnum; + + /* decode the user notice */ + userNotice = CERT_DecodeUserNotice(noticeItem); + if (userNotice == NULL) { + return (NULL); + } + + org = &userNotice->noticeReference.organization; + if ((org->len != 0) && (policyStringCB != NULL)) { + /* has a noticeReference */ + + /* extract the org string */ + len = org->len; + stringbuf = (char *)PORT_Alloc(len + 1); + if (stringbuf != NULL) { + PORT_Memcpy(stringbuf, org->data, len); + stringbuf[len] = '\0'; + + noticeNumbers = userNotice->noticeReference.noticeNumbers; + while (*noticeNumbers != NULL) { + /* XXX - only one byte integers right now*/ + strnum = (*noticeNumbers)->data[0]; + policystr = + (*policyStringCB)(stringbuf, strnum, policyStringCBArg); + if (policystr != NULL) { + if (retstr != NULL) { + retstr = PR_sprintf_append(retstr, "\n%s", policystr); + } else { + retstr = PR_sprintf_append(retstr, "%s", policystr); + } + + PORT_Free(policystr); + } + + noticeNumbers++; + } + + PORT_Free(stringbuf); + } + } + + if (retstr == NULL) { + if (userNotice->displayText.len != 0) { + displayText = &userNotice->displayText; + + if (displayText->len > 2) { + if (displayText->data[0] == SEC_ASN1_VISIBLE_STRING) { + headerlen = 2; + if (displayText->data[1] & 0x80) { + /* multibyte length */ + headerlen += (displayText->data[1] & 0x7f); + } + + len = displayText->len - headerlen; + retstr = (char *)PORT_Alloc(len + 1); + if (retstr != NULL) { + PORT_Memcpy(retstr, &displayText->data[headerlen], len); + retstr[len] = '\0'; + } + } + } + } + } + + CERT_DestroyUserNotice(userNotice); + + return (retstr); +} + +char * +CERT_GetCertCommentString(CERTCertificate *cert) +{ + char *retstring = NULL; + SECStatus rv; + SECItem policyItem; + CERTCertificatePolicies *policies = NULL; + CERTPolicyInfo **policyInfos; + CERTPolicyQualifier **policyQualifiers, *qualifier; + + policyItem.data = NULL; + + rv = CERT_FindCertExtension(cert, SEC_OID_X509_CERTIFICATE_POLICIES, + &policyItem); + if (rv != SECSuccess) { + goto nopolicy; + } + + policies = CERT_DecodeCertificatePoliciesExtension(&policyItem); + if (policies == NULL) { + goto nopolicy; + } + + policyInfos = policies->policyInfos; + /* search through policyInfos looking for the verisign policy */ + while (*policyInfos != NULL) { + if ((*policyInfos)->oid == SEC_OID_VERISIGN_USER_NOTICES) { + policyQualifiers = (*policyInfos)->policyQualifiers; + /* search through the policy qualifiers looking for user notice */ + while (policyQualifiers != NULL && *policyQualifiers != NULL) { + qualifier = *policyQualifiers; + if (qualifier->oid == SEC_OID_PKIX_USER_NOTICE_QUALIFIER) { + retstring = + stringFromUserNotice(&qualifier->qualifierValue); + break; + } + + policyQualifiers++; + } + break; + } + policyInfos++; + } + +nopolicy: + if (policyItem.data != NULL) { + PORT_Free(policyItem.data); + } + + if (policies != NULL) { + CERT_DestroyCertificatePoliciesExtension(policies); + } + + if (retstring == NULL) { + retstring = + CERT_FindNSStringExtension(cert, SEC_OID_NS_CERT_EXT_COMMENT); + } + + if (retstring != NULL) { + breakLines(retstring); + } + + return (retstring); +} + +const SEC_ASN1Template CERT_OidSeqTemplate[] = { + { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN, offsetof(CERTOidSequence, oids), + SEC_ASN1_SUB(SEC_ObjectIDTemplate) } +}; + +CERTOidSequence * +CERT_DecodeOidSequence(const SECItem *seqItem) +{ + PLArenaPool *arena = NULL; + SECStatus rv; + CERTOidSequence *oidSeq; + SECItem newSeqItem; + + /* make a new arena */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if (!arena) { + goto loser; + } + + /* allocate the userNotice structure */ + oidSeq = + (CERTOidSequence *)PORT_ArenaZAlloc(arena, sizeof(CERTOidSequence)); + + if (oidSeq == NULL) { + goto loser; + } + + oidSeq->arena = arena; + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newSeqItem, seqItem); + if (rv != SECSuccess) { + goto loser; + } + + /* decode the user notice */ + rv = + SEC_QuickDERDecodeItem(arena, oidSeq, CERT_OidSeqTemplate, &newSeqItem); + + if (rv != SECSuccess) { + goto loser; + } + + return (oidSeq); + +loser: + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + return (NULL); +} + +void +CERT_DestroyOidSequence(CERTOidSequence *oidSeq) +{ + if (oidSeq != NULL) { + PORT_FreeArena(oidSeq->arena, PR_FALSE); + } + return; +} + +PRBool +CERT_GovtApprovedBitSet(CERTCertificate *cert) +{ + SECStatus rv; + SECItem extItem; + CERTOidSequence *oidSeq = NULL; + PRBool ret; + SECItem **oids; + SECItem *oid; + SECOidTag oidTag; + + extItem.data = NULL; + rv = CERT_FindCertExtension(cert, SEC_OID_X509_EXT_KEY_USAGE, &extItem); + if (rv != SECSuccess) { + goto loser; + } + + oidSeq = CERT_DecodeOidSequence(&extItem); + if (oidSeq == NULL) { + goto loser; + } + + oids = oidSeq->oids; + while (oids != NULL && *oids != NULL) { + oid = *oids; + + oidTag = SECOID_FindOIDTag(oid); + + if (oidTag == SEC_OID_NS_KEY_USAGE_GOVT_APPROVED) { + goto success; + } + + oids++; + } + +loser: + ret = PR_FALSE; + goto done; +success: + ret = PR_TRUE; +done: + if (oidSeq != NULL) { + CERT_DestroyOidSequence(oidSeq); + } + if (extItem.data != NULL) { + PORT_Free(extItem.data); + } + return (ret); +} + +SECStatus +CERT_EncodePolicyConstraintsExtension(PLArenaPool *arena, + CERTCertificatePolicyConstraints *constr, + SECItem *dest) +{ + SECStatus rv = SECSuccess; + + PORT_Assert(constr != NULL && dest != NULL); + if (constr == NULL || dest == NULL) { + return SECFailure; + } + + if (SEC_ASN1EncodeItem(arena, dest, constr, + CERT_PolicyConstraintsTemplate) == NULL) { + rv = SECFailure; + } + return (rv); +} + +SECStatus +CERT_EncodePolicyMappingExtension(PLArenaPool *arena, + CERTCertificatePolicyMappings *mapping, + SECItem *dest) +{ + SECStatus rv = SECSuccess; + + PORT_Assert(mapping != NULL && dest != NULL); + if (mapping == NULL || dest == NULL) { + return SECFailure; + } + + if (SEC_ASN1EncodeItem(arena, dest, mapping, CERT_PolicyMappingsTemplate) == + NULL) { + rv = SECFailure; + } + return (rv); +} + +SECStatus +CERT_EncodeCertPoliciesExtension(PLArenaPool *arena, CERTPolicyInfo **info, + SECItem *dest) +{ + SECStatus rv = SECSuccess; + + PORT_Assert(info != NULL && dest != NULL); + if (info == NULL || dest == NULL) { + return SECFailure; + } + + if (SEC_ASN1EncodeItem(arena, dest, info, + CERT_CertificatePoliciesTemplate) == NULL) { + rv = SECFailure; + } + return (rv); +} + +SECStatus +CERT_EncodeUserNotice(PLArenaPool *arena, CERTUserNotice *notice, SECItem *dest) +{ + SECStatus rv = SECSuccess; + + PORT_Assert(notice != NULL && dest != NULL); + if (notice == NULL || dest == NULL) { + return SECFailure; + } + + if (SEC_ASN1EncodeItem(arena, dest, notice, CERT_UserNoticeTemplate) == + NULL) { + rv = SECFailure; + } + + return (rv); +} + +SECStatus +CERT_EncodeNoticeReference(PLArenaPool *arena, CERTNoticeReference *reference, + SECItem *dest) +{ + SECStatus rv = SECSuccess; + + PORT_Assert(reference != NULL && dest != NULL); + if (reference == NULL || dest == NULL) { + return SECFailure; + } + + if (SEC_ASN1EncodeItem(arena, dest, reference, + CERT_NoticeReferenceTemplate) == NULL) { + rv = SECFailure; + } + + return (rv); +} + +SECStatus +CERT_EncodeInhibitAnyExtension(PLArenaPool *arena, + CERTCertificateInhibitAny *certInhibitAny, + SECItem *dest) +{ + SECStatus rv = SECSuccess; + + PORT_Assert(certInhibitAny != NULL && dest != NULL); + if (certInhibitAny == NULL || dest == NULL) { + return SECFailure; + } + + if (SEC_ASN1EncodeItem(arena, dest, certInhibitAny, + CERT_InhibitAnyTemplate) == NULL) { + rv = SECFailure; + } + return (rv); +} diff --git a/security/nss/lib/certdb/secname.c b/security/nss/lib/certdb/secname.c new file mode 100644 index 0000000000..654dfdf3f0 --- /dev/null +++ b/security/nss/lib/certdb/secname.c @@ -0,0 +1,701 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cert.h" +#include "secoid.h" +#include "secder.h" /* XXX remove this when remove the DERTemplates */ +#include "secasn1.h" +#include "secitem.h" +#include +#include "secerr.h" +#include "certi.h" + +static const SEC_ASN1Template cert_AVATemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTAVA) }, + { SEC_ASN1_OBJECT_ID, offsetof(CERTAVA, type) }, + { SEC_ASN1_ANY, offsetof(CERTAVA, value) }, + { 0 } +}; + +const SEC_ASN1Template CERT_RDNTemplate[] = { + { SEC_ASN1_SET_OF, offsetof(CERTRDN, avas), cert_AVATemplate, + sizeof(CERTRDN) } +}; + +static int +CountArray(void **array) +{ + int count = 0; + if (array) { + while (*array++) { + count++; + } + } + return count; +} + +static void ** +AddToArray(PLArenaPool *arena, void **array, void *element) +{ + unsigned count; + void **ap; + + /* Count up number of slots already in use in the array */ + count = 0; + ap = array; + if (ap) { + while (*ap++) { + count++; + } + } + + if (array) { + array = + (void **)PORT_ArenaGrow(arena, array, (count + 1) * sizeof(void *), + (count + 2) * sizeof(void *)); + } else { + array = (void **)PORT_ArenaAlloc(arena, (count + 2) * sizeof(void *)); + } + if (array) { + array[count] = element; + array[count + 1] = 0; + } + return array; +} + +SECOidTag +CERT_GetAVATag(CERTAVA *ava) +{ + SECOidData *oid; + if (!ava->type.data) + return (SECOidTag)-1; + + oid = SECOID_FindOID(&ava->type); + + if (oid) { + return (oid->offset); + } + return (SECOidTag)-1; +} + +static SECStatus +SetupAVAType(PLArenaPool *arena, SECOidTag type, SECItem *it, unsigned *maxLenp) +{ + unsigned char *oid; + unsigned oidLen; + unsigned char *cp; + int maxLen; + SECOidData *oidrec; + + oidrec = SECOID_FindOIDByTag(type); + if (oidrec == NULL) + return SECFailure; + + oid = oidrec->oid.data; + oidLen = oidrec->oid.len; + + maxLen = cert_AVAOidTagToMaxLen(type); + if (maxLen < 0) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + it->data = cp = (unsigned char *)PORT_ArenaAlloc(arena, oidLen); + if (cp == NULL) { + return SECFailure; + } + it->len = oidLen; + PORT_Memcpy(cp, oid, oidLen); + *maxLenp = (unsigned)maxLen; + return SECSuccess; +} + +static SECStatus +SetupAVAValue(PLArenaPool *arena, int valueType, const SECItem *in, + SECItem *out, unsigned maxLen) +{ + PRUint8 *value, *cp, *ucs4Val; + unsigned valueLen, valueLenLen, total; + unsigned ucs4Len = 0, ucs4MaxLen; + + value = in->data; + valueLen = in->len; + switch (valueType) { + case SEC_ASN1_PRINTABLE_STRING: + case SEC_ASN1_IA5_STRING: + case SEC_ASN1_T61_STRING: + case SEC_ASN1_UTF8_STRING: /* no conversion required */ + break; + case SEC_ASN1_UNIVERSAL_STRING: + ucs4MaxLen = valueLen * 6; + ucs4Val = (PRUint8 *)PORT_ArenaZAlloc(arena, ucs4MaxLen); + if (!ucs4Val || + !PORT_UCS4_UTF8Conversion(PR_TRUE, value, valueLen, ucs4Val, + ucs4MaxLen, &ucs4Len)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + value = ucs4Val; + valueLen = ucs4Len; + maxLen *= 4; + break; + default: + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (valueLen > maxLen) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + valueLenLen = DER_LengthLength(valueLen); + total = 1 + valueLenLen + valueLen; + cp = (PRUint8 *)PORT_ArenaAlloc(arena, total); + if (!cp) { + return SECFailure; + } + out->data = cp; + out->len = total; + cp = (PRUint8 *)DER_StoreHeader(cp, valueType, valueLen); + PORT_Memcpy(cp, value, valueLen); + return SECSuccess; +} + +CERTAVA * +CERT_CreateAVAFromRaw(PLArenaPool *pool, const SECItem *OID, + const SECItem *value) +{ + CERTAVA *ava; + int rv; + + ava = PORT_ArenaZNew(pool, CERTAVA); + if (ava) { + rv = SECITEM_CopyItem(pool, &ava->type, OID); + if (rv) + return NULL; + + rv = SECITEM_CopyItem(pool, &ava->value, value); + if (rv) + return NULL; + } + return ava; +} + +CERTAVA * +CERT_CreateAVAFromSECItem(PLArenaPool *arena, SECOidTag kind, int valueType, + SECItem *value) +{ + CERTAVA *ava; + int rv; + unsigned maxLen; + + ava = (CERTAVA *)PORT_ArenaZAlloc(arena, sizeof(CERTAVA)); + if (ava) { + rv = SetupAVAType(arena, kind, &ava->type, &maxLen); + if (rv) { + /* Illegal AVA type */ + return NULL; + } + rv = SetupAVAValue(arena, valueType, value, &ava->value, maxLen); + if (rv) { + /* Illegal value type */ + return NULL; + } + } + return ava; +} + +CERTAVA * +CERT_CreateAVA(PLArenaPool *arena, SECOidTag kind, int valueType, char *value) +{ + SECItem item = { siBuffer, NULL, 0 }; + + item.data = (PRUint8 *)value; + item.len = PORT_Strlen(value); + + return CERT_CreateAVAFromSECItem(arena, kind, valueType, &item); +} + +CERTAVA * +CERT_CopyAVA(PLArenaPool *arena, CERTAVA *from) +{ + CERTAVA *ava; + int rv; + + ava = (CERTAVA *)PORT_ArenaZAlloc(arena, sizeof(CERTAVA)); + if (ava) { + rv = SECITEM_CopyItem(arena, &ava->type, &from->type); + if (rv) + goto loser; + rv = SECITEM_CopyItem(arena, &ava->value, &from->value); + if (rv) + goto loser; + } + return ava; + +loser: + return 0; +} + +CERTRDN * +CERT_CreateRDN(PLArenaPool *arena, CERTAVA *ava0, ...) +{ + CERTAVA *ava; + CERTRDN *rdn; + va_list ap; + unsigned count; + CERTAVA **avap; + + rdn = (CERTRDN *)PORT_ArenaAlloc(arena, sizeof(CERTRDN)); + if (rdn) { + /* Count number of avas going into the rdn */ + count = 0; + if (ava0) { + count++; + va_start(ap, ava0); + while ((ava = va_arg(ap, CERTAVA *)) != 0) { + count++; + } + va_end(ap); + } + + /* Now fill in the pointers */ + rdn->avas = avap = + (CERTAVA **)PORT_ArenaAlloc(arena, (count + 1) * sizeof(CERTAVA *)); + if (!avap) { + return 0; + } + if (ava0) { + *avap++ = ava0; + va_start(ap, ava0); + while ((ava = va_arg(ap, CERTAVA *)) != 0) { + *avap++ = ava; + } + va_end(ap); + } + *avap++ = 0; + } + return rdn; +} + +SECStatus +CERT_AddAVA(PLArenaPool *arena, CERTRDN *rdn, CERTAVA *ava) +{ + rdn->avas = (CERTAVA **)AddToArray(arena, (void **)rdn->avas, ava); + return rdn->avas ? SECSuccess : SECFailure; +} + +SECStatus +CERT_CopyRDN(PLArenaPool *arena, CERTRDN *to, CERTRDN *from) +{ + CERTAVA **avas, *fava, *tava; + SECStatus rv = SECSuccess; + + /* Copy each ava from from */ + avas = from->avas; + if (avas) { + if (avas[0] == NULL) { + rv = CERT_AddAVA(arena, to, NULL); + return rv; + } + while ((fava = *avas++) != 0) { + tava = CERT_CopyAVA(arena, fava); + if (!tava) { + rv = SECFailure; + break; + } + rv = CERT_AddAVA(arena, to, tava); + if (rv != SECSuccess) + break; + } + } + return rv; +} + +/************************************************************************/ + +const SEC_ASN1Template CERT_NameTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, offsetof(CERTName, rdns), CERT_RDNTemplate, + sizeof(CERTName) } +}; + +SEC_ASN1_CHOOSER_IMPLEMENT(CERT_NameTemplate) + +CERTName * +CERT_CreateName(CERTRDN *rdn0, ...) +{ + CERTRDN *rdn; + CERTName *name; + va_list ap; + unsigned count; + CERTRDN **rdnp; + PLArenaPool *arena; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + return (0); + } + + name = (CERTName *)PORT_ArenaAlloc(arena, sizeof(CERTName)); + if (name) { + name->arena = arena; + + /* Count number of RDNs going into the Name */ + if (!rdn0) { + count = 0; + } else { + count = 1; + va_start(ap, rdn0); + while ((rdn = va_arg(ap, CERTRDN *)) != 0) { + count++; + } + va_end(ap); + } + + /* Allocate space (including space for terminal null ptr) */ + name->rdns = rdnp = + (CERTRDN **)PORT_ArenaAlloc(arena, (count + 1) * sizeof(CERTRDN *)); + if (!name->rdns) { + goto loser; + } + + /* Now fill in the pointers */ + if (count > 0) { + *rdnp++ = rdn0; + va_start(ap, rdn0); + while ((rdn = va_arg(ap, CERTRDN *)) != 0) { + *rdnp++ = rdn; + } + va_end(ap); + } + + /* null terminate the list */ + *rdnp++ = 0; + } + return name; + +loser: + PORT_FreeArena(arena, PR_FALSE); + return (0); +} + +void +CERT_DestroyName(CERTName *name) +{ + if (name) { + PLArenaPool *arena = name->arena; + name->rdns = NULL; + name->arena = NULL; + if (arena) + PORT_FreeArena(arena, PR_FALSE); + } +} + +SECStatus +CERT_AddRDN(CERTName *name, CERTRDN *rdn) +{ + name->rdns = (CERTRDN **)AddToArray(name->arena, (void **)name->rdns, rdn); + return name->rdns ? SECSuccess : SECFailure; +} + +SECStatus +CERT_CopyName(PLArenaPool *arena, CERTName *to, const CERTName *from) +{ + CERTRDN **rdns, *frdn, *trdn; + SECStatus rv = SECSuccess; + + if (!to || !from) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + CERT_DestroyName(to); + to->arena = arena; + + /* Copy each rdn from from */ + rdns = from->rdns; + if (rdns) { + if (rdns[0] == NULL) { + rv = CERT_AddRDN(to, NULL); + return rv; + } + while ((frdn = *rdns++) != NULL) { + trdn = CERT_CreateRDN(arena, NULL); + if (!trdn) { + rv = SECFailure; + break; + } + rv = CERT_CopyRDN(arena, trdn, frdn); + if (rv != SECSuccess) + break; + rv = CERT_AddRDN(to, trdn); + if (rv != SECSuccess) + break; + } + } + return rv; +} + +/************************************************************************/ + +static void +canonicalize(SECItem *foo) +{ + int ch, lastch, len, src, dest; + + /* strip trailing whitespace. */ + len = foo->len; + while (len > 0 && ((ch = foo->data[len - 1]) == ' ' || ch == '\t' || + ch == '\r' || ch == '\n')) { + len--; + } + + src = 0; + /* strip leading whitespace. */ + while (src < len && ((ch = foo->data[src]) == ' ' || ch == '\t' || + ch == '\r' || ch == '\n')) { + src++; + } + dest = 0; + lastch = ' '; + while (src < len) { + ch = foo->data[src++]; + if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') { + ch = ' '; + if (ch == lastch) + continue; + } else if (ch >= 'A' && ch <= 'Z') { + ch |= 0x20; /* downshift */ + } + foo->data[dest++] = lastch = ch; + } + foo->len = dest; +} + +/* SECItems a and b contain DER-encoded printable strings. */ +SECComparison +CERT_CompareDERPrintableStrings(const SECItem *a, const SECItem *b) +{ + SECComparison rv = SECLessThan; + SECItem *aVal = CERT_DecodeAVAValue(a); + SECItem *bVal = CERT_DecodeAVAValue(b); + + if (aVal && aVal->len && aVal->data && bVal && bVal->len && bVal->data) { + canonicalize(aVal); + canonicalize(bVal); + rv = SECITEM_CompareItem(aVal, bVal); + } + SECITEM_FreeItem(aVal, PR_TRUE); + SECITEM_FreeItem(bVal, PR_TRUE); + return rv; +} + +SECComparison +CERT_CompareAVA(const CERTAVA *a, const CERTAVA *b) +{ + SECComparison rv; + + rv = SECITEM_CompareItem(&a->type, &b->type); + if (SECEqual != rv) + return rv; /* Attribute types don't match. */ + /* Let's be optimistic. Maybe the values will just compare equal. */ + rv = SECITEM_CompareItem(&a->value, &b->value); + if (SECEqual == rv) + return rv; /* values compared exactly. */ + if (a->value.len && a->value.data && b->value.len && b->value.data) { + /* Here, the values did not match. + ** If the values had different encodings, convert them to the same + ** encoding and compare that way. + */ + if (a->value.data[0] != b->value.data[0]) { + /* encodings differ. Convert both to UTF-8 and compare. */ + SECItem *aVal = CERT_DecodeAVAValue(&a->value); + SECItem *bVal = CERT_DecodeAVAValue(&b->value); + if (aVal && aVal->len && aVal->data && bVal && bVal->len && + bVal->data) { + rv = SECITEM_CompareItem(aVal, bVal); + } + SECITEM_FreeItem(aVal, PR_TRUE); + SECITEM_FreeItem(bVal, PR_TRUE); + } else if (a->value.data[0] == 0x13) { /* both are printable strings. */ + /* printable strings */ + rv = CERT_CompareDERPrintableStrings(&a->value, &b->value); + } + } + return rv; +} + +SECComparison +CERT_CompareRDN(const CERTRDN *a, const CERTRDN *b) +{ + CERTAVA **aavas, *aava; + CERTAVA **bavas, *bava; + int ac, bc; + SECComparison rv = SECEqual; + + aavas = a->avas; + bavas = b->avas; + + /* + ** Make sure array of ava's are the same length. If not, then we are + ** not equal + */ + ac = CountArray((void **)aavas); + bc = CountArray((void **)bavas); + if (ac < bc) + return SECLessThan; + if (ac > bc) + return SECGreaterThan; + + while (NULL != (aava = *aavas++)) { + for (bavas = b->avas; NULL != (bava = *bavas++);) { + rv = SECITEM_CompareItem(&aava->type, &bava->type); + if (SECEqual == rv) { + rv = CERT_CompareAVA(aava, bava); + if (SECEqual != rv) + return rv; + break; + } + } + if (!bava) /* didn't find a match */ + return SECGreaterThan; + } + return rv; +} + +SECComparison +CERT_CompareName(const CERTName *a, const CERTName *b) +{ + CERTRDN **ardns; + CERTRDN **brdns; + int ac, bc; + SECComparison rv = SECEqual; + + ardns = a->rdns; + brdns = b->rdns; + + /* + ** Make sure array of rdn's are the same length. If not, then we are + ** not equal + */ + ac = CountArray((void **)ardns); + bc = CountArray((void **)brdns); + if (ac < bc) + return SECLessThan; + if (ac > bc) + return SECGreaterThan; + + while (rv == SECEqual && *ardns) { + rv = CERT_CompareRDN(*ardns++, *brdns++); + } + return rv; +} + +/* Moved from certhtml.c */ +SECItem * +CERT_DecodeAVAValue(const SECItem *derAVAValue) +{ + SECItem *retItem; + const SEC_ASN1Template *theTemplate = NULL; + enum { conv_none, + conv_ucs4, + conv_ucs2, + conv_iso88591 } convert = conv_none; + SECItem avaValue = { siBuffer, 0 }; + PORTCheapArenaPool tmpArena; + + if (!derAVAValue || !derAVAValue->len || !derAVAValue->data) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + switch (derAVAValue->data[0]) { + case SEC_ASN1_UNIVERSAL_STRING: + convert = conv_ucs4; + theTemplate = SEC_ASN1_GET(SEC_UniversalStringTemplate); + break; + case SEC_ASN1_IA5_STRING: + theTemplate = SEC_ASN1_GET(SEC_IA5StringTemplate); + break; + case SEC_ASN1_PRINTABLE_STRING: + theTemplate = SEC_ASN1_GET(SEC_PrintableStringTemplate); + break; + case SEC_ASN1_T61_STRING: + /* + * Per common practice, we're not decoding actual T.61, but instead + * treating T61-labeled strings as containing ISO-8859-1. + */ + convert = conv_iso88591; + theTemplate = SEC_ASN1_GET(SEC_T61StringTemplate); + break; + case SEC_ASN1_BMP_STRING: + convert = conv_ucs2; + theTemplate = SEC_ASN1_GET(SEC_BMPStringTemplate); + break; + case SEC_ASN1_UTF8_STRING: + /* No conversion needed ! */ + theTemplate = SEC_ASN1_GET(SEC_UTF8StringTemplate); + break; + default: + PORT_SetError(SEC_ERROR_INVALID_AVA); + return NULL; + } + + PORT_Memset(&avaValue, 0, sizeof(SECItem)); + PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); + if (SEC_QuickDERDecodeItem(&tmpArena.arena, &avaValue, theTemplate, + derAVAValue) != SECSuccess) { + PORT_DestroyCheapArena(&tmpArena); + return NULL; + } + + if (convert != conv_none) { + unsigned int utf8ValLen = avaValue.len * 3; + unsigned char *utf8Val = + (unsigned char *)PORT_ArenaZAlloc(&tmpArena.arena, utf8ValLen); + + switch (convert) { + case conv_ucs4: + if (avaValue.len % 4 != 0 || + !PORT_UCS4_UTF8Conversion(PR_FALSE, avaValue.data, + avaValue.len, utf8Val, utf8ValLen, + &utf8ValLen)) { + PORT_DestroyCheapArena(&tmpArena); + PORT_SetError(SEC_ERROR_INVALID_AVA); + return NULL; + } + break; + case conv_ucs2: + if (avaValue.len % 2 != 0 || + !PORT_UCS2_UTF8Conversion(PR_FALSE, avaValue.data, + avaValue.len, utf8Val, utf8ValLen, + &utf8ValLen)) { + PORT_DestroyCheapArena(&tmpArena); + PORT_SetError(SEC_ERROR_INVALID_AVA); + return NULL; + } + break; + case conv_iso88591: + if (!PORT_ISO88591_UTF8Conversion(avaValue.data, avaValue.len, + utf8Val, utf8ValLen, + &utf8ValLen)) { + PORT_DestroyCheapArena(&tmpArena); + PORT_SetError(SEC_ERROR_INVALID_AVA); + return NULL; + } + break; + case conv_none: + PORT_Assert(0); /* not reached */ + break; + } + + avaValue.data = utf8Val; + avaValue.len = utf8ValLen; + } + + retItem = SECITEM_DupItem(&avaValue); + PORT_DestroyCheapArena(&tmpArena); + return retItem; +} diff --git a/security/nss/lib/certdb/stanpcertdb.c b/security/nss/lib/certdb/stanpcertdb.c new file mode 100644 index 0000000000..e255df1053 --- /dev/null +++ b/security/nss/lib/certdb/stanpcertdb.c @@ -0,0 +1,1137 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prtime.h" + +#include "cert.h" +#include "certi.h" +#include "certdb.h" +#include "secitem.h" +#include "secder.h" + +/* Call to PK11_FreeSlot below */ + +#include "secasn1.h" +#include "secerr.h" +#include "nssilock.h" +#include "prmon.h" +#include "base64.h" +#include "sechash.h" +#include "plhash.h" +#include "pk11func.h" /* sigh */ + +#include "nsspki.h" +#include "pki.h" +#include "pkim.h" +#include "pki3hack.h" +#include "ckhelper.h" +#include "base.h" +#include "pkistore.h" +#include "dev3hack.h" +#include "dev.h" +#include "secmodi.h" + +extern void CERT_MaybeLockCertTempPerm(const CERTCertificate *cert); +extern void CERT_MaybeUnlockCertTempPerm(const CERTCertificate *cert); + +PRBool +SEC_CertNicknameConflict(const char *nickname, const SECItem *derSubject, + CERTCertDBHandle *handle) +{ + CERTCertificate *cert; + PRBool conflict = PR_FALSE; + + cert = CERT_FindCertByNickname(handle, nickname); + + if (!cert) { + return conflict; + } + + conflict = !SECITEM_ItemsAreEqual(derSubject, &cert->derSubject); + CERT_DestroyCertificate(cert); + return conflict; +} + +SECStatus +SEC_DeletePermCertificate(CERTCertificate *cert) +{ + PRStatus nssrv; + NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); + NSSCertificate *c = STAN_GetNSSCertificate(cert); + CERTCertTrust *certTrust; + + if (c == NULL) { + /* error code is set */ + return SECFailure; + } + + certTrust = nssTrust_GetCERTCertTrustForCert(c, cert); + if (certTrust) { + NSSTrust *nssTrust = nssTrustDomain_FindTrustForCertificate(td, c); + if (nssTrust) { + nssrv = STAN_DeleteCertTrustMatchingSlot(c); + if (nssrv != PR_SUCCESS) { + CERT_MapStanError(); + } + /* This call always returns PR_SUCCESS! */ + (void)nssTrust_Destroy(nssTrust); + } + } + + /* get rid of the token instances */ + nssrv = NSSCertificate_DeleteStoredObject(c, NULL); + + /* get rid of the cache entry */ + nssTrustDomain_LockCertCache(td); + nssTrustDomain_RemoveCertFromCacheLOCKED(td, c); + nssTrustDomain_UnlockCertCache(td); + + return (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure; +} + +SECStatus +CERT_GetCertTrust(const CERTCertificate *cert, CERTCertTrust *trust) +{ + SECStatus rv; + CERT_LockCertTrust(cert); + if (!cert || cert->trust == NULL) { + rv = SECFailure; + } else { + *trust = *cert->trust; + rv = SECSuccess; + } + CERT_UnlockCertTrust(cert); + return (rv); +} + +extern const NSSError NSS_ERROR_NO_ERROR; +extern const NSSError NSS_ERROR_INTERNAL_ERROR; +extern const NSSError NSS_ERROR_NO_MEMORY; +extern const NSSError NSS_ERROR_INVALID_POINTER; +extern const NSSError NSS_ERROR_INVALID_ARENA; +extern const NSSError NSS_ERROR_INVALID_ARENA_MARK; +extern const NSSError NSS_ERROR_DUPLICATE_POINTER; +extern const NSSError NSS_ERROR_POINTER_NOT_REGISTERED; +extern const NSSError NSS_ERROR_TRACKER_NOT_EMPTY; +extern const NSSError NSS_ERROR_TRACKER_NOT_INITIALIZED; +extern const NSSError NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD; +extern const NSSError NSS_ERROR_VALUE_TOO_LARGE; +extern const NSSError NSS_ERROR_UNSUPPORTED_TYPE; +extern const NSSError NSS_ERROR_BUFFER_TOO_SHORT; +extern const NSSError NSS_ERROR_INVALID_ATOB_CONTEXT; +extern const NSSError NSS_ERROR_INVALID_BASE64; +extern const NSSError NSS_ERROR_INVALID_BTOA_CONTEXT; +extern const NSSError NSS_ERROR_INVALID_ITEM; +extern const NSSError NSS_ERROR_INVALID_STRING; +extern const NSSError NSS_ERROR_INVALID_ASN1ENCODER; +extern const NSSError NSS_ERROR_INVALID_ASN1DECODER; +extern const NSSError NSS_ERROR_INVALID_BER; +extern const NSSError NSS_ERROR_INVALID_ATAV; +extern const NSSError NSS_ERROR_INVALID_ARGUMENT; +extern const NSSError NSS_ERROR_INVALID_UTF8; +extern const NSSError NSS_ERROR_INVALID_NSSOID; +extern const NSSError NSS_ERROR_UNKNOWN_ATTRIBUTE; +extern const NSSError NSS_ERROR_NOT_FOUND; +extern const NSSError NSS_ERROR_INVALID_PASSWORD; +extern const NSSError NSS_ERROR_USER_CANCELED; +extern const NSSError NSS_ERROR_MAXIMUM_FOUND; +extern const NSSError NSS_ERROR_CERTIFICATE_ISSUER_NOT_FOUND; +extern const NSSError NSS_ERROR_CERTIFICATE_IN_CACHE; +extern const NSSError NSS_ERROR_HASH_COLLISION; +extern const NSSError NSS_ERROR_DEVICE_ERROR; +extern const NSSError NSS_ERROR_INVALID_CERTIFICATE; +extern const NSSError NSS_ERROR_BUSY; +extern const NSSError NSS_ERROR_ALREADY_INITIALIZED; +extern const NSSError NSS_ERROR_PKCS11; + +/* Look at the stan error stack and map it to NSS 3 errors */ +#define STAN_MAP_ERROR(x, y) \ + else if (error == (x)) { secError = y; } + +/* + * map Stan errors into NSS errors + * This function examines the stan error stack and automatically sets + * PORT_SetError(); to the appropriate SEC_ERROR value. + */ +void +CERT_MapStanError() +{ + PRInt32 *errorStack; + NSSError error, prevError; + int secError; + int i; + + errorStack = NSS_GetErrorStack(); + if (errorStack == 0) { + PORT_SetError(0); + return; + } + error = prevError = CKR_GENERAL_ERROR; + /* get the 'top 2' error codes from the stack */ + for (i = 0; errorStack[i]; i++) { + prevError = error; + error = errorStack[i]; + } + if (error == NSS_ERROR_PKCS11) { + /* map it */ + secError = PK11_MapError(prevError); + } + STAN_MAP_ERROR(NSS_ERROR_NO_ERROR, 0) + STAN_MAP_ERROR(NSS_ERROR_NO_MEMORY, SEC_ERROR_NO_MEMORY) + STAN_MAP_ERROR(NSS_ERROR_INVALID_BASE64, SEC_ERROR_BAD_DATA) + STAN_MAP_ERROR(NSS_ERROR_INVALID_BER, SEC_ERROR_BAD_DER) + STAN_MAP_ERROR(NSS_ERROR_INVALID_ATAV, SEC_ERROR_INVALID_AVA) + STAN_MAP_ERROR(NSS_ERROR_INVALID_PASSWORD, SEC_ERROR_BAD_PASSWORD) + STAN_MAP_ERROR(NSS_ERROR_BUSY, SEC_ERROR_BUSY) + STAN_MAP_ERROR(NSS_ERROR_DEVICE_ERROR, SEC_ERROR_IO) + STAN_MAP_ERROR(NSS_ERROR_CERTIFICATE_ISSUER_NOT_FOUND, + SEC_ERROR_UNKNOWN_ISSUER) + STAN_MAP_ERROR(NSS_ERROR_INVALID_CERTIFICATE, SEC_ERROR_CERT_NOT_VALID) + STAN_MAP_ERROR(NSS_ERROR_INVALID_UTF8, SEC_ERROR_BAD_DATA) + STAN_MAP_ERROR(NSS_ERROR_INVALID_NSSOID, SEC_ERROR_BAD_DATA) + + /* these are library failure for lack of a better error code */ + STAN_MAP_ERROR(NSS_ERROR_NOT_FOUND, SEC_ERROR_LIBRARY_FAILURE) + STAN_MAP_ERROR(NSS_ERROR_CERTIFICATE_IN_CACHE, SEC_ERROR_LIBRARY_FAILURE) + STAN_MAP_ERROR(NSS_ERROR_MAXIMUM_FOUND, SEC_ERROR_LIBRARY_FAILURE) + STAN_MAP_ERROR(NSS_ERROR_USER_CANCELED, SEC_ERROR_LIBRARY_FAILURE) + STAN_MAP_ERROR(NSS_ERROR_TRACKER_NOT_INITIALIZED, SEC_ERROR_LIBRARY_FAILURE) + STAN_MAP_ERROR(NSS_ERROR_ALREADY_INITIALIZED, SEC_ERROR_LIBRARY_FAILURE) + STAN_MAP_ERROR(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD, + SEC_ERROR_LIBRARY_FAILURE) + STAN_MAP_ERROR(NSS_ERROR_HASH_COLLISION, SEC_ERROR_LIBRARY_FAILURE) + + STAN_MAP_ERROR(NSS_ERROR_INTERNAL_ERROR, SEC_ERROR_LIBRARY_FAILURE) + + /* these are all invalid arguments */ + STAN_MAP_ERROR(NSS_ERROR_INVALID_ARGUMENT, SEC_ERROR_INVALID_ARGS) + STAN_MAP_ERROR(NSS_ERROR_INVALID_POINTER, SEC_ERROR_INVALID_ARGS) + STAN_MAP_ERROR(NSS_ERROR_INVALID_ARENA, SEC_ERROR_INVALID_ARGS) + STAN_MAP_ERROR(NSS_ERROR_INVALID_ARENA_MARK, SEC_ERROR_INVALID_ARGS) + STAN_MAP_ERROR(NSS_ERROR_DUPLICATE_POINTER, SEC_ERROR_INVALID_ARGS) + STAN_MAP_ERROR(NSS_ERROR_POINTER_NOT_REGISTERED, SEC_ERROR_INVALID_ARGS) + STAN_MAP_ERROR(NSS_ERROR_TRACKER_NOT_EMPTY, SEC_ERROR_INVALID_ARGS) + STAN_MAP_ERROR(NSS_ERROR_VALUE_TOO_LARGE, SEC_ERROR_INVALID_ARGS) + STAN_MAP_ERROR(NSS_ERROR_UNSUPPORTED_TYPE, SEC_ERROR_INVALID_ARGS) + STAN_MAP_ERROR(NSS_ERROR_BUFFER_TOO_SHORT, SEC_ERROR_INVALID_ARGS) + STAN_MAP_ERROR(NSS_ERROR_INVALID_ATOB_CONTEXT, SEC_ERROR_INVALID_ARGS) + STAN_MAP_ERROR(NSS_ERROR_INVALID_BTOA_CONTEXT, SEC_ERROR_INVALID_ARGS) + STAN_MAP_ERROR(NSS_ERROR_INVALID_ITEM, SEC_ERROR_INVALID_ARGS) + STAN_MAP_ERROR(NSS_ERROR_INVALID_STRING, SEC_ERROR_INVALID_ARGS) + STAN_MAP_ERROR(NSS_ERROR_INVALID_ASN1ENCODER, SEC_ERROR_INVALID_ARGS) + STAN_MAP_ERROR(NSS_ERROR_INVALID_ASN1DECODER, SEC_ERROR_INVALID_ARGS) + STAN_MAP_ERROR(NSS_ERROR_UNKNOWN_ATTRIBUTE, SEC_ERROR_INVALID_ARGS) + else { secError = SEC_ERROR_LIBRARY_FAILURE; } + PORT_SetError(secError); +} + +SECStatus +CERT_ChangeCertTrust(CERTCertDBHandle *handle, CERTCertificate *cert, + CERTCertTrust *trust) +{ + SECStatus rv = SECSuccess; + PRStatus ret; + + ret = STAN_ChangeCertTrust(cert, trust); + if (ret != PR_SUCCESS) { + rv = SECFailure; + CERT_MapStanError(); + } + return rv; +} + +extern const NSSError NSS_ERROR_INVALID_CERTIFICATE; + +SECStatus +__CERT_AddTempCertToPerm(CERTCertificate *cert, char *nickname, + CERTCertTrust *trust) +{ + NSSUTF8 *stanNick; + PK11SlotInfo *slot; + NSSToken *internal; + NSSCryptoContext *context; + nssCryptokiObject *permInstance; + NSSCertificate *c = STAN_GetNSSCertificate(cert); + nssCertificateStoreTrace lockTrace = { NULL, NULL, PR_FALSE, PR_FALSE }; + nssCertificateStoreTrace unlockTrace = { NULL, NULL, PR_FALSE, PR_FALSE }; + SECStatus rv; + PRStatus ret; + + if (c == NULL) { + CERT_MapStanError(); + return SECFailure; + } + + context = c->object.cryptoContext; + if (!context) { + PORT_SetError(SEC_ERROR_ADDING_CERT); + return SECFailure; /* wasn't a temp cert */ + } + stanNick = nssCertificate_GetNickname(c, NULL); + if (stanNick && nickname && strcmp(nickname, stanNick) != 0) { + /* different: take the new nickname */ + cert->nickname = NULL; + nss_ZFreeIf(stanNick); + stanNick = NULL; + } + if (!stanNick && nickname) { + /* Either there was no nickname yet, or we have a new nickname */ + stanNick = nssUTF8_Duplicate((NSSUTF8 *)nickname, NULL); + } /* else: old stanNick is identical to new nickname */ + /* Delete the temp instance */ + nssCertificateStore_Lock(context->certStore, &lockTrace); + nssCertificateStore_RemoveCertLOCKED(context->certStore, c); + nssCertificateStore_Unlock(context->certStore, &lockTrace, &unlockTrace); + c->object.cryptoContext = NULL; + + /* if the id has not been set explicitly yet, create one from the public + * key. */ + if (c->id.data == NULL) { + SECItem *keyID = pk11_mkcertKeyID(cert); + if (keyID) { + nssItem_Create(c->object.arena, &c->id, keyID->len, keyID->data); + SECITEM_FreeItem(keyID, PR_TRUE); + } + /* if any of these failed, continue with our null c->id */ + } + + /* Import the perm instance onto the internal token */ + slot = PK11_GetInternalKeySlot(); + internal = PK11Slot_GetNSSToken(slot); + if (!internal) { + PK11_FreeSlot(slot); + PORT_SetError(SEC_ERROR_NO_TOKEN); + return SECFailure; + } + permInstance = nssToken_ImportCertificate( + internal, NULL, NSSCertificateType_PKIX, &c->id, stanNick, &c->encoding, + &c->issuer, &c->subject, &c->serial, cert->emailAddr, PR_TRUE); + (void)nssToken_Destroy(internal); + nss_ZFreeIf(stanNick); + stanNick = NULL; + PK11_FreeSlot(slot); + if (!permInstance) { + if (NSS_GetError() == NSS_ERROR_INVALID_CERTIFICATE) { + PORT_SetError(SEC_ERROR_REUSED_ISSUER_AND_SERIAL); + } + return SECFailure; + } + nssPKIObject_AddInstance(&c->object, permInstance); + nssTrustDomain_AddCertsToCache(STAN_GetDefaultTrustDomain(), &c, 1); + /* reset the CERTCertificate fields */ + CERT_LockCertTempPerm(cert); + cert->nssCertificate = NULL; + CERT_UnlockCertTempPerm(cert); + cert = STAN_GetCERTCertificateOrRelease(c); /* should return same pointer */ + if (!cert) { + CERT_MapStanError(); + return SECFailure; + } + CERT_LockCertTempPerm(cert); + cert->istemp = PR_FALSE; + cert->isperm = PR_TRUE; + CERT_UnlockCertTempPerm(cert); + if (!trust) { + return SECSuccess; + } + ret = STAN_ChangeCertTrust(cert, trust); + rv = SECSuccess; + if (ret != PR_SUCCESS) { + rv = SECFailure; + CERT_MapStanError(); + } + return rv; +} + +SECStatus +CERT_AddTempCertToPerm(CERTCertificate *cert, char *nickname, + CERTCertTrust *trust) +{ + return __CERT_AddTempCertToPerm(cert, nickname, trust); +} + +CERTCertificate * +CERT_NewTempCertificate(CERTCertDBHandle *handle, SECItem *derCert, + char *nickname, PRBool isperm, PRBool copyDER) +{ + NSSCertificate *c; + CERTCertificate *cc; + NSSCertificate *tempCert = NULL; + nssPKIObject *pkio; + NSSCryptoContext *gCC = STAN_GetDefaultCryptoContext(); + NSSTrustDomain *gTD = STAN_GetDefaultTrustDomain(); + if (!isperm) { + NSSDER encoding; + NSSITEM_FROM_SECITEM(&encoding, derCert); + /* First, see if it is already a temp cert */ + c = NSSCryptoContext_FindCertificateByEncodedCertificate(gCC, + &encoding); + if (!c && handle) { + /* Then, see if it is already a perm cert */ + c = NSSTrustDomain_FindCertificateByEncodedCertificate(handle, + &encoding); + } + if (c) { + /* actually, that search ends up going by issuer/serial, + * so it is still possible to return a cert with the same + * issuer/serial but a different encoding, and we're + * going to reject that + */ + if (!nssItem_Equal(&c->encoding, &encoding, NULL)) { + nssCertificate_Destroy(c); + PORT_SetError(SEC_ERROR_REUSED_ISSUER_AND_SERIAL); + cc = NULL; + } else { + cc = STAN_GetCERTCertificateOrRelease(c); + if (cc == NULL) { + CERT_MapStanError(); + } + } + return cc; + } + } + pkio = nssPKIObject_Create(NULL, NULL, gTD, gCC, nssPKIMonitor); + if (!pkio) { + CERT_MapStanError(); + return NULL; + } + c = nss_ZNEW(pkio->arena, NSSCertificate); + if (!c) { + CERT_MapStanError(); + nssPKIObject_Destroy(pkio); + return NULL; + } + c->object = *pkio; + if (copyDER) { + nssItem_Create(c->object.arena, &c->encoding, derCert->len, + derCert->data); + } else { + NSSITEM_FROM_SECITEM(&c->encoding, derCert); + } + /* Forces a decoding of the cert in order to obtain the parts used + * below + */ + /* 'c' is not adopted here, if we fail loser frees what has been + * allocated so far for 'c' */ + cc = STAN_GetCERTCertificate(c); + if (!cc) { + CERT_MapStanError(); + goto loser; + } + nssItem_Create(c->object.arena, &c->issuer, cc->derIssuer.len, + cc->derIssuer.data); + nssItem_Create(c->object.arena, &c->subject, cc->derSubject.len, + cc->derSubject.data); + /* CERTCertificate stores serial numbers decoded. I need the DER + * here. sigh. + */ + SECItem derSerial = { 0 }; + CERT_SerialNumberFromDERCert(&cc->derCert, &derSerial); + if (!derSerial.data) + goto loser; + nssItem_Create(c->object.arena, &c->serial, derSerial.len, + derSerial.data); + PORT_Free(derSerial.data); + + if (nickname) { + c->object.tempName = + nssUTF8_Create(c->object.arena, nssStringType_UTF8String, + (NSSUTF8 *)nickname, PORT_Strlen(nickname)); + } + if (cc->emailAddr && cc->emailAddr[0]) { + c->email = nssUTF8_Create( + c->object.arena, nssStringType_PrintableString, + (NSSUTF8 *)cc->emailAddr, PORT_Strlen(cc->emailAddr)); + } + + tempCert = NSSCryptoContext_FindOrImportCertificate(gCC, c); + if (!tempCert) { + CERT_MapStanError(); + goto loser; + } + /* destroy our copy */ + NSSCertificate_Destroy(c); + /* and use the stored entry */ + c = tempCert; + cc = STAN_GetCERTCertificateOrRelease(c); + if (!cc) { + /* STAN_GetCERTCertificateOrRelease destroys c on failure. */ + CERT_MapStanError(); + return NULL; + } + + CERT_LockCertTempPerm(cc); + cc->istemp = PR_TRUE; + cc->isperm = PR_FALSE; + CERT_UnlockCertTempPerm(cc); + return cc; +loser: + /* Perhaps this should be nssCertificate_Destroy(c) */ + nssPKIObject_Destroy(&c->object); + return NULL; +} + +/* This symbol is exported for backward compatibility. */ +CERTCertificate * +__CERT_NewTempCertificate(CERTCertDBHandle *handle, SECItem *derCert, + char *nickname, PRBool isperm, PRBool copyDER) +{ + return CERT_NewTempCertificate(handle, derCert, nickname, isperm, copyDER); +} + +static CERTCertificate * +common_FindCertByIssuerAndSN(CERTCertDBHandle *handle, + CERTIssuerAndSN *issuerAndSN, + void *wincx) +{ + PK11SlotInfo *slot; + CERTCertificate *cert; + + cert = PK11_FindCertByIssuerAndSN(&slot, issuerAndSN, wincx); + if (cert && slot) { + PK11_FreeSlot(slot); + } + + return cert; +} + +/* maybe all the wincx's should be some const for internal token login? */ +CERTCertificate * +CERT_FindCertByIssuerAndSN(CERTCertDBHandle *handle, + CERTIssuerAndSN *issuerAndSN) +{ + return common_FindCertByIssuerAndSN(handle, issuerAndSN, NULL); +} + +/* maybe all the wincx's should be some const for internal token login? */ +CERTCertificate * +CERT_FindCertByIssuerAndSNCX(CERTCertDBHandle *handle, + CERTIssuerAndSN *issuerAndSN, + void *wincx) +{ + return common_FindCertByIssuerAndSN(handle, issuerAndSN, wincx); +} + +static NSSCertificate * +get_best_temp_or_perm(NSSCertificate *ct, NSSCertificate *cp) +{ + NSSUsage usage; + NSSCertificate *arr[3]; + if (!ct) { + return nssCertificate_AddRef(cp); + } else if (!cp) { + return nssCertificate_AddRef(ct); + } + arr[0] = ct; + arr[1] = cp; + arr[2] = NULL; + usage.anyUsage = PR_TRUE; + return nssCertificateArray_FindBestCertificate(arr, NULL, &usage, NULL); +} + +CERTCertificate * +CERT_FindCertByName(CERTCertDBHandle *handle, SECItem *name) +{ + NSSCertificate *cp, *ct, *c; + NSSDER subject; + NSSUsage usage; + NSSCryptoContext *cc; + NSSITEM_FROM_SECITEM(&subject, name); + usage.anyUsage = PR_TRUE; + cc = STAN_GetDefaultCryptoContext(); + ct = NSSCryptoContext_FindBestCertificateBySubject(cc, &subject, NULL, + &usage, NULL); + cp = NSSTrustDomain_FindBestCertificateBySubject(handle, &subject, NULL, + &usage, NULL); + c = get_best_temp_or_perm(ct, cp); + if (ct) { + CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(ct)); + } + if (cp) { + CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(cp)); + } + return c ? STAN_GetCERTCertificateOrRelease(c) : NULL; +} + +CERTCertificate * +CERT_FindCertByKeyID(CERTCertDBHandle *handle, SECItem *name, SECItem *keyID) +{ + CERTCertList *list; + CERTCertificate *cert = NULL; + CERTCertListNode *node; + + list = CERT_CreateSubjectCertList(NULL, handle, name, 0, PR_FALSE); + if (list == NULL) + return NULL; + + node = CERT_LIST_HEAD(list); + while (!CERT_LIST_END(node, list)) { + if (node->cert && + SECITEM_ItemsAreEqual(&node->cert->subjectKeyID, keyID)) { + cert = CERT_DupCertificate(node->cert); + goto done; + } + node = CERT_LIST_NEXT(node); + } + PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER); + +done: + CERT_DestroyCertList(list); + return cert; +} + +CERTCertificate * +CERT_FindCertByNickname(CERTCertDBHandle *handle, const char *nickname) +{ + NSSCryptoContext *cc; + NSSCertificate *c, *ct; + CERTCertificate *cert; + NSSUsage usage; + usage.anyUsage = PR_TRUE; + cc = STAN_GetDefaultCryptoContext(); + ct = NSSCryptoContext_FindBestCertificateByNickname(cc, nickname, NULL, + &usage, NULL); + cert = PK11_FindCertFromNickname(nickname, NULL); + c = NULL; + if (cert) { + c = get_best_temp_or_perm(ct, STAN_GetNSSCertificate(cert)); + CERT_DestroyCertificate(cert); + if (ct) { + CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(ct)); + } + } else { + c = ct; + } + return c ? STAN_GetCERTCertificateOrRelease(c) : NULL; +} + +CERTCertificate * +CERT_FindCertByDERCert(CERTCertDBHandle *handle, SECItem *derCert) +{ + NSSCryptoContext *cc; + NSSCertificate *c; + NSSDER encoding; + NSSITEM_FROM_SECITEM(&encoding, derCert); + cc = STAN_GetDefaultCryptoContext(); + c = NSSCryptoContext_FindCertificateByEncodedCertificate(cc, &encoding); + if (!c) { + c = NSSTrustDomain_FindCertificateByEncodedCertificate(handle, + &encoding); + if (!c) + return NULL; + } + return STAN_GetCERTCertificateOrRelease(c); +} + +static CERTCertificate * +common_FindCertByNicknameOrEmailAddrForUsage(CERTCertDBHandle *handle, + const char *name, PRBool anyUsage, + SECCertUsage lookingForUsage, + void *wincx) +{ + NSSCryptoContext *cc; + NSSCertificate *c, *ct; + CERTCertificate *cert = NULL; + NSSUsage usage; + CERTCertList *certlist; + + if (NULL == name) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + usage.anyUsage = anyUsage; + + if (!anyUsage) { + usage.nss3lookingForCA = PR_FALSE; + usage.nss3usage = lookingForUsage; + } + + cc = STAN_GetDefaultCryptoContext(); + ct = NSSCryptoContext_FindBestCertificateByNickname(cc, name, NULL, &usage, + NULL); + if (!ct && PORT_Strchr(name, '@') != NULL) { + char *lowercaseName = CERT_FixupEmailAddr(name); + if (lowercaseName) { + ct = NSSCryptoContext_FindBestCertificateByEmail( + cc, lowercaseName, NULL, &usage, NULL); + PORT_Free(lowercaseName); + } + } + + if (anyUsage) { + cert = PK11_FindCertFromNickname(name, wincx); + } else { + if (ct) { + /* Does ct really have the required usage? */ + nssDecodedCert *dc; + dc = nssCertificate_GetDecoding(ct); + if (!dc->matchUsage(dc, &usage)) { + CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(ct)); + ct = NULL; + } + } + + certlist = PK11_FindCertsFromNickname(name, wincx); + if (certlist) { + SECStatus rv = + CERT_FilterCertListByUsage(certlist, lookingForUsage, PR_FALSE); + if (SECSuccess == rv && !CERT_LIST_EMPTY(certlist)) { + cert = CERT_DupCertificate(CERT_LIST_HEAD(certlist)->cert); + } + CERT_DestroyCertList(certlist); + } + } + + if (cert) { + c = get_best_temp_or_perm(ct, STAN_GetNSSCertificate(cert)); + CERT_DestroyCertificate(cert); + if (ct) { + CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(ct)); + } + } else { + c = ct; + } + return c ? STAN_GetCERTCertificateOrRelease(c) : NULL; +} + +CERTCertificate * +CERT_FindCertByNicknameOrEmailAddr(CERTCertDBHandle *handle, const char *name) +{ + return common_FindCertByNicknameOrEmailAddrForUsage(handle, name, PR_TRUE, + 0, NULL); +} + +CERTCertificate * +CERT_FindCertByNicknameOrEmailAddrCX(CERTCertDBHandle *handle, const char *name, + void *wincx) +{ + return common_FindCertByNicknameOrEmailAddrForUsage(handle, name, PR_TRUE, + 0, wincx); +} + +CERTCertificate * +CERT_FindCertByNicknameOrEmailAddrForUsage(CERTCertDBHandle *handle, + const char *name, + SECCertUsage lookingForUsage) +{ + return common_FindCertByNicknameOrEmailAddrForUsage(handle, name, PR_FALSE, + lookingForUsage, NULL); +} + +CERTCertificate * +CERT_FindCertByNicknameOrEmailAddrForUsageCX(CERTCertDBHandle *handle, + const char *name, + SECCertUsage lookingForUsage, + void *wincx) +{ + return common_FindCertByNicknameOrEmailAddrForUsage(handle, name, PR_FALSE, + lookingForUsage, wincx); +} + +static void +add_to_subject_list(CERTCertList *certList, CERTCertificate *cert, + PRBool validOnly, PRTime sorttime) +{ + SECStatus secrv; + if (!validOnly || + CERT_CheckCertValidTimes(cert, sorttime, PR_FALSE) == + secCertTimeValid) { + secrv = CERT_AddCertToListSorted(certList, cert, CERT_SortCBValidity, + (void *)&sorttime); + if (secrv != SECSuccess) { + CERT_DestroyCertificate(cert); + } + } else { + CERT_DestroyCertificate(cert); + } +} + +CERTCertList * +CERT_CreateSubjectCertList(CERTCertList *certList, CERTCertDBHandle *handle, + const SECItem *name, PRTime sorttime, + PRBool validOnly) +{ + NSSCryptoContext *cc; + NSSCertificate **tSubjectCerts, **pSubjectCerts; + NSSCertificate **ci; + CERTCertificate *cert; + NSSDER subject; + PRBool myList = PR_FALSE; + cc = STAN_GetDefaultCryptoContext(); + NSSITEM_FROM_SECITEM(&subject, name); + /* Collect both temp and perm certs for the subject */ + tSubjectCerts = + NSSCryptoContext_FindCertificatesBySubject(cc, &subject, NULL, 0, NULL); + pSubjectCerts = NSSTrustDomain_FindCertificatesBySubject(handle, &subject, + NULL, 0, NULL); + if (!tSubjectCerts && !pSubjectCerts) { + return NULL; + } + if (certList == NULL) { + certList = CERT_NewCertList(); + myList = PR_TRUE; + if (!certList) + goto loser; + } + /* Iterate over the matching temp certs. Add them to the list */ + ci = tSubjectCerts; + while (ci && *ci) { + cert = STAN_GetCERTCertificateOrRelease(*ci); + /* *ci may be invalid at this point, don't reference it again */ + if (cert) { + /* NOTE: add_to_subject_list adopts the incoming cert. */ + add_to_subject_list(certList, cert, validOnly, sorttime); + } + ci++; + } + /* Iterate over the matching perm certs. Add them to the list */ + ci = pSubjectCerts; + while (ci && *ci) { + cert = STAN_GetCERTCertificateOrRelease(*ci); + /* *ci may be invalid at this point, don't reference it again */ + if (cert) { + /* NOTE: add_to_subject_list adopts the incoming cert. */ + add_to_subject_list(certList, cert, validOnly, sorttime); + } + ci++; + } + /* all the references have been adopted or freed at this point, just + * free the arrays now */ + nss_ZFreeIf(tSubjectCerts); + nss_ZFreeIf(pSubjectCerts); + return certList; +loser: + /* need to free the references in tSubjectCerts and pSubjectCerts! */ + nssCertificateArray_Destroy(tSubjectCerts); + nssCertificateArray_Destroy(pSubjectCerts); + if (myList && certList != NULL) { + CERT_DestroyCertList(certList); + } + return NULL; +} + +void +CERT_DestroyCertificate(CERTCertificate *cert) +{ + if (cert) { + /* don't use STAN_GetNSSCertificate because we don't want to + * go to the trouble of translating the CERTCertificate into + * an NSSCertificate just to destroy it. If it hasn't been done + * yet, don't do it at all + * + * cert->nssCertificate contains its own locks and refcount, but as it + * may be NULL, the pointer itself must be guarded by some other lock. + * Rather than creating a new global lock for only this purpose, share + * an existing global lock that happens to be taken near the write in + * fill_CERTCertificateFields(). The longer-term goal is to refactor + * all these global locks to be certificate-scoped. */ + CERT_MaybeLockCertTempPerm(cert); + NSSCertificate *tmp = cert->nssCertificate; + CERT_MaybeUnlockCertTempPerm(cert); + if (tmp) { + /* delete the NSSCertificate */ + NSSCertificate_Destroy(tmp); + } else if (cert->arena) { + PORT_FreeArena(cert->arena, PR_FALSE); + } + } + return; +} + +int +CERT_GetDBContentVersion(CERTCertDBHandle *handle) +{ + /* should read the DB content version from the pkcs #11 device */ + return 0; +} + +SECStatus +certdb_SaveSingleProfile(CERTCertificate *cert, const char *emailAddr, + SECItem *emailProfile, SECItem *profileTime) +{ + PRTime oldtime; + PRTime newtime; + SECStatus rv = SECFailure; + PRBool saveit; + SECItem oldprof, oldproftime; + SECItem *oldProfile = NULL; + SECItem *oldProfileTime = NULL; + PK11SlotInfo *slot = NULL; + NSSCertificate *c; + NSSCryptoContext *cc; + nssSMIMEProfile *stanProfile = NULL; + PRBool freeOldProfile = PR_FALSE; + + c = STAN_GetNSSCertificate(cert); + if (!c) + return SECFailure; + cc = c->object.cryptoContext; + if (cc != NULL) { + stanProfile = nssCryptoContext_FindSMIMEProfileForCertificate(cc, c); + if (stanProfile) { + PORT_Assert(stanProfile->profileData); + SECITEM_FROM_NSSITEM(&oldprof, stanProfile->profileData); + oldProfile = &oldprof; + SECITEM_FROM_NSSITEM(&oldproftime, stanProfile->profileTime); + oldProfileTime = &oldproftime; + } + } else { + oldProfile = PK11_FindSMimeProfile(&slot, (char *)emailAddr, + &cert->derSubject, &oldProfileTime); + freeOldProfile = PR_TRUE; + } + + saveit = PR_FALSE; + + /* both profileTime and emailProfile have to exist or not exist */ + if (emailProfile == NULL) { + profileTime = NULL; + } else if (profileTime == NULL) { + emailProfile = NULL; + } + + if (oldProfileTime == NULL) { + saveit = PR_TRUE; + } else { + /* there was already a profile for this email addr */ + if (profileTime) { + /* we have an old and new profile - save whichever is more recent*/ + if (oldProfileTime->len == 0) { + /* always replace if old entry doesn't have a time */ + oldtime = LL_MININT; + } else { + rv = DER_UTCTimeToTime(&oldtime, oldProfileTime); + if (rv != SECSuccess) { + goto loser; + } + } + + rv = DER_UTCTimeToTime(&newtime, profileTime); + if (rv != SECSuccess) { + goto loser; + } + + if (LL_CMP(newtime, >, oldtime)) { + /* this is a newer profile, save it and cert */ + saveit = PR_TRUE; + } + } else { + saveit = PR_TRUE; + } + } + + if (saveit) { + if (cc) { + if (stanProfile && profileTime && emailProfile) { + /* stanProfile is already stored in the crypto context, + * overwrite the data + */ + NSSArena *arena = stanProfile->object.arena; + stanProfile->profileTime = nssItem_Create( + arena, NULL, profileTime->len, profileTime->data); + stanProfile->profileData = nssItem_Create( + arena, NULL, emailProfile->len, emailProfile->data); + } else if (profileTime && emailProfile) { + PRStatus nssrv; + NSSItem profTime, profData; + NSSITEM_FROM_SECITEM(&profTime, profileTime); + NSSITEM_FROM_SECITEM(&profData, emailProfile); + stanProfile = nssSMIMEProfile_Create(c, &profTime, &profData); + if (!stanProfile) + goto loser; + nssrv = nssCryptoContext_ImportSMIMEProfile(cc, stanProfile); + rv = (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure; + } + } else { + rv = PK11_SaveSMimeProfile(slot, (char *)emailAddr, + &cert->derSubject, emailProfile, + profileTime); + } + } else { + rv = SECSuccess; + } + +loser: + if (oldProfile && freeOldProfile) { + SECITEM_FreeItem(oldProfile, PR_TRUE); + } + if (oldProfileTime && freeOldProfile) { + SECITEM_FreeItem(oldProfileTime, PR_TRUE); + } + if (stanProfile) { + nssSMIMEProfile_Destroy(stanProfile); + } + if (slot) { + PK11_FreeSlot(slot); + } + + return (rv); +} + +/* + * + * Manage S/MIME profiles + * + */ + +SECStatus +CERT_SaveSMimeProfile(CERTCertificate *cert, SECItem *emailProfile, + SECItem *profileTime) +{ + const char *emailAddr; + SECStatus rv; + PRBool isperm = PR_FALSE; + + if (!cert) { + return SECFailure; + } + + if (cert->slot && !PK11_IsInternal(cert->slot)) { + /* this cert comes from an external source, we need to add it + to the cert db before creating an S/MIME profile */ + PK11SlotInfo *internalslot = PK11_GetInternalKeySlot(); + if (!internalslot) { + return SECFailure; + } + rv = PK11_ImportCert(internalslot, cert, CK_INVALID_HANDLE, NULL, + PR_FALSE); + + PK11_FreeSlot(internalslot); + if (rv != SECSuccess) { + return SECFailure; + } + } + + rv = CERT_GetCertIsPerm(cert, &isperm); + if (rv != SECSuccess) { + return SECFailure; + } + if (cert->slot && isperm && CERT_IsUserCert(cert) && + (!emailProfile || !emailProfile->len)) { + /* Don't clobber emailProfile for user certs. */ + return SECSuccess; + } + + for (emailAddr = CERT_GetFirstEmailAddress(cert); emailAddr != NULL; + emailAddr = CERT_GetNextEmailAddress(cert, emailAddr)) { + rv = certdb_SaveSingleProfile(cert, emailAddr, emailProfile, + profileTime); + if (rv != SECSuccess) { + return SECFailure; + } + } + return SECSuccess; +} + +SECItem * +CERT_FindSMimeProfile(CERTCertificate *cert) +{ + PK11SlotInfo *slot = NULL; + NSSCertificate *c; + NSSCryptoContext *cc; + SECItem *rvItem = NULL; + + if (!cert || !cert->emailAddr || !cert->emailAddr[0]) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + c = STAN_GetNSSCertificate(cert); + if (!c) + return NULL; + cc = c->object.cryptoContext; + if (cc != NULL) { + nssSMIMEProfile *stanProfile; + stanProfile = nssCryptoContext_FindSMIMEProfileForCertificate(cc, c); + if (stanProfile) { + rvItem = + SECITEM_AllocItem(NULL, NULL, stanProfile->profileData->size); + if (rvItem) { + rvItem->data = stanProfile->profileData->data; + } + nssSMIMEProfile_Destroy(stanProfile); + } + return rvItem; + } + rvItem = + PK11_FindSMimeProfile(&slot, cert->emailAddr, &cert->derSubject, NULL); + if (slot) { + PK11_FreeSlot(slot); + } + return rvItem; +} + +SECStatus +CERT_GetCertIsPerm(const CERTCertificate *cert, PRBool *isperm) +{ + if (cert == NULL) { + return SECFailure; + } + + CERT_LockCertTempPerm(cert); + *isperm = cert->isperm; + CERT_UnlockCertTempPerm(cert); + return SECSuccess; +} + +SECStatus +CERT_GetCertIsTemp(const CERTCertificate *cert, PRBool *istemp) +{ + if (cert == NULL) { + return SECFailure; + } + + CERT_LockCertTempPerm(cert); + *istemp = cert->istemp; + CERT_UnlockCertTempPerm(cert); + return SECSuccess; +} + +/* + * deprecated functions that are now just stubs. + */ +/* + * Close the database + */ +void +__CERT_ClosePermCertDB(CERTCertDBHandle *handle) +{ + PORT_Assert("CERT_ClosePermCertDB is Deprecated" == NULL); + return; +} + +SECStatus +CERT_OpenCertDBFilename(CERTCertDBHandle *handle, char *certdbname, + PRBool readOnly) +{ + PORT_Assert("CERT_OpenCertDBFilename is Deprecated" == NULL); + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return SECFailure; +} + +SECItem * +SECKEY_HashPassword(char *pw, SECItem *salt) +{ + PORT_Assert("SECKEY_HashPassword is Deprecated" == NULL); + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return NULL; +} + +SECStatus +__CERT_TraversePermCertsForSubject(CERTCertDBHandle *handle, + SECItem *derSubject, void *cb, void *cbarg) +{ + PORT_Assert("CERT_TraversePermCertsForSubject is Deprecated" == NULL); + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return SECFailure; +} + +SECStatus +__CERT_TraversePermCertsForNickname(CERTCertDBHandle *handle, char *nickname, + void *cb, void *cbarg) +{ + PORT_Assert("CERT_TraversePermCertsForNickname is Deprecated" == NULL); + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return SECFailure; +} diff --git a/security/nss/lib/certdb/xauthkid.c b/security/nss/lib/certdb/xauthkid.c new file mode 100644 index 0000000000..c7ef046db0 --- /dev/null +++ b/security/nss/lib/certdb/xauthkid.c @@ -0,0 +1,128 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * X.509 v3 Subject Key Usage Extension + * + */ + +#include "prtypes.h" +#include "seccomon.h" +#include "secdert.h" +#include "secoidt.h" +#include "secasn1t.h" +#include "secasn1.h" +#include "secport.h" +#include "certt.h" +#include "genname.h" +#include "secerr.h" + +SEC_ASN1_MKSUB(SEC_IntegerTemplate) +SEC_ASN1_MKSUB(SEC_OctetStringTemplate) + +const SEC_ASN1Template CERTAuthKeyIDTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTAuthKeyID) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, + offsetof(CERTAuthKeyID, keyID), SEC_ASN1_SUB(SEC_OctetStringTemplate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(CERTAuthKeyID, DERAuthCertIssuer), CERT_GeneralNamesTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2, + offsetof(CERTAuthKeyID, authCertSerialNumber), + SEC_ASN1_SUB(SEC_IntegerTemplate) }, + { 0 } +}; + +SECStatus +CERT_EncodeAuthKeyID(PLArenaPool *arena, CERTAuthKeyID *value, + SECItem *encodedValue) +{ + SECStatus rv = SECFailure; + + PORT_Assert(value); + PORT_Assert(arena); + PORT_Assert(value->DERAuthCertIssuer == NULL); + PORT_Assert(encodedValue); + + do { + + /* If both of the authCertIssuer and the serial number exist, encode + the name first. Otherwise, it is an error if one exist and the other + is not. + */ + if (value->authCertIssuer) { + if (!value->authCertSerialNumber.data) { + PORT_SetError(SEC_ERROR_EXTENSION_VALUE_INVALID); + break; + } + + value->DERAuthCertIssuer = + cert_EncodeGeneralNames(arena, value->authCertIssuer); + if (!value->DERAuthCertIssuer) { + PORT_SetError(SEC_ERROR_EXTENSION_VALUE_INVALID); + break; + } + } else if (value->authCertSerialNumber.data) { + PORT_SetError(SEC_ERROR_EXTENSION_VALUE_INVALID); + break; + } + + if (SEC_ASN1EncodeItem(arena, encodedValue, value, + CERTAuthKeyIDTemplate) == NULL) + break; + rv = SECSuccess; + + } while (0); + return (rv); +} + +CERTAuthKeyID * +CERT_DecodeAuthKeyID(PLArenaPool *arena, const SECItem *encodedValue) +{ + CERTAuthKeyID *value = NULL; + SECStatus rv = SECFailure; + void *mark; + SECItem newEncodedValue; + + PORT_Assert(arena); + + do { + mark = PORT_ArenaMark(arena); + value = (CERTAuthKeyID *)PORT_ArenaZAlloc(arena, sizeof(*value)); + if (value == NULL) + break; + value->DERAuthCertIssuer = NULL; + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newEncodedValue, encodedValue); + if (rv != SECSuccess) { + break; + } + + rv = SEC_QuickDERDecodeItem(arena, value, CERTAuthKeyIDTemplate, + &newEncodedValue); + if (rv != SECSuccess) + break; + + value->authCertIssuer = + cert_DecodeGeneralNames(arena, value->DERAuthCertIssuer); + if (value->authCertIssuer == NULL) + break; + + /* what if the general name contains other format but not URI ? + hl + */ + if ((value->authCertSerialNumber.data && !value->authCertIssuer) || + (!value->authCertSerialNumber.data && value->authCertIssuer)) { + PORT_SetError(SEC_ERROR_EXTENSION_VALUE_INVALID); + break; + } + } while (0); + + if (rv != SECSuccess) { + PORT_ArenaRelease(arena, mark); + return ((CERTAuthKeyID *)NULL); + } + PORT_ArenaUnmark(arena, mark); + return (value); +} diff --git a/security/nss/lib/certdb/xbsconst.c b/security/nss/lib/certdb/xbsconst.c new file mode 100644 index 0000000000..df70dbbefb --- /dev/null +++ b/security/nss/lib/certdb/xbsconst.c @@ -0,0 +1,146 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * X.509 v3 Basic Constraints Extension + */ + +#include "prtypes.h" +#include /* for LONG_MAX */ +#include "seccomon.h" +#include "secdert.h" +#include "secoidt.h" +#include "secasn1t.h" +#include "secasn1.h" +#include "certt.h" +#include "secder.h" +#include "prprf.h" +#include "secerr.h" + +typedef struct EncodedContext { + SECItem isCA; + SECItem pathLenConstraint; + SECItem encodedValue; + PLArenaPool *arena; +} EncodedContext; + +static const SEC_ASN1Template CERTBasicConstraintsTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(EncodedContext) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN, /* XXX DER_DEFAULT */ + offsetof(EncodedContext, isCA) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER, + offsetof(EncodedContext, pathLenConstraint) }, + { 0 } +}; + +static unsigned char hexTrue = 0xff; +static unsigned char hexFalse = 0x00; + +#define GEN_BREAK(status) \ + rv = status; \ + break; + +SECStatus +CERT_EncodeBasicConstraintValue(PLArenaPool *arena, CERTBasicConstraints *value, + SECItem *encodedValue) +{ + EncodedContext encodeContext; + PLArenaPool *our_pool = NULL; + SECStatus rv = SECSuccess; + + do { + PORT_Memset(&encodeContext, 0, sizeof(encodeContext)); + if (!value->isCA && value->pathLenConstraint >= 0) { + PORT_SetError(SEC_ERROR_EXTENSION_VALUE_INVALID); + GEN_BREAK(SECFailure); + } + + encodeContext.arena = arena; + if (value->isCA == PR_TRUE) { + encodeContext.isCA.data = &hexTrue; + encodeContext.isCA.len = 1; + } + + /* If the pathLenConstraint is less than 0, then it should be + * omitted from the encoding. + */ + if (value->isCA && value->pathLenConstraint >= 0) { + our_pool = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if (our_pool == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + GEN_BREAK(SECFailure); + } + if (SEC_ASN1EncodeUnsignedInteger( + our_pool, &encodeContext.pathLenConstraint, + (unsigned long)value->pathLenConstraint) == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + GEN_BREAK(SECFailure); + } + } + if (SEC_ASN1EncodeItem(arena, encodedValue, &encodeContext, + CERTBasicConstraintsTemplate) == NULL) { + GEN_BREAK(SECFailure); + } + } while (0); + if (our_pool) + PORT_FreeArena(our_pool, PR_FALSE); + return (rv); +} + +SECStatus +CERT_DecodeBasicConstraintValue(CERTBasicConstraints *value, + const SECItem *encodedValue) +{ + EncodedContext decodeContext; + PORTCheapArenaPool tmpArena; + SECStatus rv = SECSuccess; + + do { + PORT_Memset(&decodeContext, 0, sizeof(decodeContext)); + /* initialize the value just in case we got "0x30 00", or when the + pathLenConstraint is omitted. + */ + decodeContext.isCA.data = &hexFalse; + decodeContext.isCA.len = 1; + + PORT_InitCheapArena(&tmpArena, SEC_ASN1_DEFAULT_ARENA_SIZE); + + rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &decodeContext, + CERTBasicConstraintsTemplate, encodedValue); + if (rv == SECFailure) + break; + + value->isCA = decodeContext.isCA.data + ? (PRBool)(decodeContext.isCA.data[0] != 0) + : PR_FALSE; + if (decodeContext.pathLenConstraint.data == NULL) { + /* if the pathLenConstraint is not encoded, and the current setting + is CA, then the pathLenConstraint should be set to a negative + number + for unlimited certificate path. + */ + if (value->isCA) { + value->pathLenConstraint = CERT_UNLIMITED_PATH_CONSTRAINT; + } else { + value->pathLenConstraint = 0; + } + } else if (value->isCA) { + long len = DER_GetInteger(&decodeContext.pathLenConstraint); + if (len < 0 || len == LONG_MAX) { + PORT_SetError(SEC_ERROR_BAD_DER); + GEN_BREAK(SECFailure); + } + value->pathLenConstraint = len; + } else { + /* here we get an error where the subject is not a CA, but + the pathLenConstraint is set */ + PORT_SetError(SEC_ERROR_BAD_DER); + GEN_BREAK(SECFailure); + break; + } + } while (0); + + PORT_DestroyCheapArena(&tmpArena); + return (rv); +} diff --git a/security/nss/lib/certdb/xconst.c b/security/nss/lib/certdb/xconst.c new file mode 100644 index 0000000000..9a5634a906 --- /dev/null +++ b/security/nss/lib/certdb/xconst.c @@ -0,0 +1,269 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * X.509 Extension Encoding + */ + +#include "prtypes.h" +#include "seccomon.h" +#include "secdert.h" +#include "secoidt.h" +#include "secasn1t.h" +#include "secasn1.h" +#include "cert.h" +#include "secder.h" +#include "prprf.h" +#include "xconst.h" +#include "genname.h" +#include "secasn1.h" +#include "secerr.h" + +static const SEC_ASN1Template CERTSubjectKeyIDTemplate[] = { + { SEC_ASN1_OCTET_STRING } +}; + +static const SEC_ASN1Template CERTIA5TypeTemplate[] = { + { SEC_ASN1_IA5_STRING } +}; + +SEC_ASN1_MKSUB(SEC_GeneralizedTimeTemplate) + +static const SEC_ASN1Template CERTPrivateKeyUsagePeriodTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTPrivKeyUsagePeriod) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, + offsetof(CERTPrivKeyUsagePeriod, notBefore), + SEC_ASN1_SUB(SEC_GeneralizedTimeTemplate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1, + offsetof(CERTPrivKeyUsagePeriod, notAfter), + SEC_ASN1_SUB(SEC_GeneralizedTimeTemplate) }, + { 0 } +}; + +const SEC_ASN1Template CERTAltNameTemplate[] = { + { SEC_ASN1_CONSTRUCTED, offsetof(CERTAltNameEncodedContext, encodedGenName), + CERT_GeneralNamesTemplate } +}; + +const SEC_ASN1Template CERTAuthInfoAccessItemTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTAuthInfoAccess) }, + { SEC_ASN1_OBJECT_ID, offsetof(CERTAuthInfoAccess, method) }, + { SEC_ASN1_ANY, offsetof(CERTAuthInfoAccess, derLocation) }, + { 0 } +}; + +const SEC_ASN1Template CERTAuthInfoAccessTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, 0, CERTAuthInfoAccessItemTemplate } +}; + +SECStatus +CERT_EncodeSubjectKeyID(PLArenaPool *arena, const SECItem *srcString, + SECItem *encodedValue) +{ + SECStatus rv = SECSuccess; + + if (!srcString) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + if (SEC_ASN1EncodeItem(arena, encodedValue, srcString, + CERTSubjectKeyIDTemplate) == NULL) { + rv = SECFailure; + } + + return (rv); +} + +SECStatus +CERT_EncodePrivateKeyUsagePeriod(PLArenaPool *arena, + CERTPrivKeyUsagePeriod *pkup, + SECItem *encodedValue) +{ + SECStatus rv = SECSuccess; + + if (SEC_ASN1EncodeItem(arena, encodedValue, pkup, + CERTPrivateKeyUsagePeriodTemplate) == NULL) { + rv = SECFailure; + } + return (rv); +} + +CERTPrivKeyUsagePeriod * +CERT_DecodePrivKeyUsagePeriodExtension(PLArenaPool *arena, SECItem *extnValue) +{ + SECStatus rv; + CERTPrivKeyUsagePeriod *pPeriod; + SECItem newExtnValue; + + /* allocate the certificate policies structure */ + pPeriod = PORT_ArenaZNew(arena, CERTPrivKeyUsagePeriod); + if (pPeriod == NULL) { + goto loser; + } + + pPeriod->arena = arena; + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newExtnValue, extnValue); + if (rv != SECSuccess) { + goto loser; + } + + rv = SEC_QuickDERDecodeItem( + arena, pPeriod, CERTPrivateKeyUsagePeriodTemplate, &newExtnValue); + if (rv != SECSuccess) { + goto loser; + } + return pPeriod; + +loser: + return NULL; +} + +SECStatus +CERT_EncodeIA5TypeExtension(PLArenaPool *arena, char *value, + SECItem *encodedValue) +{ + SECItem encodeContext; + SECStatus rv = SECSuccess; + + PORT_Memset(&encodeContext, 0, sizeof(encodeContext)); + + if (value != NULL) { + encodeContext.data = (unsigned char *)value; + encodeContext.len = strlen(value); + } + if (SEC_ASN1EncodeItem(arena, encodedValue, &encodeContext, + CERTIA5TypeTemplate) == NULL) { + rv = SECFailure; + } + + return (rv); +} + +SECStatus +CERT_EncodeAltNameExtension(PLArenaPool *arena, CERTGeneralName *value, + SECItem *encodedValue) +{ + SECItem **encodedGenName; + SECStatus rv = SECSuccess; + + encodedGenName = cert_EncodeGeneralNames(arena, value); + if (SEC_ASN1EncodeItem(arena, encodedValue, &encodedGenName, + CERT_GeneralNamesTemplate) == NULL) { + rv = SECFailure; + } + + return rv; +} + +CERTGeneralName * +CERT_DecodeAltNameExtension(PLArenaPool *reqArena, SECItem *EncodedAltName) +{ + SECStatus rv = SECSuccess; + CERTAltNameEncodedContext encodedContext; + SECItem *newEncodedAltName; + + if (!reqArena) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + newEncodedAltName = SECITEM_ArenaDupItem(reqArena, EncodedAltName); + if (!newEncodedAltName) { + return NULL; + } + + encodedContext.encodedGenName = NULL; + PORT_Memset(&encodedContext, 0, sizeof(CERTAltNameEncodedContext)); + rv = SEC_QuickDERDecodeItem(reqArena, &encodedContext, + CERT_GeneralNamesTemplate, newEncodedAltName); + if (rv == SECFailure) { + goto loser; + } + if (encodedContext.encodedGenName && encodedContext.encodedGenName[0]) + return cert_DecodeGeneralNames(reqArena, encodedContext.encodedGenName); + /* Extension contained an empty GeneralNames sequence */ + /* Treat as extension not found */ + PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND); +loser: + return NULL; +} + +SECStatus +CERT_EncodeNameConstraintsExtension(PLArenaPool *arena, + CERTNameConstraints *value, + SECItem *encodedValue) +{ + SECStatus rv = SECSuccess; + + rv = cert_EncodeNameConstraints(value, arena, encodedValue); + return rv; +} + +CERTNameConstraints * +CERT_DecodeNameConstraintsExtension(PLArenaPool *arena, + const SECItem *encodedConstraints) +{ + return cert_DecodeNameConstraints(arena, encodedConstraints); +} + +CERTAuthInfoAccess ** +CERT_DecodeAuthInfoAccessExtension(PLArenaPool *reqArena, + const SECItem *encodedExtension) +{ + CERTAuthInfoAccess **info = NULL; + SECStatus rv; + int i; + SECItem *newEncodedExtension; + + if (!reqArena) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + newEncodedExtension = SECITEM_ArenaDupItem(reqArena, encodedExtension); + if (!newEncodedExtension) { + return NULL; + } + + rv = SEC_QuickDERDecodeItem(reqArena, &info, CERTAuthInfoAccessTemplate, + newEncodedExtension); + if (rv != SECSuccess || info == NULL) { + return NULL; + } + + for (i = 0; info[i] != NULL; i++) { + info[i]->location = + CERT_DecodeGeneralName(reqArena, &(info[i]->derLocation), NULL); + } + return info; +} + +SECStatus +CERT_EncodeInfoAccessExtension(PLArenaPool *arena, CERTAuthInfoAccess **info, + SECItem *dest) +{ + SECItem *dummy; + int i; + + PORT_Assert(info != NULL); + PORT_Assert(dest != NULL); + if (info == NULL || dest == NULL) { + return SECFailure; + } + + for (i = 0; info[i] != NULL; i++) { + if (CERT_EncodeGeneralName(info[i]->location, &(info[i]->derLocation), + arena) == NULL) + /* Note that this may leave some of the locations filled in. */ + return SECFailure; + } + dummy = SEC_ASN1EncodeItem(arena, dest, &info, CERTAuthInfoAccessTemplate); + if (dummy == NULL) { + return SECFailure; + } + return SECSuccess; +} diff --git a/security/nss/lib/certdb/xconst.h b/security/nss/lib/certdb/xconst.h new file mode 100644 index 0000000000..8cf2e826e0 --- /dev/null +++ b/security/nss/lib/certdb/xconst.h @@ -0,0 +1,30 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef _XCONST_H_ +#define _XCONST_H_ + +#include "certt.h" + +typedef struct CERTAltNameEncodedContextStr { + SECItem **encodedGenName; +} CERTAltNameEncodedContext; + +SEC_BEGIN_PROTOS + +extern SECStatus CERT_EncodePrivateKeyUsagePeriod(PLArenaPool *arena, + CERTPrivKeyUsagePeriod *pkup, + SECItem *encodedValue); + +extern SECStatus CERT_EncodeNameConstraintsExtension(PLArenaPool *arena, + CERTNameConstraints *value, + SECItem *encodedValue); + +extern SECStatus CERT_EncodeIA5TypeExtension(PLArenaPool *arena, char *value, + SECItem *encodedValue); + +SECStatus cert_EncodeAuthInfoAccessExtension(PLArenaPool *arena, + CERTAuthInfoAccess **info, + SECItem *dest); +SEC_END_PROTOS +#endif diff --git a/security/nss/lib/certhigh/Makefile b/security/nss/lib/certhigh/Makefile new file mode 100644 index 0000000000..bb88772853 --- /dev/null +++ b/security/nss/lib/certhigh/Makefile @@ -0,0 +1,45 @@ +#! gmake +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +####################################################################### +# (1) Include initial platform-independent assignments (MANDATORY). # +####################################################################### + +include manifest.mn + +####################################################################### +# (2) Include "global" configuration information. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/config.mk + +####################################################################### +# (3) Include "component" configuration information. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (4) Include "local" platform-dependent assignments (OPTIONAL). # +####################################################################### + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### + + diff --git a/security/nss/lib/certhigh/certhigh.c b/security/nss/lib/certhigh/certhigh.c new file mode 100644 index 0000000000..7ae80b193e --- /dev/null +++ b/security/nss/lib/certhigh/certhigh.c @@ -0,0 +1,1210 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "nspr.h" +#include "secerr.h" +#include "secasn1.h" +#include "seccomon.h" +#include "pk11func.h" +#include "certdb.h" +#include "certt.h" +#include "cert.h" +#include "certxutl.h" + +#include "certi.h" +#include "nsspki.h" +#include "pki.h" +#include "pkit.h" +#include "pkitm.h" +#include "pki3hack.h" + +PRBool +CERT_MatchNickname(char *name1, char *name2) +{ + char *nickname1 = NULL; + char *nickname2 = NULL; + char *token1; + char *token2; + + /* first deal with the straight comparison */ + if (PORT_Strcmp(name1, name2) == 0) { + return PR_TRUE; + } + /* we need to handle the case where one name has an explicit token and the other + * doesn't */ + token1 = PORT_Strchr(name1, ':'); + token2 = PORT_Strchr(name2, ':'); + if ((token1 && token2) || (!token1 && !token2)) { + /* either both token names are specified or neither are, not match */ + return PR_FALSE; + } + if (token1) { + nickname1 = token1; + nickname2 = name2; + } else { + nickname1 = token2; + nickname2 = name1; + } + nickname1++; + if (PORT_Strcmp(nickname1, nickname2) != 0) { + return PR_FALSE; + } + /* Bug 1192443 - compare the other token with the internal slot here */ + return PR_TRUE; +} + +/* + * Find all user certificates that match the given criteria. + * + * "handle" - database to search + * "usage" - certificate usage to match + * "oneCertPerName" - if set then only return the "best" cert per + * name + * "validOnly" - only return certs that are curently valid + * "proto_win" - window handle passed to pkcs11 + */ +CERTCertList * +CERT_FindUserCertsByUsage(CERTCertDBHandle *handle, + SECCertUsage usage, + PRBool oneCertPerName, + PRBool validOnly, + void *proto_win) +{ + CERTCertNicknames *nicknames = NULL; + char **nnptr; + int nn; + CERTCertificate *cert = NULL; + CERTCertList *certList = NULL; + SECStatus rv; + PRTime time; + CERTCertListNode *node = NULL; + CERTCertListNode *freenode = NULL; + int n; + + time = PR_Now(); + + nicknames = CERT_GetCertNicknames(handle, SEC_CERT_NICKNAMES_USER, + proto_win); + + if ((nicknames == NULL) || (nicknames->numnicknames == 0)) { + goto loser; + } + + nnptr = nicknames->nicknames; + nn = nicknames->numnicknames; + + while (nn > 0) { + cert = NULL; + /* use the pk11 call so that we pick up any certs on tokens, + * which may require login + */ + if (proto_win != NULL) { + cert = PK11_FindCertFromNickname(*nnptr, proto_win); + } + + /* Sigh, It turns out if the cert is already in the temp db, because + * it's in the perm db, then the nickname lookup doesn't work. + * since we already have the cert here, though, than we can just call + * CERT_CreateSubjectCertList directly. For those cases where we didn't + * find the cert in pkcs #11 (because we didn't have a password arg, + * or because the nickname is for a peer, server, or CA cert, then we + * go look the cert up. + */ + if (cert == NULL) { + cert = CERT_FindCertByNickname(handle, *nnptr); + } + + if (cert != NULL) { + /* collect certs for this nickname, sorting them into the list */ + certList = CERT_CreateSubjectCertList(certList, handle, + &cert->derSubject, time, validOnly); + + CERT_FilterCertListForUserCerts(certList); + + /* drop the extra reference */ + CERT_DestroyCertificate(cert); + } + + nnptr++; + nn--; + } + + /* remove certs with incorrect usage */ + rv = CERT_FilterCertListByUsage(certList, usage, PR_FALSE); + + if (rv != SECSuccess) { + goto loser; + } + + /* remove any extra certs for each name */ + if (oneCertPerName) { + PRBool *flags; + + nn = nicknames->numnicknames; + nnptr = nicknames->nicknames; + + if (!certList) { + goto loser; + } + + flags = (PRBool *)PORT_ZAlloc(sizeof(PRBool) * nn); + if (flags == NULL) { + goto loser; + } + + node = CERT_LIST_HEAD(certList); + + /* treverse all certs in the list */ + while (!CERT_LIST_END(node, certList)) { + + /* find matching nickname index */ + for (n = 0; n < nn; n++) { + if (CERT_MatchNickname(nnptr[n], node->cert->nickname)) { + /* We found a match. If this is the first one, then + * set the flag and move on to the next cert. If this + * is not the first one then delete it from the list. + */ + if (flags[n]) { + /* We have already seen a cert with this nickname, + * so delete this one. + */ + freenode = node; + node = CERT_LIST_NEXT(node); + CERT_RemoveCertListNode(freenode); + } else { + /* keep the first cert for each nickname, but set the + * flag so we know to delete any others with the same + * nickname. + */ + flags[n] = PR_TRUE; + node = CERT_LIST_NEXT(node); + } + break; + } + } + if (n == nn) { + /* if we get here it means that we didn't find a matching + * nickname, which should not happen. + */ + PORT_Assert(0); + node = CERT_LIST_NEXT(node); + } + } + PORT_Free(flags); + } + + goto done; + +loser: + if (certList != NULL) { + CERT_DestroyCertList(certList); + certList = NULL; + } + +done: + if (nicknames != NULL) { + CERT_FreeNicknames(nicknames); + } + + return (certList); +} + +/* + * Find a user certificate that matchs the given criteria. + * + * "handle" - database to search + * "nickname" - nickname to match + * "usage" - certificate usage to match + * "validOnly" - only return certs that are curently valid + * "proto_win" - window handle passed to pkcs11 + */ +CERTCertificate * +CERT_FindUserCertByUsage(CERTCertDBHandle *handle, + const char *nickname, + SECCertUsage usage, + PRBool validOnly, + void *proto_win) +{ + CERTCertificate *cert = NULL; + CERTCertList *certList = NULL; + SECStatus rv; + PRTime time; + + time = PR_Now(); + + /* use the pk11 call so that we pick up any certs on tokens, + * which may require login + */ + /* XXX - why is this restricted? */ + if (proto_win != NULL) { + cert = PK11_FindCertFromNickname(nickname, proto_win); + } + + /* sigh, There are still problems find smart cards from the temp + * db. This will get smart cards working again. The real fix + * is to make sure we can search the temp db by their token nickname. + */ + if (cert == NULL) { + cert = CERT_FindCertByNickname(handle, nickname); + } + + if (cert != NULL) { + unsigned int requiredKeyUsage; + unsigned int requiredCertType; + + rv = CERT_KeyUsageAndTypeForCertUsage(usage, PR_FALSE, + &requiredKeyUsage, &requiredCertType); + if (rv != SECSuccess) { + /* drop the extra reference */ + CERT_DestroyCertificate(cert); + cert = NULL; + goto loser; + } + /* If we already found the right cert, just return it */ + if ((!validOnly || CERT_CheckCertValidTimes(cert, time, PR_FALSE) == secCertTimeValid) && + (CERT_CheckKeyUsage(cert, requiredKeyUsage) == SECSuccess) && + (cert->nsCertType & requiredCertType) && + CERT_IsUserCert(cert)) { + return (cert); + } + + /* collect certs for this nickname, sorting them into the list */ + certList = CERT_CreateSubjectCertList(certList, handle, + &cert->derSubject, time, validOnly); + + CERT_FilterCertListForUserCerts(certList); + + /* drop the extra reference */ + CERT_DestroyCertificate(cert); + cert = NULL; + } + + if (certList == NULL) { + goto loser; + } + + /* remove certs with incorrect usage */ + rv = CERT_FilterCertListByUsage(certList, usage, PR_FALSE); + + if (rv != SECSuccess) { + goto loser; + } + + if (!CERT_LIST_EMPTY(certList)) { + cert = CERT_DupCertificate(CERT_LIST_HEAD(certList)->cert); + } + +loser: + if (certList != NULL) { + CERT_DestroyCertList(certList); + } + + return (cert); +} + +CERTCertList * +CERT_MatchUserCert(CERTCertDBHandle *handle, + SECCertUsage usage, + int nCANames, char **caNames, + void *proto_win) +{ + CERTCertList *certList = NULL; + SECStatus rv; + + certList = CERT_FindUserCertsByUsage(handle, usage, PR_TRUE, PR_TRUE, + proto_win); + if (certList == NULL) { + goto loser; + } + + rv = CERT_FilterCertListByCANames(certList, nCANames, caNames, usage); + if (rv != SECSuccess) { + goto loser; + } + + goto done; + +loser: + if (certList != NULL) { + CERT_DestroyCertList(certList); + certList = NULL; + } + +done: + + return (certList); +} + +typedef struct stringNode { + struct stringNode *next; + char *string; +} stringNode; + +static PRStatus +CollectNicknames(NSSCertificate *c, void *data) +{ + CERTCertNicknames *names; + PRBool saveit = PR_FALSE; + stringNode *node; + int len; +#ifdef notdef + NSSTrustDomain *td; + NSSTrust *trust; +#endif + char *stanNickname; + char *nickname = NULL; + + names = (CERTCertNicknames *)data; + + stanNickname = nssCertificate_GetNickname(c, NULL); + + if (stanNickname) { + nss_ZFreeIf(stanNickname); + stanNickname = NULL; + if (names->what == SEC_CERT_NICKNAMES_USER) { + saveit = NSSCertificate_IsPrivateKeyAvailable(c, NULL, NULL); + } +#ifdef notdef + else { + td = NSSCertificate_GetTrustDomain(c); + if (!td) { + return PR_SUCCESS; + } + trust = nssTrustDomain_FindTrustForCertificate(td, c); + + switch (names->what) { + case SEC_CERT_NICKNAMES_ALL: + if ((trust->sslFlags & (CERTDB_VALID_CA | CERTDB_VALID_PEER)) || + (trust->emailFlags & (CERTDB_VALID_CA | CERTDB_VALID_PEER)) || + (trust->objectSigningFlags & + (CERTDB_VALID_CA | CERTDB_VALID_PEER))) { + saveit = PR_TRUE; + } + + break; + case SEC_CERT_NICKNAMES_SERVER: + if (trust->sslFlags & CERTDB_VALID_PEER) { + saveit = PR_TRUE; + } + + break; + case SEC_CERT_NICKNAMES_CA: + if (((trust->sslFlags & CERTDB_VALID_CA) == CERTDB_VALID_CA) || + ((trust->emailFlags & CERTDB_VALID_CA) == CERTDB_VALID_CA) || + ((trust->objectSigningFlags & CERTDB_VALID_CA) == + CERTDB_VALID_CA)) { + saveit = PR_TRUE; + } + break; + } + } +#endif + } + + /* traverse the list of collected nicknames and make sure we don't make + * a duplicate + */ + if (saveit) { + nickname = STAN_GetCERTCertificateName(NULL, c); + /* nickname can only be NULL here if we are having memory + * alloc problems */ + if (nickname == NULL) { + return PR_FAILURE; + } + node = (stringNode *)names->head; + while (node != NULL) { + if (PORT_Strcmp(nickname, node->string) == 0) { + /* if the string matches, then don't save this one */ + saveit = PR_FALSE; + break; + } + node = node->next; + } + } + + if (saveit) { + + /* allocate the node */ + node = (stringNode *)PORT_ArenaAlloc(names->arena, sizeof(stringNode)); + if (node == NULL) { + PORT_Free(nickname); + return PR_FAILURE; + } + + /* copy the string */ + len = PORT_Strlen(nickname) + 1; + node->string = (char *)PORT_ArenaAlloc(names->arena, len); + if (node->string == NULL) { + PORT_Free(nickname); + return PR_FAILURE; + } + PORT_Memcpy(node->string, nickname, len); + + /* link it into the list */ + node->next = (stringNode *)names->head; + names->head = (void *)node; + + /* bump the count */ + names->numnicknames++; + } + + if (nickname) + PORT_Free(nickname); + return (PR_SUCCESS); +} + +CERTCertNicknames * +CERT_GetCertNicknames(CERTCertDBHandle *handle, int what, void *wincx) +{ + PLArenaPool *arena; + CERTCertNicknames *names; + int i; + stringNode *node; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return (NULL); + } + + names = (CERTCertNicknames *)PORT_ArenaAlloc(arena, sizeof(CERTCertNicknames)); + if (names == NULL) { + goto loser; + } + + names->arena = arena; + names->head = NULL; + names->numnicknames = 0; + names->nicknames = NULL; + names->what = what; + names->totallen = 0; + + /* make sure we are logged in */ + (void)pk11_TraverseAllSlots(NULL, NULL, PR_TRUE, wincx); + + NSSTrustDomain_TraverseCertificates(handle, + CollectNicknames, (void *)names); + if (names->numnicknames) { + names->nicknames = (char **)PORT_ArenaAlloc(arena, + names->numnicknames * + sizeof(char *)); + + if (names->nicknames == NULL) { + goto loser; + } + + node = (stringNode *)names->head; + + for (i = 0; i < names->numnicknames; i++) { + PORT_Assert(node != NULL); + + names->nicknames[i] = node->string; + names->totallen += PORT_Strlen(node->string); + node = node->next; + } + + PORT_Assert(node == NULL); + } + + return (names); + +loser: + PORT_FreeArena(arena, PR_FALSE); + return (NULL); +} + +void +CERT_FreeNicknames(CERTCertNicknames *nicknames) +{ + PORT_FreeArena(nicknames->arena, PR_FALSE); + + return; +} + +/* [ FROM pcertdb.c ] */ + +typedef struct dnameNode { + struct dnameNode *next; + SECItem name; +} dnameNode; + +void +CERT_FreeDistNames(CERTDistNames *names) +{ + PORT_FreeArena(names->arena, PR_FALSE); + + return; +} + +static SECStatus +CollectDistNames(CERTCertificate *cert, SECItem *k, void *data) +{ + CERTDistNames *names; + PRBool saveit = PR_FALSE; + CERTCertTrust trust; + dnameNode *node; + int len; + + names = (CERTDistNames *)data; + + if (CERT_GetCertTrust(cert, &trust) == SECSuccess) { + /* only collect names of CAs trusted for issuing SSL clients */ + if (trust.sslFlags & CERTDB_TRUSTED_CLIENT_CA) { + saveit = PR_TRUE; + } + } + + if (saveit) { + /* allocate the node */ + node = (dnameNode *)PORT_ArenaAlloc(names->arena, sizeof(dnameNode)); + if (node == NULL) { + return (SECFailure); + } + + /* copy the name */ + node->name.len = len = cert->derSubject.len; + node->name.type = siBuffer; + node->name.data = (unsigned char *)PORT_ArenaAlloc(names->arena, len); + if (node->name.data == NULL) { + return (SECFailure); + } + PORT_Memcpy(node->name.data, cert->derSubject.data, len); + + /* link it into the list */ + node->next = (dnameNode *)names->head; + names->head = (void *)node; + + /* bump the count */ + names->nnames++; + } + + return (SECSuccess); +} + +/* + * Return all of the CAs that are "trusted" for SSL. + */ +CERTDistNames * +CERT_DupDistNames(CERTDistNames *orig) +{ + PLArenaPool *arena; + CERTDistNames *names; + int i; + SECStatus rv; + + /* allocate an arena to use */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return (NULL); + } + + /* allocate the header structure */ + names = (CERTDistNames *)PORT_ArenaAlloc(arena, sizeof(CERTDistNames)); + if (names == NULL) { + goto loser; + } + + /* initialize the header struct */ + names->arena = arena; + names->head = NULL; + names->nnames = orig->nnames; + names->names = NULL; + + /* construct the array from the list */ + if (orig->nnames) { + names->names = (SECItem *)PORT_ArenaNewArray(arena, SECItem, + orig->nnames); + if (names->names == NULL) { + goto loser; + } + for (i = 0; i < orig->nnames; i++) { + rv = SECITEM_CopyItem(arena, &names->names[i], &orig->names[i]); + if (rv != SECSuccess) { + goto loser; + } + } + } + return (names); + +loser: + PORT_FreeArena(arena, PR_FALSE); + return (NULL); +} + +CERTDistNames * +CERT_GetSSLCACerts(CERTCertDBHandle *handle) +{ + PLArenaPool *arena; + CERTDistNames *names; + int i; + SECStatus rv; + dnameNode *node; + + /* allocate an arena to use */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return (NULL); + } + + /* allocate the header structure */ + names = (CERTDistNames *)PORT_ArenaAlloc(arena, sizeof(CERTDistNames)); + if (names == NULL) { + goto loser; + } + + /* initialize the header struct */ + names->arena = arena; + names->head = NULL; + names->nnames = 0; + names->names = NULL; + + /* collect the names from the database */ + rv = PK11_TraverseSlotCerts(CollectDistNames, (void *)names, NULL); + if (rv) { + goto loser; + } + + /* construct the array from the list */ + if (names->nnames) { + names->names = (SECItem *)PORT_ArenaAlloc(arena, names->nnames * sizeof(SECItem)); + + if (names->names == NULL) { + goto loser; + } + + node = (dnameNode *)names->head; + + for (i = 0; i < names->nnames; i++) { + PORT_Assert(node != NULL); + + names->names[i] = node->name; + node = node->next; + } + + PORT_Assert(node == NULL); + } + + return (names); + +loser: + PORT_FreeArena(arena, PR_FALSE); + return (NULL); +} + +CERTDistNames * +CERT_DistNamesFromCertList(CERTCertList *certList) +{ + CERTDistNames *dnames = NULL; + PLArenaPool *arena; + CERTCertListNode *node = NULL; + SECItem *names = NULL; + int listLen = 0, i = 0; + + if (certList == NULL) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + node = CERT_LIST_HEAD(certList); + while (!CERT_LIST_END(node, certList)) { + listLen += 1; + node = CERT_LIST_NEXT(node); + } + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) + goto loser; + dnames = PORT_ArenaZNew(arena, CERTDistNames); + if (dnames == NULL) + goto loser; + + dnames->arena = arena; + dnames->nnames = listLen; + dnames->names = names = PORT_ArenaZNewArray(arena, SECItem, listLen); + if (names == NULL) + goto loser; + + node = CERT_LIST_HEAD(certList); + while (!CERT_LIST_END(node, certList)) { + CERTCertificate *cert = node->cert; + SECStatus rv = SECITEM_CopyItem(arena, &names[i++], &cert->derSubject); + if (rv == SECFailure) { + goto loser; + } + node = CERT_LIST_NEXT(node); + } + return dnames; +loser: + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + return NULL; +} + +CERTDistNames * +CERT_DistNamesFromNicknames(CERTCertDBHandle *handle, char **nicknames, + int nnames) +{ + CERTDistNames *dnames = NULL; + PLArenaPool *arena; + int i, rv; + SECItem *names = NULL; + CERTCertificate *cert = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) + goto loser; + dnames = PORT_ArenaZNew(arena, CERTDistNames); + if (dnames == NULL) + goto loser; + + dnames->arena = arena; + dnames->nnames = nnames; + dnames->names = names = PORT_ArenaZNewArray(arena, SECItem, nnames); + if (names == NULL) + goto loser; + + for (i = 0; i < nnames; i++) { + cert = CERT_FindCertByNicknameOrEmailAddr(handle, nicknames[i]); + if (cert == NULL) + goto loser; + rv = SECITEM_CopyItem(arena, &names[i], &cert->derSubject); + if (rv == SECFailure) + goto loser; + CERT_DestroyCertificate(cert); + } + return dnames; + +loser: + if (cert != NULL) + CERT_DestroyCertificate(cert); + if (arena != NULL) + PORT_FreeArena(arena, PR_FALSE); + return NULL; +} + +/* [ from pcertdb.c - calls Ascii to Name ] */ +/* + * Lookup a certificate in the database by name + */ +CERTCertificate * +CERT_FindCertByNameString(CERTCertDBHandle *handle, char *nameStr) +{ + CERTName *name; + SECItem *nameItem; + CERTCertificate *cert = NULL; + PLArenaPool *arena = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if (arena == NULL) { + goto loser; + } + + name = CERT_AsciiToName(nameStr); + + if (name) { + nameItem = SEC_ASN1EncodeItem(arena, NULL, (void *)name, + CERT_NameTemplate); + if (nameItem != NULL) { + cert = CERT_FindCertByName(handle, nameItem); + } + CERT_DestroyName(name); + } + +loser: + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + + return (cert); +} + +/* From certv3.c */ + +CERTCrlDistributionPoints * +CERT_FindCRLDistributionPoints(CERTCertificate *cert) +{ + SECItem encodedExtenValue; + SECStatus rv; + CERTCrlDistributionPoints *dps; + + encodedExtenValue.data = NULL; + encodedExtenValue.len = 0; + + rv = cert_FindExtension(cert->extensions, SEC_OID_X509_CRL_DIST_POINTS, + &encodedExtenValue); + if (rv != SECSuccess) { + return (NULL); + } + + dps = CERT_DecodeCRLDistributionPoints(cert->arena, &encodedExtenValue); + + PORT_Free(encodedExtenValue.data); + + return dps; +} + +/* From crl.c */ +CERTSignedCrl * +CERT_ImportCRL(CERTCertDBHandle *handle, SECItem *derCRL, char *url, int type, void *wincx) +{ + CERTSignedCrl *retCrl = NULL; + PK11SlotInfo *slot = PK11_GetInternalKeySlot(); + retCrl = PK11_ImportCRL(slot, derCRL, url, type, wincx, + CRL_IMPORT_DEFAULT_OPTIONS, NULL, CRL_DECODE_DEFAULT_OPTIONS); + PK11_FreeSlot(slot); + + return retCrl; +} + +/* From certdb.c */ +static SECStatus +cert_ImportCAChain(SECItem *certs, int numcerts, SECCertUsage certUsage, PRBool trusted) +{ + SECStatus rv; + SECItem *derCert; + CERTCertificate *cert = NULL; + CERTCertificate *newcert = NULL; + CERTCertDBHandle *handle; + CERTCertTrust trust; + PRBool isca; + char *nickname; + unsigned int certtype; + PRBool istemp = PR_FALSE; + + handle = CERT_GetDefaultCertDB(); + + while (numcerts--) { + derCert = certs; + certs++; + + /* decode my certificate */ + /* This use is ok -- only looks at decoded parts, calls NewTemp later */ + newcert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL); + if (newcert == NULL) { + goto loser; + } + + if (!trusted) { + /* make sure that cert is valid */ + rv = CERT_CertTimesValid(newcert); + if (rv == SECFailure) { + goto endloop; + } + } + + /* does it have the CA extension */ + + /* + * Make sure that if this is an intermediate CA in the chain that + * it was given permission by its signer to be a CA. + */ + isca = CERT_IsCACert(newcert, &certtype); + + if (!isca) { + if (!trusted) { + goto endloop; + } + trust.sslFlags = CERTDB_VALID_CA; + trust.emailFlags = CERTDB_VALID_CA; + trust.objectSigningFlags = CERTDB_VALID_CA; + } else { + /* SSL ca's must have the ssl bit set */ + if ((certUsage == certUsageSSLCA) && + ((certtype & NS_CERT_TYPE_SSL_CA) != NS_CERT_TYPE_SSL_CA)) { + goto endloop; + } + + /* it passed all of the tests, so lets add it to the database */ + /* mark it as a CA */ + PORT_Memset((void *)&trust, 0, sizeof(trust)); + switch (certUsage) { + case certUsageSSLCA: + trust.sslFlags = CERTDB_VALID_CA; + break; + case certUsageUserCertImport: + if ((certtype & NS_CERT_TYPE_SSL_CA) == NS_CERT_TYPE_SSL_CA) { + trust.sslFlags = CERTDB_VALID_CA; + } + if ((certtype & NS_CERT_TYPE_EMAIL_CA) == + NS_CERT_TYPE_EMAIL_CA) { + trust.emailFlags = CERTDB_VALID_CA; + } + if ((certtype & NS_CERT_TYPE_OBJECT_SIGNING_CA) == + NS_CERT_TYPE_OBJECT_SIGNING_CA) { + trust.objectSigningFlags = CERTDB_VALID_CA; + } + break; + default: + PORT_Assert(0); + break; + } + } + + cert = CERT_NewTempCertificate(handle, derCert, NULL, + PR_FALSE, PR_FALSE); + if (cert == NULL) { + goto loser; + } + + /* if the cert is temp, make it perm; otherwise we're done */ + rv = CERT_GetCertIsTemp(cert, &istemp); + if (rv != SECSuccess) { + goto loser; + } + if (istemp) { + /* get a default nickname for it */ + nickname = CERT_MakeCANickname(cert); + + rv = CERT_AddTempCertToPerm(cert, nickname, &trust); + + /* free the nickname */ + if (nickname) { + PORT_Free(nickname); + } + } else { + rv = SECSuccess; + } + + if (rv != SECSuccess) { + goto loser; + } + + endloop: + if (newcert) { + CERT_DestroyCertificate(newcert); + newcert = NULL; + } + } + + rv = SECSuccess; + goto done; +loser: + rv = SECFailure; +done: + + if (newcert) { + CERT_DestroyCertificate(newcert); + newcert = NULL; + } + + if (cert) { + CERT_DestroyCertificate(cert); + cert = NULL; + } + + return (rv); +} + +SECStatus +CERT_ImportCAChain(SECItem *certs, int numcerts, SECCertUsage certUsage) +{ + return cert_ImportCAChain(certs, numcerts, certUsage, PR_FALSE); +} + +SECStatus +CERT_ImportCAChainTrusted(SECItem *certs, int numcerts, SECCertUsage certUsage) +{ + return cert_ImportCAChain(certs, numcerts, certUsage, PR_TRUE); +} + +/* Moved from certdb.c */ +/* +** CERT_CertChainFromCert +** +** Construct a CERTCertificateList consisting of the given certificate and all +** of the issuer certs until we either get to a self-signed cert or can't find +** an issuer. Since we don't know how many certs are in the chain we have to +** build a linked list first as we count them. +*/ + +typedef struct certNode { + struct certNode *next; + CERTCertificate *cert; +} certNode; + +CERTCertificateList * +CERT_CertChainFromCert(CERTCertificate *cert, SECCertUsage usage, + PRBool includeRoot) +{ + CERTCertificateList *chain = NULL; + NSSCertificate **stanChain; + NSSCertificate *stanCert; + PLArenaPool *arena; + NSSUsage nssUsage; + int i, len; + NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); + NSSCryptoContext *cc = STAN_GetDefaultCryptoContext(); + + stanCert = STAN_GetNSSCertificate(cert); + if (!stanCert) { + /* error code is set */ + return NULL; + } + nssUsage.anyUsage = PR_FALSE; + nssUsage.nss3usage = usage; + nssUsage.nss3lookingForCA = PR_FALSE; + stanChain = NSSCertificate_BuildChain(stanCert, NULL, &nssUsage, NULL, NULL, + CERT_MAX_CERT_CHAIN, NULL, NULL, td, cc); + if (!stanChain) { + PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER); + return NULL; + } + + len = 0; + stanCert = stanChain[0]; + while (stanCert) { + stanCert = stanChain[++len]; + } + + arena = PORT_NewArena(4096); + if (arena == NULL) { + goto loser; + } + + chain = (CERTCertificateList *)PORT_ArenaAlloc(arena, + sizeof(CERTCertificateList)); + if (!chain) + goto loser; + chain->certs = (SECItem *)PORT_ArenaAlloc(arena, len * sizeof(SECItem)); + if (!chain->certs) + goto loser; + i = 0; + stanCert = stanChain[i]; + while (stanCert) { + SECItem derCert; + CERTCertificate *cCert = STAN_GetCERTCertificate(stanCert); + if (!cCert) { + goto loser; + } + derCert.len = (unsigned int)stanCert->encoding.size; + derCert.data = (unsigned char *)stanCert->encoding.data; + derCert.type = siBuffer; + if (SECITEM_CopyItem(arena, &chain->certs[i], &derCert) != SECSuccess) { + CERT_DestroyCertificate(cCert); + goto loser; + } + stanCert = stanChain[++i]; + if (!stanCert && !cCert->isRoot) { + /* reached the end of the chain, but the final cert is + * not a root. Don't discard it. + */ + includeRoot = PR_TRUE; + } + CERT_DestroyCertificate(cCert); + } + if (!includeRoot && len > 1) { + chain->len = len - 1; + } else { + chain->len = len; + } + + chain->arena = arena; + nss_ZFreeIf(stanChain); + return chain; +loser: + i = 0; + stanCert = stanChain[i]; + while (stanCert) { + CERTCertificate *cCert = STAN_GetCERTCertificate(stanCert); + if (cCert) { + CERT_DestroyCertificate(cCert); + } + stanCert = stanChain[++i]; + } + nss_ZFreeIf(stanChain); + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + return NULL; +} + +/* Builds a CERTCertificateList holding just one DER-encoded cert, namely +** the one for the cert passed as an argument. +*/ +CERTCertificateList * +CERT_CertListFromCert(CERTCertificate *cert) +{ + CERTCertificateList *chain = NULL; + int rv; + PLArenaPool *arena; + + /* arena for SecCertificateList */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) + goto no_memory; + + /* build the CERTCertificateList */ + chain = (CERTCertificateList *)PORT_ArenaAlloc(arena, sizeof(CERTCertificateList)); + if (chain == NULL) + goto no_memory; + chain->certs = (SECItem *)PORT_ArenaAlloc(arena, 1 * sizeof(SECItem)); + if (chain->certs == NULL) + goto no_memory; + rv = SECITEM_CopyItem(arena, chain->certs, &(cert->derCert)); + if (rv < 0) + goto loser; + chain->len = 1; + chain->arena = arena; + + return chain; + +no_memory: + PORT_SetError(SEC_ERROR_NO_MEMORY); +loser: + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + return NULL; +} + +CERTCertificateList * +CERT_DupCertList(const CERTCertificateList *oldList) +{ + CERTCertificateList *newList = NULL; + PLArenaPool *arena = NULL; + SECItem *newItem; + SECItem *oldItem; + int len = oldList->len; + int rv; + + /* arena for SecCertificateList */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) + goto no_memory; + + /* now build the CERTCertificateList */ + newList = PORT_ArenaNew(arena, CERTCertificateList); + if (newList == NULL) + goto no_memory; + newList->arena = arena; + newItem = (SECItem *)PORT_ArenaAlloc(arena, len * sizeof(SECItem)); + if (newItem == NULL) + goto no_memory; + newList->certs = newItem; + newList->len = len; + + for (oldItem = oldList->certs; len > 0; --len, ++newItem, ++oldItem) { + rv = SECITEM_CopyItem(arena, newItem, oldItem); + if (rv < 0) + goto loser; + } + return newList; + +no_memory: + PORT_SetError(SEC_ERROR_NO_MEMORY); +loser: + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + return NULL; +} + +void +CERT_DestroyCertificateList(CERTCertificateList *list) +{ + PORT_FreeArena(list->arena, PR_FALSE); +} diff --git a/security/nss/lib/certhigh/certhigh.gyp b/security/nss/lib/certhigh/certhigh.gyp new file mode 100644 index 0000000000..5817c3eb5f --- /dev/null +++ b/security/nss/lib/certhigh/certhigh.gyp @@ -0,0 +1,31 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +{ + 'includes': [ + '../../coreconf/config.gypi' + ], + 'targets': [ + { + 'target_name': 'certhi', + 'type': 'static_library', + 'sources': [ + 'certhigh.c', + 'certhtml.c', + 'certreq.c', + 'certvfy.c', + 'certvfypkix.c', + 'crlv2.c', + 'ocsp.c', + 'ocspsig.c', + 'xcrldist.c' + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_exports' + ] + } + ], + 'variables': { + 'module': 'nss' + } +} \ No newline at end of file diff --git a/security/nss/lib/certhigh/certhtml.c b/security/nss/lib/certhigh/certhtml.c new file mode 100644 index 0000000000..2d708cc950 --- /dev/null +++ b/security/nss/lib/certhigh/certhtml.c @@ -0,0 +1,321 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * certhtml.c --- convert a cert to html + */ + +#include "seccomon.h" +#include "secitem.h" +#include "sechash.h" +#include "cert.h" +#include "keyhi.h" +#include "secder.h" +#include "prprf.h" +#include "secport.h" +#include "secasn1.h" +#include "pk11func.h" + +static char *hex = "0123456789ABCDEF"; + +/* +** Convert a der-encoded integer to a hex printable string form +*/ +char * +CERT_Hexify(SECItem *i, int do_colon) +{ + unsigned char *cp, *end; + char *rv, *o; + + if (!i->len) { + return PORT_Strdup("00"); + } + + rv = o = (char *)PORT_Alloc(i->len * 3); + if (!rv) + return rv; + + cp = i->data; + end = cp + i->len; + while (cp < end) { + unsigned char ch = *cp++; + *o++ = hex[(ch >> 4) & 0xf]; + *o++ = hex[ch & 0xf]; + if (cp != end) { + if (do_colon) { + *o++ = ':'; + } + } + } + *o = 0; /* Null terminate the string */ + return rv; +} + +#define BREAK "
" +#define BREAKLEN 4 +#define COMMA ", " +#define COMMALEN 2 + +#define MAX_OUS 20 +#define MAX_DC MAX_OUS + +char * +CERT_FormatName(CERTName *name) +{ + CERTRDN **rdns; + CERTRDN *rdn; + CERTAVA **avas; + CERTAVA *ava; + char *buf = 0; + char *tmpbuf = 0; + SECItem *cn = 0; + SECItem *email = 0; + SECItem *org = 0; + SECItem *loc = 0; + SECItem *state = 0; + SECItem *country = 0; + SECItem *dq = 0; + + unsigned len = 0; + int tag; + int i; + int ou_count = 0; + int dc_count = 0; + PRBool first; + SECItem *orgunit[MAX_OUS]; + SECItem *dc[MAX_DC]; + + /* Loop over name components and gather the interesting ones */ + rdns = name->rdns; + while ((rdn = *rdns++) != 0) { + avas = rdn->avas; + while ((ava = *avas++) != 0) { + tag = CERT_GetAVATag(ava); + switch (tag) { + case SEC_OID_AVA_COMMON_NAME: + if (cn) { + break; + } + cn = CERT_DecodeAVAValue(&ava->value); + if (!cn) { + goto loser; + } + len += cn->len; + // cn will always have BREAK after it + len += BREAKLEN; + break; + case SEC_OID_AVA_COUNTRY_NAME: + if (country) { + break; + } + country = CERT_DecodeAVAValue(&ava->value); + if (!country) { + goto loser; + } + len += country->len; + // country may have COMMA after it (if we over-count len, + // that's fine - we'll just allocate a buffer larger than we + // need) + len += COMMALEN; + break; + case SEC_OID_AVA_LOCALITY: + if (loc) { + break; + } + loc = CERT_DecodeAVAValue(&ava->value); + if (!loc) { + goto loser; + } + len += loc->len; + // loc may have COMMA after it + len += COMMALEN; + break; + case SEC_OID_AVA_STATE_OR_PROVINCE: + if (state) { + break; + } + state = CERT_DecodeAVAValue(&ava->value); + if (!state) { + goto loser; + } + len += state->len; + // state currently won't have COMMA after it, but this is a + // (probably vain) attempt to future-proof this code + len += COMMALEN; + break; + case SEC_OID_AVA_ORGANIZATION_NAME: + if (org) { + break; + } + org = CERT_DecodeAVAValue(&ava->value); + if (!org) { + goto loser; + } + len += org->len; + // org will have BREAK after it + len += BREAKLEN; + break; + case SEC_OID_AVA_DN_QUALIFIER: + if (dq) { + break; + } + dq = CERT_DecodeAVAValue(&ava->value); + if (!dq) { + goto loser; + } + len += dq->len; + // dq will have BREAK after it + len += BREAKLEN; + break; + case SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME: + if (ou_count < MAX_OUS) { + orgunit[ou_count] = CERT_DecodeAVAValue(&ava->value); + if (!orgunit[ou_count]) { + goto loser; + } + len += orgunit[ou_count++]->len; + // each ou will have BREAK after it + len += BREAKLEN; + } + break; + case SEC_OID_AVA_DC: + if (dc_count < MAX_DC) { + dc[dc_count] = CERT_DecodeAVAValue(&ava->value); + if (!dc[dc_count]) { + goto loser; + } + len += dc[dc_count++]->len; + // each dc will have BREAK after it + len += BREAKLEN; + } + break; + case SEC_OID_PKCS9_EMAIL_ADDRESS: + case SEC_OID_RFC1274_MAIL: + if (email) { + break; + } + email = CERT_DecodeAVAValue(&ava->value); + if (!email) { + goto loser; + } + len += email->len; + // email will have BREAK after it + len += BREAKLEN; + break; + default: + break; + } + } + } + + // there may be a final BREAK + len += BREAKLEN; + + /* allocate buffer */ + buf = (char *)PORT_Alloc(len); + if (!buf) { + goto loser; + } + + tmpbuf = buf; + + if (cn) { + PORT_Memcpy(tmpbuf, cn->data, cn->len); + tmpbuf += cn->len; + PORT_Memcpy(tmpbuf, BREAK, BREAKLEN); + tmpbuf += BREAKLEN; + } + if (email) { + PORT_Memcpy(tmpbuf, email->data, email->len); + tmpbuf += (email->len); + PORT_Memcpy(tmpbuf, BREAK, BREAKLEN); + tmpbuf += BREAKLEN; + } + for (i = ou_count - 1; i >= 0; i--) { + PORT_Memcpy(tmpbuf, orgunit[i]->data, orgunit[i]->len); + tmpbuf += (orgunit[i]->len); + PORT_Memcpy(tmpbuf, BREAK, BREAKLEN); + tmpbuf += BREAKLEN; + } + if (dq) { + PORT_Memcpy(tmpbuf, dq->data, dq->len); + tmpbuf += (dq->len); + PORT_Memcpy(tmpbuf, BREAK, BREAKLEN); + tmpbuf += BREAKLEN; + } + if (org) { + PORT_Memcpy(tmpbuf, org->data, org->len); + tmpbuf += (org->len); + PORT_Memcpy(tmpbuf, BREAK, BREAKLEN); + tmpbuf += BREAKLEN; + } + for (i = dc_count - 1; i >= 0; i--) { + PORT_Memcpy(tmpbuf, dc[i]->data, dc[i]->len); + tmpbuf += (dc[i]->len); + PORT_Memcpy(tmpbuf, BREAK, BREAKLEN); + tmpbuf += BREAKLEN; + } + first = PR_TRUE; + if (loc) { + PORT_Memcpy(tmpbuf, loc->data, loc->len); + tmpbuf += (loc->len); + first = PR_FALSE; + } + if (state) { + if (!first) { + PORT_Memcpy(tmpbuf, COMMA, COMMALEN); + tmpbuf += COMMALEN; + } + PORT_Memcpy(tmpbuf, state->data, state->len); + tmpbuf += (state->len); + first = PR_FALSE; + } + if (country) { + if (!first) { + PORT_Memcpy(tmpbuf, COMMA, COMMALEN); + tmpbuf += COMMALEN; + } + PORT_Memcpy(tmpbuf, country->data, country->len); + tmpbuf += (country->len); + first = PR_FALSE; + } + if (!first) { + PORT_Memcpy(tmpbuf, BREAK, BREAKLEN); + tmpbuf += BREAKLEN; + } + + *tmpbuf = 0; + +/* fall through and clean */ +loser: + if (cn) { + SECITEM_FreeItem(cn, PR_TRUE); + } + if (email) { + SECITEM_FreeItem(email, PR_TRUE); + } + for (i = ou_count - 1; i >= 0; i--) { + SECITEM_FreeItem(orgunit[i], PR_TRUE); + } + if (dq) { + SECITEM_FreeItem(dq, PR_TRUE); + } + if (org) { + SECITEM_FreeItem(org, PR_TRUE); + } + for (i = dc_count - 1; i >= 0; i--) { + SECITEM_FreeItem(dc[i], PR_TRUE); + } + if (loc) { + SECITEM_FreeItem(loc, PR_TRUE); + } + if (state) { + SECITEM_FreeItem(state, PR_TRUE); + } + if (country) { + SECITEM_FreeItem(country, PR_TRUE); + } + + return (buf); +} diff --git a/security/nss/lib/certhigh/certreq.c b/security/nss/lib/certhigh/certreq.c new file mode 100644 index 0000000000..2ab4f1ab7b --- /dev/null +++ b/security/nss/lib/certhigh/certreq.c @@ -0,0 +1,331 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "cert.h" +#include "certt.h" +#include "secder.h" +#include "keyhi.h" +#include "secitem.h" +#include "secasn1.h" +#include "secerr.h" + +SEC_ASN1_MKSUB(SEC_AnyTemplate) + +const SEC_ASN1Template CERT_AttributeTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTAttribute) }, + { SEC_ASN1_OBJECT_ID, offsetof(CERTAttribute, attrType) }, + { SEC_ASN1_SET_OF | SEC_ASN1_XTRN, offsetof(CERTAttribute, attrValue), + SEC_ASN1_SUB(SEC_AnyTemplate) }, + { 0 } +}; + +const SEC_ASN1Template CERT_SetOfAttributeTemplate[] = { + { SEC_ASN1_SET_OF, 0, CERT_AttributeTemplate }, +}; + +const SEC_ASN1Template CERT_CertificateRequestTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTCertificateRequest) }, + { SEC_ASN1_INTEGER, + offsetof(CERTCertificateRequest, version) }, + { SEC_ASN1_INLINE, + offsetof(CERTCertificateRequest, subject), + CERT_NameTemplate }, + { SEC_ASN1_INLINE, + offsetof(CERTCertificateRequest, subjectPublicKeyInfo), + CERT_SubjectPublicKeyInfoTemplate }, + { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(CERTCertificateRequest, attributes), + CERT_SetOfAttributeTemplate }, + { 0 } +}; + +SEC_ASN1_CHOOSER_IMPLEMENT(CERT_CertificateRequestTemplate) + +CERTCertificate * +CERT_CreateCertificate(unsigned long serialNumber, + CERTName *issuer, + CERTValidity *validity, + CERTCertificateRequest *req) +{ + CERTCertificate *c; + int rv; + PLArenaPool *arena; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + + if (!arena) { + return (0); + } + + c = (CERTCertificate *)PORT_ArenaZAlloc(arena, sizeof(CERTCertificate)); + + if (!c) { + PORT_FreeArena(arena, PR_FALSE); + return 0; + } + + c->referenceCount = 1; + c->arena = arena; + + /* + * Default is a plain version 1. + * If extensions are added, it will get changed as appropriate. + */ + rv = DER_SetUInteger(arena, &c->version, SEC_CERTIFICATE_VERSION_1); + if (rv) + goto loser; + + rv = DER_SetUInteger(arena, &c->serialNumber, serialNumber); + if (rv) + goto loser; + + rv = CERT_CopyName(arena, &c->issuer, issuer); + if (rv) + goto loser; + + rv = CERT_CopyValidity(arena, &c->validity, validity); + if (rv) + goto loser; + + rv = CERT_CopyName(arena, &c->subject, &req->subject); + if (rv) + goto loser; + rv = SECKEY_CopySubjectPublicKeyInfo(arena, &c->subjectPublicKeyInfo, + &req->subjectPublicKeyInfo); + if (rv) + goto loser; + + return c; + +loser: + CERT_DestroyCertificate(c); + return 0; +} + +/************************************************************************/ +/* It's clear from the comments that the original author of this + * function expected the template for certificate requests to treat + * the attributes as a SET OF ANY. This function expected to be + * passed an array of SECItems each of which contained an already encoded + * Attribute. But the cert request template does not treat the + * Attributes as a SET OF ANY, and AFAIK never has. Instead the template + * encodes attributes as a SET OF xxxxxxx. That is, it expects to encode + * each of the Attributes, not have them pre-encoded. Consequently an + * array of SECItems containing encoded Attributes is of no value to this + * function. But we cannot change the signature of this public function. + * It must continue to take SECItems. + * + * I have recoded this function so that each SECItem contains an + * encoded cert extension. The encoded cert extensions form the list for the + * single attribute of the cert request. In this implementation there is at most + * one attribute and it is always of type SEC_OID_PKCS9_EXTENSION_REQUEST. + */ + +CERTCertificateRequest * +CERT_CreateCertificateRequest(CERTName *subject, + CERTSubjectPublicKeyInfo *spki, + SECItem **attributes) +{ + CERTCertificateRequest *certreq; + PLArenaPool *arena; + CERTAttribute *attribute; + SECOidData *oidData; + SECStatus rv; + int i = 0; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + return NULL; + } + + certreq = PORT_ArenaZNew(arena, CERTCertificateRequest); + if (!certreq) { + PORT_FreeArena(arena, PR_FALSE); + return NULL; + } + /* below here it is safe to goto loser */ + + certreq->arena = arena; + + rv = DER_SetUInteger(arena, &certreq->version, + SEC_CERTIFICATE_REQUEST_VERSION); + if (rv != SECSuccess) + goto loser; + + rv = CERT_CopyName(arena, &certreq->subject, subject); + if (rv != SECSuccess) + goto loser; + + rv = SECKEY_CopySubjectPublicKeyInfo(arena, + &certreq->subjectPublicKeyInfo, + spki); + if (rv != SECSuccess) + goto loser; + + certreq->attributes = PORT_ArenaZNewArray(arena, CERTAttribute *, 2); + if (!certreq->attributes) + goto loser; + + /* Copy over attribute information */ + if (!attributes || !attributes[0]) { + /* + ** Invent empty attribute information. According to the + ** pkcs#10 spec, attributes has this ASN.1 type: + ** + ** attributes [0] IMPLICIT Attributes + ** + ** Which means, we should create a NULL terminated list + ** with the first entry being NULL; + */ + certreq->attributes[0] = NULL; + return certreq; + } + + /* allocate space for attributes */ + attribute = PORT_ArenaZNew(arena, CERTAttribute); + if (!attribute) + goto loser; + + oidData = SECOID_FindOIDByTag(SEC_OID_PKCS9_EXTENSION_REQUEST); + PORT_Assert(oidData); + if (!oidData) + goto loser; + rv = SECITEM_CopyItem(arena, &attribute->attrType, &oidData->oid); + if (rv != SECSuccess) + goto loser; + + for (i = 0; attributes[i] != NULL; i++) + ; + attribute->attrValue = PORT_ArenaZNewArray(arena, SECItem *, i + 1); + if (!attribute->attrValue) + goto loser; + + /* copy attributes */ + for (i = 0; attributes[i]; i++) { + /* + ** Attributes are a SetOf Attribute which implies + ** lexigraphical ordering. It is assumes that the + ** attributes are passed in sorted. If we need to + ** add functionality to sort them, there is an + ** example in the PKCS 7 code. + */ + attribute->attrValue[i] = SECITEM_ArenaDupItem(arena, attributes[i]); + if (!attribute->attrValue[i]) + goto loser; + } + + certreq->attributes[0] = attribute; + + return certreq; + +loser: + CERT_DestroyCertificateRequest(certreq); + return NULL; +} + +void +CERT_DestroyCertificateRequest(CERTCertificateRequest *req) +{ + if (req && req->arena) { + PORT_FreeArena(req->arena, PR_FALSE); + } + return; +} + +static void +setCRExt(void *o, CERTCertExtension **exts) +{ + ((CERTCertificateRequest *)o)->attributes = (struct CERTAttributeStr **)exts; +} + +/* +** Set up to start gathering cert extensions for a cert request. +** The list is created as CertExtensions and converted to an +** attribute list by CERT_FinishCRAttributes(). + */ +extern void *cert_StartExtensions(void *owner, PLArenaPool *ownerArena, + void (*setExts)(void *object, CERTCertExtension **exts)); +void * +CERT_StartCertificateRequestAttributes(CERTCertificateRequest *req) +{ + return (cert_StartExtensions((void *)req, req->arena, setCRExt)); +} + +/* +** At entry req->attributes actually contains an list of cert extensions-- +** req-attributes is overloaded until the list is DER encoded (the first +** ...EncodeItem() below). +** We turn this into an attribute list by encapsulating it +** in a PKCS 10 Attribute structure + */ +SECStatus +CERT_FinishCertificateRequestAttributes(CERTCertificateRequest *req) +{ + SECItem *extlist; + SECOidData *oidrec; + CERTAttribute *attribute; + + if (!req || !req->arena) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + if (req->attributes == NULL || req->attributes[0] == NULL) + return SECSuccess; + + extlist = SEC_ASN1EncodeItem(req->arena, NULL, &req->attributes, + SEC_ASN1_GET(CERT_SequenceOfCertExtensionTemplate)); + if (extlist == NULL) + return (SECFailure); + + oidrec = SECOID_FindOIDByTag(SEC_OID_PKCS9_EXTENSION_REQUEST); + if (oidrec == NULL) + return SECFailure; + + /* now change the list of cert extensions into a list of attributes + */ + req->attributes = PORT_ArenaZNewArray(req->arena, CERTAttribute *, 2); + + attribute = PORT_ArenaZNew(req->arena, CERTAttribute); + + if (req->attributes == NULL || attribute == NULL || + SECITEM_CopyItem(req->arena, &attribute->attrType, &oidrec->oid) != 0) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + attribute->attrValue = PORT_ArenaZNewArray(req->arena, SECItem *, 2); + + if (attribute->attrValue == NULL) + return SECFailure; + + attribute->attrValue[0] = extlist; + attribute->attrValue[1] = NULL; + req->attributes[0] = attribute; + req->attributes[1] = NULL; + + return SECSuccess; +} + +SECStatus +CERT_GetCertificateRequestExtensions(CERTCertificateRequest *req, + CERTCertExtension ***exts) +{ + if (req == NULL || exts == NULL) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (req->attributes == NULL || *req->attributes == NULL) + return SECSuccess; + + if ((*req->attributes)->attrValue == NULL) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + return (SEC_ASN1DecodeItem(req->arena, exts, + SEC_ASN1_GET(CERT_SequenceOfCertExtensionTemplate), + (*req->attributes)->attrValue[0])); +} diff --git a/security/nss/lib/certhigh/certvfy.c b/security/nss/lib/certhigh/certvfy.c new file mode 100644 index 0000000000..8e742279f3 --- /dev/null +++ b/security/nss/lib/certhigh/certvfy.c @@ -0,0 +1,2158 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "nspr.h" +#include "secerr.h" +#include "secport.h" +#include "seccomon.h" +#include "secoid.h" +#include "genname.h" +#include "keyhi.h" +#include "cert.h" +#include "certdb.h" +#include "certi.h" +#include "cryptohi.h" + +#ifndef NSS_DISABLE_LIBPKIX +#include "pkix.h" +#include "pkix_pl_cert.h" +#else +#include "nss.h" +#endif /* NSS_DISABLE_LIBPKIX */ + +#include "nsspki.h" +#include "pkitm.h" +#include "pkim.h" +#include "pki3hack.h" +#include "base.h" +#include "keyi.h" + +/* + * Check the validity times of a certificate + */ +SECStatus +CERT_CertTimesValid(CERTCertificate *c) +{ + SECCertTimeValidity valid = CERT_CheckCertValidTimes(c, PR_Now(), PR_TRUE); + return (valid == secCertTimeValid) ? SECSuccess : SECFailure; +} + +static SECStatus +checkKeyParams(const SECAlgorithmID *sigAlgorithm, const SECKEYPublicKey *key) +{ + SECStatus rv; + SECOidTag sigAlg; + SECOidTag curve; + PRUint32 policyFlags = 0; + PRInt32 minLen, len; + + sigAlg = SECOID_GetAlgorithmTag(sigAlgorithm); + + switch (sigAlg) { + case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE: + case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE: + case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE: + case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE: + case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE: + if (key->keyType != ecKey) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; + } + + curve = SECKEY_GetECCOid(&key->u.ec.DEREncodedParams); + if (curve != 0) { + if (NSS_GetAlgorithmPolicy(curve, &policyFlags) == SECFailure || + !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) { + PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); + return SECFailure; + } + return SECSuccess; + } + PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE); + return SECFailure; + + case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: { + PORTCheapArenaPool tmpArena; + SECOidTag hashAlg; + SECOidTag maskHashAlg; + + PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); + rv = sec_DecodeRSAPSSParams(&tmpArena.arena, + &sigAlgorithm->parameters, + &hashAlg, &maskHashAlg, NULL); + PORT_DestroyCheapArena(&tmpArena); + if (rv != SECSuccess) { + return SECFailure; + } + + if (NSS_GetAlgorithmPolicy(hashAlg, &policyFlags) == SECSuccess && + !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) { + PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); + return SECFailure; + } + if (NSS_GetAlgorithmPolicy(maskHashAlg, &policyFlags) == SECSuccess && + !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) { + PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); + return SECFailure; + } + } + /* fall through to RSA key checking */ + case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: + case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION: + case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION: + case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION: + case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION: + case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE: + case SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE: + if (key->keyType != rsaKey && key->keyType != rsaPssKey) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; + } + + len = 8 * key->u.rsa.modulus.len; + + rv = NSS_OptionGet(NSS_RSA_MIN_KEY_SIZE, &minLen); + if (rv != SECSuccess) { + return SECFailure; + } + + if (len < minLen) { + return SECFailure; + } + + return SECSuccess; + case SEC_OID_ANSIX9_DSA_SIGNATURE: + case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST: + case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST: + case SEC_OID_SDN702_DSA_SIGNATURE: + case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA224_DIGEST: + case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST: + if (key->keyType != dsaKey) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; + } + + len = 8 * key->u.dsa.params.prime.len; + + rv = NSS_OptionGet(NSS_DSA_MIN_KEY_SIZE, &minLen); + if (rv != SECSuccess) { + return SECFailure; + } + + if (len < minLen) { + return SECFailure; + } + + return SECSuccess; + default: + return SECSuccess; + } +} + +/* + * verify the signature of a signed data object with the given DER publickey + */ +SECStatus +CERT_VerifySignedDataWithPublicKey(const CERTSignedData *sd, + SECKEYPublicKey *pubKey, + void *wincx) +{ + SECStatus rv; + SECItem sig; + SECOidTag sigAlg; + SECOidTag encAlg; + SECOidTag hashAlg; + PRUint32 policyFlags; + + if (!pubKey || !sd) { + PORT_SetError(PR_INVALID_ARGUMENT_ERROR); + return SECFailure; + } + + /* Can we use this algorithm for signature verification? */ + sigAlg = SECOID_GetAlgorithmTag(&sd->signatureAlgorithm); + rv = sec_DecodeSigAlg(pubKey, sigAlg, + &sd->signatureAlgorithm.parameters, + &encAlg, &hashAlg); + if (rv != SECSuccess) { + return SECFailure; /* error is set */ + } + rv = NSS_GetAlgorithmPolicy(encAlg, &policyFlags); + if (rv == SECSuccess && + !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) { + PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); + return SECFailure; + } + rv = NSS_GetAlgorithmPolicy(hashAlg, &policyFlags); + if (rv == SECSuccess && + !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) { + PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); + return SECFailure; + } + rv = checkKeyParams(&sd->signatureAlgorithm, pubKey); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); + return SECFailure; + } + + /* check the signature */ + sig = sd->signature; + /* convert sig->len from bit counts to byte count. */ + DER_ConvertBitString(&sig); + + rv = VFY_VerifyDataWithAlgorithmID(sd->data.data, sd->data.len, pubKey, + &sig, &sd->signatureAlgorithm, + &hashAlg, wincx); + if (rv != SECSuccess) { + return SECFailure; /* error is set */ + } + + /* for some algorithms, hash algorithm is only known after verification */ + rv = NSS_GetAlgorithmPolicy(hashAlg, &policyFlags); + if (rv == SECSuccess && + !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) { + PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); + return SECFailure; + } + return SECSuccess; +} + +/* + * verify the signature of a signed data object with the given DER publickey + */ +SECStatus +CERT_VerifySignedDataWithPublicKeyInfo(CERTSignedData *sd, + CERTSubjectPublicKeyInfo *pubKeyInfo, + void *wincx) +{ + SECKEYPublicKey *pubKey; + SECStatus rv = SECFailure; + + /* get cert's public key */ + pubKey = SECKEY_ExtractPublicKey(pubKeyInfo); + if (pubKey) { + rv = CERT_VerifySignedDataWithPublicKey(sd, pubKey, wincx); + SECKEY_DestroyPublicKey(pubKey); + } + return rv; +} + +/* + * verify the signature of a signed data object with the given certificate + */ +SECStatus +CERT_VerifySignedData(CERTSignedData *sd, CERTCertificate *cert, + PRTime t, void *wincx) +{ + SECKEYPublicKey *pubKey = 0; + SECStatus rv = SECFailure; + SECCertTimeValidity validity; + + /* check the certificate's validity */ + validity = CERT_CheckCertValidTimes(cert, t, PR_FALSE); + if (validity != secCertTimeValid) { + return rv; + } + + /* get cert's public key */ + pubKey = CERT_ExtractPublicKey(cert); + if (pubKey) { + rv = CERT_VerifySignedDataWithPublicKey(sd, pubKey, wincx); + SECKEY_DestroyPublicKey(pubKey); + } + return rv; +} + +SECStatus +SEC_CheckCRL(CERTCertDBHandle *handle, CERTCertificate *cert, + CERTCertificate *caCert, PRTime t, void *wincx) +{ + return CERT_CheckCRL(cert, caCert, NULL, t, wincx); +} + +/* + * Find the issuer of a cert. Use the authorityKeyID if it exists. + */ +CERTCertificate * +CERT_FindCertIssuer(CERTCertificate *cert, PRTime validTime, SECCertUsage usage) +{ + NSSCertificate *me; + NSSTime *nssTime; + NSSTrustDomain *td; + NSSCryptoContext *cc; + NSSCertificate *chain[3]; + NSSUsage nssUsage; + PRStatus status; + + me = STAN_GetNSSCertificate(cert); + if (!me) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + nssTime = NSSTime_SetPRTime(NULL, validTime); + nssUsage.anyUsage = PR_FALSE; + nssUsage.nss3usage = usage; + nssUsage.nss3lookingForCA = PR_TRUE; + memset(chain, 0, 3 * sizeof(NSSCertificate *)); + td = STAN_GetDefaultTrustDomain(); + cc = STAN_GetDefaultCryptoContext(); + (void)NSSCertificate_BuildChain(me, nssTime, &nssUsage, NULL, + chain, 2, NULL, &status, td, cc); + nss_ZFreeIf(nssTime); + if (status == PR_SUCCESS) { + PORT_Assert(me == chain[0]); + /* if it's a root, the chain will only have one cert */ + if (!chain[1]) { + /* already has a reference from the call to BuildChain */ + return cert; + } + NSSCertificate_Destroy(chain[0]); /* the first cert in the chain */ + return STAN_GetCERTCertificate(chain[1]); /* return the 2nd */ + } + if (chain[0]) { + PORT_Assert(me == chain[0]); + NSSCertificate_Destroy(chain[0]); /* the first cert in the chain */ + } + PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER); + return NULL; +} + +/* + * return required trust flags for various cert usages for CAs + */ +SECStatus +CERT_TrustFlagsForCACertUsage(SECCertUsage usage, + unsigned int *retFlags, + SECTrustType *retTrustType) +{ + unsigned int requiredFlags; + SECTrustType trustType; + + switch (usage) { + case certUsageSSLClient: + requiredFlags = CERTDB_TRUSTED_CLIENT_CA; + trustType = trustSSL; + break; + case certUsageSSLServer: + case certUsageSSLCA: + requiredFlags = CERTDB_TRUSTED_CA; + trustType = trustSSL; + break; + case certUsageIPsec: + requiredFlags = CERTDB_TRUSTED_CA; + trustType = trustSSL; + break; + case certUsageSSLServerWithStepUp: + requiredFlags = CERTDB_TRUSTED_CA | CERTDB_GOVT_APPROVED_CA; + trustType = trustSSL; + break; + case certUsageEmailSigner: + case certUsageEmailRecipient: + requiredFlags = CERTDB_TRUSTED_CA; + trustType = trustEmail; + break; + case certUsageObjectSigner: + requiredFlags = CERTDB_TRUSTED_CA; + trustType = trustObjectSigning; + break; + case certUsageVerifyCA: + case certUsageAnyCA: + case certUsageStatusResponder: + requiredFlags = CERTDB_TRUSTED_CA; + trustType = trustTypeNone; + break; + default: + PORT_Assert(0); + goto loser; + } + if (retFlags != NULL) { + *retFlags = requiredFlags; + } + if (retTrustType != NULL) { + *retTrustType = trustType; + } + + return (SECSuccess); +loser: + return (SECFailure); +} + +void +cert_AddToVerifyLog(CERTVerifyLog *log, CERTCertificate *cert, long error, + unsigned int depth, void *arg) +{ + CERTVerifyLogNode *node, *tnode; + + PORT_Assert(log != NULL); + + node = (CERTVerifyLogNode *)PORT_ArenaAlloc(log->arena, + sizeof(CERTVerifyLogNode)); + if (node != NULL) { + node->cert = CERT_DupCertificate(cert); + node->error = error; + node->depth = depth; + node->arg = arg; + + if (log->tail == NULL) { + /* empty list */ + log->head = log->tail = node; + node->prev = NULL; + node->next = NULL; + } else if (depth >= log->tail->depth) { + /* add to tail */ + node->prev = log->tail; + log->tail->next = node; + log->tail = node; + node->next = NULL; + } else if (depth < log->head->depth) { + /* add at head */ + node->prev = NULL; + node->next = log->head; + log->head->prev = node; + log->head = node; + } else { + /* add in middle */ + tnode = log->tail; + while (tnode != NULL) { + if (depth >= tnode->depth) { + /* insert after tnode */ + node->prev = tnode; + node->next = tnode->next; + tnode->next->prev = node; + tnode->next = node; + break; + } + + tnode = tnode->prev; + } + } + + log->count++; + } + return; +} + +#define EXIT_IF_NOT_LOGGING(log) \ + if (log == NULL) { \ + goto loser; \ + } + +#define LOG_ERROR_OR_EXIT(log, cert, depth, arg) \ + if (log != NULL) { \ + cert_AddToVerifyLog(log, cert, PORT_GetError(), depth, \ + (void *)(PRWord)arg); \ + } else { \ + goto loser; \ + } + +#define LOG_ERROR(log, cert, depth, arg) \ + if (log != NULL) { \ + cert_AddToVerifyLog(log, cert, PORT_GetError(), depth, \ + (void *)(PRWord)arg); \ + } + +/* /C=CN/O=WoSign CA Limited/CN=CA \xE6\xB2\x83\xE9\x80\x9A\xE6\xA0\xB9\xE8\xAF\x81\xE4\xB9\xA6 + * Using a consistent naming convention, this would actually be called + * 'CA沃通根证书DN', but since GCC 6.2.1 apparently can't handle UTF-8 + * identifiers, this will have to do. + */ +static const unsigned char CAWoSignRootDN[72] = { + 0x30, 0x46, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11, + 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D, + 0x69, 0x74, 0x65, 0x64, 0x31, 0x1B, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x0C, 0x12, 0x43, 0x41, 0x20, 0xE6, 0xB2, 0x83, 0xE9, 0x80, 0x9A, 0xE6, 0xA0, + 0xB9, 0xE8, 0xAF, 0x81, 0xE4, 0xB9, 0xA6 +}; + +/* /C=CN/O=WoSign CA Limited/CN=CA WoSign ECC Root */ +static const unsigned char CAWoSignECCRootDN[72] = { + 0x30, 0x46, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11, + 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D, + 0x69, 0x74, 0x65, 0x64, 0x31, 0x1B, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x12, 0x43, 0x41, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x45, + 0x43, 0x43, 0x20, 0x52, 0x6F, 0x6F, 0x74 +}; + +/* /C=CN/O=WoSign CA Limited/CN=Certification Authority of WoSign */ +static const unsigned char CertificationAuthorityofWoSignDN[87] = { + 0x30, 0x55, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11, + 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D, + 0x69, 0x74, 0x65, 0x64, 0x31, 0x2A, 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x21, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20, + 0x6F, 0x66, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E +}; + +/* /C=CN/O=WoSign CA Limited/CN=Certification Authority of WoSign G2 */ +static const unsigned char CertificationAuthorityofWoSignG2DN[90] = { + 0x30, 0x58, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11, + 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D, + 0x69, 0x74, 0x65, 0x64, 0x31, 0x2D, 0x30, 0x2B, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x24, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20, + 0x6F, 0x66, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x47, 0x32 +}; + +/* /C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority */ +static const unsigned char StartComCertificationAuthorityDN[127] = { + 0x30, 0x7D, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x49, 0x4C, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x0D, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x4C, 0x74, 0x64, 0x2E, + 0x31, 0x2B, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x22, 0x53, 0x65, + 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6C, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, + 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, + 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79 +}; + +/* /C=IL/O=StartCom Ltd./CN=StartCom Certification Authority G2 */ +static const unsigned char StartComCertificationAuthorityG2DN[85] = { + 0x30, 0x53, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x49, 0x4C, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x0D, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x4C, 0x74, 0x64, 0x2E, + 0x31, 0x2C, 0x30, 0x2A, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x23, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, + 0x72, 0x69, 0x74, 0x79, 0x20, 0x47, 0x32 +}; + +struct DataAndLength { + const unsigned char *data; + PRUint32 len; +}; + +static const struct DataAndLength StartComAndWoSignDNs[] = { + { CAWoSignRootDN, + sizeof(CAWoSignRootDN) }, + { CAWoSignECCRootDN, + sizeof(CAWoSignECCRootDN) }, + { CertificationAuthorityofWoSignDN, + sizeof(CertificationAuthorityofWoSignDN) }, + { CertificationAuthorityofWoSignG2DN, + sizeof(CertificationAuthorityofWoSignG2DN) }, + { StartComCertificationAuthorityDN, + sizeof(StartComCertificationAuthorityDN) }, + { StartComCertificationAuthorityG2DN, + sizeof(StartComCertificationAuthorityG2DN) }, +}; + +static PRBool +CertIsStartComOrWoSign(const CERTCertificate *cert) +{ + int i; + const struct DataAndLength *dn = StartComAndWoSignDNs; + + for (i = 0; i < sizeof(StartComAndWoSignDNs) / sizeof(struct DataAndLength); ++i, dn++) { + if (cert->derSubject.len == dn->len && + memcmp(cert->derSubject.data, dn->data, dn->len) == 0) { + return PR_TRUE; + } + } + return PR_FALSE; +} + +SECStatus +isIssuerCertAllowedAtCertIssuanceTime(CERTCertificate *issuerCert, + CERTCertificate *referenceCert) +{ + if (!issuerCert || !referenceCert) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (CertIsStartComOrWoSign(issuerCert)) { + /* PRTime is microseconds since the epoch, whereas JS time is milliseconds. + * (new Date("2016-10-21T00:00:00Z")).getTime() * 1000 + */ + static const PRTime OCTOBER_21_2016 = 1477008000000000; + + PRTime notBefore, notAfter; + SECStatus rv; + + rv = CERT_GetCertTimes(referenceCert, ¬Before, ¬After); + if (rv != SECSuccess) + return rv; + + if (notBefore > OCTOBER_21_2016) { + return SECFailure; + } + } + + return SECSuccess; +} + +static SECStatus +cert_VerifyCertChainOld(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, PRBool *sigerror, + SECCertUsage certUsage, PRTime t, void *wincx, + CERTVerifyLog *log, PRBool *revoked) +{ + SECTrustType trustType; + CERTBasicConstraints basicConstraint; + CERTCertificate *issuerCert = NULL; + CERTCertificate *subjectCert = NULL; + CERTCertificate *badCert = NULL; + PRBool isca; + SECStatus rv; + SECStatus rvFinal = SECSuccess; + int count; + int currentPathLen = 0; + int pathLengthLimit = CERT_UNLIMITED_PATH_CONSTRAINT; + unsigned int caCertType; + unsigned int requiredCAKeyUsage; + unsigned int requiredFlags; + PLArenaPool *arena = NULL; + CERTGeneralName *namesList = NULL; + CERTCertificate **certsList = NULL; + int certsListLen = 16; + int namesCount = 0; + PRBool subjectCertIsSelfIssued; + CERTCertTrust issuerTrust; + + if (revoked) { + *revoked = PR_FALSE; + } + + if (CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_TRUE, + &requiredCAKeyUsage, + &caCertType) != + SECSuccess) { + PORT_Assert(0); + EXIT_IF_NOT_LOGGING(log); + requiredCAKeyUsage = 0; + caCertType = 0; + } + + switch (certUsage) { + case certUsageSSLClient: + case certUsageSSLServer: + case certUsageIPsec: + case certUsageSSLCA: + case certUsageSSLServerWithStepUp: + case certUsageEmailSigner: + case certUsageEmailRecipient: + case certUsageObjectSigner: + case certUsageVerifyCA: + case certUsageAnyCA: + case certUsageStatusResponder: + if (CERT_TrustFlagsForCACertUsage(certUsage, &requiredFlags, + &trustType) != SECSuccess) { + PORT_Assert(0); + EXIT_IF_NOT_LOGGING(log); + /* XXX continuing with requiredFlags = 0 seems wrong. It'll + * cause the following test to be true incorrectly: + * flags = SEC_GET_TRUST_FLAGS(issuerCert->trust, trustType); + * if (( flags & requiredFlags ) == requiredFlags) { + * rv = rvFinal; + * goto done; + * } + * There are three other instances of this problem. + */ + requiredFlags = 0; + trustType = trustSSL; + } + break; + default: + PORT_Assert(0); + EXIT_IF_NOT_LOGGING(log); + requiredFlags = 0; + trustType = trustSSL; /* This used to be 0, but we need something + * that matches the enumeration type. + */ + caCertType = 0; + } + + subjectCert = CERT_DupCertificate(cert); + if (subjectCert == NULL) { + goto loser; + } + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + goto loser; + } + + certsList = PORT_ZNewArray(CERTCertificate *, certsListLen); + if (certsList == NULL) + goto loser; + + /* RFC 3280 says that the name constraints will apply to the names + ** in the leaf (EE) cert, whether it is self issued or not, so + ** we pretend that it is not. + */ + subjectCertIsSelfIssued = PR_FALSE; + for (count = 0; count < CERT_MAX_CERT_CHAIN; count++) { + PRBool validCAOverride = PR_FALSE; + + /* Construct a list of names for the current and all previous + * certifcates (except leaf (EE) certs, root CAs, and self-issued + * intermediate CAs) to be verified against the name constraints + * extension of the issuer certificate. + */ + if (subjectCertIsSelfIssued == PR_FALSE) { + CERTGeneralName *subjectNameList; + int subjectNameListLen; + int i; + PRBool getSubjectCN = (!count && + (certUsage == certUsageSSLServer || certUsage == certUsageIPsec)); + subjectNameList = + CERT_GetConstrainedCertificateNames(subjectCert, arena, + getSubjectCN); + if (!subjectNameList) + goto loser; + subjectNameListLen = CERT_GetNamesLength(subjectNameList); + if (!subjectNameListLen) + goto loser; + if (certsListLen <= namesCount + subjectNameListLen) { + CERTCertificate **tmpCertsList; + certsListLen = (namesCount + subjectNameListLen) * 2; + tmpCertsList = + (CERTCertificate **)PORT_Realloc(certsList, + certsListLen * + sizeof(CERTCertificate *)); + if (tmpCertsList == NULL) { + goto loser; + } + certsList = tmpCertsList; + } + for (i = 0; i < subjectNameListLen; i++) { + certsList[namesCount + i] = subjectCert; + } + namesCount += subjectNameListLen; + namesList = cert_CombineNamesLists(namesList, subjectNameList); + } + + /* check if the cert has an unsupported critical extension */ + if (subjectCert->options.bits.hasUnsupportedCriticalExt) { + PORT_SetError(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION); + LOG_ERROR_OR_EXIT(log, subjectCert, count, 0); + } + + /* check that the signatureAlgorithm field of the certificate + * matches the signature field of the tbsCertificate */ + if (SECOID_CompareAlgorithmID( + &subjectCert->signatureWrap.signatureAlgorithm, + &subjectCert->signature)) { + PORT_SetError(SEC_ERROR_ALGORITHM_MISMATCH); + LOG_ERROR(log, subjectCert, count, 0); + goto loser; + } + + /* find the certificate of the issuer */ + issuerCert = CERT_FindCertIssuer(subjectCert, t, certUsage); + if (!issuerCert) { + PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER); + LOG_ERROR(log, subjectCert, count, 0); + goto loser; + } + + /* verify the signature on the cert */ + if (checkSig) { + rv = CERT_VerifySignedData(&subjectCert->signatureWrap, + issuerCert, t, wincx); + + if (rv != SECSuccess) { + if (sigerror) { + *sigerror = PR_TRUE; + } + if (PORT_GetError() == SEC_ERROR_EXPIRED_CERTIFICATE) { + PORT_SetError(SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE); + LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, 0); + } else { + if (PORT_GetError() != + SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + } + LOG_ERROR_OR_EXIT(log, subjectCert, count, 0); + } + } + } + + /* If the basicConstraint extension is included in an immediate CA + * certificate, make sure that the isCA flag is on. If the + * pathLenConstraint component exists, it must be greater than the + * number of CA certificates we have seen so far. If the extension + * is omitted, we will assume that this is a CA certificate with + * an unlimited pathLenConstraint (since it already passes the + * netscape-cert-type extension checking). + */ + + rv = CERT_FindBasicConstraintExten(issuerCert, &basicConstraint); + if (rv != SECSuccess) { + if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) { + LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, 0); + } + pathLengthLimit = CERT_UNLIMITED_PATH_CONSTRAINT; + /* no basic constraints found, we aren't (yet) a CA. */ + isca = PR_FALSE; + } else { + if (basicConstraint.isCA == PR_FALSE) { + PORT_SetError(SEC_ERROR_CA_CERT_INVALID); + LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, 0); + } + pathLengthLimit = basicConstraint.pathLenConstraint; + isca = PR_TRUE; + } + /* make sure that the path len constraint is properly set.*/ + if (pathLengthLimit >= 0 && currentPathLen > pathLengthLimit) { + PORT_SetError(SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID); + LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, pathLengthLimit); + } + + /* make sure that the entire chain is within the name space of the + * current issuer certificate. + */ + rv = CERT_CompareNameSpace(issuerCert, namesList, certsList, + arena, &badCert); + if (rv != SECSuccess || badCert != NULL) { + PORT_SetError(SEC_ERROR_CERT_NOT_IN_NAME_SPACE); + LOG_ERROR_OR_EXIT(log, badCert, count + 1, 0); + goto loser; + } + + rv = isIssuerCertAllowedAtCertIssuanceTime(issuerCert, cert); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER); + LOG_ERROR(log, issuerCert, count + 1, 0); + goto loser; + } + + /* XXX - the error logging may need to go down into CRL stuff at some + * point + */ + /* check revoked list (issuer) */ + rv = SEC_CheckCRL(handle, subjectCert, issuerCert, t, wincx); + if (rv == SECFailure) { + if (revoked) { + *revoked = PR_TRUE; + } + LOG_ERROR_OR_EXIT(log, subjectCert, count, 0); + } else if (rv == SECWouldBlock) { + /* We found something fishy, so we intend to issue an + * error to the user, but the user may wish to continue + * processing, in which case we better make sure nothing + * worse has happened... so keep cranking the loop */ + rvFinal = SECFailure; + if (revoked) { + *revoked = PR_TRUE; + } + LOG_ERROR(log, subjectCert, count, 0); + } + + if (CERT_GetCertTrust(issuerCert, &issuerTrust) == SECSuccess) { + /* we have some trust info, but this does NOT imply that this + * cert is actually trusted for any purpose. The cert may be + * explicitly UNtrusted. We won't know until we examine the + * trust bits. + */ + unsigned int flags; + + if (certUsage != certUsageAnyCA && + certUsage != certUsageStatusResponder) { + + /* + * XXX This choice of trustType seems arbitrary. + */ + if (certUsage == certUsageVerifyCA) { + if (subjectCert->nsCertType & NS_CERT_TYPE_EMAIL_CA) { + trustType = trustEmail; + } else if (subjectCert->nsCertType & NS_CERT_TYPE_SSL_CA) { + trustType = trustSSL; + } else { + trustType = trustObjectSigning; + } + } + + flags = SEC_GET_TRUST_FLAGS(&issuerTrust, trustType); + if ((flags & requiredFlags) == requiredFlags) { + /* we found a trusted one, so return */ + rv = rvFinal; + goto done; + } + if (flags & CERTDB_VALID_CA) { + validCAOverride = PR_TRUE; + } + /* is it explicitly distrusted? */ + if ((flags & CERTDB_TERMINAL_RECORD) && + ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0)) { + /* untrusted -- the cert is explicitly untrusted, not + * just that it doesn't chain to a trusted cert */ + PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER); + LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, flags); + } + } else { + /* Check if we have any valid trust when cheching for + * certUsageAnyCA or certUsageStatusResponder. */ + for (trustType = trustSSL; trustType < trustTypeNone; + trustType++) { + flags = SEC_GET_TRUST_FLAGS(&issuerTrust, trustType); + if ((flags & requiredFlags) == requiredFlags) { + rv = rvFinal; + goto done; + } + if (flags & CERTDB_VALID_CA) + validCAOverride = PR_TRUE; + } + /* We have 2 separate loops because we want any single trust + * bit to allow this usage to return trusted. Only if none of + * the trust bits are on do we check to see if the cert is + * untrusted */ + for (trustType = trustSSL; trustType < trustTypeNone; + trustType++) { + flags = SEC_GET_TRUST_FLAGS(&issuerTrust, trustType); + /* is it explicitly distrusted? */ + if ((flags & CERTDB_TERMINAL_RECORD) && + ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0)) { + /* untrusted -- the cert is explicitly untrusted, not + * just that it doesn't chain to a trusted cert */ + PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER); + LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, flags); + } + } + } + } + + if (!validCAOverride) { + /* + * Make sure that if this is an intermediate CA in the chain that + * it was given permission by its signer to be a CA. + */ + /* + * if basicConstraints says it is a ca, then we check the + * nsCertType. If the nsCertType has any CA bits set, then + * it must have the right one. + */ + if (!isca || (issuerCert->nsCertType & NS_CERT_TYPE_CA)) { + isca = (issuerCert->nsCertType & caCertType) ? PR_TRUE : PR_FALSE; + } + + if (!isca) { + PORT_SetError(SEC_ERROR_CA_CERT_INVALID); + LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, 0); + } + + /* make sure key usage allows cert signing */ + if (CERT_CheckKeyUsage(issuerCert, requiredCAKeyUsage) != SECSuccess) { + PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE); + LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, requiredCAKeyUsage); + } + } + + /* make sure that the issuer is not self signed. If it is, then + * stop here to prevent looping. + */ + if (issuerCert->isRoot) { + PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER); + LOG_ERROR(log, issuerCert, count + 1, 0); + goto loser; + } + /* The issuer cert will be the subject cert in the next loop. + * A cert is self-issued if its subject and issuer are equal and + * both are of non-zero length. + */ + subjectCertIsSelfIssued = (PRBool) + SECITEM_ItemsAreEqual(&issuerCert->derIssuer, + &issuerCert->derSubject) && + issuerCert->derSubject.len > + 0; + if (subjectCertIsSelfIssued == PR_FALSE) { + /* RFC 3280 says only non-self-issued intermediate CA certs + * count in path length. + */ + ++currentPathLen; + } + + CERT_DestroyCertificate(subjectCert); + subjectCert = issuerCert; + issuerCert = NULL; + } + + PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER); + LOG_ERROR(log, subjectCert, count, 0); +loser: + rv = SECFailure; +done: + if (certsList != NULL) { + PORT_Free(certsList); + } + if (issuerCert) { + CERT_DestroyCertificate(issuerCert); + } + + if (subjectCert) { + CERT_DestroyCertificate(subjectCert); + } + + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + return rv; +} + +SECStatus +cert_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, PRBool *sigerror, + SECCertUsage certUsage, PRTime t, void *wincx, + CERTVerifyLog *log, PRBool *revoked) +{ + if (CERT_GetUsePKIXForValidation()) { + return cert_VerifyCertChainPkix(cert, checkSig, certUsage, t, + wincx, log, sigerror, revoked); + } + return cert_VerifyCertChainOld(handle, cert, checkSig, sigerror, + certUsage, t, wincx, log, revoked); +} + +SECStatus +CERT_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertUsage certUsage, PRTime t, + void *wincx, CERTVerifyLog *log) +{ + return cert_VerifyCertChain(handle, cert, checkSig, NULL, certUsage, t, + wincx, log, NULL); +} + +/* + * verify that a CA can sign a certificate with the requested usage. + */ +SECStatus +CERT_VerifyCACertForUsage(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertUsage certUsage, PRTime t, + void *wincx, CERTVerifyLog *log) +{ + SECTrustType trustType; + CERTBasicConstraints basicConstraint; + PRBool isca; + PRBool validCAOverride = PR_FALSE; + SECStatus rv; + SECStatus rvFinal = SECSuccess; + unsigned int flags; + unsigned int caCertType; + unsigned int requiredCAKeyUsage; + unsigned int requiredFlags; + CERTCertificate *issuerCert; + CERTCertTrust certTrust; + + if (CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_TRUE, + &requiredCAKeyUsage, + &caCertType) != SECSuccess) { + PORT_Assert(0); + EXIT_IF_NOT_LOGGING(log); + requiredCAKeyUsage = 0; + caCertType = 0; + } + + switch (certUsage) { + case certUsageSSLClient: + case certUsageSSLServer: + case certUsageIPsec: + case certUsageSSLCA: + case certUsageSSLServerWithStepUp: + case certUsageEmailSigner: + case certUsageEmailRecipient: + case certUsageObjectSigner: + case certUsageVerifyCA: + case certUsageStatusResponder: + if (CERT_TrustFlagsForCACertUsage(certUsage, &requiredFlags, + &trustType) != SECSuccess) { + PORT_Assert(0); + EXIT_IF_NOT_LOGGING(log); + requiredFlags = 0; + trustType = trustSSL; + } + break; + default: + PORT_Assert(0); + EXIT_IF_NOT_LOGGING(log); + requiredFlags = 0; + trustType = trustSSL; /* This used to be 0, but we need something + * that matches the enumeration type. + */ + caCertType = 0; + } + + /* If the basicConstraint extension is included in an intermmediate CA + * certificate, make sure that the isCA flag is on. If the + * pathLenConstraint component exists, it must be greater than the + * number of CA certificates we have seen so far. If the extension + * is omitted, we will assume that this is a CA certificate with + * an unlimited pathLenConstraint (since it already passes the + * netscape-cert-type extension checking). + */ + + rv = CERT_FindBasicConstraintExten(cert, &basicConstraint); + if (rv != SECSuccess) { + if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) { + LOG_ERROR_OR_EXIT(log, cert, 0, 0); + } + /* no basic constraints found, we aren't (yet) a CA. */ + isca = PR_FALSE; + } else { + if (basicConstraint.isCA == PR_FALSE) { + PORT_SetError(SEC_ERROR_CA_CERT_INVALID); + LOG_ERROR_OR_EXIT(log, cert, 0, 0); + } + + /* can't check path length if we don't know the previous path */ + isca = PR_TRUE; + } + + if (CERT_GetCertTrust(cert, &certTrust) == SECSuccess) { + /* we have some trust info, but this does NOT imply that this + * cert is actually trusted for any purpose. The cert may be + * explicitly UNtrusted. We won't know until we examine the + * trust bits. + */ + if (certUsage == certUsageStatusResponder) { + /* Check the special case of certUsageStatusResponder */ + issuerCert = CERT_FindCertIssuer(cert, t, certUsage); + if (issuerCert) { + if (SEC_CheckCRL(handle, cert, issuerCert, t, wincx) != + SECSuccess) { + PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE); + CERT_DestroyCertificate(issuerCert); + goto loser; + } + CERT_DestroyCertificate(issuerCert); + } + /* XXX We have NOT determined that this cert is trusted. + * For years, NSS has treated this as trusted, + * but it seems incorrect. + */ + rv = rvFinal; + goto done; + } + + /* + * check the trust params of the issuer + */ + flags = SEC_GET_TRUST_FLAGS(&certTrust, trustType); + if ((flags & requiredFlags) == requiredFlags) { + /* we found a trusted one, so return */ + rv = rvFinal; + goto done; + } + if (flags & CERTDB_VALID_CA) { + validCAOverride = PR_TRUE; + } + /* is it explicitly distrusted? */ + if ((flags & CERTDB_TERMINAL_RECORD) && + ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0)) { + /* untrusted -- the cert is explicitly untrusted, not + * just that it doesn't chain to a trusted cert */ + PORT_SetError(SEC_ERROR_UNTRUSTED_CERT); + LOG_ERROR_OR_EXIT(log, cert, 0, flags); + } + } + if (!validCAOverride) { + /* + * Make sure that if this is an intermediate CA in the chain that + * it was given permission by its signer to be a CA. + */ + /* + * if basicConstraints says it is a ca, then we check the + * nsCertType. If the nsCertType has any CA bits set, then + * it must have the right one. + */ + if (!isca || (cert->nsCertType & NS_CERT_TYPE_CA)) { + isca = (cert->nsCertType & caCertType) ? PR_TRUE : PR_FALSE; + } + + if (!isca) { + PORT_SetError(SEC_ERROR_CA_CERT_INVALID); + LOG_ERROR_OR_EXIT(log, cert, 0, 0); + } + + /* make sure key usage allows cert signing */ + if (CERT_CheckKeyUsage(cert, requiredCAKeyUsage) != SECSuccess) { + PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE); + LOG_ERROR_OR_EXIT(log, cert, 0, requiredCAKeyUsage); + } + } + /* make sure that the issuer is not self signed. If it is, then + * stop here to prevent looping. + */ + if (cert->isRoot) { + PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER); + LOG_ERROR(log, cert, 0, 0); + goto loser; + } + + return CERT_VerifyCertChain(handle, cert, checkSig, certUsage, t, + wincx, log); +loser: + rv = SECFailure; +done: + return rv; +} + +#define NEXT_USAGE() \ + { \ + i *= 2; \ + certUsage++; \ + continue; \ + } + +#define VALID_USAGE() \ + { \ + NEXT_USAGE(); \ + } + +#define INVALID_USAGE() \ + { \ + if (returnedUsages) { \ + *returnedUsages &= (~i); \ + } \ + if (PR_TRUE == requiredUsage) { \ + valid = SECFailure; \ + } \ + NEXT_USAGE(); \ + } + +/* + * check the leaf cert against trust and usage. + * returns success if the cert is not distrusted. If the cert is + * trusted, then the trusted bool will be true. + * returns failure if the cert is distrusted. If failure, flags + * will return the flag bits that indicated distrust. + */ +SECStatus +cert_CheckLeafTrust(CERTCertificate *cert, SECCertUsage certUsage, + unsigned int *failedFlags, PRBool *trusted) +{ + unsigned int flags; + CERTCertTrust trust; + + *failedFlags = 0; + *trusted = PR_FALSE; + + /* check trust flags to see if this cert is directly trusted */ + if (CERT_GetCertTrust(cert, &trust) == SECSuccess) { + switch (certUsage) { + case certUsageSSLClient: + case certUsageSSLServer: + case certUsageIPsec: + flags = trust.sslFlags; + + /* is the cert directly trusted or not trusted ? */ + if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is + * authoritative */ + if (flags & CERTDB_TRUSTED) { /* trust this cert */ + *trusted = PR_TRUE; + return SECSuccess; + } else { /* don't trust this cert */ + *failedFlags = flags; + return SECFailure; + } + } + break; + case certUsageSSLServerWithStepUp: + /* XXX - step up certs can't be directly trusted, only distrust */ + flags = trust.sslFlags; + if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is + * authoritative */ + if ((flags & CERTDB_TRUSTED) == 0) { + /* don't trust this cert */ + *failedFlags = flags; + return SECFailure; + } + } + break; + case certUsageSSLCA: + flags = trust.sslFlags; + if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is + * authoritative */ + if ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0) { + /* don't trust this cert */ + *failedFlags = flags; + return SECFailure; + } + } + break; + case certUsageEmailSigner: + case certUsageEmailRecipient: + flags = trust.emailFlags; + if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is + * authoritative */ + if (flags & CERTDB_TRUSTED) { /* trust this cert */ + *trusted = PR_TRUE; + return SECSuccess; + } else { /* don't trust this cert */ + *failedFlags = flags; + return SECFailure; + } + } + + break; + case certUsageObjectSigner: + flags = trust.objectSigningFlags; + + if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is + * authoritative */ + if (flags & CERTDB_TRUSTED) { /* trust this cert */ + *trusted = PR_TRUE; + return SECSuccess; + } else { /* don't trust this cert */ + *failedFlags = flags; + return SECFailure; + } + } + break; + case certUsageVerifyCA: + case certUsageStatusResponder: + flags = trust.sslFlags; + /* is the cert directly trusted or not trusted ? */ + if ((flags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) == + (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) { + *trusted = PR_TRUE; + return SECSuccess; + } + flags = trust.emailFlags; + /* is the cert directly trusted or not trusted ? */ + if ((flags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) == + (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) { + *trusted = PR_TRUE; + return SECSuccess; + } + flags = trust.objectSigningFlags; + /* is the cert directly trusted or not trusted ? */ + if ((flags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) == + (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) { + *trusted = PR_TRUE; + return SECSuccess; + } + /* fall through to test distrust */ + case certUsageAnyCA: + case certUsageUserCertImport: + /* do we distrust these certs explicitly */ + flags = trust.sslFlags; + if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is + * authoritative */ + if ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0) { + *failedFlags = flags; + return SECFailure; + } + } + flags = trust.emailFlags; + if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is + * authoritative */ + if ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0) { + *failedFlags = flags; + return SECFailure; + } + } + /* fall through */ + case certUsageProtectedObjectSigner: + flags = trust.objectSigningFlags; + if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is + * authoritative */ + if ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0) { + *failedFlags = flags; + return SECFailure; + } + } + break; + } + } + return SECSuccess; +} + +/* + * verify a certificate by checking if it's valid and that we + * trust the issuer. + * + * certificateUsage contains a bitfield of all cert usages that are + * required for verification to succeed + * + * a bitfield of cert usages is returned in *returnedUsages + * if requiredUsages is non-zero, the returned bitmap is only + * for those required usages, otherwise it is for all usages + * + */ +SECStatus +CERT_VerifyCertificate(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertificateUsage requiredUsages, PRTime t, + void *wincx, CERTVerifyLog *log, SECCertificateUsage *returnedUsages) +{ + SECStatus rv; + SECStatus valid; + unsigned int requiredKeyUsage; + unsigned int requiredCertType; + unsigned int flags; + unsigned int certType; + PRBool allowOverride; + SECCertTimeValidity validity; + CERTStatusConfig *statusConfig; + PRInt32 i; + SECCertUsage certUsage = 0; + PRBool checkedOCSP = PR_FALSE; + PRBool checkAllUsages = PR_FALSE; + PRBool revoked = PR_FALSE; + PRBool sigerror = PR_FALSE; + PRBool trusted = PR_FALSE; + + if (!requiredUsages) { + /* there are no required usages, so the user probably wants to + get status for all usages */ + checkAllUsages = PR_TRUE; + } + + if (returnedUsages) { + *returnedUsages = 0; + } else { + /* we don't have a place to return status for all usages, + so we can skip checks for usages that aren't required */ + checkAllUsages = PR_FALSE; + } + valid = SECSuccess; /* start off assuming cert is valid */ + + /* make sure that the cert is valid at time t */ + allowOverride = (PRBool)((requiredUsages & certificateUsageSSLServer) || + (requiredUsages & certificateUsageSSLServerWithStepUp) || + (requiredUsages & certificateUsageIPsec)); + validity = CERT_CheckCertValidTimes(cert, t, allowOverride); + if (validity != secCertTimeValid) { + valid = SECFailure; + LOG_ERROR_OR_EXIT(log, cert, 0, validity); + } + + /* check key usage and netscape cert type */ + cert_GetCertType(cert); + certType = cert->nsCertType; + + for (i = 1; i <= certificateUsageHighest && + (SECSuccess == valid || returnedUsages || log);) { + PRBool requiredUsage = (i & requiredUsages) ? PR_TRUE : PR_FALSE; + if (PR_FALSE == requiredUsage && PR_FALSE == checkAllUsages) { + NEXT_USAGE(); + } + if (returnedUsages) { + *returnedUsages |= i; /* start off assuming this usage is valid */ + } + switch (certUsage) { + case certUsageSSLClient: + case certUsageSSLServer: + case certUsageSSLServerWithStepUp: + case certUsageSSLCA: + case certUsageEmailSigner: + case certUsageEmailRecipient: + case certUsageObjectSigner: + case certUsageStatusResponder: + case certUsageIPsec: + rv = CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_FALSE, + &requiredKeyUsage, + &requiredCertType); + if (rv != SECSuccess) { + PORT_Assert(0); + /* EXIT_IF_NOT_LOGGING(log); XXX ??? */ + requiredKeyUsage = 0; + requiredCertType = 0; + INVALID_USAGE(); + } + break; + + case certUsageAnyCA: + case certUsageProtectedObjectSigner: + case certUsageUserCertImport: + case certUsageVerifyCA: + /* these usages cannot be verified */ + NEXT_USAGE(); + + default: + PORT_Assert(0); + requiredKeyUsage = 0; + requiredCertType = 0; + INVALID_USAGE(); + } + if (CERT_CheckKeyUsage(cert, requiredKeyUsage) != SECSuccess) { + if (PR_TRUE == requiredUsage) { + PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE); + } + LOG_ERROR(log, cert, 0, requiredKeyUsage); + INVALID_USAGE(); + } + if (!(certType & requiredCertType)) { + if (PR_TRUE == requiredUsage) { + PORT_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE); + } + LOG_ERROR(log, cert, 0, requiredCertType); + INVALID_USAGE(); + } + + rv = cert_CheckLeafTrust(cert, certUsage, &flags, &trusted); + if (rv == SECFailure) { + if (PR_TRUE == requiredUsage) { + PORT_SetError(SEC_ERROR_UNTRUSTED_CERT); + } + LOG_ERROR(log, cert, 0, flags); + INVALID_USAGE(); + } else if (trusted) { + VALID_USAGE(); + } + + if (PR_TRUE == revoked || PR_TRUE == sigerror) { + INVALID_USAGE(); + } + + rv = cert_VerifyCertChain(handle, cert, + checkSig, &sigerror, + certUsage, t, wincx, log, + &revoked); + + if (rv != SECSuccess) { + /* EXIT_IF_NOT_LOGGING(log); XXX ???? */ + INVALID_USAGE(); + } + + /* + * Check OCSP revocation status, but only if the cert we are checking + * is not a status responder itself. We only do this in the case + * where we checked the cert chain (above); explicit trust "wins" + * (avoids status checking, just as it avoids CRL checking) by + * bypassing this code. + */ + + if (PR_FALSE == checkedOCSP) { + checkedOCSP = PR_TRUE; /* only check OCSP once */ + statusConfig = CERT_GetStatusConfig(handle); + if (requiredUsages != certificateUsageStatusResponder && + statusConfig != NULL) { + if (statusConfig->statusChecker != NULL) { + rv = (*statusConfig->statusChecker)(handle, cert, + t, wincx); + if (rv != SECSuccess) { + LOG_ERROR(log, cert, 0, 0); + revoked = PR_TRUE; + INVALID_USAGE(); + } + } + } + } + + NEXT_USAGE(); + } + +loser: + return (valid); +} + +SECStatus +CERT_VerifyCert(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertUsage certUsage, PRTime t, + void *wincx, CERTVerifyLog *log) +{ + return cert_VerifyCertWithFlags(handle, cert, checkSig, certUsage, t, + CERT_VERIFYCERT_USE_DEFAULTS, wincx, log); +} + +SECStatus +cert_VerifyCertWithFlags(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertUsage certUsage, PRTime t, + PRUint32 flags, void *wincx, CERTVerifyLog *log) +{ + SECStatus rv; + unsigned int requiredKeyUsage; + unsigned int requiredCertType; + unsigned int failedFlags; + unsigned int certType; + PRBool trusted; + PRBool allowOverride; + SECCertTimeValidity validity; + CERTStatusConfig *statusConfig; + +#ifdef notdef + /* check if this cert is in the Evil list */ + rv = CERT_CheckForEvilCert(cert); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE); + LOG_ERROR_OR_EXIT(log, cert, 0, 0); + } +#endif + + /* make sure that the cert is valid at time t */ + allowOverride = (PRBool)((certUsage == certUsageSSLServer) || + (certUsage == certUsageSSLServerWithStepUp) || + (certUsage == certUsageIPsec)); + validity = CERT_CheckCertValidTimes(cert, t, allowOverride); + if (validity != secCertTimeValid) { + LOG_ERROR_OR_EXIT(log, cert, 0, validity); + } + + /* check key usage and netscape cert type */ + cert_GetCertType(cert); + certType = cert->nsCertType; + switch (certUsage) { + case certUsageSSLClient: + case certUsageSSLServer: + case certUsageSSLServerWithStepUp: + case certUsageIPsec: + case certUsageSSLCA: + case certUsageEmailSigner: + case certUsageEmailRecipient: + case certUsageObjectSigner: + case certUsageStatusResponder: + rv = CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_FALSE, + &requiredKeyUsage, + &requiredCertType); + if (rv != SECSuccess) { + PORT_Assert(0); + EXIT_IF_NOT_LOGGING(log); + requiredKeyUsage = 0; + requiredCertType = 0; + } + break; + case certUsageVerifyCA: + case certUsageAnyCA: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_CA; + if (!(certType & NS_CERT_TYPE_CA)) { + certType |= NS_CERT_TYPE_CA; + } + break; + default: + PORT_Assert(0); + EXIT_IF_NOT_LOGGING(log); + requiredKeyUsage = 0; + requiredCertType = 0; + } + if (CERT_CheckKeyUsage(cert, requiredKeyUsage) != SECSuccess) { + PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE); + LOG_ERROR_OR_EXIT(log, cert, 0, requiredKeyUsage); + } + if (!(certType & requiredCertType)) { + PORT_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE); + LOG_ERROR_OR_EXIT(log, cert, 0, requiredCertType); + } + + rv = cert_CheckLeafTrust(cert, certUsage, &failedFlags, &trusted); + if (rv == SECFailure) { + PORT_SetError(SEC_ERROR_UNTRUSTED_CERT); + LOG_ERROR_OR_EXIT(log, cert, 0, failedFlags); + } else if (trusted) { + goto done; + } + + rv = CERT_VerifyCertChain(handle, cert, checkSig, certUsage, + t, wincx, log); + if (rv != SECSuccess) { + EXIT_IF_NOT_LOGGING(log); + } + + /* + * Check revocation status, but only if the cert we are checking is not a + * status responder itself and the caller did not ask us to skip the check. + * We only do this in the case where we checked the cert chain (above); + * explicit trust "wins" (avoids status checking, just as it avoids CRL + * checking, which is all done inside VerifyCertChain) by bypassing this + * code. + */ + if (!(flags & CERT_VERIFYCERT_SKIP_OCSP) && + certUsage != certUsageStatusResponder) { + statusConfig = CERT_GetStatusConfig(handle); + if (statusConfig && statusConfig->statusChecker) { + rv = (*statusConfig->statusChecker)(handle, cert, + t, wincx); + if (rv != SECSuccess) { + LOG_ERROR_OR_EXIT(log, cert, 0, 0); + } + } + } + +done: + if (log && log->head) { + return SECFailure; + } + return (SECSuccess); + +loser: + rv = SECFailure; + + return (rv); +} + +/* + * verify a certificate by checking if its valid and that we + * trust the issuer. Verify time against now. + */ +SECStatus +CERT_VerifyCertificateNow(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertificateUsage requiredUsages, + void *wincx, SECCertificateUsage *returnedUsages) +{ + return (CERT_VerifyCertificate(handle, cert, checkSig, + requiredUsages, PR_Now(), wincx, NULL, returnedUsages)); +} + +/* obsolete, do not use for new code */ +SECStatus +CERT_VerifyCertNow(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool checkSig, SECCertUsage certUsage, void *wincx) +{ + return (CERT_VerifyCert(handle, cert, checkSig, + certUsage, PR_Now(), wincx, NULL)); +} + +/* [ FROM pcertdb.c ] */ +/* + * Supported usage values and types: + * certUsageSSLClient + * certUsageSSLServer + * certUsageSSLServerWithStepUp + * certUsageIPsec + * certUsageEmailSigner + * certUsageEmailRecipient + * certUsageObjectSigner + */ + +CERTCertificate * +CERT_FindMatchingCert(CERTCertDBHandle *handle, SECItem *derName, + CERTCertOwner owner, SECCertUsage usage, + PRBool preferTrusted, PRTime validTime, PRBool validOnly) +{ + CERTCertList *certList = NULL; + CERTCertificate *cert = NULL; + CERTCertTrust certTrust; + unsigned int requiredTrustFlags; + SECTrustType requiredTrustType; + unsigned int flags; + + PRBool lookingForCA = PR_FALSE; + SECStatus rv; + CERTCertListNode *node; + CERTCertificate *saveUntrustedCA = NULL; + + /* if preferTrusted is set, must be a CA cert */ + PORT_Assert(!(preferTrusted && (owner != certOwnerCA))); + + if (owner == certOwnerCA) { + lookingForCA = PR_TRUE; + if (preferTrusted) { + rv = CERT_TrustFlagsForCACertUsage(usage, &requiredTrustFlags, + &requiredTrustType); + if (rv != SECSuccess) { + goto loser; + } + requiredTrustFlags |= CERTDB_VALID_CA; + } + } + + certList = CERT_CreateSubjectCertList(NULL, handle, derName, validTime, + validOnly); + if (certList != NULL) { + rv = CERT_FilterCertListByUsage(certList, usage, lookingForCA); + if (rv != SECSuccess) { + goto loser; + } + + node = CERT_LIST_HEAD(certList); + + while (!CERT_LIST_END(node, certList)) { + cert = node->cert; + + /* looking for a trusted CA cert */ + if ((owner == certOwnerCA) && preferTrusted && + (requiredTrustType != trustTypeNone)) { + + if (CERT_GetCertTrust(cert, &certTrust) != SECSuccess) { + flags = 0; + } else { + flags = SEC_GET_TRUST_FLAGS(&certTrust, requiredTrustType); + } + + if ((flags & requiredTrustFlags) != requiredTrustFlags) { + /* cert is not trusted */ + /* if this is the first cert to get this far, then save + * it, so we can use it if we can't find a trusted one + */ + if (saveUntrustedCA == NULL) { + saveUntrustedCA = cert; + } + goto endloop; + } + } + /* if we got this far, then this cert meets all criteria */ + break; + + endloop: + node = CERT_LIST_NEXT(node); + cert = NULL; + } + + /* use the saved one if we have it */ + if (cert == NULL) { + cert = saveUntrustedCA; + } + + /* if we found one then bump the ref count before freeing the list */ + if (cert != NULL) { + /* bump the ref count */ + cert = CERT_DupCertificate(cert); + } + + CERT_DestroyCertList(certList); + } + + return (cert); + +loser: + if (certList != NULL) { + CERT_DestroyCertList(certList); + } + + return (NULL); +} + +/* [ From certdb.c ] */ +/* + * Filter a list of certificates, removing those certs that do not have + * one of the named CA certs somewhere in their cert chain. + * + * "certList" - the list of certificates to filter + * "nCANames" - number of CA names + * "caNames" - array of CA names in string(rfc 1485) form + * "usage" - what use the certs are for, this is used when + * selecting CA certs + */ +SECStatus +CERT_FilterCertListByCANames(CERTCertList *certList, int nCANames, + char **caNames, SECCertUsage usage) +{ + CERTCertificate *issuerCert = NULL; + CERTCertificate *subjectCert; + CERTCertListNode *node, *freenode; + CERTCertificate *cert; + int n; + char **names; + PRBool found; + PRTime time; + + if (nCANames <= 0) { + return (SECSuccess); + } + + time = PR_Now(); + + node = CERT_LIST_HEAD(certList); + + while (!CERT_LIST_END(node, certList)) { + cert = node->cert; + + subjectCert = CERT_DupCertificate(cert); + + /* traverse the CA certs for this cert */ + found = PR_FALSE; + while (subjectCert != NULL) { + n = nCANames; + names = caNames; + + if (subjectCert->issuerName != NULL) { + while (n > 0) { + if (PORT_Strcmp(*names, subjectCert->issuerName) == 0) { + found = PR_TRUE; + break; + } + + n--; + names++; + } + } + + if (found) { + break; + } + + issuerCert = CERT_FindCertIssuer(subjectCert, time, usage); + if (issuerCert == subjectCert) { + CERT_DestroyCertificate(issuerCert); + issuerCert = NULL; + break; + } + CERT_DestroyCertificate(subjectCert); + subjectCert = issuerCert; + } + CERT_DestroyCertificate(subjectCert); + if (!found) { + /* CA was not found, so remove this cert from the list */ + freenode = node; + node = CERT_LIST_NEXT(node); + CERT_RemoveCertListNode(freenode); + } else { + /* CA was found, so leave it in the list */ + node = CERT_LIST_NEXT(node); + } + } + + return (SECSuccess); +} + +/* + * Given a certificate, return a string containing the nickname, and possibly + * one of the validity strings, based on the current validity state of the + * certificate. + * + * "arena" - arena to allocate returned string from. If NULL, then heap + * is used. + * "cert" - the cert to get nickname from + * "expiredString" - the string to append to the nickname if the cert is + * expired. + * "notYetGoodString" - the string to append to the nickname if the cert is + * not yet good. + */ +char * +CERT_GetCertNicknameWithValidity(PLArenaPool *arena, CERTCertificate *cert, + char *expiredString, char *notYetGoodString) +{ + SECCertTimeValidity validity; + char *nickname = NULL, *tmpstr = NULL; + const char *srcNickname = cert->nickname; + if (!srcNickname) { + srcNickname = "{???}"; + } + + validity = CERT_CheckCertValidTimes(cert, PR_Now(), PR_FALSE); + + /* if the cert is good, then just use the nickname directly */ + if (validity == secCertTimeValid) { + if (arena == NULL) { + nickname = PORT_Strdup(srcNickname); + } else { + nickname = PORT_ArenaStrdup(arena, srcNickname); + } + + if (nickname == NULL) { + goto loser; + } + } else { + + /* if the cert is not valid, then tack one of the strings on the + * end + */ + if (validity == secCertTimeExpired) { + tmpstr = PR_smprintf("%s%s", srcNickname, + expiredString); + } else if (validity == secCertTimeNotValidYet) { + /* not yet valid */ + tmpstr = PR_smprintf("%s%s", srcNickname, + notYetGoodString); + } else { + /* undetermined */ + tmpstr = PR_smprintf("%s", + "(NULL) (Validity Unknown)"); + } + + if (tmpstr == NULL) { + goto loser; + } + + if (arena) { + /* copy the string into the arena and free the malloc'd one */ + nickname = PORT_ArenaStrdup(arena, tmpstr); + PORT_Free(tmpstr); + } else { + nickname = tmpstr; + } + if (nickname == NULL) { + goto loser; + } + } + return (nickname); + +loser: + return (NULL); +} + +/* + * Collect the nicknames from all certs in a CertList. If the cert is not + * valid, append a string to that nickname. + * + * "certList" - the list of certificates + * "expiredString" - the string to append to the nickname of any expired cert + * "notYetGoodString" - the string to append to the nickname of any cert + * that is not yet valid + */ +CERTCertNicknames * +CERT_NicknameStringsFromCertList(CERTCertList *certList, char *expiredString, + char *notYetGoodString) +{ + CERTCertNicknames *names; + PLArenaPool *arena; + CERTCertListNode *node; + char **nn; + + /* allocate an arena */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + return (NULL); + } + + /* allocate the structure */ + names = PORT_ArenaAlloc(arena, sizeof(CERTCertNicknames)); + if (names == NULL) { + goto loser; + } + + /* init the structure */ + names->arena = arena; + names->head = NULL; + names->numnicknames = 0; + names->nicknames = NULL; + names->totallen = 0; + + /* count the certs in the list */ + node = CERT_LIST_HEAD(certList); + while (!CERT_LIST_END(node, certList)) { + names->numnicknames++; + node = CERT_LIST_NEXT(node); + } + + /* allocate nicknames array */ + names->nicknames = PORT_ArenaAlloc(arena, + sizeof(char *) * names->numnicknames); + if (names->nicknames == NULL) { + goto loser; + } + + /* just in case printf can't deal with null strings */ + if (expiredString == NULL) { + expiredString = ""; + } + + if (notYetGoodString == NULL) { + notYetGoodString = ""; + } + + /* traverse the list of certs and collect the nicknames */ + nn = names->nicknames; + node = CERT_LIST_HEAD(certList); + while (!CERT_LIST_END(node, certList)) { + *nn = CERT_GetCertNicknameWithValidity(arena, node->cert, + expiredString, + notYetGoodString); + if (*nn == NULL) { + goto loser; + } + + names->totallen += PORT_Strlen(*nn); + + nn++; + node = CERT_LIST_NEXT(node); + } + + return (names); + +loser: + PORT_FreeArena(arena, PR_FALSE); + return (NULL); +} + +/* + * Extract the nickname from a nickmake string that may have either + * expiredString or notYetGoodString appended. + * + * Args: + * "namestring" - the string containing the nickname, and possibly + * one of the validity label strings + * "expiredString" - the expired validity label string + * "notYetGoodString" - the not yet good validity label string + * + * Returns the raw nickname + */ +char * +CERT_ExtractNicknameString(char *namestring, char *expiredString, + char *notYetGoodString) +{ + int explen, nyglen, namelen; + int retlen; + char *retstr; + + namelen = PORT_Strlen(namestring); + explen = PORT_Strlen(expiredString); + nyglen = PORT_Strlen(notYetGoodString); + + if (namelen > explen) { + if (PORT_Strcmp(expiredString, &namestring[namelen - explen]) == 0) { + retlen = namelen - explen; + retstr = (char *)PORT_Alloc(retlen + 1); + if (retstr == NULL) { + goto loser; + } + + PORT_Memcpy(retstr, namestring, retlen); + retstr[retlen] = '\0'; + goto done; + } + } + + if (namelen > nyglen) { + if (PORT_Strcmp(notYetGoodString, &namestring[namelen - nyglen]) == 0) { + retlen = namelen - nyglen; + retstr = (char *)PORT_Alloc(retlen + 1); + if (retstr == NULL) { + goto loser; + } + + PORT_Memcpy(retstr, namestring, retlen); + retstr[retlen] = '\0'; + goto done; + } + } + + /* if name string is shorter than either invalid string, then it must + * be a raw nickname + */ + retstr = PORT_Strdup(namestring); + +done: + return (retstr); + +loser: + return (NULL); +} + +CERTCertList * +CERT_GetCertChainFromCert(CERTCertificate *cert, PRTime time, SECCertUsage usage) +{ + CERTCertList *chain = NULL; + int count = 0; + + if (NULL == cert) { + return NULL; + } + + cert = CERT_DupCertificate(cert); + if (NULL == cert) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + chain = CERT_NewCertList(); + if (NULL == chain) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return NULL; + } + + while (cert != NULL && ++count <= CERT_MAX_CERT_CHAIN) { + if (SECSuccess != CERT_AddCertToListTail(chain, cert)) { + /* return partial chain */ + PORT_SetError(SEC_ERROR_NO_MEMORY); + return chain; + } + + if (cert->isRoot) { + /* return complete chain */ + return chain; + } + + cert = CERT_FindCertIssuer(cert, time, usage); + } + + /* return partial chain */ + PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER); + return chain; +} diff --git a/security/nss/lib/certhigh/certvfypkix.c b/security/nss/lib/certhigh/certvfypkix.c new file mode 100644 index 0000000000..1f6762b919 --- /dev/null +++ b/security/nss/lib/certhigh/certvfypkix.c @@ -0,0 +1,2302 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* + * nss_pkix_proxy.h + * + * PKIX - NSS proxy functions + * + * NOTE: All structures, functions, data types are parts of library private + * api and are subjects to change in any following releases. + * + */ +#include "prerror.h" +#include "prprf.h" + +#include "nspr.h" +#include "pk11func.h" +#include "certdb.h" +#include "cert.h" +#include "secerr.h" +#include "nssb64.h" +#include "secasn1.h" +#include "secder.h" +#include "pkit.h" + +#ifndef NSS_DISABLE_LIBPKIX +#include "pkix_pl_common.h" + +extern PRLogModuleInfo *pkixLog; + +#ifdef PKIX_OBJECT_LEAK_TEST + +extern PKIX_UInt32 +pkix_pl_lifecycle_ObjectLeakCheck(int *); + +extern SECStatus +pkix_pl_lifecycle_ObjectTableUpdate(int *objCountTable); + +PRInt32 parallelFnInvocationCount; +#endif /* PKIX_OBJECT_LEAK_TEST */ + +static PRBool usePKIXValidationEngine = PR_FALSE; +#endif /* NSS_DISABLE_LIBPKIX */ + +/* + * FUNCTION: CERT_SetUsePKIXForValidation + * DESCRIPTION: + * + * Enables or disables use of libpkix for certificate validation + * + * PARAMETERS: + * "enable" + * PR_TRUE: enables use of libpkix for cert validation. + * PR_FALSE: disables. + * THREAD SAFETY: + * NOT Thread Safe. + * RETURNS: + * Returns SECSuccess if successfully enabled + */ +SECStatus +CERT_SetUsePKIXForValidation(PRBool enable) +{ +#ifdef NSS_DISABLE_LIBPKIX + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return SECFailure; +#else + usePKIXValidationEngine = (enable > 0) ? PR_TRUE : PR_FALSE; + return SECSuccess; +#endif /* NSS_DISABLE_LIBPKIX */ +} + +/* + * FUNCTION: CERT_GetUsePKIXForValidation + * DESCRIPTION: + * + * Checks if libpkix building function should be use for certificate + * chain building. + * + * PARAMETERS: + * NONE + * THREAD SAFETY: + * NOT Thread Safe + * RETURNS: + * Returns PR_TRUE if libpkix should be used. PR_FALSE otherwise. + */ +PRBool +CERT_GetUsePKIXForValidation() +{ +#ifdef NSS_DISABLE_LIBPKIX + return PR_FALSE; +#else + return usePKIXValidationEngine; +#endif /* NSS_DISABLE_LIBPKIX */ +} + +#ifndef NSS_DISABLE_LIBPKIX +#ifdef NOTDEF +/* + * FUNCTION: cert_NssKeyUsagesToPkix + * DESCRIPTION: + * + * Converts nss key usage bit field(PRUint32) to pkix key usage + * bit field. + * + * PARAMETERS: + * "nssKeyUsage" + * Nss key usage bit field. + * "pkixKeyUsage" + * Pkix key usage big field. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +cert_NssKeyUsagesToPkix( + PRUint32 nssKeyUsage, + PKIX_UInt32 *pPkixKeyUsage, + void *plContext) +{ + PKIX_UInt32 pkixKeyUsage = 0; + + PKIX_ENTER(CERTVFYPKIX, "cert_NssKeyUsagesToPkix"); + PKIX_NULLCHECK_ONE(pPkixKeyUsage); + + *pPkixKeyUsage = 0; + + if (nssKeyUsage & KU_DIGITAL_SIGNATURE) { + pkixKeyUsage |= PKIX_DIGITAL_SIGNATURE; + } + + if (nssKeyUsage & KU_NON_REPUDIATION) { + pkixKeyUsage |= PKIX_NON_REPUDIATION; + } + + if (nssKeyUsage & KU_KEY_ENCIPHERMENT) { + pkixKeyUsage |= PKIX_KEY_ENCIPHERMENT; + } + + if (nssKeyUsage & KU_DATA_ENCIPHERMENT) { + pkixKeyUsage |= PKIX_DATA_ENCIPHERMENT; + } + + if (nssKeyUsage & KU_KEY_AGREEMENT) { + pkixKeyUsage |= PKIX_KEY_AGREEMENT; + } + + if (nssKeyUsage & KU_KEY_CERT_SIGN) { + pkixKeyUsage |= PKIX_KEY_CERT_SIGN; + } + + if (nssKeyUsage & KU_CRL_SIGN) { + pkixKeyUsage |= PKIX_CRL_SIGN; + } + + if (nssKeyUsage & KU_ENCIPHER_ONLY) { + pkixKeyUsage |= PKIX_ENCIPHER_ONLY; + } + + /* Not supported. XXX we should support this once it is + * fixed in NSS */ + /* pkixKeyUsage |= PKIX_DECIPHER_ONLY; */ + + *pPkixKeyUsage = pkixKeyUsage; + + PKIX_RETURN(CERTVFYPKIX); +} + +extern SECOidTag ekuOidStrings[]; + +enum { + ekuIndexSSLServer = 0, + ekuIndexSSLClient, + ekuIndexCodeSigner, + ekuIndexEmail, + ekuIndexTimeStamp, + ekuIndexStatusResponder, + ekuIndexUnknown +} ekuIndex; + +typedef struct { + SECCertUsage certUsage; + PRUint32 ekuStringIndex; +} SECCertUsageToEku; + +const SECCertUsageToEku certUsageEkuStringMap[] = { + { certUsageSSLClient, ekuIndexSSLClient }, + { certUsageSSLServer, ekuIndexSSLServer }, + { certUsageSSLCA, ekuIndexSSLServer }, + { certUsageEmailSigner, ekuIndexEmail }, + { certUsageEmailRecipient, ekuIndexEmail }, + { certUsageObjectSigner, ekuIndexCodeSigner }, + { certUsageUserCertImport, ekuIndexUnknown }, + { certUsageVerifyCA, ekuIndexUnknown }, + { certUsageProtectedObjectSigner, ekuIndexUnknown }, + { certUsageStatusResponder, ekuIndexStatusResponder }, + { certUsageAnyCA, ekuIndexUnknown }, +}; + +/* + * FUNCTION: cert_NssCertificateUsageToPkixKUAndEKU + * DESCRIPTION: + * + * Converts nss CERTCertificateUsage bit field to pkix key and + * extended key usages. + * + * PARAMETERS: + * "cert" + * Pointer to CERTCertificate structure of validating cert. + * "requiredCertUsages" + * Required usage that will be converted to pkix eku and ku. + * "requiredKeyUsage", + * Additional key usages impose to cert. + * "isCA", + * it true, convert usages for cert that is a CA cert. + * "ppkixEKUList" + * Returned address of a list of pkix extended key usages. + * "ppkixKU" + * Returned address of pkix required key usages bit field. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Cert Verify Error if the function fails in an unrecoverable way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +cert_NssCertificateUsageToPkixKUAndEKU( + CERTCertificate *cert, + SECCertUsage requiredCertUsage, + PRUint32 requiredKeyUsages, + PRBool isCA, + PKIX_List **ppkixEKUList, + PKIX_UInt32 *ppkixKU, + void *plContext) +{ + PKIX_List *ekuOidsList = NULL; + PKIX_PL_OID *ekuOid = NULL; + int i = 0; + int ekuIndex = ekuIndexUnknown; + + PKIX_ENTER(CERTVFYPKIX, "cert_NssCertificateUsageToPkixEku"); + PKIX_NULLCHECK_TWO(ppkixEKUList, ppkixKU); + + PKIX_CHECK( + PKIX_List_Create(&ekuOidsList, plContext), + PKIX_LISTCREATEFAILED); + + for (; i < PR_ARRAY_SIZE(certUsageEkuStringMap); i++) { + const SECCertUsageToEku *usageToEkuElem = + &certUsageEkuStringMap[i]; + if (usageToEkuElem->certUsage == requiredCertUsage) { + ekuIndex = usageToEkuElem->ekuStringIndex; + break; + } + } + if (ekuIndex != ekuIndexUnknown) { + PRUint32 reqKeyUsage = 0; + PRUint32 reqCertType = 0; + + CERT_KeyUsageAndTypeForCertUsage(requiredCertUsage, isCA, + &reqKeyUsage, + &reqCertType); + + requiredKeyUsages |= reqKeyUsage; + + PKIX_CHECK( + PKIX_PL_OID_Create(ekuOidStrings[ekuIndex], &ekuOid, + plContext), + PKIX_OIDCREATEFAILED); + + PKIX_CHECK( + PKIX_List_AppendItem(ekuOidsList, (PKIX_PL_Object *)ekuOid, + plContext), + PKIX_LISTAPPENDITEMFAILED); + + PKIX_DECREF(ekuOid); + } + + PKIX_CHECK( + cert_NssKeyUsagesToPkix(requiredKeyUsages, ppkixKU, plContext), + PKIX_NSSCERTIFICATEUSAGETOPKIXKUANDEKUFAILED); + + *ppkixEKUList = ekuOidsList; + ekuOidsList = NULL; + +cleanup: + + PKIX_DECREF(ekuOid); + PKIX_DECREF(ekuOidsList); + + PKIX_RETURN(CERTVFYPKIX); +} + +#endif + +/* + * FUNCTION: cert_ProcessingParamsSetKeyAndCertUsage + * DESCRIPTION: + * + * Converts cert usage to pkix KU type and sets + * converted data into PKIX_ProcessingParams object. It also sets + * proper cert usage into nsscontext object. + * + * PARAMETERS: + * "procParams" + * Pointer to PKIX_ProcessingParams used during validation. + * "requiredCertUsage" + * Required certificate usages the certificate and chain is built and + * validated for. + * "requiredKeyUsage" + * Request additional key usages the certificate should be validated for. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Cert Verify Error if the function fails in an unrecoverable way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +cert_ProcessingParamsSetKeyAndCertUsage( + PKIX_ProcessingParams *procParams, + SECCertUsage requiredCertUsage, + PRUint32 requiredKeyUsages, + void *plContext) +{ + PKIX_CertSelector *certSelector = NULL; + PKIX_ComCertSelParams *certSelParams = NULL; + PKIX_PL_NssContext *nssContext = (PKIX_PL_NssContext *)plContext; + + PKIX_ENTER(CERTVFYPKIX, "cert_ProcessingParamsSetKeyAndCertUsage"); + PKIX_NULLCHECK_TWO(procParams, nssContext); + + PKIX_CHECK( + pkix_pl_NssContext_SetCertUsage( + ((SECCertificateUsage)1) << requiredCertUsage, nssContext), + PKIX_NSSCONTEXTSETCERTUSAGEFAILED); + + if (requiredKeyUsages) { + PKIX_CHECK( + PKIX_ProcessingParams_GetTargetCertConstraints(procParams, + &certSelector, plContext), + PKIX_PROCESSINGPARAMSGETTARGETCERTCONSTRAINTSFAILED); + + PKIX_CHECK( + PKIX_CertSelector_GetCommonCertSelectorParams(certSelector, + &certSelParams, plContext), + PKIX_CERTSELECTORGETCOMMONCERTSELECTORPARAMSFAILED); + + PKIX_CHECK( + PKIX_ComCertSelParams_SetKeyUsage(certSelParams, requiredKeyUsages, + plContext), + PKIX_COMCERTSELPARAMSSETKEYUSAGEFAILED); + } +cleanup: + PKIX_DECREF(certSelector); + PKIX_DECREF(certSelParams); + + PKIX_RETURN(CERTVFYPKIX); +} + +/* + * Unused parameters: + * + * CERTCertList *initialChain, + * CERTCertStores certStores, + * CERTCertRevCheckers certRevCheckers, + * CERTCertChainCheckers certChainCheckers, + * SECItem *initPolicies, + * PRBool policyQualifierRejected, + * PRBool anyPolicyInhibited, + * PRBool reqExplicitPolicy, + * PRBool policyMappingInhibited, + * PKIX_CertSelector certConstraints, + */ + +/* + * FUNCTION: cert_CreatePkixProcessingParams + * DESCRIPTION: + * + * Creates and fills in PKIX_ProcessingParams structure to be used + * for certificate chain building. + * + * PARAMETERS: + * "cert" + * Pointer to the CERTCertificate: the leaf certificate of a chain. + * "time" + * Validity time. + * "wincx" + * Nss db password token. + * "useArena" + * Flags to use arena for data allocation during chain building process. + * "pprocParams" + * Address to return created processing parameters. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Cert Verify Error if the function fails in an unrecoverable way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +cert_CreatePkixProcessingParams( + CERTCertificate *cert, + PRBool checkSig, + PRTime time, + void *wincx, + PRBool useArena, + PRBool disableOCSPRemoteFetching, + PKIX_ProcessingParams **pprocParams, + void **pplContext) +{ + PKIX_List *anchors = NULL; + PKIX_PL_Cert *targetCert = NULL; + PKIX_PL_Date *date = NULL; + PKIX_ProcessingParams *procParams = NULL; + PKIX_CertSelector *certSelector = NULL; + PKIX_ComCertSelParams *certSelParams = NULL; + PKIX_CertStore *certStore = NULL; + PKIX_List *certStores = NULL; + PKIX_RevocationChecker *revChecker = NULL; + PKIX_UInt32 methodFlags = 0; + void *plContext = NULL; + CERTStatusConfig *statusConfig = NULL; + + PKIX_ENTER(CERTVFYPKIX, "cert_CreatePkixProcessingParams"); + PKIX_NULLCHECK_TWO(cert, pprocParams); + + PKIX_CHECK( + PKIX_PL_NssContext_Create(0, useArena, wincx, &plContext), + PKIX_NSSCONTEXTCREATEFAILED); + + *pplContext = plContext; + + /* Functions should be implemented in patch for 390532 */ + PKIX_CHECK( + pkix_pl_NssContext_SetCertSignatureCheck(checkSig, + (PKIX_PL_NssContext *)plContext), + PKIX_NSSCONTEXTSETCERTSIGNCHECKFAILED); + + PKIX_CHECK( + PKIX_ProcessingParams_Create(&procParams, plContext), + PKIX_PROCESSINGPARAMSCREATEFAILED); + + PKIX_CHECK( + PKIX_ComCertSelParams_Create(&certSelParams, plContext), + PKIX_COMCERTSELPARAMSCREATEFAILED); + + PKIX_CHECK( + PKIX_PL_Cert_CreateFromCERTCertificate(cert, &targetCert, plContext), + PKIX_CERTCREATEWITHNSSCERTFAILED); + + PKIX_CHECK( + PKIX_ComCertSelParams_SetCertificate(certSelParams, + targetCert, plContext), + PKIX_COMCERTSELPARAMSSETCERTIFICATEFAILED); + + PKIX_CHECK( + PKIX_CertSelector_Create(NULL, NULL, &certSelector, plContext), + PKIX_COULDNOTCREATECERTSELECTOROBJECT); + + PKIX_CHECK( + PKIX_CertSelector_SetCommonCertSelectorParams(certSelector, + certSelParams, plContext), + PKIX_CERTSELECTORSETCOMMONCERTSELECTORPARAMSFAILED); + + PKIX_CHECK( + PKIX_ProcessingParams_SetTargetCertConstraints(procParams, + certSelector, plContext), + PKIX_PROCESSINGPARAMSSETTARGETCERTCONSTRAINTSFAILED); + + /* Turn off quialification of target cert since leaf cert is + * already check for date validity, key usages and extended + * key usages. */ + PKIX_CHECK( + PKIX_ProcessingParams_SetQualifyTargetCert(procParams, PKIX_FALSE, + plContext), + PKIX_PROCESSINGPARAMSSETQUALIFYTARGETCERTFLAGFAILED); + + PKIX_CHECK( + PKIX_PL_Pk11CertStore_Create(&certStore, plContext), + PKIX_PK11CERTSTORECREATEFAILED); + + PKIX_CHECK( + PKIX_List_Create(&certStores, plContext), + PKIX_UNABLETOCREATELIST); + + PKIX_CHECK( + PKIX_List_AppendItem(certStores, (PKIX_PL_Object *)certStore, + plContext), + PKIX_LISTAPPENDITEMFAILED); + + PKIX_CHECK( + PKIX_ProcessingParams_SetCertStores(procParams, certStores, + plContext), + PKIX_PROCESSINGPARAMSADDCERTSTOREFAILED); + + PKIX_CHECK( + PKIX_PL_Date_CreateFromPRTime(time, &date, plContext), + PKIX_DATECREATEFROMPRTIMEFAILED); + + PKIX_CHECK( + PKIX_ProcessingParams_SetDate(procParams, date, plContext), + PKIX_PROCESSINGPARAMSSETDATEFAILED); + + PKIX_CHECK( + PKIX_RevocationChecker_Create( + PKIX_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST | + PKIX_REV_MI_NO_OVERALL_INFO_REQUIREMENT, + PKIX_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST | + PKIX_REV_MI_NO_OVERALL_INFO_REQUIREMENT, + &revChecker, plContext), + PKIX_REVOCATIONCHECKERCREATEFAILED); + + PKIX_CHECK( + PKIX_ProcessingParams_SetRevocationChecker(procParams, revChecker, + plContext), + PKIX_PROCESSINGPARAMSSETREVOCATIONCHECKERFAILED); + + /* CRL method flags */ + methodFlags = + PKIX_REV_M_TEST_USING_THIS_METHOD | + PKIX_REV_M_FORBID_NETWORK_FETCHING | + PKIX_REV_M_SKIP_TEST_ON_MISSING_SOURCE | /* 0 */ + PKIX_REV_M_IGNORE_MISSING_FRESH_INFO | /* 0 */ + PKIX_REV_M_CONTINUE_TESTING_ON_FRESH_INFO; + + /* add CRL revocation method to check the leaf certificate */ + PKIX_CHECK( + PKIX_RevocationChecker_CreateAndAddMethod(revChecker, procParams, + PKIX_RevocationMethod_CRL, methodFlags, + 0, NULL, PKIX_TRUE, plContext), + PKIX_REVOCATIONCHECKERADDMETHODFAILED); + + /* add CRL revocation method for other certs in the chain. */ + PKIX_CHECK( + PKIX_RevocationChecker_CreateAndAddMethod(revChecker, procParams, + PKIX_RevocationMethod_CRL, methodFlags, + 0, NULL, PKIX_FALSE, plContext), + PKIX_REVOCATIONCHECKERADDMETHODFAILED); + + /* For compatibility with the old code, need to check that + * statusConfig is set in the db handle and status checker + * is defined befor allow ocsp status check on the leaf cert.*/ + statusConfig = CERT_GetStatusConfig(CERT_GetDefaultCertDB()); + if (statusConfig != NULL && statusConfig->statusChecker != NULL) { + + /* Enable OCSP revocation checking for the leaf cert. */ + /* OCSP method flags */ + methodFlags = + PKIX_REV_M_TEST_USING_THIS_METHOD | + PKIX_REV_M_ALLOW_NETWORK_FETCHING | /* 0 */ + PKIX_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE | /* 0 */ + PKIX_REV_M_SKIP_TEST_ON_MISSING_SOURCE | /* 0 */ + PKIX_REV_M_IGNORE_MISSING_FRESH_INFO | /* 0 */ + PKIX_REV_M_CONTINUE_TESTING_ON_FRESH_INFO; + + /* Disabling ocsp fetching when checking the status + * of ocsp response signer. Here and in the next if, + * adjust flags for ocsp signer cert validation case. */ + if (disableOCSPRemoteFetching) { + methodFlags |= PKIX_REV_M_FORBID_NETWORK_FETCHING; + } + + if (ocsp_FetchingFailureIsVerificationFailure() && + !disableOCSPRemoteFetching) { + methodFlags |= + PKIX_REV_M_FAIL_ON_MISSING_FRESH_INFO; + } + + /* add OCSP revocation method to check only the leaf certificate.*/ + PKIX_CHECK( + PKIX_RevocationChecker_CreateAndAddMethod(revChecker, procParams, + PKIX_RevocationMethod_OCSP, methodFlags, + 1, NULL, PKIX_TRUE, plContext), + PKIX_REVOCATIONCHECKERADDMETHODFAILED); + } + + PKIX_CHECK( + PKIX_ProcessingParams_SetAnyPolicyInhibited(procParams, PR_FALSE, + plContext), + PKIX_PROCESSINGPARAMSSETANYPOLICYINHIBITED); + + PKIX_CHECK( + PKIX_ProcessingParams_SetExplicitPolicyRequired(procParams, PR_FALSE, + plContext), + PKIX_PROCESSINGPARAMSSETEXPLICITPOLICYREQUIRED); + + PKIX_CHECK( + PKIX_ProcessingParams_SetPolicyMappingInhibited(procParams, PR_FALSE, + plContext), + PKIX_PROCESSINGPARAMSSETPOLICYMAPPINGINHIBITED); + + *pprocParams = procParams; + procParams = NULL; + +cleanup: + PKIX_DECREF(anchors); + PKIX_DECREF(targetCert); + PKIX_DECREF(date); + PKIX_DECREF(certSelector); + PKIX_DECREF(certSelParams); + PKIX_DECREF(certStore); + PKIX_DECREF(certStores); + PKIX_DECREF(procParams); + PKIX_DECREF(revChecker); + + PKIX_RETURN(CERTVFYPKIX); +} + +/* + * FUNCTION: cert_PkixToNssCertsChain + * DESCRIPTION: + * + * Converts pkix cert list into nss cert list. + * + * PARAMETERS: + * "pkixCertChain" + * Pkix certificate list. + * "pvalidChain" + * An address of returned nss certificate list. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Cert Verify Error if the function fails in an unrecoverable way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +cert_PkixToNssCertsChain( + PKIX_List *pkixCertChain, + CERTCertList **pvalidChain, + void *plContext) +{ + PLArenaPool *arena = NULL; + CERTCertificate *nssCert = NULL; + CERTCertList *validChain = NULL; + PKIX_PL_Object *certItem = NULL; + PKIX_UInt32 length = 0; + PKIX_UInt32 i = 0; + + PKIX_ENTER(CERTVFYPKIX, "cert_PkixToNssCertsChain"); + PKIX_NULLCHECK_ONE(pvalidChain); + + if (pkixCertChain == NULL) { + goto cleanup; + } + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + PKIX_ERROR(PKIX_OUTOFMEMORY); + } + validChain = (CERTCertList *)PORT_ArenaZAlloc(arena, sizeof(CERTCertList)); + if (validChain == NULL) { + PKIX_ERROR(PKIX_PORTARENAALLOCFAILED); + } + PR_INIT_CLIST(&validChain->list); + validChain->arena = arena; + arena = NULL; + + PKIX_CHECK( + PKIX_List_GetLength(pkixCertChain, &length, plContext), + PKIX_LISTGETLENGTHFAILED); + + for (i = 0; i < length; i++) { + CERTCertListNode *node = NULL; + + PKIX_CHECK( + PKIX_List_GetItem(pkixCertChain, i, &certItem, plContext), + PKIX_LISTGETITEMFAILED); + + PKIX_CHECK( + PKIX_PL_Cert_GetCERTCertificate((PKIX_PL_Cert *)certItem, &nssCert, + plContext), + PKIX_CERTGETCERTCERTIFICATEFAILED); + + node = + (CERTCertListNode *)PORT_ArenaZAlloc(validChain->arena, + sizeof(CERTCertListNode)); + if (node == NULL) { + PKIX_ERROR(PKIX_PORTARENAALLOCFAILED); + } + + PR_INSERT_BEFORE(&node->links, &validChain->list); + + node->cert = nssCert; + nssCert = NULL; + + PKIX_DECREF(certItem); + } + + *pvalidChain = validChain; + +cleanup: + if (PKIX_ERROR_RECEIVED) { + if (validChain) { + CERT_DestroyCertList(validChain); + } else if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + if (nssCert) { + CERT_DestroyCertificate(nssCert); + } + } + PKIX_DECREF(certItem); + + PKIX_RETURN(CERTVFYPKIX); +} + +/* + * FUNCTION: cert_BuildAndValidateChain + * DESCRIPTION: + * + * The function builds and validates a cert chain based on certificate + * selection criterias from procParams. This function call PKIX_BuildChain + * to accomplish chain building. If PKIX_BuildChain returns with incomplete + * IO, the function waits with PR_Poll until the blocking IO is finished and + * return control back to PKIX_BuildChain. + * + * PARAMETERS: + * "procParams" + * Processing parameters to be used during chain building. + * "pResult" + * Returned build result. + * "pVerifyNode" + * Returned pointed to verify node structure: the tree-like structure + * that reports points of chain building failures. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Cert Verify Error if the function fails in an unrecoverable way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +cert_BuildAndValidateChain( + PKIX_ProcessingParams *procParams, + PKIX_BuildResult **pResult, + PKIX_VerifyNode **pVerifyNode, + void *plContext) +{ + PKIX_BuildResult *result = NULL; + PKIX_VerifyNode *verifyNode = NULL; + void *nbioContext = NULL; + void *state = NULL; + + PKIX_ENTER(CERTVFYPKIX, "cert_BuildAndVerifyChain"); + PKIX_NULLCHECK_TWO(procParams, pResult); + + do { + if (nbioContext && state) { + /* PKIX-XXX: need to test functionality of NBIO handling in libPkix. + * See bug 391180 */ + PRInt32 filesReady = 0; + PRPollDesc *pollDesc = (PRPollDesc *)nbioContext; + filesReady = PR_Poll(pollDesc, 1, PR_INTERVAL_NO_TIMEOUT); + if (filesReady <= 0) { + PKIX_ERROR(PKIX_PRPOLLRETBADFILENUM); + } + } + + PKIX_CHECK( + PKIX_BuildChain(procParams, &nbioContext, &state, + &result, &verifyNode, plContext), + PKIX_UNABLETOBUILDCHAIN); + + } while (nbioContext && state); + + *pResult = result; + +cleanup: + if (pVerifyNode) { + *pVerifyNode = verifyNode; + } + + PKIX_RETURN(CERTVFYPKIX); +} + +/* + * FUNCTION: cert_PkixErrorToNssCode + * DESCRIPTION: + * + * Converts pkix error(PKIX_Error) structure to PR error codes. + * + * PKIX-XXX to be implemented. See 391183. + * + * PARAMETERS: + * "error" + * Pkix error that will be converted. + * "nssCode" + * Corresponding nss error code. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Cert Verify Error if the function fails in an unrecoverable way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +cert_PkixErrorToNssCode( + PKIX_Error *error, + SECErrorCodes *pNssErr, + void *plContext) +{ + int errLevel = 0; + (void)errLevel; /* Suppress unused var warning (Bug 1738028) */ + + PKIX_Int32 nssErr = 0; + PKIX_Error *errPtr = error; + + PKIX_ENTER(CERTVFYPKIX, "cert_PkixErrorToNssCode"); + PKIX_NULLCHECK_TWO(error, pNssErr); + + /* Loop until we find at least one error with non-null + * plErr code, that is going to be nss error code. */ + while (errPtr) { + if (errPtr->plErr && !nssErr) { + nssErr = errPtr->plErr; + if (!pkixLog) + break; + } + if (pkixLog) { +#ifdef PKIX_ERROR_DESCRIPTION + PR_LOG(pkixLog, 2, ("Error at level %d: %s\n", errLevel, PKIX_ErrorText[errPtr->errCode])); +#else + PR_LOG(pkixLog, 2, ("Error at level %d: Error code %d\n", errLevel, errPtr->errCode)); +#endif /* PKIX_ERROR_DESCRIPTION */ + } + errPtr = errPtr->cause; + errLevel += 1; + } + PORT_Assert(nssErr); + if (!nssErr) { + *pNssErr = SEC_ERROR_LIBPKIX_INTERNAL; + } else { + *pNssErr = nssErr; + } + + PKIX_RETURN(CERTVFYPKIX); +} + +/* + * FUNCTION: cert_GetLogFromVerifyNode + * DESCRIPTION: + * + * Recursive function that converts verify node tree-like set of structures + * to CERTVerifyLog. + * + * PARAMETERS: + * "log" + * Pointed to already allocated CERTVerifyLog structure. + * "node" + * A node of PKIX_VerifyNode tree. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Cert Verify Error if the function fails in an unrecoverable way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +cert_GetLogFromVerifyNode( + CERTVerifyLog *log, + PKIX_VerifyNode *node, + void *plContext) +{ + PKIX_List *children = NULL; + PKIX_VerifyNode *childNode = NULL; + + PKIX_ENTER(CERTVFYPKIX, "cert_GetLogFromVerifyNode"); + + children = node->children; + + if (children == NULL) { + PKIX_ERRORCODE errCode = PKIX_ANCHORDIDNOTCHAINTOCERT; + if (node->error && node->error->errCode != errCode) { + if (log != NULL) { + SECErrorCodes nssErrorCode = 0; + CERTCertificate *cert = NULL; + + cert = node->verifyCert->nssCert; + + PKIX_CHECK( + cert_PkixErrorToNssCode(node->error, &nssErrorCode, + plContext), + PKIX_GETPKIXERRORCODEFAILED); + + cert_AddToVerifyLog(log, cert, nssErrorCode, node->depth, NULL); + } + } + PKIX_RETURN(CERTVFYPKIX); + } else { + PRUint32 i = 0; + PKIX_UInt32 length = 0; + + PKIX_CHECK( + PKIX_List_GetLength(children, &length, plContext), + PKIX_LISTGETLENGTHFAILED); + + for (i = 0; i < length; i++) { + + PKIX_CHECK( + PKIX_List_GetItem(children, i, (PKIX_PL_Object **)&childNode, + plContext), + PKIX_LISTGETITEMFAILED); + + PKIX_CHECK( + cert_GetLogFromVerifyNode(log, childNode, plContext), + PKIX_ERRORINRECURSIVEEQUALSCALL); + + PKIX_DECREF(childNode); + } + } + +cleanup: + PKIX_DECREF(childNode); + + PKIX_RETURN(CERTVFYPKIX); +} + +/* + * FUNCTION: cert_GetBuildResults + * DESCRIPTION: + * + * Converts pkix build results to nss results. This function is called + * regardless of build result. + * + * If it called after chain was successfully constructed, then it will + * convert: + * * pkix cert list that represent the chain to nss cert list + * * trusted root the chain was anchored to nss certificate. + * + * In case of failure it will convert: + * * pkix error to PR error code(will set it with PORT_SetError) + * * pkix validation log to nss CERTVerifyLog + * + * PARAMETERS: + * "buildResult" + * Build results returned by PKIX_BuildChain. + * "verifyNode" + * Tree-like structure of chain building/validation failures + * returned by PKIX_BuildChain. Ignored in case of success. + * "error" + * Final error returned by PKIX_BuildChain. Should be NULL in + * case of success. + * "log" + * Address of pre-allocated(if not NULL) CERTVerifyLog structure. + * "ptrustedRoot" + * Address of returned trusted root the chain was anchored to. + * "pvalidChain" + * Address of returned valid chain. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Cert Verify Error if the function fails in an unrecoverable way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +cert_GetBuildResults( + PKIX_BuildResult *buildResult, + PKIX_VerifyNode *verifyNode, + PKIX_Error *error, + CERTVerifyLog *log, + CERTCertificate **ptrustedRoot, + CERTCertList **pvalidChain, + void *plContext) +{ + PKIX_ValidateResult *validResult = NULL; + CERTCertList *validChain = NULL; + CERTCertificate *trustedRoot = NULL; + PKIX_TrustAnchor *trustAnchor = NULL; + PKIX_PL_Cert *trustedCert = NULL; + PKIX_List *pkixCertChain = NULL; + + PKIX_ENTER(CERTVFYPKIX, "cert_GetBuildResults"); + if (buildResult == NULL && error == NULL) { + PKIX_ERROR(PKIX_NULLARGUMENT); + } + + if (error) { + SECErrorCodes nssErrorCode = 0; + if (verifyNode) { + PKIX_Error *tmpError = + cert_GetLogFromVerifyNode(log, verifyNode, plContext); + if (tmpError) { + PKIX_PL_Object_DecRef((PKIX_PL_Object *)tmpError, plContext); + } + } + cert_PkixErrorToNssCode(error, &nssErrorCode, plContext); + PORT_SetError(nssErrorCode); + goto cleanup; + } + + if (pvalidChain) { + PKIX_CHECK( + PKIX_BuildResult_GetCertChain(buildResult, &pkixCertChain, + plContext), + PKIX_BUILDRESULTGETCERTCHAINFAILED); + + PKIX_CHECK( + cert_PkixToNssCertsChain(pkixCertChain, &validChain, plContext), + PKIX_CERTCHAINTONSSCHAINFAILED); + } + + if (ptrustedRoot) { + PKIX_CHECK( + PKIX_BuildResult_GetValidateResult(buildResult, &validResult, + plContext), + PKIX_BUILDRESULTGETVALIDATERESULTFAILED); + + PKIX_CHECK( + PKIX_ValidateResult_GetTrustAnchor(validResult, &trustAnchor, + plContext), + PKIX_VALIDATERESULTGETTRUSTANCHORFAILED); + + PKIX_CHECK( + PKIX_TrustAnchor_GetTrustedCert(trustAnchor, &trustedCert, + plContext), + PKIX_TRUSTANCHORGETTRUSTEDCERTFAILED); + + PKIX_CHECK( + PKIX_PL_Cert_GetCERTCertificate(trustedCert, &trustedRoot, + plContext), + PKIX_CERTGETCERTCERTIFICATEFAILED); + } + + PORT_Assert(!PKIX_ERROR_RECEIVED); + + if (trustedRoot) { + *ptrustedRoot = trustedRoot; + } + if (validChain) { + *pvalidChain = validChain; + } + +cleanup: + if (PKIX_ERROR_RECEIVED) { + if (trustedRoot) { + CERT_DestroyCertificate(trustedRoot); + } + if (validChain) { + CERT_DestroyCertList(validChain); + } + } + PKIX_DECREF(trustAnchor); + PKIX_DECREF(trustedCert); + PKIX_DECREF(pkixCertChain); + PKIX_DECREF(validResult); + PKIX_DECREF(error); + PKIX_DECREF(verifyNode); + PKIX_DECREF(buildResult); + + PKIX_RETURN(CERTVFYPKIX); +} +#endif /* NSS_DISABLE_LIBPKIX */ + +/* + * FUNCTION: cert_VerifyCertChainPkix + * DESCRIPTION: + * + * The main wrapper function that is called from CERT_VerifyCert and + * CERT_VerifyCACertForUsage functions to validate cert with libpkix. + * + * PARAMETERS: + * "cert" + * Leaf certificate of a chain we want to build. + * "checkSig" + * Certificate signatures will not be verified if this + * flag is set to PR_FALSE. + * "requiredUsage" + * Required usage for certificate and chain. + * "time" + * Validity time. + * "wincx" + * Nss database password token. + * "log" + * Address of already allocated CERTVerifyLog structure. Not + * used if NULL; + * "pSigerror" + * Address of PRBool. If not NULL, returns true is cert chain + * was invalidated because of bad certificate signature. + * "pRevoked" + * Address of PRBool. If not NULL, returns true is cert chain + * was invalidated because a revoked certificate was found in + * the chain. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * SECFailure is chain building process has failed. SECSuccess otherwise. + */ +SECStatus +cert_VerifyCertChainPkix( + CERTCertificate *cert, + PRBool checkSig, + SECCertUsage requiredUsage, + PRTime time, + void *wincx, + CERTVerifyLog *log, + PRBool *pSigerror, + PRBool *pRevoked) +{ +#ifdef NSS_DISABLE_LIBPKIX + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return SECFailure; +#else + PKIX_ProcessingParams *procParams = NULL; + PKIX_BuildResult *result = NULL; + PKIX_VerifyNode *verifyNode = NULL; + PKIX_Error *error = NULL; + + SECStatus rv = SECFailure; + void *plContext = NULL; + +#ifdef PKIX_OBJECT_LEAK_TEST + int leakedObjNum = 0; + int memLeakLoopCount = 0; + int objCountTable[PKIX_NUMTYPES]; + int fnInvLocalCount = 0; + PKIX_Boolean savedUsePkixEngFlag = usePKIXValidationEngine; + + if (usePKIXValidationEngine) { + /* current memory leak testing implementation does not allow + * to run simultaneous tests one the same or a different threads. + * Setting the variable to false, to make additional chain + * validations be handled by old nss. */ + usePKIXValidationEngine = PR_FALSE; + } + testStartFnStackPosition = 2; + fnStackNameArr[0] = "cert_VerifyCertChainPkix"; + fnStackInvCountArr[0] = 0; + PKIX_Boolean abortOnLeak = + (PR_GetEnvSecure("PKIX_OBJECT_LEAK_TEST_ABORT_ON_LEAK") == NULL) ? PKIX_FALSE + : PKIX_TRUE; + runningLeakTest = PKIX_TRUE; + + /* Prevent multi-threaded run of object leak test */ + fnInvLocalCount = PR_ATOMIC_INCREMENT(¶llelFnInvocationCount); + PORT_Assert(fnInvLocalCount == 1); + + do { + rv = SECFailure; + plContext = NULL; + procParams = NULL; + result = NULL; + verifyNode = NULL; + error = NULL; + errorGenerated = PKIX_FALSE; + stackPosition = 0; + + if (leakedObjNum) { + pkix_pl_lifecycle_ObjectTableUpdate(objCountTable); + } + memLeakLoopCount += 1; +#endif /* PKIX_OBJECT_LEAK_TEST */ + + error = + cert_CreatePkixProcessingParams(cert, checkSig, time, wincx, + PR_FALSE /*use arena*/, + requiredUsage == certUsageStatusResponder, + &procParams, &plContext); + if (error) { + goto cleanup; + } + + error = + cert_ProcessingParamsSetKeyAndCertUsage(procParams, requiredUsage, 0, + plContext); + if (error) { + goto cleanup; + } + + error = + cert_BuildAndValidateChain(procParams, &result, &verifyNode, plContext); + if (error) { + goto cleanup; + } + + if (pRevoked) { + /* Currently always PR_FALSE. Will be fixed as a part of 394077 */ + *pRevoked = PR_FALSE; + } + if (pSigerror) { + /* Currently always PR_FALSE. Will be fixed as a part of 394077 */ + *pSigerror = PR_FALSE; + } + rv = SECSuccess; + + cleanup: + error = cert_GetBuildResults(result, verifyNode, error, log, NULL, NULL, + plContext); + if (error) { + PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext); + } + if (procParams) { + PKIX_PL_Object_DecRef((PKIX_PL_Object *)procParams, plContext); + } + if (plContext) { + PKIX_PL_NssContext_Destroy(plContext); + } + +#ifdef PKIX_OBJECT_LEAK_TEST + leakedObjNum = + pkix_pl_lifecycle_ObjectLeakCheck(leakedObjNum ? objCountTable : NULL); + + if (pkixLog && leakedObjNum) { + PR_LOG(pkixLog, 1, ("The generated error caused an object leaks. Loop %d." + "Stack %s\n", + memLeakLoopCount, errorFnStackString)); + } + PR_Free(errorFnStackString); + errorFnStackString = NULL; + if (abortOnLeak) { + PORT_Assert(leakedObjNum == 0); + } + + } while (errorGenerated); + + runningLeakTest = PKIX_FALSE; + PR_ATOMIC_DECREMENT(¶llelFnInvocationCount); + usePKIXValidationEngine = savedUsePkixEngFlag; +#endif /* PKIX_OBJECT_LEAK_TEST */ + + return rv; +#endif /* NSS_DISABLE_LIBPKIX */ +} + +#ifndef NSS_DISABLE_LIBPKIX +PKIX_CertSelector * +cert_GetTargetCertConstraints(CERTCertificate *target, void *plContext) +{ + PKIX_ComCertSelParams *certSelParams = NULL; + PKIX_CertSelector *certSelector = NULL; + PKIX_CertSelector *r = NULL; + PKIX_PL_Cert *eeCert = NULL; + PKIX_Error *error = NULL; + + error = PKIX_PL_Cert_CreateFromCERTCertificate(target, &eeCert, plContext); + if (error != NULL) + goto cleanup; + + error = PKIX_CertSelector_Create(NULL, NULL, &certSelector, plContext); + if (error != NULL) + goto cleanup; + + error = PKIX_ComCertSelParams_Create(&certSelParams, plContext); + if (error != NULL) + goto cleanup; + + error = PKIX_ComCertSelParams_SetCertificate( + certSelParams, eeCert, plContext); + if (error != NULL) + goto cleanup; + + error = PKIX_CertSelector_SetCommonCertSelectorParams(certSelector, certSelParams, plContext); + if (error != NULL) + goto cleanup; + + error = PKIX_PL_Object_IncRef((PKIX_PL_Object *)certSelector, plContext); + if (error == NULL) + r = certSelector; + +cleanup: + if (certSelParams != NULL) + PKIX_PL_Object_DecRef((PKIX_PL_Object *)certSelParams, plContext); + + if (eeCert != NULL) + PKIX_PL_Object_DecRef((PKIX_PL_Object *)eeCert, plContext); + + if (certSelector != NULL) + PKIX_PL_Object_DecRef((PKIX_PL_Object *)certSelector, plContext); + + if (error != NULL) { + SECErrorCodes nssErr; + + cert_PkixErrorToNssCode(error, &nssErr, plContext); + PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext); + PORT_SetError(nssErr); + } + + return r; +} + +static PKIX_List * +cert_GetCertStores(void *plContext) +{ + PKIX_CertStore *certStore = NULL; + PKIX_List *certStores = NULL; + PKIX_List *r = NULL; + PKIX_Error *error = NULL; + + error = PKIX_PL_Pk11CertStore_Create(&certStore, plContext); + if (error != NULL) + goto cleanup; + + error = PKIX_List_Create(&certStores, plContext); + if (error != NULL) + goto cleanup; + + error = PKIX_List_AppendItem(certStores, + (PKIX_PL_Object *)certStore, plContext); + if (error != NULL) + goto cleanup; + + error = PKIX_PL_Object_IncRef((PKIX_PL_Object *)certStores, plContext); + if (error == NULL) + r = certStores; + +cleanup: + if (certStores != NULL) + PKIX_PL_Object_DecRef((PKIX_PL_Object *)certStores, plContext); + + if (certStore != NULL) + PKIX_PL_Object_DecRef((PKIX_PL_Object *)certStore, plContext); + + if (error != NULL) { + SECErrorCodes nssErr; + + cert_PkixErrorToNssCode(error, &nssErr, plContext); + PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext); + PORT_SetError(nssErr); + } + + return r; +} + +struct fake_PKIX_PL_CertStruct { + CERTCertificate *nssCert; +}; + +/* This needs to be part of the PKIX_PL_* */ +/* This definitely needs to go away, and be replaced with + a real accessor function in PKIX */ +static CERTCertificate * +cert_NSSCertFromPKIXCert(const PKIX_PL_Cert *pkix_cert) +{ + struct fake_PKIX_PL_CertStruct *fcert = NULL; + + fcert = (struct fake_PKIX_PL_CertStruct *)pkix_cert; + + return CERT_DupCertificate(fcert->nssCert); +} + +PKIX_List * +cert_PKIXMakeOIDList(const SECOidTag *oids, int oidCount, void *plContext) +{ + PKIX_List *r = NULL; + PKIX_List *policyList = NULL; + PKIX_PL_OID *policyOID = NULL; + PKIX_Error *error = NULL; + int i; + + error = PKIX_List_Create(&policyList, plContext); + if (error != NULL) { + goto cleanup; + } + + for (i = 0; i < oidCount; i++) { + error = PKIX_PL_OID_Create(oids[i], &policyOID, plContext); + if (error) { + goto cleanup; + } + error = PKIX_List_AppendItem(policyList, + (PKIX_PL_Object *)policyOID, plContext); + if (error != NULL) { + goto cleanup; + } + PKIX_PL_Object_DecRef((PKIX_PL_Object *)policyOID, plContext); + policyOID = NULL; + } + + error = PKIX_List_SetImmutable(policyList, plContext); + if (error != NULL) + goto cleanup; + + error = PKIX_PL_Object_IncRef((PKIX_PL_Object *)policyList, plContext); + if (error == NULL) + r = policyList; + +cleanup: + if (policyOID != NULL) { + PKIX_PL_Object_DecRef((PKIX_PL_Object *)policyOID, plContext); + } + if (policyList != NULL) { + PKIX_PL_Object_DecRef((PKIX_PL_Object *)policyList, plContext); + } + if (error != NULL) { + PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext); + } + + return r; +} + +CERTValOutParam * +cert_pkix_FindOutputParam(CERTValOutParam *params, const CERTValParamOutType t) +{ + CERTValOutParam *i; + if (params == NULL) { + return NULL; + } + for (i = params; i->type != cert_po_end; i++) { + if (i->type == t) { + return i; + } + } + return NULL; +} + +static PKIX_Error * +setRevocationMethod(PKIX_RevocationChecker *revChecker, + PKIX_ProcessingParams *procParams, + const CERTRevocationTests *revTest, + CERTRevocationMethodIndex certRevMethod, + PKIX_RevocationMethodType pkixRevMethod, + PKIX_Boolean verifyResponderUsages, + PKIX_Boolean isLeafTest, + void *plContext) +{ + PKIX_UInt32 methodFlags = 0; + PKIX_Error *error = NULL; + PKIX_UInt32 priority = 0; + + if (revTest->number_of_defined_methods <= (PRUint32)certRevMethod) { + return NULL; + } + if (revTest->preferred_methods) { + unsigned int i = 0; + for (; i < revTest->number_of_preferred_methods; i++) { + if (revTest->preferred_methods[i] == certRevMethod) + break; + } + priority = i; + } + methodFlags = revTest->cert_rev_flags_per_method[certRevMethod]; + if (verifyResponderUsages && + pkixRevMethod == PKIX_RevocationMethod_OCSP) { + methodFlags |= PKIX_REV_M_FORBID_NETWORK_FETCHING; + } + error = + PKIX_RevocationChecker_CreateAndAddMethod(revChecker, procParams, + pkixRevMethod, methodFlags, + priority, NULL, + isLeafTest, plContext); + return error; +} + +SECStatus +cert_pkixSetParam(PKIX_ProcessingParams *procParams, + const CERTValInParam *param, void *plContext) +{ + PKIX_Error *error = NULL; + SECStatus r = SECSuccess; + PKIX_PL_Date *date = NULL; + PKIX_List *policyOIDList = NULL; + PKIX_List *certListPkix = NULL; + const CERTRevocationFlags *flags; + SECErrorCodes errCode = SEC_ERROR_INVALID_ARGS; + const CERTCertList *certList = NULL; + CERTCertListNode *node; + PKIX_PL_Cert *certPkix = NULL; + PKIX_TrustAnchor *trustAnchor = NULL; + PKIX_RevocationChecker *revChecker = NULL; + PKIX_PL_NssContext *nssContext = (PKIX_PL_NssContext *)plContext; + + /* XXX we need a way to map generic PKIX error to generic NSS errors */ + + switch (param->type) { + + case cert_pi_policyOID: + + /* needed? */ + error = PKIX_ProcessingParams_SetExplicitPolicyRequired( + procParams, PKIX_TRUE, plContext); + + if (error != NULL) { + break; + } + + policyOIDList = cert_PKIXMakeOIDList(param->value.array.oids, + param->value.arraySize, plContext); + if (policyOIDList == NULL) { + r = SECFailure; + PORT_SetError(SEC_ERROR_INVALID_ARGS); + break; + } + + error = PKIX_ProcessingParams_SetInitialPolicies( + procParams, policyOIDList, plContext); + break; + + case cert_pi_date: + if (param->value.scalar.time == 0) { + error = PKIX_PL_Date_Create_UTCTime(NULL, &date, plContext); + if (error != NULL) { + errCode = SEC_ERROR_INVALID_TIME; + break; + } + } else { + error = pkix_pl_Date_CreateFromPRTime(param->value.scalar.time, + &date, plContext); + if (error != NULL) { + errCode = SEC_ERROR_INVALID_TIME; + break; + } + } + + error = PKIX_ProcessingParams_SetDate(procParams, date, plContext); + if (error != NULL) { + errCode = SEC_ERROR_INVALID_TIME; + } + break; + + case cert_pi_revocationFlags: { + PKIX_UInt32 leafIMFlags = 0; + PKIX_UInt32 chainIMFlags = 0; + PKIX_Boolean validatingResponderCert = PKIX_FALSE; + + flags = param->value.pointer.revocation; + if (!flags) { + PORT_SetError(errCode); + r = SECFailure; + break; + } + + leafIMFlags = + flags->leafTests.cert_rev_method_independent_flags; + chainIMFlags = + flags->chainTests.cert_rev_method_independent_flags; + + error = + PKIX_RevocationChecker_Create(leafIMFlags, chainIMFlags, + &revChecker, plContext); + if (error) { + break; + } + + error = + PKIX_ProcessingParams_SetRevocationChecker(procParams, + revChecker, plContext); + if (error) { + break; + } + + if (((PKIX_PL_NssContext *)plContext)->certificateUsage & + certificateUsageStatusResponder) { + validatingResponderCert = PKIX_TRUE; + } + + error = setRevocationMethod(revChecker, + procParams, &flags->leafTests, + cert_revocation_method_crl, + PKIX_RevocationMethod_CRL, + validatingResponderCert, + PKIX_TRUE, plContext); + if (error) { + break; + } + + error = setRevocationMethod(revChecker, + procParams, &flags->leafTests, + cert_revocation_method_ocsp, + PKIX_RevocationMethod_OCSP, + validatingResponderCert, + PKIX_TRUE, plContext); + if (error) { + break; + } + + error = setRevocationMethod(revChecker, + procParams, &flags->chainTests, + cert_revocation_method_crl, + PKIX_RevocationMethod_CRL, + validatingResponderCert, + PKIX_FALSE, plContext); + if (error) { + break; + } + + error = setRevocationMethod(revChecker, + procParams, &flags->chainTests, + cert_revocation_method_ocsp, + PKIX_RevocationMethod_OCSP, + validatingResponderCert, + PKIX_FALSE, plContext); + if (error) { + break; + } + + } break; + + case cert_pi_trustAnchors: + certList = param->value.pointer.chain; + if (!certList) { + PORT_SetError(errCode); + r = SECFailure; + break; + } + error = PKIX_List_Create(&certListPkix, plContext); + if (error != NULL) { + break; + } + for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList); + node = CERT_LIST_NEXT(node)) { + error = PKIX_PL_Cert_CreateFromCERTCertificate(node->cert, + &certPkix, plContext); + if (error) { + break; + } + error = PKIX_TrustAnchor_CreateWithCert(certPkix, &trustAnchor, + plContext); + if (error) { + break; + } + error = PKIX_List_AppendItem(certListPkix, + (PKIX_PL_Object *)trustAnchor, plContext); + if (error) { + break; + } + PKIX_PL_Object_DecRef((PKIX_PL_Object *)trustAnchor, plContext); + trustAnchor = NULL; + PKIX_PL_Object_DecRef((PKIX_PL_Object *)certPkix, plContext); + certPkix = NULL; + } + error = + PKIX_ProcessingParams_SetTrustAnchors(procParams, certListPkix, + plContext); + break; + + case cert_pi_useAIACertFetch: + error = + PKIX_ProcessingParams_SetUseAIAForCertFetching(procParams, + (PRBool)(param->value.scalar.b != + 0), + plContext); + break; + + case cert_pi_chainVerifyCallback: { + const CERTChainVerifyCallback *chainVerifyCallback = + param->value.pointer.chainVerifyCallback; + if (!chainVerifyCallback || !chainVerifyCallback->isChainValid) { + PORT_SetError(errCode); + r = SECFailure; + break; + } + + nssContext->chainVerifyCallback = *chainVerifyCallback; + } break; + + case cert_pi_useOnlyTrustAnchors: + error = + PKIX_ProcessingParams_SetUseOnlyTrustAnchors(procParams, + (PRBool)(param->value.scalar.b != + 0), + plContext); + break; + + default: + PORT_SetError(errCode); + r = SECFailure; + break; + } + + if (policyOIDList != NULL) + PKIX_PL_Object_DecRef((PKIX_PL_Object *)policyOIDList, plContext); + + if (date != NULL) + PKIX_PL_Object_DecRef((PKIX_PL_Object *)date, plContext); + + if (revChecker != NULL) + PKIX_PL_Object_DecRef((PKIX_PL_Object *)revChecker, plContext); + + if (certListPkix) + PKIX_PL_Object_DecRef((PKIX_PL_Object *)certListPkix, plContext); + + if (trustAnchor) + PKIX_PL_Object_DecRef((PKIX_PL_Object *)trustAnchor, plContext); + + if (certPkix) + PKIX_PL_Object_DecRef((PKIX_PL_Object *)certPkix, plContext); + + if (error != NULL) { + PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext); + PORT_SetError(errCode); + r = SECFailure; + } + + return r; +} + +void +cert_pkixDestroyValOutParam(CERTValOutParam *params) +{ + CERTValOutParam *i; + + if (params == NULL) { + return; + } + for (i = params; i->type != cert_po_end; i++) { + switch (i->type) { + case cert_po_trustAnchor: + if (i->value.pointer.cert) { + CERT_DestroyCertificate(i->value.pointer.cert); + i->value.pointer.cert = NULL; + } + break; + + case cert_po_certList: + if (i->value.pointer.chain) { + CERT_DestroyCertList(i->value.pointer.chain); + i->value.pointer.chain = NULL; + } + break; + + default: + break; + } + } +} + +static PRUint64 certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy_LeafFlags[2] = { + /* crl */ + CERT_REV_M_TEST_USING_THIS_METHOD | + CERT_REV_M_FORBID_NETWORK_FETCHING | + CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO, + /* ocsp */ + CERT_REV_M_TEST_USING_THIS_METHOD +}; + +static PRUint64 certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy_ChainFlags[2] = { + /* crl */ + CERT_REV_M_TEST_USING_THIS_METHOD | + CERT_REV_M_FORBID_NETWORK_FETCHING | + CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO, + /* ocsp */ + 0 +}; + +static CERTRevocationMethodIndex + certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy_Method_Preference = { + cert_revocation_method_crl + }; + +static const CERTRevocationFlags certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy = { + { /* leafTests */ + 2, + certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy_LeafFlags, + 1, + &certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy_Method_Preference, + 0 }, + { /* chainTests */ + 2, + certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy_ChainFlags, + 0, + 0, + 0 } +}; +#endif /* NSS_DISABLE_LIBPKIX */ + +extern const CERTRevocationFlags * +CERT_GetClassicOCSPEnabledSoftFailurePolicy() +{ +#ifdef NSS_DISABLE_LIBPKIX + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return NULL; +#else + return &certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy; +#endif /* NSS_DISABLE_LIBPKIX */ +} + +#ifndef NSS_DISABLE_LIBPKIX +static PRUint64 certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy_LeafFlags[2] = { + /* crl */ + CERT_REV_M_TEST_USING_THIS_METHOD | + CERT_REV_M_FORBID_NETWORK_FETCHING | + CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO, + /* ocsp */ + CERT_REV_M_TEST_USING_THIS_METHOD | + CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO +}; + +static PRUint64 certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy_ChainFlags[2] = { + /* crl */ + CERT_REV_M_TEST_USING_THIS_METHOD | + CERT_REV_M_FORBID_NETWORK_FETCHING | + CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO, + /* ocsp */ + 0 +}; + +static CERTRevocationMethodIndex + certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy_Method_Preference = { + cert_revocation_method_crl + }; + +static const CERTRevocationFlags certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy = { + { /* leafTests */ + 2, + certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy_LeafFlags, + 1, + &certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy_Method_Preference, + 0 }, + { /* chainTests */ + 2, + certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy_ChainFlags, + 0, + 0, + 0 } +}; +#endif /* NSS_DISABLE_LIBPKIX */ + +extern const CERTRevocationFlags * +CERT_GetClassicOCSPEnabledHardFailurePolicy() +{ +#ifdef NSS_DISABLE_LIBPKIX + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return NULL; +#else + return &certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy; +#endif /* NSS_DISABLE_LIBPKIX */ +} + +#ifndef NSS_DISABLE_LIBPKIX +static PRUint64 certRev_NSS_3_11_Ocsp_Disabled_Policy_LeafFlags[2] = { + /* crl */ + CERT_REV_M_TEST_USING_THIS_METHOD | + CERT_REV_M_FORBID_NETWORK_FETCHING | + CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO, + /* ocsp */ + 0 +}; + +static PRUint64 certRev_NSS_3_11_Ocsp_Disabled_Policy_ChainFlags[2] = { + /* crl */ + CERT_REV_M_TEST_USING_THIS_METHOD | + CERT_REV_M_FORBID_NETWORK_FETCHING | + CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO, + /* ocsp */ + 0 +}; + +static const CERTRevocationFlags certRev_NSS_3_11_Ocsp_Disabled_Policy = { + { /* leafTests */ + 2, + certRev_NSS_3_11_Ocsp_Disabled_Policy_LeafFlags, + 0, + 0, + 0 }, + { /* chainTests */ + 2, + certRev_NSS_3_11_Ocsp_Disabled_Policy_ChainFlags, + 0, + 0, + 0 } +}; +#endif /* NSS_DISABLE_LIBPKIX */ + +extern const CERTRevocationFlags * +CERT_GetClassicOCSPDisabledPolicy() +{ +#ifdef NSS_DISABLE_LIBPKIX + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return NULL; +#else + return &certRev_NSS_3_11_Ocsp_Disabled_Policy; +#endif /* NSS_DISABLE_LIBPKIX */ +} + +#ifndef NSS_DISABLE_LIBPKIX +static PRUint64 certRev_PKIX_Verify_Nist_Policy_LeafFlags[2] = { + /* crl */ + CERT_REV_M_TEST_USING_THIS_METHOD | + CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO | + CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE, + /* ocsp */ + 0 +}; + +static PRUint64 certRev_PKIX_Verify_Nist_Policy_ChainFlags[2] = { + /* crl */ + CERT_REV_M_TEST_USING_THIS_METHOD | + CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO | + CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE, + /* ocsp */ + 0 +}; + +static const CERTRevocationFlags certRev_PKIX_Verify_Nist_Policy = { + { /* leafTests */ + 2, + certRev_PKIX_Verify_Nist_Policy_LeafFlags, + 0, + 0, + 0 }, + { /* chainTests */ + 2, + certRev_PKIX_Verify_Nist_Policy_ChainFlags, + 0, + 0, + 0 } +}; +#endif /* NSS_DISABLE_LIBPKIX */ + +extern const CERTRevocationFlags * +CERT_GetPKIXVerifyNistRevocationPolicy() +{ +#ifdef NSS_DISABLE_LIBPKIX + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return NULL; +#else + return &certRev_PKIX_Verify_Nist_Policy; +#endif /* NSS_DISABLE_LIBPKIX */ +} + +CERTRevocationFlags * +CERT_AllocCERTRevocationFlags( + PRUint32 number_leaf_methods, PRUint32 number_leaf_pref_methods, + PRUint32 number_chain_methods, PRUint32 number_chain_pref_methods) +{ +#ifdef NSS_DISABLE_LIBPKIX + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return NULL; +#else + CERTRevocationFlags *flags; + + flags = PORT_New(CERTRevocationFlags); + if (!flags) + return (NULL); + + flags->leafTests.number_of_defined_methods = number_leaf_methods; + flags->leafTests.cert_rev_flags_per_method = + PORT_NewArray(PRUint64, number_leaf_methods); + + flags->leafTests.number_of_preferred_methods = number_leaf_pref_methods; + flags->leafTests.preferred_methods = + PORT_NewArray(CERTRevocationMethodIndex, number_leaf_pref_methods); + + flags->chainTests.number_of_defined_methods = number_chain_methods; + flags->chainTests.cert_rev_flags_per_method = + PORT_NewArray(PRUint64, number_chain_methods); + + flags->chainTests.number_of_preferred_methods = number_chain_pref_methods; + flags->chainTests.preferred_methods = + PORT_NewArray(CERTRevocationMethodIndex, number_chain_pref_methods); + + if (!flags->leafTests.cert_rev_flags_per_method || + !flags->leafTests.preferred_methods || + !flags->chainTests.cert_rev_flags_per_method || + !flags->chainTests.preferred_methods) { + CERT_DestroyCERTRevocationFlags(flags); + return (NULL); + } + + return flags; +#endif /* NSS_DISABLE_LIBPKIX */ +} + +void +CERT_DestroyCERTRevocationFlags(CERTRevocationFlags *flags) +{ +#ifdef NSS_DISABLE_LIBPKIX + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return; +#else + if (!flags) + return; + + if (flags->leafTests.cert_rev_flags_per_method) + PORT_Free(flags->leafTests.cert_rev_flags_per_method); + + if (flags->leafTests.preferred_methods) + PORT_Free(flags->leafTests.preferred_methods); + + if (flags->chainTests.cert_rev_flags_per_method) + PORT_Free(flags->chainTests.cert_rev_flags_per_method); + + if (flags->chainTests.preferred_methods) + PORT_Free(flags->chainTests.preferred_methods); + + PORT_Free(flags); +#endif /* NSS_DISABLE_LIBPKIX */ +} + +/* + * CERT_PKIXVerifyCert + * + * Verify a Certificate using the PKIX library. + * + * Parameters: + * cert - the target certificate to verify. Must be non-null + * params - an array of type/value parameters which can be + * used to modify the behavior of the validation + * algorithm, or supply additional constraints. + * + * outputTrustAnchor - the trust anchor which the certificate + * chains to. The caller is responsible + * for freeing this. + * + * Example Usage: + * CERTValParam args[3]; + * args[0].type = cvpt_policyOID; + * args[0].value.si = oid; + * args[1].type = revCheckRequired; + * args[1].value.b = PR_TRUE; + * args[2].type = cvpt_end; + * + * CERT_PKIXVerifyCert(cert, &output, args + */ +SECStatus +CERT_PKIXVerifyCert( + CERTCertificate *cert, + SECCertificateUsage usages, + CERTValInParam *paramsIn, + CERTValOutParam *paramsOut, + void *wincx) +{ +#ifdef NSS_DISABLE_LIBPKIX + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return SECFailure; +#else + SECStatus r = SECFailure; + PKIX_Error *error = NULL; + PKIX_ProcessingParams *procParams = NULL; + PKIX_BuildResult *buildResult = NULL; + void *nbioContext = NULL; /* for non-blocking IO */ + void *buildState = NULL; /* for non-blocking IO */ + PKIX_CertSelector *certSelector = NULL; + PKIX_List *certStores = NULL; + PKIX_ValidateResult *valResult = NULL; + PKIX_VerifyNode *verifyNode = NULL; + PKIX_TrustAnchor *trustAnchor = NULL; + PKIX_PL_Cert *trustAnchorCert = NULL; + PKIX_List *builtCertList = NULL; + CERTValOutParam *oparam = NULL; + int i = 0; + + void *plContext = NULL; + +#ifdef PKIX_OBJECT_LEAK_TEST + int leakedObjNum = 0; + int memLeakLoopCount = 0; + int objCountTable[PKIX_NUMTYPES]; + int fnInvLocalCount = 0; + PKIX_Boolean savedUsePkixEngFlag = usePKIXValidationEngine; + + if (usePKIXValidationEngine) { + /* current memory leak testing implementation does not allow + * to run simultaneous tests one the same or a different threads. + * Setting the variable to false, to make additional chain + * validations be handled by old nss. */ + usePKIXValidationEngine = PR_FALSE; + } + testStartFnStackPosition = 1; + fnStackNameArr[0] = "CERT_PKIXVerifyCert"; + fnStackInvCountArr[0] = 0; + PKIX_Boolean abortOnLeak = + (PR_GetEnvSecure("PKIX_OBJECT_LEAK_TEST_ABORT_ON_LEAK") == NULL) ? PKIX_FALSE + : PKIX_TRUE; + runningLeakTest = PKIX_TRUE; + + /* Prevent multi-threaded run of object leak test */ + fnInvLocalCount = PR_ATOMIC_INCREMENT(¶llelFnInvocationCount); + PORT_Assert(fnInvLocalCount == 1); + + do { + r = SECFailure; + error = NULL; + procParams = NULL; + buildResult = NULL; + nbioContext = NULL; /* for non-blocking IO */ + buildState = NULL; /* for non-blocking IO */ + certSelector = NULL; + certStores = NULL; + valResult = NULL; + verifyNode = NULL; + trustAnchor = NULL; + trustAnchorCert = NULL; + builtCertList = NULL; + oparam = NULL; + i = 0; + errorGenerated = PKIX_FALSE; + stackPosition = 0; + + if (leakedObjNum) { + pkix_pl_lifecycle_ObjectTableUpdate(objCountTable); + } + memLeakLoopCount += 1; +#endif /* PKIX_OBJECT_LEAK_TEST */ + + error = PKIX_PL_NssContext_Create( + 0, PR_FALSE /*use arena*/, wincx, &plContext); + if (error != NULL) { /* need pkix->nss error map */ + PORT_SetError(SEC_ERROR_CERT_NOT_VALID); + goto cleanup; + } + + error = pkix_pl_NssContext_SetCertUsage(usages, plContext); + if (error != NULL) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto cleanup; + } + + error = PKIX_ProcessingParams_Create(&procParams, plContext); + if (error != NULL) { /* need pkix->nss error map */ + PORT_SetError(SEC_ERROR_CERT_NOT_VALID); + goto cleanup; + } + + /* local cert store should be set into procParams before + * filling in revocation settings. */ + certStores = cert_GetCertStores(plContext); + if (certStores == NULL) { + goto cleanup; + } + error = PKIX_ProcessingParams_SetCertStores(procParams, certStores, plContext); + if (error != NULL) { + goto cleanup; + } + + /* now process the extensible input parameters structure */ + if (paramsIn != NULL) { + i = 0; + while (paramsIn[i].type != cert_pi_end) { + if (paramsIn[i].type >= cert_pi_max) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto cleanup; + } + if (cert_pkixSetParam(procParams, + ¶msIn[i], plContext) != + SECSuccess) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto cleanup; + } + i++; + } + } + + certSelector = cert_GetTargetCertConstraints(cert, plContext); + if (certSelector == NULL) { + goto cleanup; + } + error = PKIX_ProcessingParams_SetTargetCertConstraints(procParams, certSelector, plContext); + if (error != NULL) { + goto cleanup; + } + + error = PKIX_BuildChain(procParams, &nbioContext, + &buildState, &buildResult, &verifyNode, + plContext); + if (error != NULL) { + goto cleanup; + } + + error = PKIX_BuildResult_GetValidateResult(buildResult, &valResult, + plContext); + if (error != NULL) { + goto cleanup; + } + + error = PKIX_ValidateResult_GetTrustAnchor(valResult, &trustAnchor, + plContext); + if (error != NULL) { + goto cleanup; + } + + if (trustAnchor != NULL) { + error = PKIX_TrustAnchor_GetTrustedCert(trustAnchor, &trustAnchorCert, + plContext); + if (error != NULL) { + goto cleanup; + } + } + +#ifdef PKIX_OBJECT_LEAK_TEST + /* Can not continue if error was generated but not returned. + * Jumping to cleanup. */ + if (errorGenerated) + goto cleanup; +#endif /* PKIX_OBJECT_LEAK_TEST */ + + oparam = cert_pkix_FindOutputParam(paramsOut, cert_po_trustAnchor); + if (oparam != NULL) { + if (trustAnchorCert != NULL) { + oparam->value.pointer.cert = + cert_NSSCertFromPKIXCert(trustAnchorCert); + } else { + oparam->value.pointer.cert = NULL; + } + } + + error = PKIX_BuildResult_GetCertChain(buildResult, &builtCertList, + plContext); + if (error != NULL) { + goto cleanup; + } + + oparam = cert_pkix_FindOutputParam(paramsOut, cert_po_certList); + if (oparam != NULL) { + error = cert_PkixToNssCertsChain(builtCertList, + &oparam->value.pointer.chain, + plContext); + if (error) + goto cleanup; + } + + r = SECSuccess; + + cleanup: + if (verifyNode) { + /* Return validation log only upon error. */ + oparam = cert_pkix_FindOutputParam(paramsOut, cert_po_errorLog); +#ifdef PKIX_OBJECT_LEAK_TEST + if (!errorGenerated) +#endif /* PKIX_OBJECT_LEAK_TEST */ + if (r && oparam != NULL) { + PKIX_Error *tmpError = + cert_GetLogFromVerifyNode(oparam->value.pointer.log, + verifyNode, plContext); + if (tmpError) { + PKIX_PL_Object_DecRef((PKIX_PL_Object *)tmpError, plContext); + } + } + PKIX_PL_Object_DecRef((PKIX_PL_Object *)verifyNode, plContext); + } + + if (procParams != NULL) + PKIX_PL_Object_DecRef((PKIX_PL_Object *)procParams, plContext); + + if (trustAnchorCert != NULL) + PKIX_PL_Object_DecRef((PKIX_PL_Object *)trustAnchorCert, plContext); + + if (trustAnchor != NULL) + PKIX_PL_Object_DecRef((PKIX_PL_Object *)trustAnchor, plContext); + + if (valResult != NULL) + PKIX_PL_Object_DecRef((PKIX_PL_Object *)valResult, plContext); + + if (buildResult != NULL) + PKIX_PL_Object_DecRef((PKIX_PL_Object *)buildResult, plContext); + + if (certStores != NULL) + PKIX_PL_Object_DecRef((PKIX_PL_Object *)certStores, plContext); + + if (certSelector != NULL) + PKIX_PL_Object_DecRef((PKIX_PL_Object *)certSelector, plContext); + + if (builtCertList != NULL) + PKIX_PL_Object_DecRef((PKIX_PL_Object *)builtCertList, plContext); + + if (error != NULL) { + SECErrorCodes nssErrorCode = 0; + + cert_PkixErrorToNssCode(error, &nssErrorCode, plContext); + cert_pkixDestroyValOutParam(paramsOut); + PORT_SetError(nssErrorCode); + PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext); + } + + PKIX_PL_NssContext_Destroy(plContext); + +#ifdef PKIX_OBJECT_LEAK_TEST + leakedObjNum = + pkix_pl_lifecycle_ObjectLeakCheck(leakedObjNum ? objCountTable : NULL); + + if (pkixLog && leakedObjNum) { + PR_LOG(pkixLog, 1, ("The generated error caused an object leaks. Loop %d." + "Stack %s\n", + memLeakLoopCount, errorFnStackString)); + } + PR_Free(errorFnStackString); + errorFnStackString = NULL; + if (abortOnLeak) { + PORT_Assert(leakedObjNum == 0); + } + + } while (errorGenerated); + + runningLeakTest = PKIX_FALSE; + PR_ATOMIC_DECREMENT(¶llelFnInvocationCount); + usePKIXValidationEngine = savedUsePkixEngFlag; +#endif /* PKIX_OBJECT_LEAK_TEST */ + + return r; +#endif /* NSS_DISABLE_LIBPKIX */ +} diff --git a/security/nss/lib/certhigh/crlv2.c b/security/nss/lib/certhigh/crlv2.c new file mode 100644 index 0000000000..d58d4e083b --- /dev/null +++ b/security/nss/lib/certhigh/crlv2.c @@ -0,0 +1,160 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Code for dealing with x.509 v3 crl and crl entries extensions. + */ + +#include "cert.h" +#include "secitem.h" +#include "secoid.h" +#include "secoidt.h" +#include "secder.h" +#include "secasn1.h" +#include "certxutl.h" + +SECStatus +CERT_FindCRLExtensionByOID(CERTCrl *crl, SECItem *oid, SECItem *value) +{ + return (cert_FindExtensionByOID(crl->extensions, oid, value)); +} + +SECStatus +CERT_FindCRLExtension(CERTCrl *crl, int tag, SECItem *value) +{ + return (cert_FindExtension(crl->extensions, tag, value)); +} + +/* Callback to set extensions and adjust verison */ +static void +SetCrlExts(void *object, CERTCertExtension **exts) +{ + CERTCrl *crl = (CERTCrl *)object; + + crl->extensions = exts; + DER_SetUInteger(crl->arena, &crl->version, SEC_CRL_VERSION_2); +} + +void * +CERT_StartCRLExtensions(CERTCrl *crl) +{ + return (cert_StartExtensions((void *)crl, crl->arena, SetCrlExts)); +} + +static void +SetCrlEntryExts(void *object, CERTCertExtension **exts) +{ + CERTCrlEntry *crlEntry = (CERTCrlEntry *)object; + + crlEntry->extensions = exts; +} + +void * +CERT_StartCRLEntryExtensions(CERTCrl *crl, CERTCrlEntry *entry) +{ + return (cert_StartExtensions(entry, crl->arena, SetCrlEntryExts)); +} + +SECStatus +CERT_FindCRLNumberExten(PLArenaPool *arena, CERTCrl *crl, + SECItem *value) +{ + SECItem encodedExtenValue; + SECItem *tmpItem = NULL; + SECStatus rv; + void *mark = NULL; + + encodedExtenValue.data = NULL; + encodedExtenValue.len = 0; + + rv = cert_FindExtension(crl->extensions, SEC_OID_X509_CRL_NUMBER, + &encodedExtenValue); + if (rv != SECSuccess) + return (rv); + + mark = PORT_ArenaMark(arena); + + tmpItem = SECITEM_ArenaDupItem(arena, &encodedExtenValue); + if (tmpItem) { + rv = SEC_QuickDERDecodeItem(arena, value, + SEC_ASN1_GET(SEC_IntegerTemplate), + tmpItem); + } else { + rv = SECFailure; + } + + PORT_Free(encodedExtenValue.data); + if (rv == SECFailure) { + PORT_ArenaRelease(arena, mark); + } else { + PORT_ArenaUnmark(arena, mark); + } + return (rv); +} + +SECStatus +CERT_FindCRLEntryReasonExten(CERTCrlEntry *crlEntry, + CERTCRLEntryReasonCode *value) +{ + SECItem wrapperItem = { siBuffer, 0 }; + SECItem tmpItem = { siBuffer, 0 }; + SECStatus rv; + PLArenaPool *arena = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + return (SECFailure); + } + + rv = cert_FindExtension(crlEntry->extensions, SEC_OID_X509_REASON_CODE, + &wrapperItem); + if (rv != SECSuccess) { + goto loser; + } + + rv = SEC_QuickDERDecodeItem(arena, &tmpItem, + SEC_ASN1_GET(SEC_EnumeratedTemplate), + &wrapperItem); + + if (rv != SECSuccess) { + goto loser; + } + + *value = (CERTCRLEntryReasonCode)DER_GetInteger(&tmpItem); + +loser: + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + + if (wrapperItem.data) { + PORT_Free(wrapperItem.data); + } + + return (rv); +} + +SECStatus +CERT_FindInvalidDateExten(CERTCrl *crl, PRTime *value) +{ + SECItem encodedExtenValue; + SECItem decodedExtenValue = { siBuffer, 0 }; + SECStatus rv; + + encodedExtenValue.data = decodedExtenValue.data = NULL; + encodedExtenValue.len = decodedExtenValue.len = 0; + + rv = cert_FindExtension(crl->extensions, SEC_OID_X509_INVALID_DATE, &encodedExtenValue); + if (rv != SECSuccess) + return (rv); + + rv = SEC_ASN1DecodeItem(NULL, &decodedExtenValue, + SEC_ASN1_GET(SEC_GeneralizedTimeTemplate), + &encodedExtenValue); + if (rv == SECSuccess) + rv = DER_GeneralizedTimeToTime(value, &encodedExtenValue); + PORT_Free(decodedExtenValue.data); + PORT_Free(encodedExtenValue.data); + return (rv); +} diff --git a/security/nss/lib/certhigh/exports.gyp b/security/nss/lib/certhigh/exports.gyp new file mode 100644 index 0000000000..b8e2f3ebca --- /dev/null +++ b/security/nss/lib/certhigh/exports.gyp @@ -0,0 +1,33 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +{ + 'includes': [ + '../../coreconf/config.gypi' + ], + 'targets': [ + { + 'target_name': 'lib_certhigh_exports', + 'type': 'none', + 'copies': [ + { + 'files': [ + 'ocsp.h', + 'ocspt.h' + ], + 'destination': '<(nss_public_dist_dir)/<(module)' + }, + { + 'files': [ + 'ocspi.h', + 'ocspti.h' + ], + 'destination': '<(nss_private_dist_dir)/<(module)' + } + ] + } + ], + 'variables': { + 'module': 'nss' + } +} diff --git a/security/nss/lib/certhigh/manifest.mn b/security/nss/lib/certhigh/manifest.mn new file mode 100644 index 0000000000..b843e06869 --- /dev/null +++ b/security/nss/lib/certhigh/manifest.mn @@ -0,0 +1,35 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +CORE_DEPTH = ../.. + +EXPORTS = \ + ocsp.h \ + ocspt.h \ + $(NULL) + +PRIVATE_EXPORTS = \ + ocspti.h \ + ocspi.h \ + $(NULL) + +MODULE = nss + +CSRCS = \ + certhtml.c \ + certreq.c \ + crlv2.c \ + ocsp.c \ + ocspsig.c \ + certhigh.c \ + certvfy.c \ + certvfypkix.c \ + xcrldist.c \ + $(NULL) + +LIBRARY_NAME = certhi +SHARED_LIBRARY = $(NULL) + +# This part of the code, including all sub-dirs, can be optimized for size +export ALLOW_OPT_CODE_SIZE = 1 diff --git a/security/nss/lib/certhigh/ocsp.c b/security/nss/lib/certhigh/ocsp.c new file mode 100644 index 0000000000..4fccbc9012 --- /dev/null +++ b/security/nss/lib/certhigh/ocsp.c @@ -0,0 +1,6119 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Implementation of OCSP services, for both client and server. + * (XXX, really, mostly just for client right now, but intended to do both.) + */ + +#include "prerror.h" +#include "prprf.h" +#include "plarena.h" +#include "prnetdb.h" + +#include "seccomon.h" +#include "secitem.h" +#include "secoidt.h" +#include "secasn1.h" +#include "secder.h" +#include "cert.h" +#include "certi.h" +#include "xconst.h" +#include "secerr.h" +#include "secoid.h" +#include "hasht.h" +#include "sechash.h" +#include "secasn1.h" +#include "plbase64.h" +#include "keyhi.h" +#include "cryptohi.h" +#include "ocsp.h" +#include "ocspti.h" +#include "ocspi.h" +#include "genname.h" +#include "certxutl.h" +#include "pk11func.h" /* for PK11_HashBuf */ +#include +#include + +#define DEFAULT_OCSP_CACHE_SIZE 1000 +#define DEFAULT_MINIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT 1 * 60 * 60L +#define DEFAULT_MAXIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT 24 * 60 * 60L +#define DEFAULT_OSCP_TIMEOUT_SECONDS 60 +#define MICROSECONDS_PER_SECOND 1000000L + +typedef struct OCSPCacheItemStr OCSPCacheItem; +typedef struct OCSPCacheDataStr OCSPCacheData; + +struct OCSPCacheItemStr { + /* LRU linking */ + OCSPCacheItem *moreRecent; + OCSPCacheItem *lessRecent; + + /* key */ + CERTOCSPCertID *certID; + /* CertID's arena also used to allocate "this" cache item */ + + /* cache control information */ + PRTime nextFetchAttemptTime; + + /* Cached contents. Use a separate arena, because lifetime is different */ + PLArenaPool *certStatusArena; /* NULL means: no cert status cached */ + ocspCertStatus certStatus; + + /* This may contain an error code when no OCSP response is available. */ + SECErrorCodes missingResponseError; + + PRPackedBool haveThisUpdate; + PRPackedBool haveNextUpdate; + PRTime thisUpdate; + PRTime nextUpdate; +}; + +struct OCSPCacheDataStr { + PLHashTable *entries; + PRUint32 numberOfEntries; + OCSPCacheItem *MRUitem; /* most recently used cache item */ + OCSPCacheItem *LRUitem; /* least recently used cache item */ +}; + +static struct OCSPGlobalStruct { + PRMonitor *monitor; + const SEC_HttpClientFcn *defaultHttpClientFcn; + PRInt32 maxCacheEntries; + PRUint32 minimumSecondsToNextFetchAttempt; + PRUint32 maximumSecondsToNextFetchAttempt; + PRUint32 timeoutSeconds; + OCSPCacheData cache; + SEC_OcspFailureMode ocspFailureMode; + CERT_StringFromCertFcn alternateOCSPAIAFcn; + PRBool forcePost; +} OCSP_Global = { NULL, + NULL, + DEFAULT_OCSP_CACHE_SIZE, + DEFAULT_MINIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT, + DEFAULT_MAXIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT, + DEFAULT_OSCP_TIMEOUT_SECONDS, + { NULL, 0, NULL, NULL }, + ocspMode_FailureIsVerificationFailure, + NULL, + PR_FALSE }; + +/* Forward declarations */ +static SECItem * +ocsp_GetEncodedOCSPResponseFromRequest(PLArenaPool *arena, + CERTOCSPRequest *request, + const char *location, + const char *method, + PRTime time, + PRBool addServiceLocator, + void *pwArg, + CERTOCSPRequest **pRequest); +static SECStatus +ocsp_GetOCSPStatusFromNetwork(CERTCertDBHandle *handle, + CERTOCSPCertID *certID, + CERTCertificate *cert, + PRTime time, + void *pwArg, + PRBool *certIDWasConsumed, + SECStatus *rv_ocsp); + +static SECStatus +ocsp_GetDecodedVerifiedSingleResponseForID(CERTCertDBHandle *handle, + CERTOCSPCertID *certID, + CERTCertificate *cert, + PRTime time, + void *pwArg, + const SECItem *encodedResponse, + CERTOCSPResponse **pDecodedResponse, + CERTOCSPSingleResponse **pSingle); + +static SECStatus +ocsp_CertRevokedAfter(ocspRevokedInfo *revokedInfo, PRTime time); + +static CERTOCSPCertID * +cert_DupOCSPCertID(const CERTOCSPCertID *src); + +#ifndef DEBUG +#define OCSP_TRACE(msg) +#define OCSP_TRACE_TIME(msg, time) +#define OCSP_TRACE_CERT(cert) +#define OCSP_TRACE_CERTID(certid) +#else +#define OCSP_TRACE(msg) ocsp_Trace msg +#define OCSP_TRACE_TIME(msg, time) ocsp_dumpStringWithTime(msg, time) +#define OCSP_TRACE_CERT(cert) dumpCertificate(cert) +#define OCSP_TRACE_CERTID(certid) dumpCertID(certid) + +#if defined(XP_UNIX) || defined(XP_WIN32) || defined(XP_MACOSX) +#define NSS_HAVE_GETENV 1 +#endif + +static PRBool +wantOcspTrace(void) +{ + static PRBool firstTime = PR_TRUE; + static PRBool wantTrace = PR_FALSE; + +#ifdef NSS_HAVE_GETENV + if (firstTime) { + char *ev = PR_GetEnvSecure("NSS_TRACE_OCSP"); + if (ev && ev[0]) { + wantTrace = PR_TRUE; + } + firstTime = PR_FALSE; + } +#endif + return wantTrace; +} + +static void +ocsp_Trace(const char *format, ...) +{ + char buf[2000]; + va_list args; + + if (!wantOcspTrace()) + return; + va_start(args, format); + PR_vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + PR_LogPrint("%s", buf); +} + +static void +ocsp_dumpStringWithTime(const char *str, PRTime time) +{ + PRExplodedTime timePrintable; + char timestr[256]; + + if (!wantOcspTrace()) + return; + PR_ExplodeTime(time, PR_GMTParameters, &timePrintable); + if (PR_FormatTime(timestr, 256, "%a %b %d %H:%M:%S %Y", &timePrintable)) { + ocsp_Trace("OCSP %s %s\n", str, timestr); + } +} + +static void +printHexString(const char *prefix, SECItem *hexval) +{ + unsigned int i; + char *hexbuf = NULL; + + for (i = 0; i < hexval->len; i++) { + if (i != hexval->len - 1) { + hexbuf = PR_sprintf_append(hexbuf, "%02x:", hexval->data[i]); + } else { + hexbuf = PR_sprintf_append(hexbuf, "%02x", hexval->data[i]); + } + } + if (hexbuf) { + ocsp_Trace("%s %s\n", prefix, hexbuf); + PR_smprintf_free(hexbuf); + } +} + +static void +dumpCertificate(CERTCertificate *cert) +{ + if (!wantOcspTrace()) + return; + + ocsp_Trace("OCSP ----------------\n"); + ocsp_Trace("OCSP ## SUBJECT: %s\n", cert->subjectName); + { + PRTime timeBefore, timeAfter; + PRExplodedTime beforePrintable, afterPrintable; + char beforestr[256], afterstr[256]; + PRStatus rv1, rv2; + DER_DecodeTimeChoice(&timeBefore, &cert->validity.notBefore); + DER_DecodeTimeChoice(&timeAfter, &cert->validity.notAfter); + PR_ExplodeTime(timeBefore, PR_GMTParameters, &beforePrintable); + PR_ExplodeTime(timeAfter, PR_GMTParameters, &afterPrintable); + rv1 = PR_FormatTime(beforestr, 256, "%a %b %d %H:%M:%S %Y", + &beforePrintable); + rv2 = PR_FormatTime(afterstr, 256, "%a %b %d %H:%M:%S %Y", + &afterPrintable); + ocsp_Trace("OCSP ## VALIDITY: %s to %s\n", rv1 ? beforestr : "", + rv2 ? afterstr : ""); + } + ocsp_Trace("OCSP ## ISSUER: %s\n", cert->issuerName); + printHexString("OCSP ## SERIAL NUMBER:", &cert->serialNumber); +} + +static void +dumpCertID(CERTOCSPCertID *certID) +{ + if (!wantOcspTrace()) + return; + + printHexString("OCSP certID issuer", &certID->issuerNameHash); + printHexString("OCSP certID serial", &certID->serialNumber); +} +#endif + +SECStatus +SEC_RegisterDefaultHttpClient(const SEC_HttpClientFcn *fcnTable) +{ + if (!OCSP_Global.monitor) { + PORT_SetError(SEC_ERROR_NOT_INITIALIZED); + return SECFailure; + } + + PR_EnterMonitor(OCSP_Global.monitor); + OCSP_Global.defaultHttpClientFcn = fcnTable; + PR_ExitMonitor(OCSP_Global.monitor); + + return SECSuccess; +} + +SECStatus +CERT_RegisterAlternateOCSPAIAInfoCallBack( + CERT_StringFromCertFcn newCallback, + CERT_StringFromCertFcn *oldCallback) +{ + CERT_StringFromCertFcn old; + + if (!OCSP_Global.monitor) { + PORT_SetError(SEC_ERROR_NOT_INITIALIZED); + return SECFailure; + } + + PR_EnterMonitor(OCSP_Global.monitor); + old = OCSP_Global.alternateOCSPAIAFcn; + OCSP_Global.alternateOCSPAIAFcn = newCallback; + PR_ExitMonitor(OCSP_Global.monitor); + if (oldCallback) + *oldCallback = old; + return SECSuccess; +} + +static PLHashNumber PR_CALLBACK +ocsp_CacheKeyHashFunction(const void *key) +{ + CERTOCSPCertID *cid = (CERTOCSPCertID *)key; + PLHashNumber hash = 0; + unsigned int i; + unsigned char *walk; + + /* a very simple hash calculation for the initial coding phase */ + walk = (unsigned char *)cid->issuerNameHash.data; + for (i = 0; i < cid->issuerNameHash.len; ++i, ++walk) { + hash += *walk; + } + walk = (unsigned char *)cid->issuerKeyHash.data; + for (i = 0; i < cid->issuerKeyHash.len; ++i, ++walk) { + hash += *walk; + } + walk = (unsigned char *)cid->serialNumber.data; + for (i = 0; i < cid->serialNumber.len; ++i, ++walk) { + hash += *walk; + } + return hash; +} + +static PRIntn PR_CALLBACK +ocsp_CacheKeyCompareFunction(const void *v1, const void *v2) +{ + CERTOCSPCertID *cid1 = (CERTOCSPCertID *)v1; + CERTOCSPCertID *cid2 = (CERTOCSPCertID *)v2; + + return (SECEqual == SECITEM_CompareItem(&cid1->issuerNameHash, + &cid2->issuerNameHash) && + SECEqual == SECITEM_CompareItem(&cid1->issuerKeyHash, + &cid2->issuerKeyHash) && + SECEqual == SECITEM_CompareItem(&cid1->serialNumber, + &cid2->serialNumber)); +} + +static SECStatus +ocsp_CopyRevokedInfo(PLArenaPool *arena, ocspCertStatus *dest, + ocspRevokedInfo *src) +{ + SECStatus rv = SECFailure; + void *mark; + + mark = PORT_ArenaMark(arena); + + dest->certStatusInfo.revokedInfo = + (ocspRevokedInfo *)PORT_ArenaZAlloc(arena, sizeof(ocspRevokedInfo)); + if (!dest->certStatusInfo.revokedInfo) { + goto loser; + } + + rv = SECITEM_CopyItem(arena, + &dest->certStatusInfo.revokedInfo->revocationTime, + &src->revocationTime); + if (rv != SECSuccess) { + goto loser; + } + + if (src->revocationReason) { + dest->certStatusInfo.revokedInfo->revocationReason = + SECITEM_ArenaDupItem(arena, src->revocationReason); + if (!dest->certStatusInfo.revokedInfo->revocationReason) { + goto loser; + } + } else { + dest->certStatusInfo.revokedInfo->revocationReason = NULL; + } + + PORT_ArenaUnmark(arena, mark); + return SECSuccess; + +loser: + PORT_ArenaRelease(arena, mark); + return SECFailure; +} + +static SECStatus +ocsp_CopyCertStatus(PLArenaPool *arena, ocspCertStatus *dest, + ocspCertStatus *src) +{ + SECStatus rv = SECFailure; + dest->certStatusType = src->certStatusType; + + switch (src->certStatusType) { + case ocspCertStatus_good: + dest->certStatusInfo.goodInfo = + SECITEM_ArenaDupItem(arena, src->certStatusInfo.goodInfo); + if (dest->certStatusInfo.goodInfo != NULL) { + rv = SECSuccess; + } + break; + case ocspCertStatus_revoked: + rv = ocsp_CopyRevokedInfo(arena, dest, + src->certStatusInfo.revokedInfo); + break; + case ocspCertStatus_unknown: + dest->certStatusInfo.unknownInfo = + SECITEM_ArenaDupItem(arena, src->certStatusInfo.unknownInfo); + if (dest->certStatusInfo.unknownInfo != NULL) { + rv = SECSuccess; + } + break; + case ocspCertStatus_other: + default: + PORT_Assert(src->certStatusType == ocspCertStatus_other); + dest->certStatusInfo.otherInfo = + SECITEM_ArenaDupItem(arena, src->certStatusInfo.otherInfo); + if (dest->certStatusInfo.otherInfo != NULL) { + rv = SECSuccess; + } + break; + } + return rv; +} + +static void +ocsp_AddCacheItemToLinkedList(OCSPCacheData *cache, OCSPCacheItem *new_most_recent) +{ + PR_EnterMonitor(OCSP_Global.monitor); + + if (!cache->LRUitem) { + cache->LRUitem = new_most_recent; + } + new_most_recent->lessRecent = cache->MRUitem; + new_most_recent->moreRecent = NULL; + + if (cache->MRUitem) { + cache->MRUitem->moreRecent = new_most_recent; + } + cache->MRUitem = new_most_recent; + + PR_ExitMonitor(OCSP_Global.monitor); +} + +static void +ocsp_RemoveCacheItemFromLinkedList(OCSPCacheData *cache, OCSPCacheItem *item) +{ + PR_EnterMonitor(OCSP_Global.monitor); + + if (!item->lessRecent && !item->moreRecent) { + /* + * Fail gracefully on attempts to remove an item from the list, + * which is currently not part of the list. + * But check for the edge case it is the single entry in the list. + */ + if (item == cache->LRUitem && + item == cache->MRUitem) { + /* remove the single entry */ + PORT_Assert(cache->numberOfEntries == 1); + PORT_Assert(item->moreRecent == NULL); + cache->MRUitem = NULL; + cache->LRUitem = NULL; + } + PR_ExitMonitor(OCSP_Global.monitor); + return; + } + + PORT_Assert(cache->numberOfEntries > 1); + + if (item == cache->LRUitem) { + PORT_Assert(item != cache->MRUitem); + PORT_Assert(item->lessRecent == NULL); + PORT_Assert(item->moreRecent != NULL); + PORT_Assert(item->moreRecent->lessRecent == item); + cache->LRUitem = item->moreRecent; + cache->LRUitem->lessRecent = NULL; + } else if (item == cache->MRUitem) { + PORT_Assert(item->moreRecent == NULL); + PORT_Assert(item->lessRecent != NULL); + PORT_Assert(item->lessRecent->moreRecent == item); + cache->MRUitem = item->lessRecent; + cache->MRUitem->moreRecent = NULL; + } else { + /* remove an entry in the middle of the list */ + PORT_Assert(item->moreRecent != NULL); + PORT_Assert(item->lessRecent != NULL); + PORT_Assert(item->lessRecent->moreRecent == item); + PORT_Assert(item->moreRecent->lessRecent == item); + item->moreRecent->lessRecent = item->lessRecent; + item->lessRecent->moreRecent = item->moreRecent; + } + + item->lessRecent = NULL; + item->moreRecent = NULL; + + PR_ExitMonitor(OCSP_Global.monitor); +} + +static void +ocsp_MakeCacheEntryMostRecent(OCSPCacheData *cache, OCSPCacheItem *new_most_recent) +{ + OCSP_TRACE(("OCSP ocsp_MakeCacheEntryMostRecent THREADID %p\n", + PR_GetCurrentThread())); + PR_EnterMonitor(OCSP_Global.monitor); + if (cache->MRUitem == new_most_recent) { + OCSP_TRACE(("OCSP ocsp_MakeCacheEntryMostRecent ALREADY MOST\n")); + PR_ExitMonitor(OCSP_Global.monitor); + return; + } + OCSP_TRACE(("OCSP ocsp_MakeCacheEntryMostRecent NEW entry\n")); + ocsp_RemoveCacheItemFromLinkedList(cache, new_most_recent); + ocsp_AddCacheItemToLinkedList(cache, new_most_recent); + PR_ExitMonitor(OCSP_Global.monitor); +} + +static PRBool +ocsp_IsCacheDisabled(void) +{ + /* + * maxCacheEntries == 0 means unlimited cache entries + * maxCacheEntries < 0 means cache is disabled + */ + PRBool retval; + PR_EnterMonitor(OCSP_Global.monitor); + retval = (OCSP_Global.maxCacheEntries < 0); + PR_ExitMonitor(OCSP_Global.monitor); + return retval; +} + +static OCSPCacheItem * +ocsp_FindCacheEntry(OCSPCacheData *cache, CERTOCSPCertID *certID) +{ + OCSPCacheItem *found_ocsp_item = NULL; + OCSP_TRACE(("OCSP ocsp_FindCacheEntry\n")); + OCSP_TRACE_CERTID(certID); + PR_EnterMonitor(OCSP_Global.monitor); + if (ocsp_IsCacheDisabled()) + goto loser; + + found_ocsp_item = (OCSPCacheItem *)PL_HashTableLookup( + cache->entries, certID); + if (!found_ocsp_item) + goto loser; + + OCSP_TRACE(("OCSP ocsp_FindCacheEntry FOUND!\n")); + ocsp_MakeCacheEntryMostRecent(cache, found_ocsp_item); + +loser: + PR_ExitMonitor(OCSP_Global.monitor); + return found_ocsp_item; +} + +static void +ocsp_FreeCacheItem(OCSPCacheItem *item) +{ + OCSP_TRACE(("OCSP ocsp_FreeCacheItem\n")); + if (item->certStatusArena) { + PORT_FreeArena(item->certStatusArena, PR_FALSE); + } + if (item->certID->poolp) { + /* freeing this poolp arena will also free item */ + PORT_FreeArena(item->certID->poolp, PR_FALSE); + } +} + +static void +ocsp_RemoveCacheItem(OCSPCacheData *cache, OCSPCacheItem *item) +{ + /* The item we're removing could be either the least recently used item, + * or it could be an item that couldn't get updated with newer status info + * because of an allocation failure, or it could get removed because we're + * cleaning up. + */ + OCSP_TRACE(("OCSP ocsp_RemoveCacheItem, THREADID %p\n", PR_GetCurrentThread())); + PR_EnterMonitor(OCSP_Global.monitor); + + ocsp_RemoveCacheItemFromLinkedList(cache, item); +#ifdef DEBUG + { + PRBool couldRemoveFromHashTable = PL_HashTableRemove(cache->entries, + item->certID); + PORT_Assert(couldRemoveFromHashTable); + } +#else + PL_HashTableRemove(cache->entries, item->certID); +#endif + --cache->numberOfEntries; + ocsp_FreeCacheItem(item); + PR_ExitMonitor(OCSP_Global.monitor); +} + +static void +ocsp_CheckCacheSize(OCSPCacheData *cache) +{ + OCSP_TRACE(("OCSP ocsp_CheckCacheSize\n")); + PR_EnterMonitor(OCSP_Global.monitor); + if (OCSP_Global.maxCacheEntries > 0) { + /* Cache is not disabled. Number of cache entries is limited. + * The monitor ensures that maxCacheEntries remains positive. + */ + while (cache->numberOfEntries > + (PRUint32)OCSP_Global.maxCacheEntries) { + ocsp_RemoveCacheItem(cache, cache->LRUitem); + } + } + PR_ExitMonitor(OCSP_Global.monitor); +} + +SECStatus +CERT_ClearOCSPCache(void) +{ + OCSP_TRACE(("OCSP CERT_ClearOCSPCache\n")); + PR_EnterMonitor(OCSP_Global.monitor); + while (OCSP_Global.cache.numberOfEntries > 0) { + ocsp_RemoveCacheItem(&OCSP_Global.cache, + OCSP_Global.cache.LRUitem); + } + PR_ExitMonitor(OCSP_Global.monitor); + return SECSuccess; +} + +static SECStatus +ocsp_CreateCacheItemAndConsumeCertID(OCSPCacheData *cache, + CERTOCSPCertID *certID, + OCSPCacheItem **pCacheItem) +{ + PLArenaPool *arena; + void *mark; + PLHashEntry *new_hash_entry; + OCSPCacheItem *item; + + PORT_Assert(pCacheItem != NULL); + *pCacheItem = NULL; + + PR_EnterMonitor(OCSP_Global.monitor); + arena = certID->poolp; + mark = PORT_ArenaMark(arena); + + /* ZAlloc will init all Bools to False and all Pointers to NULL + and all error codes to zero/good. */ + item = (OCSPCacheItem *)PORT_ArenaZAlloc(certID->poolp, + sizeof(OCSPCacheItem)); + if (!item) { + goto loser; + } + item->certID = certID; + new_hash_entry = PL_HashTableAdd(cache->entries, item->certID, + item); + if (!new_hash_entry) { + goto loser; + } + ++cache->numberOfEntries; + PORT_ArenaUnmark(arena, mark); + ocsp_AddCacheItemToLinkedList(cache, item); + *pCacheItem = item; + + PR_ExitMonitor(OCSP_Global.monitor); + return SECSuccess; + +loser: + PORT_ArenaRelease(arena, mark); + PR_ExitMonitor(OCSP_Global.monitor); + return SECFailure; +} + +static SECStatus +ocsp_SetCacheItemResponse(OCSPCacheItem *item, + const CERTOCSPSingleResponse *response) +{ + if (item->certStatusArena) { + PORT_FreeArena(item->certStatusArena, PR_FALSE); + item->certStatusArena = NULL; + } + item->haveThisUpdate = item->haveNextUpdate = PR_FALSE; + if (response) { + SECStatus rv; + item->certStatusArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (item->certStatusArena == NULL) { + return SECFailure; + } + rv = ocsp_CopyCertStatus(item->certStatusArena, &item->certStatus, + response->certStatus); + if (rv != SECSuccess) { + PORT_FreeArena(item->certStatusArena, PR_FALSE); + item->certStatusArena = NULL; + return rv; + } + item->missingResponseError = 0; + rv = DER_GeneralizedTimeToTime(&item->thisUpdate, + &response->thisUpdate); + item->haveThisUpdate = (rv == SECSuccess); + if (response->nextUpdate) { + rv = DER_GeneralizedTimeToTime(&item->nextUpdate, + response->nextUpdate); + item->haveNextUpdate = (rv == SECSuccess); + } else { + item->haveNextUpdate = PR_FALSE; + } + } + return SECSuccess; +} + +static void +ocsp_FreshenCacheItemNextFetchAttemptTime(OCSPCacheItem *cacheItem) +{ + PRTime now; + PRTime earliestAllowedNextFetchAttemptTime; + PRTime latestTimeWhenResponseIsConsideredFresh; + + OCSP_TRACE(("OCSP ocsp_FreshenCacheItemNextFetchAttemptTime\n")); + + PR_EnterMonitor(OCSP_Global.monitor); + + now = PR_Now(); + OCSP_TRACE_TIME("now:", now); + + if (cacheItem->haveThisUpdate) { + OCSP_TRACE_TIME("thisUpdate:", cacheItem->thisUpdate); + latestTimeWhenResponseIsConsideredFresh = cacheItem->thisUpdate + + OCSP_Global.maximumSecondsToNextFetchAttempt * + MICROSECONDS_PER_SECOND; + OCSP_TRACE_TIME("latestTimeWhenResponseIsConsideredFresh:", + latestTimeWhenResponseIsConsideredFresh); + } else { + latestTimeWhenResponseIsConsideredFresh = now + + OCSP_Global.minimumSecondsToNextFetchAttempt * + MICROSECONDS_PER_SECOND; + OCSP_TRACE_TIME("no thisUpdate, " + "latestTimeWhenResponseIsConsideredFresh:", + latestTimeWhenResponseIsConsideredFresh); + } + + if (cacheItem->haveNextUpdate) { + OCSP_TRACE_TIME("have nextUpdate:", cacheItem->nextUpdate); + } + + if (cacheItem->haveNextUpdate && + cacheItem->nextUpdate < latestTimeWhenResponseIsConsideredFresh) { + latestTimeWhenResponseIsConsideredFresh = cacheItem->nextUpdate; + OCSP_TRACE_TIME("nextUpdate is smaller than latestFresh, setting " + "latestTimeWhenResponseIsConsideredFresh:", + latestTimeWhenResponseIsConsideredFresh); + } + + earliestAllowedNextFetchAttemptTime = now + + OCSP_Global.minimumSecondsToNextFetchAttempt * + MICROSECONDS_PER_SECOND; + OCSP_TRACE_TIME("earliestAllowedNextFetchAttemptTime:", + earliestAllowedNextFetchAttemptTime); + + if (latestTimeWhenResponseIsConsideredFresh < + earliestAllowedNextFetchAttemptTime) { + latestTimeWhenResponseIsConsideredFresh = + earliestAllowedNextFetchAttemptTime; + OCSP_TRACE_TIME("latest < earliest, setting latest to:", + latestTimeWhenResponseIsConsideredFresh); + } + + cacheItem->nextFetchAttemptTime = + latestTimeWhenResponseIsConsideredFresh; + OCSP_TRACE_TIME("nextFetchAttemptTime", + latestTimeWhenResponseIsConsideredFresh); + + PR_ExitMonitor(OCSP_Global.monitor); +} + +static PRBool +ocsp_IsCacheItemFresh(OCSPCacheItem *cacheItem) +{ + PRTime now; + PRBool fresh; + + now = PR_Now(); + + fresh = cacheItem->nextFetchAttemptTime > now; + + /* Work around broken OCSP responders that return unknown responses for + * certificates, especially certificates that were just recently issued. + */ + if (fresh && cacheItem->certStatusArena && + cacheItem->certStatus.certStatusType == ocspCertStatus_unknown) { + fresh = PR_FALSE; + } + + OCSP_TRACE(("OCSP ocsp_IsCacheItemFresh: %d\n", fresh)); + + return fresh; +} + +/* + * Status in *certIDWasConsumed will always be correct, regardless of + * return value. + * If the caller is unable to transfer ownership of certID, + * then the caller must set certIDWasConsumed to NULL, + * and this function will potentially duplicate the certID object. + */ +static SECStatus +ocsp_CreateOrUpdateCacheEntry(OCSPCacheData *cache, + CERTOCSPCertID *certID, + CERTOCSPSingleResponse *single, + PRBool *certIDWasConsumed) +{ + SECStatus rv; + OCSPCacheItem *cacheItem; + OCSP_TRACE(("OCSP ocsp_CreateOrUpdateCacheEntry\n")); + + if (certIDWasConsumed) + *certIDWasConsumed = PR_FALSE; + + PR_EnterMonitor(OCSP_Global.monitor); + PORT_Assert(OCSP_Global.maxCacheEntries >= 0); + + cacheItem = ocsp_FindCacheEntry(cache, certID); + + /* Don't replace an unknown or revoked entry with an error entry, even if + * the existing entry is expired. Instead, we'll continue to use the + * existing (possibly expired) cache entry until we receive a valid signed + * response to replace it. + */ + if (!single && cacheItem && cacheItem->certStatusArena && + (cacheItem->certStatus.certStatusType == ocspCertStatus_revoked || + cacheItem->certStatus.certStatusType == ocspCertStatus_unknown)) { + PR_ExitMonitor(OCSP_Global.monitor); + return SECSuccess; + } + + if (!cacheItem) { + CERTOCSPCertID *myCertID; + if (certIDWasConsumed) { + myCertID = certID; + *certIDWasConsumed = PR_TRUE; + } else { + myCertID = cert_DupOCSPCertID(certID); + if (!myCertID) { + PR_ExitMonitor(OCSP_Global.monitor); + PORT_SetError(PR_OUT_OF_MEMORY_ERROR); + return SECFailure; + } + } + + rv = ocsp_CreateCacheItemAndConsumeCertID(cache, myCertID, + &cacheItem); + if (rv != SECSuccess) { + PR_ExitMonitor(OCSP_Global.monitor); + return rv; + } + } + if (single) { + PRTime thisUpdate; + rv = DER_GeneralizedTimeToTime(&thisUpdate, &single->thisUpdate); + + if (!cacheItem->haveThisUpdate || + (rv == SECSuccess && cacheItem->thisUpdate < thisUpdate)) { + rv = ocsp_SetCacheItemResponse(cacheItem, single); + if (rv != SECSuccess) { + ocsp_RemoveCacheItem(cache, cacheItem); + PR_ExitMonitor(OCSP_Global.monitor); + return rv; + } + } else { + OCSP_TRACE(("Not caching response because the response is not " + "newer than the cache")); + } + } else { + cacheItem->missingResponseError = PORT_GetError(); + if (cacheItem->certStatusArena) { + PORT_FreeArena(cacheItem->certStatusArena, PR_FALSE); + cacheItem->certStatusArena = NULL; + } + } + ocsp_FreshenCacheItemNextFetchAttemptTime(cacheItem); + ocsp_CheckCacheSize(cache); + + PR_ExitMonitor(OCSP_Global.monitor); + return SECSuccess; +} + +extern SECStatus +CERT_SetOCSPFailureMode(SEC_OcspFailureMode ocspFailureMode) +{ + switch (ocspFailureMode) { + case ocspMode_FailureIsVerificationFailure: + case ocspMode_FailureIsNotAVerificationFailure: + break; + default: + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + PR_EnterMonitor(OCSP_Global.monitor); + OCSP_Global.ocspFailureMode = ocspFailureMode; + PR_ExitMonitor(OCSP_Global.monitor); + return SECSuccess; +} + +SECStatus +CERT_OCSPCacheSettings(PRInt32 maxCacheEntries, + PRUint32 minimumSecondsToNextFetchAttempt, + PRUint32 maximumSecondsToNextFetchAttempt) +{ + if (minimumSecondsToNextFetchAttempt > maximumSecondsToNextFetchAttempt || + maxCacheEntries < -1) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + PR_EnterMonitor(OCSP_Global.monitor); + + if (maxCacheEntries < 0) { + OCSP_Global.maxCacheEntries = -1; /* disable cache */ + } else if (maxCacheEntries == 0) { + OCSP_Global.maxCacheEntries = 0; /* unlimited cache entries */ + } else { + OCSP_Global.maxCacheEntries = maxCacheEntries; + } + + if (minimumSecondsToNextFetchAttempt < + OCSP_Global.minimumSecondsToNextFetchAttempt || + maximumSecondsToNextFetchAttempt < + OCSP_Global.maximumSecondsToNextFetchAttempt) { + /* + * Ensure our existing cache entries are not used longer than the + * new settings allow, we're lazy and just clear the cache + */ + CERT_ClearOCSPCache(); + } + + OCSP_Global.minimumSecondsToNextFetchAttempt = + minimumSecondsToNextFetchAttempt; + OCSP_Global.maximumSecondsToNextFetchAttempt = + maximumSecondsToNextFetchAttempt; + ocsp_CheckCacheSize(&OCSP_Global.cache); + + PR_ExitMonitor(OCSP_Global.monitor); + return SECSuccess; +} + +SECStatus +CERT_SetOCSPTimeout(PRUint32 seconds) +{ + /* no locking, see bug 406120 */ + OCSP_Global.timeoutSeconds = seconds; + return SECSuccess; +} + +/* this function is called at NSS initialization time */ +SECStatus +OCSP_InitGlobal(void) +{ + SECStatus rv = SECFailure; + + if (OCSP_Global.monitor == NULL) { + OCSP_Global.monitor = PR_NewMonitor(); + } + if (!OCSP_Global.monitor) + return SECFailure; + + PR_EnterMonitor(OCSP_Global.monitor); + if (!OCSP_Global.cache.entries) { + OCSP_Global.cache.entries = + PL_NewHashTable(0, + ocsp_CacheKeyHashFunction, + ocsp_CacheKeyCompareFunction, + PL_CompareValues, + NULL, + NULL); + OCSP_Global.ocspFailureMode = ocspMode_FailureIsVerificationFailure; + OCSP_Global.cache.numberOfEntries = 0; + OCSP_Global.cache.MRUitem = NULL; + OCSP_Global.cache.LRUitem = NULL; + } else { + /* + * NSS might call this function twice while attempting to init. + * But it's not allowed to call this again after any activity. + */ + PORT_Assert(OCSP_Global.cache.numberOfEntries == 0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + } + if (OCSP_Global.cache.entries) + rv = SECSuccess; + PR_ExitMonitor(OCSP_Global.monitor); + return rv; +} + +SECStatus +OCSP_ShutdownGlobal(void) +{ + if (!OCSP_Global.monitor) + return SECSuccess; + + PR_EnterMonitor(OCSP_Global.monitor); + if (OCSP_Global.cache.entries) { + CERT_ClearOCSPCache(); + PL_HashTableDestroy(OCSP_Global.cache.entries); + OCSP_Global.cache.entries = NULL; + } + PORT_Assert(OCSP_Global.cache.numberOfEntries == 0); + OCSP_Global.cache.MRUitem = NULL; + OCSP_Global.cache.LRUitem = NULL; + + OCSP_Global.defaultHttpClientFcn = NULL; + OCSP_Global.maxCacheEntries = DEFAULT_OCSP_CACHE_SIZE; + OCSP_Global.minimumSecondsToNextFetchAttempt = + DEFAULT_MINIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT; + OCSP_Global.maximumSecondsToNextFetchAttempt = + DEFAULT_MAXIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT; + OCSP_Global.ocspFailureMode = + ocspMode_FailureIsVerificationFailure; + PR_ExitMonitor(OCSP_Global.monitor); + + PR_DestroyMonitor(OCSP_Global.monitor); + OCSP_Global.monitor = NULL; + return SECSuccess; +} + +/* + * A return value of NULL means: + * The application did not register it's own HTTP client. + */ +const SEC_HttpClientFcn * +SEC_GetRegisteredHttpClient(void) +{ + const SEC_HttpClientFcn *retval; + + if (!OCSP_Global.monitor) { + PORT_SetError(SEC_ERROR_NOT_INITIALIZED); + return NULL; + } + + PR_EnterMonitor(OCSP_Global.monitor); + retval = OCSP_Global.defaultHttpClientFcn; + PR_ExitMonitor(OCSP_Global.monitor); + + return retval; +} + +/* + * The following structure is only used internally. It is allocated when + * someone turns on OCSP checking, and hangs off of the status-configuration + * structure in the certdb structure. We use it to keep configuration + * information specific to OCSP checking. + */ +typedef struct ocspCheckingContextStr { + PRBool useDefaultResponder; + char *defaultResponderURI; + char *defaultResponderNickname; + CERTCertificate *defaultResponderCert; +} ocspCheckingContext; + +SEC_ASN1_MKSUB(SEC_AnyTemplate) +SEC_ASN1_MKSUB(SEC_IntegerTemplate) +SEC_ASN1_MKSUB(SEC_NullTemplate) +SEC_ASN1_MKSUB(SEC_OctetStringTemplate) +SEC_ASN1_MKSUB(SEC_PointerToAnyTemplate) +SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate) +SEC_ASN1_MKSUB(SEC_SequenceOfAnyTemplate) +SEC_ASN1_MKSUB(SEC_PointerToGeneralizedTimeTemplate) +SEC_ASN1_MKSUB(SEC_PointerToEnumeratedTemplate) + +/* + * Forward declarations of sub-types, so I can lay out the types in the + * same order as the ASN.1 is laid out in the OCSP spec itself. + * + * These are in alphabetical order (case-insensitive); please keep it that way! + */ +extern const SEC_ASN1Template ocsp_CertIDTemplate[]; +extern const SEC_ASN1Template ocsp_PointerToSignatureTemplate[]; +extern const SEC_ASN1Template ocsp_PointerToResponseBytesTemplate[]; +extern const SEC_ASN1Template ocsp_ResponseDataTemplate[]; +extern const SEC_ASN1Template ocsp_RevokedInfoTemplate[]; +extern const SEC_ASN1Template ocsp_SingleRequestTemplate[]; +extern const SEC_ASN1Template ocsp_SingleResponseTemplate[]; +extern const SEC_ASN1Template ocsp_TBSRequestTemplate[]; + +/* + * Request-related templates... + */ + +/* + * OCSPRequest ::= SEQUENCE { + * tbsRequest TBSRequest, + * optionalSignature [0] EXPLICIT Signature OPTIONAL } + */ +static const SEC_ASN1Template ocsp_OCSPRequestTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTOCSPRequest) }, + { SEC_ASN1_POINTER, + offsetof(CERTOCSPRequest, tbsRequest), + ocsp_TBSRequestTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(CERTOCSPRequest, optionalSignature), + ocsp_PointerToSignatureTemplate }, + { 0 } +}; + +/* + * TBSRequest ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * requestorName [1] EXPLICIT GeneralName OPTIONAL, + * requestList SEQUENCE OF Request, + * requestExtensions [2] EXPLICIT Extensions OPTIONAL } + * + * Version ::= INTEGER { v1(0) } + * + * Note: this should be static but the AIX compiler doesn't like it (because it + * was forward-declared above); it is not meant to be exported, but this + * is the only way it will compile. + */ +const SEC_ASN1Template ocsp_TBSRequestTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(ocspTBSRequest) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | /* XXX DER_DEFAULT */ + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, + offsetof(ocspTBSRequest, version), + SEC_ASN1_SUB(SEC_IntegerTemplate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1, + offsetof(ocspTBSRequest, derRequestorName), + SEC_ASN1_SUB(SEC_PointerToAnyTemplate) }, + { SEC_ASN1_SEQUENCE_OF, + offsetof(ocspTBSRequest, requestList), + ocsp_SingleRequestTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 2, + offsetof(ocspTBSRequest, requestExtensions), + CERT_SequenceOfCertExtensionTemplate }, + { 0 } +}; + +/* + * Signature ::= SEQUENCE { + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + */ +static const SEC_ASN1Template ocsp_SignatureTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(ocspSignature) }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, + offsetof(ocspSignature, signatureAlgorithm), + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, + { SEC_ASN1_BIT_STRING, + offsetof(ocspSignature, signature) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, + offsetof(ocspSignature, derCerts), + SEC_ASN1_SUB(SEC_SequenceOfAnyTemplate) }, + { 0 } +}; + +/* + * This template is just an extra level to use in an explicitly-tagged + * reference to a Signature. + * + * Note: this should be static but the AIX compiler doesn't like it (because it + * was forward-declared above); it is not meant to be exported, but this + * is the only way it will compile. + */ +const SEC_ASN1Template ocsp_PointerToSignatureTemplate[] = { + { SEC_ASN1_POINTER, 0, ocsp_SignatureTemplate } +}; + +/* + * Request ::= SEQUENCE { + * reqCert CertID, + * singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL } + * + * Note: this should be static but the AIX compiler doesn't like it (because it + * was forward-declared above); it is not meant to be exported, but this + * is the only way it will compile. + */ +const SEC_ASN1Template ocsp_SingleRequestTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(ocspSingleRequest) }, + { SEC_ASN1_POINTER, + offsetof(ocspSingleRequest, reqCert), + ocsp_CertIDTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(ocspSingleRequest, singleRequestExtensions), + CERT_SequenceOfCertExtensionTemplate }, + { 0 } +}; + +/* + * This data structure and template (CertID) is used by both OCSP + * requests and responses. It is the only one that is shared. + * + * CertID ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * issuerNameHash OCTET STRING, -- Hash of Issuer DN + * issuerKeyHash OCTET STRING, -- Hash of Issuer public key + * serialNumber CertificateSerialNumber } + * + * CertificateSerialNumber ::= INTEGER + * + * Note: this should be static but the AIX compiler doesn't like it (because it + * was forward-declared above); it is not meant to be exported, but this + * is the only way it will compile. + */ +const SEC_ASN1Template ocsp_CertIDTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTOCSPCertID) }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, + offsetof(CERTOCSPCertID, hashAlgorithm), + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, + { SEC_ASN1_OCTET_STRING, + offsetof(CERTOCSPCertID, issuerNameHash) }, + { SEC_ASN1_OCTET_STRING, + offsetof(CERTOCSPCertID, issuerKeyHash) }, + { SEC_ASN1_INTEGER, + offsetof(CERTOCSPCertID, serialNumber) }, + { 0 } +}; + +/* + * Response-related templates... + */ + +/* + * OCSPResponse ::= SEQUENCE { + * responseStatus OCSPResponseStatus, + * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } + */ +const SEC_ASN1Template ocsp_OCSPResponseTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTOCSPResponse) }, + { SEC_ASN1_ENUMERATED, + offsetof(CERTOCSPResponse, responseStatus) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(CERTOCSPResponse, responseBytes), + ocsp_PointerToResponseBytesTemplate }, + { 0 } +}; + +/* + * ResponseBytes ::= SEQUENCE { + * responseType OBJECT IDENTIFIER, + * response OCTET STRING } + */ +const SEC_ASN1Template ocsp_ResponseBytesTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(ocspResponseBytes) }, + { SEC_ASN1_OBJECT_ID, + offsetof(ocspResponseBytes, responseType) }, + { SEC_ASN1_OCTET_STRING, + offsetof(ocspResponseBytes, response) }, + { 0 } +}; + +/* + * This template is just an extra level to use in an explicitly-tagged + * reference to a ResponseBytes. + * + * Note: this should be static but the AIX compiler doesn't like it (because it + * was forward-declared above); it is not meant to be exported, but this + * is the only way it will compile. + */ +const SEC_ASN1Template ocsp_PointerToResponseBytesTemplate[] = { + { SEC_ASN1_POINTER, 0, ocsp_ResponseBytesTemplate } +}; + +/* + * BasicOCSPResponse ::= SEQUENCE { + * tbsResponseData ResponseData, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + */ +static const SEC_ASN1Template ocsp_BasicOCSPResponseTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(ocspBasicOCSPResponse) }, + { SEC_ASN1_ANY | SEC_ASN1_SAVE, + offsetof(ocspBasicOCSPResponse, tbsResponseDataDER) }, + { SEC_ASN1_POINTER, + offsetof(ocspBasicOCSPResponse, tbsResponseData), + ocsp_ResponseDataTemplate }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, + offsetof(ocspBasicOCSPResponse, responseSignature.signatureAlgorithm), + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, + { SEC_ASN1_BIT_STRING, + offsetof(ocspBasicOCSPResponse, responseSignature.signature) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, + offsetof(ocspBasicOCSPResponse, responseSignature.derCerts), + SEC_ASN1_SUB(SEC_SequenceOfAnyTemplate) }, + { 0 } +}; + +/* + * ResponseData ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * responderID ResponderID, + * producedAt GeneralizedTime, + * responses SEQUENCE OF SingleResponse, + * responseExtensions [1] EXPLICIT Extensions OPTIONAL } + * + * Note: this should be static but the AIX compiler doesn't like it (because it + * was forward-declared above); it is not meant to be exported, but this + * is the only way it will compile. + */ +const SEC_ASN1Template ocsp_ResponseDataTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(ocspResponseData) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | /* XXX DER_DEFAULT */ + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, + offsetof(ocspResponseData, version), + SEC_ASN1_SUB(SEC_IntegerTemplate) }, + { SEC_ASN1_ANY, + offsetof(ocspResponseData, derResponderID) }, + { SEC_ASN1_GENERALIZED_TIME, + offsetof(ocspResponseData, producedAt) }, + { SEC_ASN1_SEQUENCE_OF, + offsetof(ocspResponseData, responses), + ocsp_SingleResponseTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(ocspResponseData, responseExtensions), + CERT_SequenceOfCertExtensionTemplate }, + { 0 } +}; + +/* + * ResponderID ::= CHOICE { + * byName [1] EXPLICIT Name, + * byKey [2] EXPLICIT KeyHash } + * + * KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key + * (excluding the tag and length fields) + * + * XXX Because the ASN.1 encoder and decoder currently do not provide + * a way to automatically handle a CHOICE, we need to do it in two + * steps, looking at the type tag and feeding the exact choice back + * to the ASN.1 code. Hopefully that will change someday and this + * can all be simplified down into a single template. Anyway, for + * now we list each choice as its own template: + */ +const SEC_ASN1Template ocsp_ResponderIDByNameTemplate[] = { + { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(ocspResponderID, responderIDValue.name), + CERT_NameTemplate } +}; +const SEC_ASN1Template ocsp_ResponderIDByKeyTemplate[] = { + { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | + SEC_ASN1_XTRN | 2, + offsetof(ocspResponderID, responderIDValue.keyHash), + SEC_ASN1_SUB(SEC_OctetStringTemplate) } +}; +static const SEC_ASN1Template ocsp_ResponderIDOtherTemplate[] = { + { SEC_ASN1_ANY, + offsetof(ocspResponderID, responderIDValue.other) } +}; + +/* Decode choice container, but leave x509 name object encoded */ +static const SEC_ASN1Template ocsp_ResponderIDDerNameTemplate[] = { + { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | + SEC_ASN1_XTRN | 1, + 0, SEC_ASN1_SUB(SEC_AnyTemplate) } +}; + +/* + * SingleResponse ::= SEQUENCE { + * certID CertID, + * certStatus CertStatus, + * thisUpdate GeneralizedTime, + * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, + * singleExtensions [1] EXPLICIT Extensions OPTIONAL } + * + * Note: this should be static but the AIX compiler doesn't like it (because it + * was forward-declared above); it is not meant to be exported, but this + * is the only way it will compile. + */ +const SEC_ASN1Template ocsp_SingleResponseTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTOCSPSingleResponse) }, + { SEC_ASN1_POINTER, + offsetof(CERTOCSPSingleResponse, certID), + ocsp_CertIDTemplate }, + { SEC_ASN1_ANY, + offsetof(CERTOCSPSingleResponse, derCertStatus) }, + { SEC_ASN1_GENERALIZED_TIME, + offsetof(CERTOCSPSingleResponse, thisUpdate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, + offsetof(CERTOCSPSingleResponse, nextUpdate), + SEC_ASN1_SUB(SEC_PointerToGeneralizedTimeTemplate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(CERTOCSPSingleResponse, singleExtensions), + CERT_SequenceOfCertExtensionTemplate }, + { 0 } +}; + +/* + * CertStatus ::= CHOICE { + * good [0] IMPLICIT NULL, + * revoked [1] IMPLICIT RevokedInfo, + * unknown [2] IMPLICIT UnknownInfo } + * + * Because the ASN.1 encoder and decoder currently do not provide + * a way to automatically handle a CHOICE, we need to do it in two + * steps, looking at the type tag and feeding the exact choice back + * to the ASN.1 code. Hopefully that will change someday and this + * can all be simplified down into a single template. Anyway, for + * now we list each choice as its own template: + */ +static const SEC_ASN1Template ocsp_CertStatusGoodTemplate[] = { + { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, + offsetof(ocspCertStatus, certStatusInfo.goodInfo), + SEC_ASN1_SUB(SEC_NullTemplate) } +}; +static const SEC_ASN1Template ocsp_CertStatusRevokedTemplate[] = { + { SEC_ASN1_POINTER | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(ocspCertStatus, certStatusInfo.revokedInfo), + ocsp_RevokedInfoTemplate } +}; +static const SEC_ASN1Template ocsp_CertStatusUnknownTemplate[] = { + { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2, + offsetof(ocspCertStatus, certStatusInfo.unknownInfo), + SEC_ASN1_SUB(SEC_NullTemplate) } +}; +static const SEC_ASN1Template ocsp_CertStatusOtherTemplate[] = { + { SEC_ASN1_POINTER | SEC_ASN1_XTRN, + offsetof(ocspCertStatus, certStatusInfo.otherInfo), + SEC_ASN1_SUB(SEC_AnyTemplate) } +}; + +/* + * RevokedInfo ::= SEQUENCE { + * revocationTime GeneralizedTime, + * revocationReason [0] EXPLICIT CRLReason OPTIONAL } + * + * Note: this should be static but the AIX compiler doesn't like it (because it + * was forward-declared above); it is not meant to be exported, but this + * is the only way it will compile. + */ +const SEC_ASN1Template ocsp_RevokedInfoTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(ocspRevokedInfo) }, + { SEC_ASN1_GENERALIZED_TIME, + offsetof(ocspRevokedInfo, revocationTime) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | + SEC_ASN1_XTRN | 0, + offsetof(ocspRevokedInfo, revocationReason), + SEC_ASN1_SUB(SEC_PointerToEnumeratedTemplate) }, + { 0 } +}; + +/* + * OCSP-specific extension templates: + */ + +/* + * ServiceLocator ::= SEQUENCE { + * issuer Name, + * locator AuthorityInfoAccessSyntax OPTIONAL } + */ +static const SEC_ASN1Template ocsp_ServiceLocatorTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(ocspServiceLocator) }, + { SEC_ASN1_POINTER, + offsetof(ocspServiceLocator, issuer), + CERT_NameTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY, + offsetof(ocspServiceLocator, locator) }, + { 0 } +}; + +/* + * REQUEST SUPPORT FUNCTIONS (encode/create/decode/destroy): + */ + +/* + * FUNCTION: CERT_EncodeOCSPRequest + * DER encodes an OCSP Request, possibly adding a signature as well. + * XXX Signing is not yet supported, however; see comments in code. + * INPUTS: + * PLArenaPool *arena + * The return value is allocated from here. + * If a NULL is passed in, allocation is done from the heap instead. + * CERTOCSPRequest *request + * The request to be encoded. + * void *pwArg + * Pointer to argument for password prompting, if needed. (Definitely + * not needed if not signing.) + * RETURN: + * Returns a NULL on error and a pointer to the SECItem with the + * encoded value otherwise. Any error is likely to be low-level + * (e.g. no memory). + */ +SECItem * +CERT_EncodeOCSPRequest(PLArenaPool *arena, CERTOCSPRequest *request, + void *pwArg) +{ + SECStatus rv; + + /* XXX All of these should generate errors if they fail. */ + PORT_Assert(request); + PORT_Assert(request->tbsRequest); + + if (request->tbsRequest->extensionHandle != NULL) { + rv = CERT_FinishExtensions(request->tbsRequest->extensionHandle); + request->tbsRequest->extensionHandle = NULL; + if (rv != SECSuccess) + return NULL; + } + + /* + * XXX When signed requests are supported and request->optionalSignature + * is not NULL: + * - need to encode tbsRequest->requestorName + * - need to encode tbsRequest + * - need to sign that encoded result (using cert in sig), filling in the + * request->optionalSignature structure with the result, the signing + * algorithm and (perhaps?) the cert (and its chain?) in derCerts + */ + + return SEC_ASN1EncodeItem(arena, NULL, request, ocsp_OCSPRequestTemplate); +} + +/* + * FUNCTION: CERT_DecodeOCSPRequest + * Decode a DER encoded OCSP Request. + * INPUTS: + * SECItem *src + * Pointer to a SECItem holding DER encoded OCSP Request. + * RETURN: + * Returns a pointer to a CERTOCSPRequest containing the decoded request. + * On error, returns NULL. Most likely error is trouble decoding + * (SEC_ERROR_OCSP_MALFORMED_REQUEST), or low-level problem (no memory). + */ +CERTOCSPRequest * +CERT_DecodeOCSPRequest(const SECItem *src) +{ + PLArenaPool *arena = NULL; + SECStatus rv = SECFailure; + CERTOCSPRequest *dest = NULL; + int i; + SECItem newSrc; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + goto loser; + } + dest = (CERTOCSPRequest *)PORT_ArenaZAlloc(arena, + sizeof(CERTOCSPRequest)); + if (dest == NULL) { + goto loser; + } + dest->arena = arena; + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newSrc, src); + if (rv != SECSuccess) { + goto loser; + } + + rv = SEC_QuickDERDecodeItem(arena, dest, ocsp_OCSPRequestTemplate, &newSrc); + if (rv != SECSuccess) { + if (PORT_GetError() == SEC_ERROR_BAD_DER) + PORT_SetError(SEC_ERROR_OCSP_MALFORMED_REQUEST); + goto loser; + } + + /* + * XXX I would like to find a way to get rid of the necessity + * of doing this copying of the arena pointer. + */ + for (i = 0; dest->tbsRequest->requestList[i] != NULL; i++) { + dest->tbsRequest->requestList[i]->arena = arena; + } + + return dest; + +loser: + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + return NULL; +} + +SECStatus +CERT_DestroyOCSPCertID(CERTOCSPCertID *certID) +{ + if (certID && certID->poolp) { + PORT_FreeArena(certID->poolp, PR_FALSE); + return SECSuccess; + } + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; +} + +/* + * Digest data using the specified algorithm. + * The necessary storage for the digest data is allocated. If "fill" is + * non-null, the data is put there, otherwise a SECItem is allocated. + * Allocation from "arena" if it is non-null, heap otherwise. Any problem + * results in a NULL being returned (and an appropriate error set). + */ + +SECItem * +ocsp_DigestValue(PLArenaPool *arena, SECOidTag digestAlg, + SECItem *fill, const SECItem *src) +{ + const SECHashObject *digestObject; + SECItem *result = NULL; + void *mark = NULL; + void *digestBuff = NULL; + + if (arena != NULL) { + mark = PORT_ArenaMark(arena); + } + + digestObject = HASH_GetHashObjectByOidTag(digestAlg); + if (digestObject == NULL) { + goto loser; + } + + if (fill == NULL || fill->data == NULL) { + result = SECITEM_AllocItem(arena, fill, digestObject->length); + if (result == NULL) { + goto loser; + } + digestBuff = result->data; + } else { + if (fill->len < digestObject->length) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto loser; + } + digestBuff = fill->data; + } + + if (PK11_HashBuf(digestAlg, digestBuff, + src->data, src->len) != SECSuccess) { + goto loser; + } + + if (arena != NULL) { + PORT_ArenaUnmark(arena, mark); + } + + if (result == NULL) { + result = fill; + } + return result; + +loser: + if (arena != NULL) { + PORT_ArenaRelease(arena, mark); + } else { + if (result != NULL) { + SECITEM_FreeItem(result, (fill == NULL) ? PR_TRUE : PR_FALSE); + } + } + return (NULL); +} + +/* + * Digest the cert's subject public key using the specified algorithm. + * The necessary storage for the digest data is allocated. If "fill" is + * non-null, the data is put there, otherwise a SECItem is allocated. + * Allocation from "arena" if it is non-null, heap otherwise. Any problem + * results in a NULL being returned (and an appropriate error set). + */ +SECItem * +CERT_GetSubjectPublicKeyDigest(PLArenaPool *arena, const CERTCertificate *cert, + SECOidTag digestAlg, SECItem *fill) +{ + SECItem spk; + + /* + * Copy just the length and data pointer (nothing needs to be freed) + * of the subject public key so we can convert the length from bits + * to bytes, which is what the digest function expects. + */ + spk = cert->subjectPublicKeyInfo.subjectPublicKey; + DER_ConvertBitString(&spk); + + return ocsp_DigestValue(arena, digestAlg, fill, &spk); +} + +/* + * Digest the cert's subject name using the specified algorithm. + */ +SECItem * +CERT_GetSubjectNameDigest(PLArenaPool *arena, const CERTCertificate *cert, + SECOidTag digestAlg, SECItem *fill) +{ + SECItem name; + + /* + * Copy just the length and data pointer (nothing needs to be freed) + * of the subject name + */ + name = cert->derSubject; + + return ocsp_DigestValue(arena, digestAlg, fill, &name); +} + +/* + * Create and fill-in a CertID. This function fills in the hash values + * (issuerNameHash and issuerKeyHash), and is hardwired to use SHA1. + * Someday it might need to be more flexible about hash algorithm, but + * for now we have no intention/need to create anything else. + * + * Error causes a null to be returned; most likely cause is trouble + * finding the certificate issuer (SEC_ERROR_UNKNOWN_ISSUER). + * Other errors are low-level problems (no memory, bad database, etc.). + */ +static CERTOCSPCertID * +ocsp_CreateCertID(PLArenaPool *arena, CERTCertificate *cert, PRTime time) +{ + CERTOCSPCertID *certID; + CERTCertificate *issuerCert = NULL; + void *mark = PORT_ArenaMark(arena); + SECStatus rv; + + PORT_Assert(arena != NULL); + + certID = PORT_ArenaZNew(arena, CERTOCSPCertID); + if (certID == NULL) { + goto loser; + } + + rv = SECOID_SetAlgorithmID(arena, &certID->hashAlgorithm, SEC_OID_SHA1, + NULL); + if (rv != SECSuccess) { + goto loser; + } + + issuerCert = CERT_FindCertIssuer(cert, time, certUsageAnyCA); + if (issuerCert == NULL) { + goto loser; + } + + if (CERT_GetSubjectNameDigest(arena, issuerCert, SEC_OID_SHA1, + &(certID->issuerNameHash)) == NULL) { + goto loser; + } + certID->issuerSHA1NameHash.data = certID->issuerNameHash.data; + certID->issuerSHA1NameHash.len = certID->issuerNameHash.len; + + if (CERT_GetSubjectNameDigest(arena, issuerCert, SEC_OID_MD5, + &(certID->issuerMD5NameHash)) == NULL) { + goto loser; + } + + if (CERT_GetSubjectNameDigest(arena, issuerCert, SEC_OID_MD2, + &(certID->issuerMD2NameHash)) == NULL) { + goto loser; + } + + if (CERT_GetSubjectPublicKeyDigest(arena, issuerCert, SEC_OID_SHA1, + &certID->issuerKeyHash) == NULL) { + goto loser; + } + certID->issuerSHA1KeyHash.data = certID->issuerKeyHash.data; + certID->issuerSHA1KeyHash.len = certID->issuerKeyHash.len; + /* cache the other two hash algorithms as well */ + if (CERT_GetSubjectPublicKeyDigest(arena, issuerCert, SEC_OID_MD5, + &certID->issuerMD5KeyHash) == NULL) { + goto loser; + } + if (CERT_GetSubjectPublicKeyDigest(arena, issuerCert, SEC_OID_MD2, + &certID->issuerMD2KeyHash) == NULL) { + goto loser; + } + + /* now we are done with issuerCert */ + CERT_DestroyCertificate(issuerCert); + issuerCert = NULL; + + rv = SECITEM_CopyItem(arena, &certID->serialNumber, &cert->serialNumber); + if (rv != SECSuccess) { + goto loser; + } + + PORT_ArenaUnmark(arena, mark); + return certID; + +loser: + if (issuerCert != NULL) { + CERT_DestroyCertificate(issuerCert); + } + PORT_ArenaRelease(arena, mark); + return NULL; +} + +CERTOCSPCertID * +CERT_CreateOCSPCertID(CERTCertificate *cert, PRTime time) +{ + PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + CERTOCSPCertID *certID; + PORT_Assert(arena != NULL); + if (!arena) + return NULL; + + certID = ocsp_CreateCertID(arena, cert, time); + if (!certID) { + PORT_FreeArena(arena, PR_FALSE); + return NULL; + } + certID->poolp = arena; + return certID; +} + +static CERTOCSPCertID * +cert_DupOCSPCertID(const CERTOCSPCertID *src) +{ + CERTOCSPCertID *dest; + PLArenaPool *arena = NULL; + + if (!src) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) + goto loser; + + dest = PORT_ArenaZNew(arena, CERTOCSPCertID); + if (!dest) + goto loser; + +#define DUPHELP(element) \ + if (src->element.data && \ + SECITEM_CopyItem(arena, &dest->element, &src->element) != \ + SECSuccess) { \ + goto loser; \ + } + + DUPHELP(hashAlgorithm.algorithm) + DUPHELP(hashAlgorithm.parameters) + DUPHELP(issuerNameHash) + DUPHELP(issuerKeyHash) + DUPHELP(serialNumber) + DUPHELP(issuerSHA1NameHash) + DUPHELP(issuerMD5NameHash) + DUPHELP(issuerMD2NameHash) + DUPHELP(issuerSHA1KeyHash) + DUPHELP(issuerMD5KeyHash) + DUPHELP(issuerMD2KeyHash) + + dest->poolp = arena; + return dest; + +loser: + if (arena) + PORT_FreeArena(arena, PR_FALSE); + PORT_SetError(PR_OUT_OF_MEMORY_ERROR); + return NULL; +} + +/* + * Callback to set Extensions in request object + */ +void +SetSingleReqExts(void *object, CERTCertExtension **exts) +{ + ocspSingleRequest *singleRequest = + (ocspSingleRequest *)object; + + singleRequest->singleRequestExtensions = exts; +} + +/* + * Add the Service Locator extension to the singleRequestExtensions + * for the given singleRequest. + * + * All errors are internal or low-level problems (e.g. no memory). + */ +static SECStatus +ocsp_AddServiceLocatorExtension(ocspSingleRequest *singleRequest, + CERTCertificate *cert) +{ + ocspServiceLocator *serviceLocator = NULL; + void *extensionHandle = NULL; + SECStatus rv = SECFailure; + + serviceLocator = PORT_ZNew(ocspServiceLocator); + if (serviceLocator == NULL) + goto loser; + + /* + * Normally it would be a bad idea to do a direct reference like + * this rather than allocate and copy the name *or* at least dup + * a reference of the cert. But all we need is to be able to read + * the issuer name during the encoding we are about to do, so a + * copy is just a waste of time. + */ + serviceLocator->issuer = &cert->issuer; + + rv = CERT_FindCertExtension(cert, SEC_OID_X509_AUTH_INFO_ACCESS, + &serviceLocator->locator); + if (rv != SECSuccess) { + if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) + goto loser; + } + + /* prepare for following loser gotos */ + rv = SECFailure; + PORT_SetError(0); + + extensionHandle = cert_StartExtensions(singleRequest, + singleRequest->arena, SetSingleReqExts); + if (extensionHandle == NULL) + goto loser; + + rv = CERT_EncodeAndAddExtension(extensionHandle, + SEC_OID_PKIX_OCSP_SERVICE_LOCATOR, + serviceLocator, PR_FALSE, + ocsp_ServiceLocatorTemplate); + +loser: + if (extensionHandle != NULL) { + /* + * Either way we have to finish out the extension context (so it gets + * freed). But careful not to override any already-set bad status. + */ + SECStatus tmprv = CERT_FinishExtensions(extensionHandle); + if (rv == SECSuccess) + rv = tmprv; + } + + /* + * Finally, free the serviceLocator structure itself and we are done. + */ + if (serviceLocator != NULL) { + if (serviceLocator->locator.data != NULL) + SECITEM_FreeItem(&serviceLocator->locator, PR_FALSE); + PORT_Free(serviceLocator); + } + + return rv; +} + +/* + * Creates an array of ocspSingleRequest based on a list of certs. + * Note that the code which later compares the request list with the + * response expects this array to be in the exact same order as the + * certs are found in the list. It would be harder to change that + * order than preserve it, but since the requirement is not obvious, + * it deserves to be mentioned. + * + * Any problem causes a null return and error set: + * SEC_ERROR_UNKNOWN_ISSUER + * Other errors are low-level problems (no memory, bad database, etc.). + */ +static ocspSingleRequest ** +ocsp_CreateSingleRequestList(PLArenaPool *arena, CERTCertList *certList, + PRTime time, PRBool includeLocator) +{ + ocspSingleRequest **requestList = NULL; + CERTCertListNode *node = NULL; + int i, count; + void *mark = PORT_ArenaMark(arena); + + node = CERT_LIST_HEAD(certList); + for (count = 0; !CERT_LIST_END(node, certList); count++) { + node = CERT_LIST_NEXT(node); + } + + if (count == 0) + goto loser; + + requestList = PORT_ArenaNewArray(arena, ocspSingleRequest *, count + 1); + if (requestList == NULL) + goto loser; + + node = CERT_LIST_HEAD(certList); + for (i = 0; !CERT_LIST_END(node, certList); i++) { + requestList[i] = PORT_ArenaZNew(arena, ocspSingleRequest); + if (requestList[i] == NULL) + goto loser; + + OCSP_TRACE(("OCSP CERT_CreateOCSPRequest %s\n", node->cert->subjectName)); + requestList[i]->arena = arena; + requestList[i]->reqCert = ocsp_CreateCertID(arena, node->cert, time); + if (requestList[i]->reqCert == NULL) + goto loser; + + if (includeLocator == PR_TRUE) { + SECStatus rv; + + rv = ocsp_AddServiceLocatorExtension(requestList[i], node->cert); + if (rv != SECSuccess) + goto loser; + } + + node = CERT_LIST_NEXT(node); + } + + PORT_Assert(i == count); + + PORT_ArenaUnmark(arena, mark); + requestList[i] = NULL; + return requestList; + +loser: + PORT_ArenaRelease(arena, mark); + return NULL; +} + +static ocspSingleRequest ** +ocsp_CreateRequestFromCert(PLArenaPool *arena, + CERTOCSPCertID *certID, + CERTCertificate *singleCert, + PRTime time, + PRBool includeLocator) +{ + ocspSingleRequest **requestList = NULL; + void *mark = PORT_ArenaMark(arena); + PORT_Assert(certID != NULL && singleCert != NULL); + + /* meaning of value 2: one entry + one end marker */ + requestList = PORT_ArenaNewArray(arena, ocspSingleRequest *, 2); + if (requestList == NULL) + goto loser; + requestList[0] = PORT_ArenaZNew(arena, ocspSingleRequest); + if (requestList[0] == NULL) + goto loser; + requestList[0]->arena = arena; + /* certID will live longer than the request */ + requestList[0]->reqCert = certID; + + if (includeLocator == PR_TRUE) { + SECStatus rv; + rv = ocsp_AddServiceLocatorExtension(requestList[0], singleCert); + if (rv != SECSuccess) + goto loser; + } + + PORT_ArenaUnmark(arena, mark); + requestList[1] = NULL; + return requestList; + +loser: + PORT_ArenaRelease(arena, mark); + return NULL; +} + +static CERTOCSPRequest * +ocsp_prepareEmptyOCSPRequest(void) +{ + PLArenaPool *arena = NULL; + CERTOCSPRequest *request = NULL; + ocspTBSRequest *tbsRequest = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + goto loser; + } + request = PORT_ArenaZNew(arena, CERTOCSPRequest); + if (request == NULL) { + goto loser; + } + request->arena = arena; + + tbsRequest = PORT_ArenaZNew(arena, ocspTBSRequest); + if (tbsRequest == NULL) { + goto loser; + } + request->tbsRequest = tbsRequest; + /* version 1 is the default, so we need not fill in a version number */ + return request; + +loser: + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + return NULL; +} + +CERTOCSPRequest * +cert_CreateSingleCertOCSPRequest(CERTOCSPCertID *certID, + CERTCertificate *singleCert, + PRTime time, + PRBool addServiceLocator, + CERTCertificate *signerCert) +{ + CERTOCSPRequest *request; + OCSP_TRACE(("OCSP cert_CreateSingleCertOCSPRequest %s\n", singleCert->subjectName)); + + /* XXX Support for signerCert may be implemented later, + * see also the comment in CERT_CreateOCSPRequest. + */ + if (signerCert != NULL) { + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return NULL; + } + + request = ocsp_prepareEmptyOCSPRequest(); + if (!request) + return NULL; + /* + * Version 1 is the default, so we need not fill in a version number. + * Now create the list of single requests, one for each cert. + */ + request->tbsRequest->requestList = + ocsp_CreateRequestFromCert(request->arena, + certID, + singleCert, + time, + addServiceLocator); + if (request->tbsRequest->requestList == NULL) { + PORT_FreeArena(request->arena, PR_FALSE); + return NULL; + } + return request; +} + +/* + * FUNCTION: CERT_CreateOCSPRequest + * Creates a CERTOCSPRequest, requesting the status of the certs in + * the given list. + * INPUTS: + * CERTCertList *certList + * A list of certs for which status will be requested. + * Note that all of these certificates should have the same issuer, + * or it's expected the response will be signed by a trusted responder. + * If the certs need to be broken up into multiple requests, that + * must be handled by the caller (and thus by having multiple calls + * to this routine), who knows about where the request(s) are being + * sent and whether there are any trusted responders in place. + * PRTime time + * Indicates the time for which the certificate status is to be + * determined -- this may be used in the search for the cert's issuer + * but has no effect on the request itself. + * PRBool addServiceLocator + * If true, the Service Locator extension should be added to the + * single request(s) for each cert. + * CERTCertificate *signerCert + * If non-NULL, means sign the request using this cert. Otherwise, + * do not sign. + * XXX note that request signing is not yet supported; see comment in code + * RETURN: + * A pointer to a CERTOCSPRequest structure containing an OCSP request + * for the cert list. On error, null is returned, with an error set + * indicating the reason. This is likely SEC_ERROR_UNKNOWN_ISSUER. + * (The issuer is needed to create a request for the certificate.) + * Other errors are low-level problems (no memory, bad database, etc.). + */ +CERTOCSPRequest * +CERT_CreateOCSPRequest(CERTCertList *certList, PRTime time, + PRBool addServiceLocator, + CERTCertificate *signerCert) +{ + CERTOCSPRequest *request = NULL; + + if (!certList) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + /* + * XXX When we are prepared to put signing of requests back in, + * we will need to allocate a signature + * structure for the request, fill in the "derCerts" field in it, + * save the signerCert there, as well as fill in the "requestorName" + * field of the tbsRequest. + */ + if (signerCert != NULL) { + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + return NULL; + } + request = ocsp_prepareEmptyOCSPRequest(); + if (!request) + return NULL; + /* + * Now create the list of single requests, one for each cert. + */ + request->tbsRequest->requestList = + ocsp_CreateSingleRequestList(request->arena, + certList, + time, + addServiceLocator); + if (request->tbsRequest->requestList == NULL) { + PORT_FreeArena(request->arena, PR_FALSE); + return NULL; + } + return request; +} + +/* + * FUNCTION: CERT_AddOCSPAcceptableResponses + * Add the AcceptableResponses extension to an OCSP Request. + * INPUTS: + * CERTOCSPRequest *request + * The request to which the extension should be added. + * ... + * A list (of one or more) of SECOidTag -- each of the response types + * to be added. The last OID *must* be SEC_OID_PKIX_OCSP_BASIC_RESPONSE. + * (This marks the end of the list, and it must be specified because a + * client conforming to the OCSP standard is required to handle the basic + * response type.) The OIDs are not checked in any way. + * RETURN: + * SECSuccess if the extension is added; SECFailure if anything goes wrong. + * All errors are internal or low-level problems (e.g. no memory). + */ + +void +SetRequestExts(void *object, CERTCertExtension **exts) +{ + CERTOCSPRequest *request = (CERTOCSPRequest *)object; + + request->tbsRequest->requestExtensions = exts; +} + +#if defined(__GNUC__) && !defined(NSS_NO_GCC48) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wvarargs" +#endif +SECStatus +CERT_AddOCSPAcceptableResponses(CERTOCSPRequest *request, + SECOidTag responseType0, ...) +{ + void *extHandle; + va_list ap; + int i, count; + SECOidTag responseType; + SECOidData *responseOid; + SECItem **acceptableResponses = NULL; + SECStatus rv = SECFailure; + + extHandle = request->tbsRequest->extensionHandle; + if (extHandle == NULL) { + extHandle = cert_StartExtensions(request, request->arena, SetRequestExts); + if (extHandle == NULL) + goto loser; + } + + /* Count number of OIDS going into the extension value. */ + count = 1; + if (responseType0 != SEC_OID_PKIX_OCSP_BASIC_RESPONSE) { + va_start(ap, responseType0); + do { + count++; + responseType = va_arg(ap, SECOidTag); + } while (responseType != SEC_OID_PKIX_OCSP_BASIC_RESPONSE); + va_end(ap); + } + + acceptableResponses = PORT_NewArray(SECItem *, count + 1); + if (acceptableResponses == NULL) + goto loser; + + i = 0; + responseOid = SECOID_FindOIDByTag(responseType0); + acceptableResponses[i++] = &(responseOid->oid); + if (count > 1) { + va_start(ap, responseType0); + for (; i < count; i++) { + responseType = va_arg(ap, SECOidTag); + responseOid = SECOID_FindOIDByTag(responseType); + acceptableResponses[i] = &(responseOid->oid); + } + va_end(ap); + } + acceptableResponses[i] = NULL; + + rv = CERT_EncodeAndAddExtension(extHandle, SEC_OID_PKIX_OCSP_RESPONSE, + &acceptableResponses, PR_FALSE, + SEC_ASN1_GET(SEC_SequenceOfObjectIDTemplate)); + if (rv != SECSuccess) + goto loser; + + PORT_Free(acceptableResponses); + if (request->tbsRequest->extensionHandle == NULL) + request->tbsRequest->extensionHandle = extHandle; + return SECSuccess; + +loser: + if (acceptableResponses != NULL) + PORT_Free(acceptableResponses); + if (extHandle != NULL) + (void)CERT_FinishExtensions(extHandle); + return rv; +} +#if defined(__GNUC__) && !defined(NSS_NO_GCC48) +#pragma GCC diagnostic pop +#endif + +/* + * FUNCTION: CERT_DestroyOCSPRequest + * Frees an OCSP Request structure. + * INPUTS: + * CERTOCSPRequest *request + * Pointer to CERTOCSPRequest to be freed. + * RETURN: + * No return value; no errors. + */ +void +CERT_DestroyOCSPRequest(CERTOCSPRequest *request) +{ + if (request == NULL) + return; + + if (request->tbsRequest != NULL) { + if (request->tbsRequest->requestorName != NULL) + CERT_DestroyGeneralNameList(request->tbsRequest->requestorName); + if (request->tbsRequest->extensionHandle != NULL) + (void)CERT_FinishExtensions(request->tbsRequest->extensionHandle); + } + + if (request->optionalSignature != NULL) { + if (request->optionalSignature->cert != NULL) + CERT_DestroyCertificate(request->optionalSignature->cert); + + /* + * XXX Need to free derCerts? Or do they come out of arena? + * (Currently we never fill in derCerts, which is why the + * answer is not obvious. Once we do, add any necessary code + * here and remove this comment.) + */ + } + + /* + * We should actually never have a request without an arena, + * but check just in case. (If there isn't one, there is not + * much we can do about it...) + */ + PORT_Assert(request->arena != NULL); + if (request->arena != NULL) + PORT_FreeArena(request->arena, PR_FALSE); +} + +/* + * RESPONSE SUPPORT FUNCTIONS (encode/create/decode/destroy): + */ + +/* + * Helper function for encoding or decoding a ResponderID -- based on the + * given type, return the associated template for that choice. + */ +static const SEC_ASN1Template * +ocsp_ResponderIDTemplateByType(CERTOCSPResponderIDType responderIDType) +{ + const SEC_ASN1Template *responderIDTemplate; + + switch (responderIDType) { + case ocspResponderID_byName: + responderIDTemplate = ocsp_ResponderIDByNameTemplate; + break; + case ocspResponderID_byKey: + responderIDTemplate = ocsp_ResponderIDByKeyTemplate; + break; + case ocspResponderID_other: + default: + PORT_Assert(responderIDType == ocspResponderID_other); + responderIDTemplate = ocsp_ResponderIDOtherTemplate; + break; + } + + return responderIDTemplate; +} + +/* + * Helper function for encoding or decoding a CertStatus -- based on the + * given type, return the associated template for that choice. + */ +static const SEC_ASN1Template * +ocsp_CertStatusTemplateByType(ocspCertStatusType certStatusType) +{ + const SEC_ASN1Template *certStatusTemplate; + + switch (certStatusType) { + case ocspCertStatus_good: + certStatusTemplate = ocsp_CertStatusGoodTemplate; + break; + case ocspCertStatus_revoked: + certStatusTemplate = ocsp_CertStatusRevokedTemplate; + break; + case ocspCertStatus_unknown: + certStatusTemplate = ocsp_CertStatusUnknownTemplate; + break; + case ocspCertStatus_other: + default: + PORT_Assert(certStatusType == ocspCertStatus_other); + certStatusTemplate = ocsp_CertStatusOtherTemplate; + break; + } + + return certStatusTemplate; +} + +/* + * Helper function for decoding a certStatus -- turn the actual DER tag + * into our local translation. + */ +static ocspCertStatusType +ocsp_CertStatusTypeByTag(int derTag) +{ + ocspCertStatusType certStatusType; + + switch (derTag) { + case 0: + certStatusType = ocspCertStatus_good; + break; + case 1: + certStatusType = ocspCertStatus_revoked; + break; + case 2: + certStatusType = ocspCertStatus_unknown; + break; + default: + certStatusType = ocspCertStatus_other; + break; + } + + return certStatusType; +} + +/* + * Helper function for decoding SingleResponses -- they each contain + * a status which is encoded as CHOICE, which needs to be decoded "by hand". + * + * Note -- on error, this routine does not release the memory it may + * have allocated; it expects its caller to do that. + */ +static SECStatus +ocsp_FinishDecodingSingleResponses(PLArenaPool *reqArena, + CERTOCSPSingleResponse **responses) +{ + ocspCertStatus *certStatus; + ocspCertStatusType certStatusType; + const SEC_ASN1Template *certStatusTemplate; + int derTag; + int i; + SECStatus rv = SECFailure; + + if (!reqArena) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (responses == NULL) /* nothing to do */ + return SECSuccess; + + for (i = 0; responses[i] != NULL; i++) { + SECItem *newStatus; + /* + * The following assert points out internal errors (problems in + * the template definitions or in the ASN.1 decoder itself, etc.). + */ + PORT_Assert(responses[i]->derCertStatus.data != NULL); + + derTag = responses[i]->derCertStatus.data[0] & SEC_ASN1_TAGNUM_MASK; + certStatusType = ocsp_CertStatusTypeByTag(derTag); + certStatusTemplate = ocsp_CertStatusTemplateByType(certStatusType); + + certStatus = PORT_ArenaZAlloc(reqArena, sizeof(ocspCertStatus)); + if (certStatus == NULL) { + goto loser; + } + newStatus = SECITEM_ArenaDupItem(reqArena, &responses[i]->derCertStatus); + if (!newStatus) { + goto loser; + } + rv = SEC_QuickDERDecodeItem(reqArena, certStatus, certStatusTemplate, + newStatus); + if (rv != SECSuccess) { + if (PORT_GetError() == SEC_ERROR_BAD_DER) + PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE); + goto loser; + } + + certStatus->certStatusType = certStatusType; + responses[i]->certStatus = certStatus; + } + + return SECSuccess; + +loser: + return rv; +} + +/* + * Helper function for decoding a responderID -- turn the actual DER tag + * into our local translation. + */ +static CERTOCSPResponderIDType +ocsp_ResponderIDTypeByTag(int derTag) +{ + CERTOCSPResponderIDType responderIDType; + + switch (derTag) { + case 1: + responderIDType = ocspResponderID_byName; + break; + case 2: + responderIDType = ocspResponderID_byKey; + break; + default: + responderIDType = ocspResponderID_other; + break; + } + + return responderIDType; +} + +/* + * Decode "src" as a BasicOCSPResponse, returning the result. + */ +static ocspBasicOCSPResponse * +ocsp_DecodeBasicOCSPResponse(PLArenaPool *arena, SECItem *src) +{ + void *mark; + ocspBasicOCSPResponse *basicResponse; + ocspResponseData *responseData; + ocspResponderID *responderID; + CERTOCSPResponderIDType responderIDType; + const SEC_ASN1Template *responderIDTemplate; + int derTag; + SECStatus rv; + SECItem newsrc; + + mark = PORT_ArenaMark(arena); + + basicResponse = PORT_ArenaZAlloc(arena, sizeof(ocspBasicOCSPResponse)); + if (basicResponse == NULL) { + goto loser; + } + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newsrc, src); + if (rv != SECSuccess) { + goto loser; + } + + rv = SEC_QuickDERDecodeItem(arena, basicResponse, + ocsp_BasicOCSPResponseTemplate, &newsrc); + if (rv != SECSuccess) { + if (PORT_GetError() == SEC_ERROR_BAD_DER) + PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE); + goto loser; + } + + responseData = basicResponse->tbsResponseData; + + /* + * The following asserts point out internal errors (problems in + * the template definitions or in the ASN.1 decoder itself, etc.). + */ + PORT_Assert(responseData != NULL); + PORT_Assert(responseData->derResponderID.data != NULL); + + /* + * XXX Because responderID is a CHOICE, which is not currently handled + * by our ASN.1 decoder, we have to decode it "by hand". + */ + derTag = responseData->derResponderID.data[0] & SEC_ASN1_TAGNUM_MASK; + responderIDType = ocsp_ResponderIDTypeByTag(derTag); + responderIDTemplate = ocsp_ResponderIDTemplateByType(responderIDType); + + responderID = PORT_ArenaZAlloc(arena, sizeof(ocspResponderID)); + if (responderID == NULL) { + goto loser; + } + + rv = SEC_QuickDERDecodeItem(arena, responderID, responderIDTemplate, + &responseData->derResponderID); + if (rv != SECSuccess) { + if (PORT_GetError() == SEC_ERROR_BAD_DER) + PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE); + goto loser; + } + + responderID->responderIDType = responderIDType; + responseData->responderID = responderID; + + /* + * XXX Each SingleResponse also contains a CHOICE, which has to be + * fixed up by hand. + */ + rv = ocsp_FinishDecodingSingleResponses(arena, responseData->responses); + if (rv != SECSuccess) { + goto loser; + } + + PORT_ArenaUnmark(arena, mark); + return basicResponse; + +loser: + PORT_ArenaRelease(arena, mark); + return NULL; +} + +/* + * Decode the responseBytes based on the responseType found in "rbytes", + * leaving the resulting translated/decoded information in there as well. + */ +static SECStatus +ocsp_DecodeResponseBytes(PLArenaPool *arena, ocspResponseBytes *rbytes) +{ + if (rbytes == NULL) { + PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE); + return SECFailure; + } + + rbytes->responseTypeTag = SECOID_FindOIDTag(&rbytes->responseType); + switch (rbytes->responseTypeTag) { + case SEC_OID_PKIX_OCSP_BASIC_RESPONSE: { + ocspBasicOCSPResponse *basicResponse; + + basicResponse = ocsp_DecodeBasicOCSPResponse(arena, + &rbytes->response); + if (basicResponse == NULL) + return SECFailure; + + rbytes->decodedResponse.basic = basicResponse; + } break; + + /* + * Add new/future response types here. + */ + + default: + PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE); + return SECFailure; + } + + return SECSuccess; +} + +/* + * FUNCTION: CERT_DecodeOCSPResponse + * Decode a DER encoded OCSP Response. + * INPUTS: + * SECItem *src + * Pointer to a SECItem holding DER encoded OCSP Response. + * RETURN: + * Returns a pointer to a CERTOCSPResponse (the decoded OCSP Response); + * the caller is responsible for destroying it. Or NULL if error (either + * response could not be decoded (SEC_ERROR_OCSP_MALFORMED_RESPONSE), + * it was of an unexpected type (SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE), + * or a low-level or internal error occurred). + */ +CERTOCSPResponse * +CERT_DecodeOCSPResponse(const SECItem *src) +{ + PLArenaPool *arena = NULL; + CERTOCSPResponse *response = NULL; + SECStatus rv = SECFailure; + ocspResponseStatus sv; + SECItem newSrc; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + goto loser; + } + response = (CERTOCSPResponse *)PORT_ArenaZAlloc(arena, + sizeof(CERTOCSPResponse)); + if (response == NULL) { + goto loser; + } + response->arena = arena; + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newSrc, src); + if (rv != SECSuccess) { + goto loser; + } + + rv = SEC_QuickDERDecodeItem(arena, response, ocsp_OCSPResponseTemplate, &newSrc); + if (rv != SECSuccess) { + if (PORT_GetError() == SEC_ERROR_BAD_DER) + PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE); + goto loser; + } + + sv = (ocspResponseStatus)DER_GetInteger(&response->responseStatus); + response->statusValue = sv; + if (sv != ocspResponse_successful) { + /* + * If the response status is anything but successful, then we + * are all done with decoding; the status is all there is. + */ + return response; + } + + /* + * A successful response contains much more information, still encoded. + * Now we need to decode that. + */ + rv = ocsp_DecodeResponseBytes(arena, response->responseBytes); + if (rv != SECSuccess) { + goto loser; + } + + return response; + +loser: + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + return NULL; +} + +/* + * The way an OCSPResponse is defined, there are many levels to descend + * before getting to the actual response information. And along the way + * we need to check that the response *type* is recognizable, which for + * now means that it is a BasicOCSPResponse, because that is the only + * type currently defined. Rather than force all routines to perform + * a bunch of sanity checking every time they want to work on a response, + * this function isolates that and gives back the interesting part. + * Note that no copying is done, this just returns a pointer into the + * substructure of the response which is passed in. + * + * XXX This routine only works when a valid response structure is passed + * into it; this is checked with many assertions. Assuming the response + * was creating by decoding, it wouldn't make it this far without being + * okay. That is a sufficient assumption since the entire OCSP interface + * is only used internally. When this interface is officially exported, + * each assertion below will need to be followed-up with setting an error + * and returning (null). + * + * FUNCTION: ocsp_GetResponseData + * Returns ocspResponseData structure and a pointer to tbs response + * data DER from a valid ocsp response. + * INPUTS: + * CERTOCSPResponse *response + * structure of a valid ocsp response + * RETURN: + * Returns a pointer to ocspResponseData structure: decoded OCSP response + * data, and a pointer(tbsResponseDataDER) to its undecoded data DER. + */ +ocspResponseData * +ocsp_GetResponseData(CERTOCSPResponse *response, SECItem **tbsResponseDataDER) +{ + ocspBasicOCSPResponse *basic; + ocspResponseData *responseData; + + PORT_Assert(response != NULL); + + PORT_Assert(response->responseBytes != NULL); + + PORT_Assert(response->responseBytes->responseTypeTag == + SEC_OID_PKIX_OCSP_BASIC_RESPONSE); + + basic = response->responseBytes->decodedResponse.basic; + PORT_Assert(basic != NULL); + + responseData = basic->tbsResponseData; + PORT_Assert(responseData != NULL); + + if (tbsResponseDataDER) { + *tbsResponseDataDER = &basic->tbsResponseDataDER; + + PORT_Assert((*tbsResponseDataDER)->data != NULL); + PORT_Assert((*tbsResponseDataDER)->len != 0); + } + + return responseData; +} + +/* + * Much like the routine above, except it returns the response signature. + * Again, no copy is done. + */ +ocspSignature * +ocsp_GetResponseSignature(CERTOCSPResponse *response) +{ + ocspBasicOCSPResponse *basic; + + PORT_Assert(response != NULL); + if (NULL == response->responseBytes) { + return NULL; + } + if (response->responseBytes->responseTypeTag != + SEC_OID_PKIX_OCSP_BASIC_RESPONSE) { + return NULL; + } + basic = response->responseBytes->decodedResponse.basic; + PORT_Assert(basic != NULL); + + return &(basic->responseSignature); +} + +/* + * FUNCTION: CERT_DestroyOCSPResponse + * Frees an OCSP Response structure. + * INPUTS: + * CERTOCSPResponse *request + * Pointer to CERTOCSPResponse to be freed. + * RETURN: + * No return value; no errors. + */ +void +CERT_DestroyOCSPResponse(CERTOCSPResponse *response) +{ + if (response != NULL) { + ocspSignature *signature = ocsp_GetResponseSignature(response); + if (signature && signature->cert != NULL) + CERT_DestroyCertificate(signature->cert); + + /* + * We should actually never have a response without an arena, + * but check just in case. (If there isn't one, there is not + * much we can do about it...) + */ + PORT_Assert(response->arena != NULL); + if (response->arena != NULL) { + PORT_FreeArena(response->arena, PR_FALSE); + } + } +} + +/* + * OVERALL OCSP CLIENT SUPPORT (make and send a request, verify a response): + */ + +/* + * Pick apart a URL, saving the important things in the passed-in pointers. + * + * We expect to find "http://[:]/[path]", though we will + * tolerate that final slash character missing, as well as beginning and + * trailing whitespace, and any-case-characters for "http". All of that + * tolerance is what complicates this routine. What we want is just to + * pick out the hostname, the port, and the path. + * + * On a successful return, the caller will need to free the output pieces + * of hostname and path, which are copies of the values found in the url. + */ +static SECStatus +ocsp_ParseURL(const char *url, char **pHostname, PRUint16 *pPort, char **pPath) +{ + unsigned short port = 80; /* default, in case not in url */ + char *hostname = NULL; + char *path = NULL; + const char *save; + char c; + int len; + + if (url == NULL) + goto loser; + + /* + * Skip beginning whitespace. + */ + c = *url; + while ((c == ' ' || c == '\t') && c != '\0') { + url++; + c = *url; + } + if (c == '\0') + goto loser; + + /* + * Confirm, then skip, protocol. (Since we only know how to do http, + * that is all we will accept). + */ + if (PORT_Strncasecmp(url, "http://", 7) != 0) + goto loser; + url += 7; + + /* + * Whatever comes next is the hostname (or host IP address). We just + * save it aside and then search for its end so we can determine its + * length and copy it. + * + * XXX Note that because we treat a ':' as a terminator character + * (and below, we expect that to mean there is a port specification + * immediately following), we will not handle IPv6 addresses. That is + * apparently an acceptable limitation, for the time being. Some day, + * when there is a clear way to specify a URL with an IPv6 address that + * can be parsed unambiguously, this code should be made to do that. + */ + save = url; + c = *url; + while (c != '/' && c != ':' && c != '\0' && c != ' ' && c != '\t') { + url++; + c = *url; + } + len = url - save; + hostname = PORT_Alloc(len + 1); + if (hostname == NULL) + goto loser; + PORT_Memcpy(hostname, save, len); + hostname[len] = '\0'; + + /* + * Now we figure out if there was a port specified or not. + * If so, we need to parse it (as a number) and skip it. + */ + if (c == ':') { + url++; + port = (unsigned short)PORT_Atoi(url); + c = *url; + while (c != '/' && c != '\0' && c != ' ' && c != '\t') { + if (c < '0' || c > '9') + goto loser; + url++; + c = *url; + } + } + + /* + * Last thing to find is a path. There *should* be a slash, + * if nothing else -- but if there is not we provide one. + */ + if (c == '/') { + save = url; + while (c != '\0' && c != ' ' && c != '\t') { + url++; + c = *url; + } + len = url - save; + path = PORT_Alloc(len + 1); + if (path == NULL) + goto loser; + PORT_Memcpy(path, save, len); + path[len] = '\0'; + } else { + path = PORT_Strdup("/"); + if (path == NULL) + goto loser; + } + + *pHostname = hostname; + *pPort = port; + *pPath = path; + return SECSuccess; + +loser: + if (hostname != NULL) + PORT_Free(hostname); + PORT_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION); + return SECFailure; +} + +/* + * Open a socket to the specified host on the specified port, and return it. + * The host is either a hostname or an IP address. + */ +static PRFileDesc * +ocsp_ConnectToHost(const char *host, PRUint16 port) +{ + PRFileDesc *sock = NULL; + PRIntervalTime timeout; + PRNetAddr addr; + char *netdbbuf = NULL; + + sock = PR_NewTCPSocket(); + if (sock == NULL) + goto loser; + + /* XXX Some day need a way to set (and get?) the following value */ + timeout = PR_SecondsToInterval(30); + + /* + * If the following converts an IP address string in "dot notation" + * into a PRNetAddr. If it fails, we assume that is because we do not + * have such an address, but instead a host *name*. In that case we + * then lookup the host by name. Using the NSPR function this way + * means we do not have to have our own logic for distinguishing a + * valid numerical IP address from a hostname. + */ + if (PR_StringToNetAddr(host, &addr) != PR_SUCCESS) { + PRIntn hostIndex; + PRHostEnt hostEntry; + + netdbbuf = PORT_Alloc(PR_NETDB_BUF_SIZE); + if (netdbbuf == NULL) + goto loser; + + if (PR_GetHostByName(host, netdbbuf, PR_NETDB_BUF_SIZE, + &hostEntry) != PR_SUCCESS) + goto loser; + + hostIndex = 0; + do { + hostIndex = PR_EnumerateHostEnt(hostIndex, &hostEntry, port, &addr); + if (hostIndex <= 0) + goto loser; + } while (PR_Connect(sock, &addr, timeout) != PR_SUCCESS); + + PORT_Free(netdbbuf); + } else { + /* + * First put the port into the address, then connect. + */ + if (PR_InitializeNetAddr(PR_IpAddrNull, port, &addr) != PR_SUCCESS) + goto loser; + if (PR_Connect(sock, &addr, timeout) != PR_SUCCESS) + goto loser; + } + + return sock; + +loser: + if (sock != NULL) + PR_Close(sock); + if (netdbbuf != NULL) + PORT_Free(netdbbuf); + return NULL; +} + +/* + * Sends an encoded OCSP request to the server identified by "location", + * and returns the socket on which it was sent (so can listen for the reply). + * "location" is expected to be a valid URL -- an error parsing it produces + * SEC_ERROR_CERT_BAD_ACCESS_LOCATION. Other errors are likely problems + * connecting to it, or writing to it, or allocating memory, and the low-level + * errors appropriate to the problem will be set. + * if (encodedRequest == NULL) + * then location MUST already include the full request, + * including base64 and urlencode, + * and the request will be sent with GET + * if (encodedRequest != NULL) + * then the request will be sent with POST + */ +static PRFileDesc * +ocsp_SendEncodedRequest(const char *location, const SECItem *encodedRequest) +{ + char *hostname = NULL; + char *path = NULL; + PRUint16 port; + SECStatus rv; + PRFileDesc *sock = NULL; + PRFileDesc *returnSock = NULL; + char *header = NULL; + char portstr[16]; + + /* + * Take apart the location, getting the hostname, port, and path. + */ + rv = ocsp_ParseURL(location, &hostname, &port, &path); + if (rv != SECSuccess) + goto loser; + + PORT_Assert(hostname != NULL); + PORT_Assert(path != NULL); + + sock = ocsp_ConnectToHost(hostname, port); + if (sock == NULL) + goto loser; + + portstr[0] = '\0'; + if (port != 80) { + PR_snprintf(portstr, sizeof(portstr), ":%d", port); + } + + if (!encodedRequest) { + header = PR_smprintf("GET %s HTTP/1.0\r\n" + "Host: %s%s\r\n\r\n", + path, hostname, portstr); + if (header == NULL) + goto loser; + + /* + * The NSPR documentation promises that if it can, it will write the full + * amount; this will not return a partial value expecting us to loop. + */ + if (PR_Write(sock, header, (PRInt32)PORT_Strlen(header)) < 0) + goto loser; + } else { + header = PR_smprintf("POST %s HTTP/1.0\r\n" + "Host: %s%s\r\n" + "Content-Type: application/ocsp-request\r\n" + "Content-Length: %u\r\n\r\n", + path, hostname, portstr, encodedRequest->len); + if (header == NULL) + goto loser; + + /* + * The NSPR documentation promises that if it can, it will write the full + * amount; this will not return a partial value expecting us to loop. + */ + if (PR_Write(sock, header, (PRInt32)PORT_Strlen(header)) < 0) + goto loser; + + if (PR_Write(sock, encodedRequest->data, + (PRInt32)encodedRequest->len) < 0) + goto loser; + } + + returnSock = sock; + sock = NULL; + +loser: + if (header != NULL) + PORT_Free(header); + if (sock != NULL) + PR_Close(sock); + if (path != NULL) + PORT_Free(path); + if (hostname != NULL) + PORT_Free(hostname); + + return returnSock; +} + +/* + * Read from "fd" into "buf" -- expect/attempt to read a given number of bytes + * Obviously, stop if hit end-of-stream. Timeout is passed in. + */ + +static int +ocsp_read(PRFileDesc *fd, char *buf, int toread, PRIntervalTime timeout) +{ + int total = 0; + + while (total < toread) { + PRInt32 got; + + got = PR_Recv(fd, buf + total, (PRInt32)(toread - total), 0, timeout); + if (got < 0) { + if (0 == total) { + total = -1; /* report the error if we didn't read anything yet */ + } + break; + } else if (got == 0) { /* EOS */ + break; + } + + total += got; + } + + return total; +} + +#define OCSP_BUFSIZE 1024 + +#define AbortHttpDecode(error) \ + { \ + if (inBuffer) \ + PORT_Free(inBuffer); \ + PORT_SetError(error); \ + return NULL; \ + } + +/* + * Reads on the given socket and returns an encoded response when received. + * Properly formatted HTTP/1.0 response headers are expected to be read + * from the socket, preceding a binary-encoded OCSP response. Problems + * with parsing cause the error SEC_ERROR_OCSP_BAD_HTTP_RESPONSE to be + * set; any other problems are likely low-level i/o or memory allocation + * errors. + */ +static SECItem * +ocsp_GetEncodedResponse(PLArenaPool *arena, PRFileDesc *sock) +{ + /* first read HTTP status line and headers */ + + char *inBuffer = NULL; + PRInt32 offset = 0; + PRInt32 inBufsize = 0; + const PRInt32 bufSizeIncrement = OCSP_BUFSIZE; /* 1 KB at a time */ + const PRInt32 maxBufSize = 8 * bufSizeIncrement; /* 8 KB max */ + const char *CRLF = "\r\n"; + const PRInt32 CRLFlen = strlen(CRLF); + const char *headerEndMark = "\r\n\r\n"; + const PRInt32 markLen = strlen(headerEndMark); + const PRIntervalTime ocsptimeout = + PR_SecondsToInterval(30); /* hardcoded to 30s for now */ + char *headerEnd = NULL; + PRBool EOS = PR_FALSE; + const char *httpprotocol = "HTTP/"; + const PRInt32 httplen = strlen(httpprotocol); + const char *httpcode = NULL; + const char *contenttype = NULL; + PRInt32 contentlength = 0; + PRInt32 bytesRead = 0; + char *statusLineEnd = NULL; + char *space = NULL; + char *nextHeader = NULL; + SECItem *result = NULL; + + /* read up to at least the end of the HTTP headers */ + do { + inBufsize += bufSizeIncrement; + inBuffer = PORT_Realloc(inBuffer, inBufsize + 1); + if (NULL == inBuffer) { + AbortHttpDecode(SEC_ERROR_NO_MEMORY); + } + bytesRead = ocsp_read(sock, inBuffer + offset, bufSizeIncrement, + ocsptimeout); + if (bytesRead > 0) { + PRInt32 searchOffset = (offset - markLen) > 0 ? offset - markLen : 0; + offset += bytesRead; + *(inBuffer + offset) = '\0'; /* NULL termination */ + headerEnd = strstr((const char *)inBuffer + searchOffset, headerEndMark); + if (bytesRead < bufSizeIncrement) { + /* we read less data than requested, therefore we are at + EOS or there was a read error */ + EOS = PR_TRUE; + } + } else { + /* recv error or EOS */ + EOS = PR_TRUE; + } + } while ((!headerEnd) && (PR_FALSE == EOS) && + (inBufsize < maxBufSize)); + + if (!headerEnd) { + AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); + } + + /* parse the HTTP status line */ + statusLineEnd = strstr((const char *)inBuffer, CRLF); + if (!statusLineEnd) { + AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); + } + *statusLineEnd = '\0'; + + /* check for HTTP/ response */ + space = strchr((const char *)inBuffer, ' '); + if (!space || PORT_Strncasecmp((const char *)inBuffer, httpprotocol, httplen) != 0) { + AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); + } + + /* check the HTTP status code of 200 */ + httpcode = space + 1; + space = strchr(httpcode, ' '); + if (!space) { + AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); + } + *space = 0; + if (0 != strcmp(httpcode, "200")) { + AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); + } + + /* parse the HTTP headers in the buffer . We only care about + content-type and content-length + */ + + nextHeader = statusLineEnd + CRLFlen; + *headerEnd = '\0'; /* terminate */ + do { + char *thisHeaderEnd = NULL; + char *value = NULL; + char *colon = strchr(nextHeader, ':'); + + if (!colon) { + AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); + } + + *colon = '\0'; + value = colon + 1; + + /* jpierre - note : the following code will only handle the basic form + of HTTP/1.0 response headers, of the form "name: value" . Headers + split among multiple lines are not supported. This is not common + and should not be an issue, but it could become one in the + future */ + + if (*value != ' ') { + AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); + } + + value++; + thisHeaderEnd = strstr(value, CRLF); + if (thisHeaderEnd) { + *thisHeaderEnd = '\0'; + } + + if (0 == PORT_Strcasecmp(nextHeader, "content-type")) { + contenttype = value; + } else if (0 == PORT_Strcasecmp(nextHeader, "content-length")) { + contentlength = atoi(value); + } + + if (thisHeaderEnd) { + nextHeader = thisHeaderEnd + CRLFlen; + } else { + nextHeader = NULL; + } + + } while (nextHeader && (nextHeader < (headerEnd + CRLFlen))); + + /* check content-type */ + if (!contenttype || + (0 != PORT_Strcasecmp(contenttype, "application/ocsp-response"))) { + AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); + } + + /* read the body of the OCSP response */ + offset = offset - (PRInt32)(headerEnd - (const char *)inBuffer) - markLen; + if (offset) { + /* move all data to the beginning of the buffer */ + PORT_Memmove(inBuffer, headerEnd + markLen, offset); + } + + /* resize buffer to only what's needed to hold the current response */ + inBufsize = (1 + (offset - 1) / bufSizeIncrement) * bufSizeIncrement; + + while ((PR_FALSE == EOS) && + ((contentlength == 0) || (offset < contentlength)) && + (inBufsize < maxBufSize)) { + /* we still need to receive more body data */ + inBufsize += bufSizeIncrement; + inBuffer = PORT_Realloc(inBuffer, inBufsize + 1); + if (NULL == inBuffer) { + AbortHttpDecode(SEC_ERROR_NO_MEMORY); + } + bytesRead = ocsp_read(sock, inBuffer + offset, bufSizeIncrement, + ocsptimeout); + if (bytesRead > 0) { + offset += bytesRead; + if (bytesRead < bufSizeIncrement) { + /* we read less data than requested, therefore we are at + EOS or there was a read error */ + EOS = PR_TRUE; + } + } else { + /* recv error or EOS */ + EOS = PR_TRUE; + } + } + + if (0 == offset) { + AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); + } + + /* + * Now allocate the item to hold the data. + */ + result = SECITEM_AllocItem(arena, NULL, offset); + if (NULL == result) { + AbortHttpDecode(SEC_ERROR_NO_MEMORY); + } + + /* + * And copy the data left in the buffer. + */ + PORT_Memcpy(result->data, inBuffer, offset); + + /* and free the temporary buffer */ + PORT_Free(inBuffer); + return result; +} + +SECStatus +CERT_ParseURL(const char *url, char **pHostname, PRUint16 *pPort, char **pPath) +{ + return ocsp_ParseURL(url, pHostname, pPort, pPath); +} + +/* + * Limit the size of http responses we are willing to accept. + */ +#define MAX_WANTED_OCSP_RESPONSE_LEN 64 * 1024 + +/* if (encodedRequest == NULL) + * then location MUST already include the full request, + * including base64 and urlencode, + * and the request will be sent with GET + * if (encodedRequest != NULL) + * then the request will be sent with POST + */ +static SECItem * +fetchOcspHttpClientV1(PLArenaPool *arena, + const SEC_HttpClientFcnV1 *hcv1, + const char *location, + const SECItem *encodedRequest) +{ + char *hostname = NULL; + char *path = NULL; + PRUint16 port; + SECItem *encodedResponse = NULL; + SEC_HTTP_SERVER_SESSION pServerSession = NULL; + SEC_HTTP_REQUEST_SESSION pRequestSession = NULL; + PRUint16 myHttpResponseCode; + const char *myHttpResponseData; + PRUint32 myHttpResponseDataLen; + + if (ocsp_ParseURL(location, &hostname, &port, &path) == SECFailure) { + PORT_SetError(SEC_ERROR_OCSP_MALFORMED_REQUEST); + goto loser; + } + + PORT_Assert(hostname != NULL); + PORT_Assert(path != NULL); + + if ((*hcv1->createSessionFcn)( + hostname, + port, + &pServerSession) != SECSuccess) { + PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR); + goto loser; + } + + /* We use a non-zero timeout, which means: + - the client will use blocking I/O + - TryFcn will not return WOULD_BLOCK nor a poll descriptor + - it's sufficient to call TryFcn once + No lock for accessing OCSP_Global.timeoutSeconds, bug 406120 + */ + + if ((*hcv1->createFcn)( + pServerSession, + "http", + path, + encodedRequest ? "POST" : "GET", + PR_TicksPerSecond() * OCSP_Global.timeoutSeconds, + &pRequestSession) != SECSuccess) { + PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR); + goto loser; + } + + if (encodedRequest && + (*hcv1->setPostDataFcn)( + pRequestSession, + (char *)encodedRequest->data, + encodedRequest->len, + "application/ocsp-request") != SECSuccess) { + PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR); + goto loser; + } + + /* we don't want result objects larger than this: */ + myHttpResponseDataLen = MAX_WANTED_OCSP_RESPONSE_LEN; + + OCSP_TRACE(("OCSP trySendAndReceive %s\n", location)); + + if ((*hcv1->trySendAndReceiveFcn)( + pRequestSession, + NULL, + &myHttpResponseCode, + NULL, + NULL, + &myHttpResponseData, + &myHttpResponseDataLen) != SECSuccess) { + PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR); + goto loser; + } + + OCSP_TRACE(("OCSP trySendAndReceive result http %d\n", myHttpResponseCode)); + + if (myHttpResponseCode != 200) { + PORT_SetError(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); + goto loser; + } + + encodedResponse = SECITEM_AllocItem(arena, NULL, myHttpResponseDataLen); + + if (!encodedResponse) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + PORT_Memcpy(encodedResponse->data, myHttpResponseData, myHttpResponseDataLen); + +loser: + if (pRequestSession != NULL) + (*hcv1->freeFcn)(pRequestSession); + if (pServerSession != NULL) + (*hcv1->freeSessionFcn)(pServerSession); + if (path != NULL) + PORT_Free(path); + if (hostname != NULL) + PORT_Free(hostname); + + return encodedResponse; +} + +/* + * FUNCTION: CERT_GetEncodedOCSPResponseByMethod + * Creates and sends a request to an OCSP responder, then reads and + * returns the (encoded) response. + * INPUTS: + * PLArenaPool *arena + * Pointer to arena from which return value will be allocated. + * If NULL, result will be allocated from the heap (and thus should + * be freed via SECITEM_FreeItem). + * CERTCertList *certList + * A list of certs for which status will be requested. + * Note that all of these certificates should have the same issuer, + * or it's expected the response will be signed by a trusted responder. + * If the certs need to be broken up into multiple requests, that + * must be handled by the caller (and thus by having multiple calls + * to this routine), who knows about where the request(s) are being + * sent and whether there are any trusted responders in place. + * const char *location + * The location of the OCSP responder (a URL). + * const char *method + * The protocol method used when retrieving the OCSP response. + * Currently support: "GET" (http GET) and "POST" (http POST). + * Additionals methods for http or other protocols might be added + * in the future. + * PRTime time + * Indicates the time for which the certificate status is to be + * determined -- this may be used in the search for the cert's issuer + * but has no other bearing on the operation. + * PRBool addServiceLocator + * If true, the Service Locator extension should be added to the + * single request(s) for each cert. + * CERTCertificate *signerCert + * If non-NULL, means sign the request using this cert. Otherwise, + * do not sign. + * void *pwArg + * Pointer to argument for password prompting, if needed. (Definitely + * not needed if not signing.) + * OUTPUTS: + * CERTOCSPRequest **pRequest + * Pointer in which to store the OCSP request created for the given + * list of certificates. It is only filled in if the entire operation + * is successful and the pointer is not null -- and in that case the + * caller is then reponsible for destroying it. + * RETURN: + * Returns a pointer to the SECItem holding the response. + * On error, returns null with error set describing the reason: + * SEC_ERROR_UNKNOWN_ISSUER + * SEC_ERROR_CERT_BAD_ACCESS_LOCATION + * SEC_ERROR_OCSP_BAD_HTTP_RESPONSE + * Other errors are low-level problems (no memory, bad database, etc.). + */ +SECItem * +CERT_GetEncodedOCSPResponseByMethod(PLArenaPool *arena, CERTCertList *certList, + const char *location, const char *method, + PRTime time, PRBool addServiceLocator, + CERTCertificate *signerCert, void *pwArg, + CERTOCSPRequest **pRequest) +{ + CERTOCSPRequest *request; + request = CERT_CreateOCSPRequest(certList, time, addServiceLocator, + signerCert); + if (!request) + return NULL; + return ocsp_GetEncodedOCSPResponseFromRequest(arena, request, location, + method, time, addServiceLocator, + pwArg, pRequest); +} + +/* + * FUNCTION: CERT_GetEncodedOCSPResponse + * Creates and sends a request to an OCSP responder, then reads and + * returns the (encoded) response. + * + * This is a legacy API that behaves identically to + * CERT_GetEncodedOCSPResponseByMethod using the "POST" method. + */ +SECItem * +CERT_GetEncodedOCSPResponse(PLArenaPool *arena, CERTCertList *certList, + const char *location, PRTime time, + PRBool addServiceLocator, + CERTCertificate *signerCert, void *pwArg, + CERTOCSPRequest **pRequest) +{ + return CERT_GetEncodedOCSPResponseByMethod(arena, certList, location, + "POST", time, addServiceLocator, + signerCert, pwArg, pRequest); +} + +/* URL encode a buffer that consists of base64-characters, only, + * which means we can use a simple encoding logic. + * + * No output buffer size checking is performed. + * You should call the function twice, to calculate the required buffer size. + * + * If the outpufBuf parameter is NULL, the function will calculate the + * required size, including the trailing zero termination char. + * + * The function returns the number of bytes calculated or produced. + */ +size_t +ocsp_UrlEncodeBase64Buf(const char *base64Buf, char *outputBuf) +{ + const char *walkInput = NULL; + char *walkOutput = outputBuf; + size_t count = 0; + + for (walkInput = base64Buf; *walkInput; ++walkInput) { + char c = *walkInput; + if (isspace(c)) + continue; + switch (c) { + case '+': + if (outputBuf) { + strcpy(walkOutput, "%2B"); + walkOutput += 3; + } + count += 3; + break; + case '/': + if (outputBuf) { + strcpy(walkOutput, "%2F"); + walkOutput += 3; + } + count += 3; + break; + case '=': + if (outputBuf) { + strcpy(walkOutput, "%3D"); + walkOutput += 3; + } + count += 3; + break; + default: + if (outputBuf) { + *walkOutput = *walkInput; + ++walkOutput; + } + ++count; + break; + } + } + if (outputBuf) { + *walkOutput = 0; + } + ++count; + return count; +} + +enum { max_get_request_size = 255 }; /* defined by RFC2560 */ + +static SECItem * +cert_GetOCSPResponse(PLArenaPool *arena, const char *location, + const SECItem *encodedRequest); + +static SECItem * +ocsp_GetEncodedOCSPResponseFromRequest(PLArenaPool *arena, + CERTOCSPRequest *request, + const char *location, + const char *method, + PRTime time, + PRBool addServiceLocator, + void *pwArg, + CERTOCSPRequest **pRequest) +{ + SECItem *encodedRequest = NULL; + SECItem *encodedResponse = NULL; + SECStatus rv; + + if (!location || !*location) /* location should be at least one byte */ + goto loser; + + rv = CERT_AddOCSPAcceptableResponses(request, + SEC_OID_PKIX_OCSP_BASIC_RESPONSE); + if (rv != SECSuccess) + goto loser; + + encodedRequest = CERT_EncodeOCSPRequest(NULL, request, pwArg); + if (encodedRequest == NULL) + goto loser; + + if (!strcmp(method, "GET")) { + encodedResponse = cert_GetOCSPResponse(arena, location, encodedRequest); + } else if (!strcmp(method, "POST")) { + encodedResponse = CERT_PostOCSPRequest(arena, location, encodedRequest); + } else { + goto loser; + } + + if (encodedResponse != NULL && pRequest != NULL) { + *pRequest = request; + request = NULL; /* avoid destroying below */ + } + +loser: + if (request != NULL) + CERT_DestroyOCSPRequest(request); + if (encodedRequest != NULL) + SECITEM_FreeItem(encodedRequest, PR_TRUE); + return encodedResponse; +} + +static SECItem * +cert_FetchOCSPResponse(PLArenaPool *arena, const char *location, + const SECItem *encodedRequest); + +/* using HTTP GET method */ +static SECItem * +cert_GetOCSPResponse(PLArenaPool *arena, const char *location, + const SECItem *encodedRequest) +{ + char *walkOutput = NULL; + char *fullGetPath = NULL; + size_t pathLength; + PRInt32 urlEncodedBufLength; + size_t base64size; + char b64ReqBuf[max_get_request_size + 1]; + size_t slashLengthIfNeeded = 0; + size_t getURLLength; + SECItem *item; + + if (!location || !*location) { + return NULL; + } + + pathLength = strlen(location); + if (location[pathLength - 1] != '/') { + slashLengthIfNeeded = 1; + } + + /* Calculation as documented by PL_Base64Encode function. + * Use integer conversion to avoid having to use function ceil(). + */ + base64size = (((encodedRequest->len + 2) / 3) * 4); + if (base64size > max_get_request_size) { + return NULL; + } + memset(b64ReqBuf, 0, sizeof(b64ReqBuf)); + PL_Base64Encode((const char *)encodedRequest->data, encodedRequest->len, + b64ReqBuf); + + urlEncodedBufLength = ocsp_UrlEncodeBase64Buf(b64ReqBuf, NULL); + getURLLength = pathLength + urlEncodedBufLength + slashLengthIfNeeded; + + /* urlEncodedBufLength already contains room for the zero terminator. + * Add another if we must add the '/' char. + */ + if (arena) { + fullGetPath = (char *)PORT_ArenaAlloc(arena, getURLLength); + } else { + fullGetPath = (char *)PORT_Alloc(getURLLength); + } + if (!fullGetPath) { + return NULL; + } + + strcpy(fullGetPath, location); + walkOutput = fullGetPath + pathLength; + + if (walkOutput > fullGetPath && slashLengthIfNeeded) { + strcpy(walkOutput, "/"); + ++walkOutput; + } + ocsp_UrlEncodeBase64Buf(b64ReqBuf, walkOutput); + + item = cert_FetchOCSPResponse(arena, fullGetPath, NULL); + if (!arena) { + PORT_Free(fullGetPath); + } + return item; +} + +SECItem * +CERT_PostOCSPRequest(PLArenaPool *arena, const char *location, + const SECItem *encodedRequest) +{ + return cert_FetchOCSPResponse(arena, location, encodedRequest); +} + +SECItem * +cert_FetchOCSPResponse(PLArenaPool *arena, const char *location, + const SECItem *encodedRequest) +{ + const SEC_HttpClientFcn *registeredHttpClient; + SECItem *encodedResponse = NULL; + + registeredHttpClient = SEC_GetRegisteredHttpClient(); + + if (registeredHttpClient && registeredHttpClient->version == 1) { + encodedResponse = fetchOcspHttpClientV1( + arena, + ®isteredHttpClient->fcnTable.ftable1, + location, + encodedRequest); + } else { + /* use internal http client */ + PRFileDesc *sock = ocsp_SendEncodedRequest(location, encodedRequest); + if (sock) { + encodedResponse = ocsp_GetEncodedResponse(arena, sock); + PR_Close(sock); + } + } + + return encodedResponse; +} + +static SECItem * +ocsp_GetEncodedOCSPResponseForSingleCert(PLArenaPool *arena, + CERTOCSPCertID *certID, + CERTCertificate *singleCert, + const char *location, + const char *method, + PRTime time, + PRBool addServiceLocator, + void *pwArg, + CERTOCSPRequest **pRequest) +{ + CERTOCSPRequest *request; + request = cert_CreateSingleCertOCSPRequest(certID, singleCert, time, + addServiceLocator, NULL); + if (!request) + return NULL; + return ocsp_GetEncodedOCSPResponseFromRequest(arena, request, location, + method, time, addServiceLocator, + pwArg, pRequest); +} + +/* Checks a certificate for the key usage extension of OCSP signer. */ +static PRBool +ocsp_CertIsOCSPDesignatedResponder(CERTCertificate *cert) +{ + SECStatus rv; + SECItem extItem; + SECItem **oids; + SECItem *oid; + SECOidTag oidTag; + PRBool retval; + CERTOidSequence *oidSeq = NULL; + + extItem.data = NULL; + rv = CERT_FindCertExtension(cert, SEC_OID_X509_EXT_KEY_USAGE, &extItem); + if (rv != SECSuccess) { + goto loser; + } + + oidSeq = CERT_DecodeOidSequence(&extItem); + if (oidSeq == NULL) { + goto loser; + } + + oids = oidSeq->oids; + while (*oids != NULL) { + oid = *oids; + + oidTag = SECOID_FindOIDTag(oid); + + if (oidTag == SEC_OID_OCSP_RESPONDER) { + goto success; + } + + oids++; + } + +loser: + retval = PR_FALSE; + PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT); + goto done; +success: + retval = PR_TRUE; +done: + if (extItem.data != NULL) { + PORT_Free(extItem.data); + } + if (oidSeq != NULL) { + CERT_DestroyOidSequence(oidSeq); + } + + return (retval); +} + +#ifdef LATER /* \ + * XXX This function is not currently used, but will \ + * be needed later when we do revocation checking of \ + * the responder certificate. Of course, it may need \ + * revising then, if the cert extension interface has \ + * changed. (Hopefully it will!) \ + */ + +/* Checks a certificate to see if it has the OCSP no check extension. */ +static PRBool +ocsp_CertHasNoCheckExtension(CERTCertificate *cert) +{ + SECStatus rv; + + rv = CERT_FindCertExtension(cert, SEC_OID_PKIX_OCSP_NO_CHECK, + NULL); + if (rv == SECSuccess) { + return PR_TRUE; + } + return PR_FALSE; +} +#endif /* LATER */ + +static PRBool +ocsp_matchcert(SECItem *certIndex, CERTCertificate *testCert) +{ + SECItem item; + unsigned char buf[HASH_LENGTH_MAX]; + + item.data = buf; + item.len = SHA1_LENGTH; + + if (CERT_GetSubjectPublicKeyDigest(NULL, testCert, SEC_OID_SHA1, + &item) == NULL) { + return PR_FALSE; + } + if (SECITEM_ItemsAreEqual(certIndex, &item)) { + return PR_TRUE; + } + if (CERT_GetSubjectPublicKeyDigest(NULL, testCert, SEC_OID_MD5, + &item) == NULL) { + return PR_FALSE; + } + if (SECITEM_ItemsAreEqual(certIndex, &item)) { + return PR_TRUE; + } + if (CERT_GetSubjectPublicKeyDigest(NULL, testCert, SEC_OID_MD2, + &item) == NULL) { + return PR_FALSE; + } + if (SECITEM_ItemsAreEqual(certIndex, &item)) { + return PR_TRUE; + } + + return PR_FALSE; +} + +static CERTCertificate * +ocsp_CertGetDefaultResponder(CERTCertDBHandle *handle, CERTOCSPCertID *certID); + +CERTCertificate * +ocsp_GetSignerCertificate(CERTCertDBHandle *handle, ocspResponseData *tbsData, + ocspSignature *signature, CERTCertificate *issuer) +{ + CERTCertificate **certs = NULL; + CERTCertificate *signerCert = NULL; + SECStatus rv = SECFailure; + PRBool lookupByName = PR_TRUE; + void *certIndex = NULL; + int certCount = 0; + + PORT_Assert(tbsData->responderID != NULL); + switch (tbsData->responderID->responderIDType) { + case ocspResponderID_byName: + lookupByName = PR_TRUE; + certIndex = &tbsData->derResponderID; + break; + case ocspResponderID_byKey: + lookupByName = PR_FALSE; + certIndex = &tbsData->responderID->responderIDValue.keyHash; + break; + case ocspResponderID_other: + default: + PORT_Assert(0); + PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE); + return NULL; + } + + /* + * If the signature contains some certificates as well, temporarily + * import them in case they are needed for verification. + * + * Note that the result of this is that each cert in "certs" needs + * to be destroyed. + */ + if (signature->derCerts != NULL) { + for (; signature->derCerts[certCount] != NULL; certCount++) { + /* just counting */ + } + rv = CERT_ImportCerts(handle, certUsageStatusResponder, certCount, + signature->derCerts, &certs, + PR_FALSE, PR_FALSE, NULL); + if (rv != SECSuccess) + goto finish; + } + + /* + * Now look up the certificate that did the signing. + * The signer can be specified either by name or by key hash. + */ + if (lookupByName) { + SECItem *crIndex = (SECItem *)certIndex; + SECItem encodedName; + PLArenaPool *arena; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena != NULL) { + + rv = SEC_QuickDERDecodeItem(arena, &encodedName, + ocsp_ResponderIDDerNameTemplate, + crIndex); + if (rv != SECSuccess) { + if (PORT_GetError() == SEC_ERROR_BAD_DER) + PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE); + } else { + signerCert = CERT_FindCertByName(handle, &encodedName); + } + PORT_FreeArena(arena, PR_FALSE); + } + } else { + /* + * The signer is either 1) a known issuer CA we passed in, + * 2) the default OCSP responder, or 3) an intermediate CA + * passed in the cert list to use. Figure out which it is. + */ + int i; + CERTCertificate *responder = + ocsp_CertGetDefaultResponder(handle, NULL); + if (responder && ocsp_matchcert(certIndex, responder)) { + signerCert = CERT_DupCertificate(responder); + } else if (issuer && ocsp_matchcert(certIndex, issuer)) { + signerCert = CERT_DupCertificate(issuer); + } + for (i = 0; (signerCert == NULL) && (i < certCount); i++) { + if (ocsp_matchcert(certIndex, certs[i])) { + signerCert = CERT_DupCertificate(certs[i]); + } + } + if (signerCert == NULL) { + PORT_SetError(SEC_ERROR_UNKNOWN_CERT); + } + } + +finish: + if (certs != NULL) { + CERT_DestroyCertArray(certs, certCount); + } + + return signerCert; +} + +SECStatus +ocsp_VerifyResponseSignature(CERTCertificate *signerCert, + ocspSignature *signature, + SECItem *tbsResponseDataDER, + void *pwArg) +{ + SECKEYPublicKey *signerKey = NULL; + SECStatus rv = SECFailure; + CERTSignedData signedData; + + /* + * Now get the public key from the signer's certificate; we need + * it to perform the verification. + */ + signerKey = CERT_ExtractPublicKey(signerCert); + if (signerKey == NULL) { + return SECFailure; + } + + /* + * We copy the signature data *pointer* and length, so that we can + * modify the length without damaging the original copy. This is a + * simple copy, not a dup, so no destroy/free is necessary. + */ + signedData.signature = signature->signature; + signedData.signatureAlgorithm = signature->signatureAlgorithm; + signedData.data = *tbsResponseDataDER; + + rv = CERT_VerifySignedDataWithPublicKey(&signedData, signerKey, pwArg); + if (rv != SECSuccess && + (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE || + PORT_GetError() == SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED)) { + PORT_SetError(SEC_ERROR_OCSP_BAD_SIGNATURE); + } + + if (signerKey != NULL) { + SECKEY_DestroyPublicKey(signerKey); + } + + return rv; +} + +/* + * FUNCTION: CERT_VerifyOCSPResponseSignature + * Check the signature on an OCSP Response. Will also perform a + * verification of the signer's certificate. Note, however, that a + * successful verification does not make any statement about the + * signer's *authority* to provide status for the certificate(s), + * that must be checked individually for each certificate. + * INPUTS: + * CERTOCSPResponse *response + * Pointer to response structure with signature to be checked. + * CERTCertDBHandle *handle + * Pointer to CERTCertDBHandle for certificate DB to use for verification. + * void *pwArg + * Pointer to argument for password prompting, if needed. + * OUTPUTS: + * CERTCertificate **pSignerCert + * Pointer in which to store signer's certificate; only filled-in if + * non-null. + * RETURN: + * Returns SECSuccess when signature is valid, anything else means invalid. + * Possible errors set: + * SEC_ERROR_OCSP_MALFORMED_RESPONSE - unknown type of ResponderID + * SEC_ERROR_INVALID_TIME - bad format of "ProducedAt" time + * SEC_ERROR_UNKNOWN_SIGNER - signer's cert could not be found + * SEC_ERROR_BAD_SIGNATURE - the signature did not verify + * Other errors are any of the many possible failures in cert verification + * (e.g. SEC_ERROR_REVOKED_CERTIFICATE, SEC_ERROR_UNTRUSTED_ISSUER) when + * verifying the signer's cert, or low-level problems (no memory, etc.) + */ +SECStatus +CERT_VerifyOCSPResponseSignature(CERTOCSPResponse *response, + CERTCertDBHandle *handle, void *pwArg, + CERTCertificate **pSignerCert, + CERTCertificate *issuer) +{ + SECItem *tbsResponseDataDER; + CERTCertificate *signerCert = NULL; + SECStatus rv = SECFailure; + PRTime producedAt; + + /* ocsp_DecodeBasicOCSPResponse will fail if asn1 decoder is unable + * to properly decode tbsData (see the function and + * ocsp_BasicOCSPResponseTemplate). Thus, tbsData can not be + * equal to null */ + ocspResponseData *tbsData = ocsp_GetResponseData(response, + &tbsResponseDataDER); + ocspSignature *signature = ocsp_GetResponseSignature(response); + + if (!signature) { + PORT_SetError(SEC_ERROR_OCSP_BAD_SIGNATURE); + return SECFailure; + } + + /* + * If this signature has already gone through verification, just + * return the cached result. + */ + if (signature->wasChecked) { + if (signature->status == SECSuccess) { + if (pSignerCert != NULL) + *pSignerCert = CERT_DupCertificate(signature->cert); + } else { + PORT_SetError(signature->failureReason); + } + return signature->status; + } + + signerCert = ocsp_GetSignerCertificate(handle, tbsData, + signature, issuer); + if (signerCert == NULL) { + rv = SECFailure; + if (PORT_GetError() == SEC_ERROR_UNKNOWN_CERT) { + /* Make the error a little more specific. */ + PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT); + } + goto finish; + } + + /* + * We could mark this true at the top of this function, or always + * below at "finish", but if the problem was just that we could not + * find the signer's cert, leave that as if the signature hasn't + * been checked in case a subsequent call might have better luck. + */ + signature->wasChecked = PR_TRUE; + + /* + * The function will also verify the signer certificate; we + * need to tell it *when* that certificate must be valid -- for our + * purposes we expect it to be valid when the response was signed. + * The value of "producedAt" is the signing time. + */ + rv = DER_GeneralizedTimeToTime(&producedAt, &tbsData->producedAt); + if (rv != SECSuccess) + goto finish; + + /* + * Just because we have a cert does not mean it is any good; check + * it for validity, trust and usage. + */ + if (!ocsp_CertIsOCSPDefaultResponder(handle, signerCert)) { + SECCertUsage certUsage; + if (CERT_IsCACert(signerCert, NULL)) { + certUsage = certUsageAnyCA; + } else { + certUsage = certUsageStatusResponder; + } + rv = cert_VerifyCertWithFlags(handle, signerCert, PR_TRUE, certUsage, + producedAt, CERT_VERIFYCERT_SKIP_OCSP, + pwArg, NULL); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT); + goto finish; + } + } + + rv = ocsp_VerifyResponseSignature(signerCert, signature, + tbsResponseDataDER, + pwArg); + +finish: + if (signature->wasChecked) + signature->status = rv; + + if (rv != SECSuccess) { + signature->failureReason = PORT_GetError(); + if (signerCert != NULL) + CERT_DestroyCertificate(signerCert); + } else { + /* + * Save signer's certificate in signature. + */ + signature->cert = signerCert; + if (pSignerCert != NULL) { + /* + * Pass pointer to signer's certificate back to our caller, + * who is also now responsible for destroying it. + */ + *pSignerCert = CERT_DupCertificate(signerCert); + } + } + + return rv; +} + +/* + * See if the request's certID and the single response's certID match. + * This can be easy or difficult, depending on whether the same hash + * algorithm was used. + */ +static PRBool +ocsp_CertIDsMatch(CERTOCSPCertID *requestCertID, + CERTOCSPCertID *responseCertID) +{ + PRBool match = PR_FALSE; + SECOidTag hashAlg; + SECItem *keyHash = NULL; + SECItem *nameHash = NULL; + + /* + * In order to match, they must have the same issuer and the same + * serial number. + * + * We just compare the easier things first. + */ + if (SECITEM_CompareItem(&requestCertID->serialNumber, + &responseCertID->serialNumber) != SECEqual) { + goto done; + } + + /* + * Make sure the "parameters" are not too bogus. Since we encoded + * requestCertID->hashAlgorithm, we don't need to check it. + */ + if (responseCertID->hashAlgorithm.parameters.len > 2) { + goto done; + } + if (SECITEM_CompareItem(&requestCertID->hashAlgorithm.algorithm, + &responseCertID->hashAlgorithm.algorithm) == + SECEqual) { + /* + * If the hash algorithms match then we can do a simple compare + * of the hash values themselves. + */ + if ((SECITEM_CompareItem(&requestCertID->issuerNameHash, + &responseCertID->issuerNameHash) == SECEqual) && + (SECITEM_CompareItem(&requestCertID->issuerKeyHash, + &responseCertID->issuerKeyHash) == SECEqual)) { + match = PR_TRUE; + } + goto done; + } + + hashAlg = SECOID_FindOIDTag(&responseCertID->hashAlgorithm.algorithm); + switch (hashAlg) { + case SEC_OID_SHA1: + keyHash = &requestCertID->issuerSHA1KeyHash; + nameHash = &requestCertID->issuerSHA1NameHash; + break; + case SEC_OID_MD5: + keyHash = &requestCertID->issuerMD5KeyHash; + nameHash = &requestCertID->issuerMD5NameHash; + break; + case SEC_OID_MD2: + keyHash = &requestCertID->issuerMD2KeyHash; + nameHash = &requestCertID->issuerMD2NameHash; + break; + default: + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return PR_FALSE; + } + + if ((keyHash != NULL) && + (SECITEM_CompareItem(nameHash, + &responseCertID->issuerNameHash) == SECEqual) && + (SECITEM_CompareItem(keyHash, + &responseCertID->issuerKeyHash) == SECEqual)) { + match = PR_TRUE; + } + +done: + return match; +} + +/* + * Find the single response for the cert specified by certID. + * No copying is done; this just returns a pointer to the appropriate + * response within responses, if it is found (and null otherwise). + * This is fine, of course, since this function is internal-use only. + */ +static CERTOCSPSingleResponse * +ocsp_GetSingleResponseForCertID(CERTOCSPSingleResponse **responses, + CERTCertDBHandle *handle, + CERTOCSPCertID *certID) +{ + CERTOCSPSingleResponse *single; + int i; + + if (responses == NULL) + return NULL; + + for (i = 0; responses[i] != NULL; i++) { + single = responses[i]; + if (ocsp_CertIDsMatch(certID, single->certID)) { + return single; + } + } + + /* + * The OCSP server should have included a response even if it knew + * nothing about the certificate in question. Since it did not, + * this will make it look as if it had. + * + * XXX Should we make this a separate error to notice the server's + * bad behavior? + */ + PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT); + return NULL; +} + +static ocspCheckingContext * +ocsp_GetCheckingContext(CERTCertDBHandle *handle) +{ + CERTStatusConfig *statusConfig; + ocspCheckingContext *ocspcx = NULL; + + statusConfig = CERT_GetStatusConfig(handle); + if (statusConfig != NULL) { + ocspcx = statusConfig->statusContext; + + /* + * This is actually an internal error, because we should never + * have a good statusConfig without a good statusContext, too. + * For lack of anything better, though, we just assert and use + * the same error as if there were no statusConfig (set below). + */ + PORT_Assert(ocspcx != NULL); + } + + if (ocspcx == NULL) + PORT_SetError(SEC_ERROR_OCSP_NOT_ENABLED); + + return ocspcx; +} + +/* + * Return cert reference if the given signerCert is the default responder for + * the given certID. If not, or if any error, return NULL. + */ +static CERTCertificate * +ocsp_CertGetDefaultResponder(CERTCertDBHandle *handle, CERTOCSPCertID *certID) +{ + ocspCheckingContext *ocspcx; + + ocspcx = ocsp_GetCheckingContext(handle); + if (ocspcx == NULL) + goto loser; + + /* + * Right now we have only one default responder. It applies to + * all certs when it is used, so the check is simple and certID + * has no bearing on the answer. Someday in the future we may + * allow configuration of different responders for different + * issuers, and then we would have to use the issuer specified + * in certID to determine if signerCert is the right one. + */ + if (ocspcx->useDefaultResponder) { + PORT_Assert(ocspcx->defaultResponderCert != NULL); + return ocspcx->defaultResponderCert; + } + +loser: + return NULL; +} + +/* + * Return true if the cert is one of the default responders configured for + * ocsp context. If not, or if any error, return false. + */ +PRBool +ocsp_CertIsOCSPDefaultResponder(CERTCertDBHandle *handle, CERTCertificate *cert) +{ + ocspCheckingContext *ocspcx; + + ocspcx = ocsp_GetCheckingContext(handle); + if (ocspcx == NULL) + return PR_FALSE; + + /* + * Right now we have only one default responder. It applies to + * all certs when it is used, so the check is simple and certID + * has no bearing on the answer. Someday in the future we may + * allow configuration of different responders for different + * issuers, and then we would have to use the issuer specified + * in certID to determine if signerCert is the right one. + */ + if (ocspcx->useDefaultResponder && + CERT_CompareCerts(ocspcx->defaultResponderCert, cert)) { + return PR_TRUE; + } + + return PR_FALSE; +} + +/* + * Check that the given signer certificate is authorized to sign status + * information for the given certID. Return true if it is, false if not + * (or if there is any error along the way). If false is returned because + * the signer is not authorized, the following error will be set: + * SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE + * Other errors are low-level problems (no memory, bad database, etc.). + * + * There are three ways to be authorized. In the order in which we check, + * using the terms used in the OCSP spec, the signer must be one of: + * 1. A "trusted responder" -- it matches a local configuration + * of OCSP signing authority for the certificate in question. + * 2. The CA who issued the certificate in question. + * 3. A "CA designated responder", aka an "authorized responder" -- it + * must be represented by a special cert issued by the CA who issued + * the certificate in question. + */ +static PRBool +ocsp_AuthorizedResponderForCertID(CERTCertDBHandle *handle, + CERTCertificate *signerCert, + CERTOCSPCertID *certID, + PRTime thisUpdate) +{ + CERTCertificate *issuerCert = NULL, *defRespCert; + SECItem *keyHash = NULL; + SECItem *nameHash = NULL; + SECOidTag hashAlg; + PRBool keyHashEQ = PR_FALSE, nameHashEQ = PR_FALSE; + + /* + * Check first for a trusted responder, which overrides everything else. + */ + if ((defRespCert = ocsp_CertGetDefaultResponder(handle, certID)) && + CERT_CompareCerts(defRespCert, signerCert)) { + return PR_TRUE; + } + + /* + * In the other two cases, we need to do an issuer comparison. + * How we do it depends on whether the signer certificate has the + * special extension (for a designated responder) or not. + * + * First, lets check if signer of the response is the actual issuer + * of the cert. For that we will use signer cert key hash and cert subj + * name hash and will compare them with already calculated issuer key + * hash and issuer name hash. The hash algorithm is picked from response + * certID hash to avoid second hash calculation. + */ + + hashAlg = SECOID_FindOIDTag(&certID->hashAlgorithm.algorithm); + + keyHash = CERT_GetSubjectPublicKeyDigest(NULL, signerCert, hashAlg, NULL); + if (keyHash != NULL) { + + keyHashEQ = + (SECITEM_CompareItem(keyHash, + &certID->issuerKeyHash) == SECEqual); + SECITEM_FreeItem(keyHash, PR_TRUE); + } + if (keyHashEQ && + (nameHash = CERT_GetSubjectNameDigest(NULL, signerCert, + hashAlg, NULL))) { + nameHashEQ = + (SECITEM_CompareItem(nameHash, + &certID->issuerNameHash) == SECEqual); + + SECITEM_FreeItem(nameHash, PR_TRUE); + if (nameHashEQ) { + /* The issuer of the cert is the the signer of the response */ + return PR_TRUE; + } + } + + keyHashEQ = PR_FALSE; + nameHashEQ = PR_FALSE; + + if (!ocsp_CertIsOCSPDesignatedResponder(signerCert)) { + PORT_SetError(SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE); + return PR_FALSE; + } + + /* + * The signer is a designated responder. Its issuer must match + * the issuer of the cert being checked. + */ + issuerCert = CERT_FindCertIssuer(signerCert, thisUpdate, + certUsageAnyCA); + if (issuerCert == NULL) { + /* + * We could leave the SEC_ERROR_UNKNOWN_ISSUER error alone, + * but the following will give slightly more information. + * Once we have an error stack, things will be much better. + */ + PORT_SetError(SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE); + return PR_FALSE; + } + + keyHash = CERT_GetSubjectPublicKeyDigest(NULL, issuerCert, hashAlg, NULL); + nameHash = CERT_GetSubjectNameDigest(NULL, issuerCert, hashAlg, NULL); + + CERT_DestroyCertificate(issuerCert); + + if (keyHash != NULL && nameHash != NULL) { + keyHashEQ = + (SECITEM_CompareItem(keyHash, + &certID->issuerKeyHash) == SECEqual); + + nameHashEQ = + (SECITEM_CompareItem(nameHash, + &certID->issuerNameHash) == SECEqual); + } + + if (keyHash) { + SECITEM_FreeItem(keyHash, PR_TRUE); + } + if (nameHash) { + SECITEM_FreeItem(nameHash, PR_TRUE); + } + + if (keyHashEQ && nameHashEQ) { + return PR_TRUE; + } + + PORT_SetError(SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE); + return PR_FALSE; +} + +/* + * We need to check that a responder gives us "recent" information. + * Since a responder can pre-package responses, we need to pick an amount + * of time that is acceptable to us, and reject any response that is + * older than that. + * + * XXX This *should* be based on some configuration parameter, so that + * different usages could specify exactly what constitutes "sufficiently + * recent". But that is not going to happen right away. For now, we + * want something from within the last 24 hours. This macro defines that + * number in seconds. + */ +#define OCSP_ALLOWABLE_LAPSE_SECONDS (24L * 60L * 60L) + +static PRBool +ocsp_TimeIsRecent(PRTime checkTime) +{ + PRTime now = PR_Now(); + PRTime lapse, tmp; + + LL_I2L(lapse, OCSP_ALLOWABLE_LAPSE_SECONDS); + LL_I2L(tmp, PR_USEC_PER_SEC); + LL_MUL(lapse, lapse, tmp); /* allowable lapse in microseconds */ + + LL_ADD(checkTime, checkTime, lapse); + if (LL_CMP(now, >, checkTime)) + return PR_FALSE; + + return PR_TRUE; +} + +#define OCSP_SLOP (5L * 60L) /* OCSP responses are allowed to be 5 minutes \ + in the future by default */ + +static PRUint32 ocspsloptime = OCSP_SLOP; /* seconds */ + +/* + * If an old response contains the revoked certificate status, we want + * to return SECSuccess so the response will be used. + */ +static SECStatus +ocsp_HandleOldSingleResponse(CERTOCSPSingleResponse *single, PRTime time) +{ + SECStatus rv; + ocspCertStatus *status = single->certStatus; + if (status->certStatusType == ocspCertStatus_revoked) { + rv = ocsp_CertRevokedAfter(status->certStatusInfo.revokedInfo, time); + if (rv != SECSuccess && + PORT_GetError() == SEC_ERROR_REVOKED_CERTIFICATE) { + /* + * Return SECSuccess now. The subsequent ocsp_CertRevokedAfter + * call in ocsp_CertHasGoodStatus will cause + * ocsp_CertHasGoodStatus to fail with + * SEC_ERROR_REVOKED_CERTIFICATE. + */ + return SECSuccess; + } + } + PORT_SetError(SEC_ERROR_OCSP_OLD_RESPONSE); + return SECFailure; +} + +/* + * Check that this single response is okay. A return of SECSuccess means: + * 1. The signer (represented by "signerCert") is authorized to give status + * for the cert represented by the individual response in "single". + * 2. The value of thisUpdate is earlier than now. + * 3. The value of producedAt is later than or the same as thisUpdate. + * 4. If nextUpdate is given: + * - The value of nextUpdate is later than now. + * - The value of producedAt is earlier than nextUpdate. + * Else if no nextUpdate: + * - The value of thisUpdate is fairly recent. + * - The value of producedAt is fairly recent. + * However we do not need to perform an explicit check for this last + * constraint because it is already guaranteed by checking that + * producedAt is later than thisUpdate and thisUpdate is recent. + * Oh, and any responder is "authorized" to say that a cert is unknown to it. + * + * If any of those checks fail, SECFailure is returned and an error is set: + * SEC_ERROR_OCSP_FUTURE_RESPONSE + * SEC_ERROR_OCSP_OLD_RESPONSE + * SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE + * Other errors are low-level problems (no memory, bad database, etc.). + */ +static SECStatus +ocsp_VerifySingleResponse(CERTOCSPSingleResponse *single, + CERTCertDBHandle *handle, + CERTCertificate *signerCert, + PRTime producedAt) +{ + CERTOCSPCertID *certID = single->certID; + PRTime now, thisUpdate, nextUpdate, tmstamp, tmp; + SECStatus rv; + + OCSP_TRACE(("OCSP ocsp_VerifySingleResponse, nextUpdate: %d\n", + ((single->nextUpdate) != 0))); + /* + * If all the responder said was that the given cert was unknown to it, + * that is a valid response. Not very interesting to us, of course, + * but all this function is concerned with is validity of the response, + * not the status of the cert. + */ + PORT_Assert(single->certStatus != NULL); + if (single->certStatus->certStatusType == ocspCertStatus_unknown) + return SECSuccess; + + /* + * We need to extract "thisUpdate" for use below and to pass along + * to AuthorizedResponderForCertID in case it needs it for doing an + * issuer look-up. + */ + rv = DER_GeneralizedTimeToTime(&thisUpdate, &single->thisUpdate); + if (rv != SECSuccess) + return rv; + + /* + * First confirm that signerCert is authorized to give this status. + */ + if (ocsp_AuthorizedResponderForCertID(handle, signerCert, certID, + thisUpdate) != PR_TRUE) + return SECFailure; + + /* + * Now check the time stuff, as described above. + */ + now = PR_Now(); + /* allow slop time for future response */ + LL_UI2L(tmstamp, ocspsloptime); /* get slop time in seconds */ + LL_UI2L(tmp, PR_USEC_PER_SEC); + LL_MUL(tmp, tmstamp, tmp); /* convert the slop time to PRTime */ + LL_ADD(tmstamp, tmp, now); /* add current time to it */ + + if (LL_CMP(thisUpdate, >, tmstamp) || LL_CMP(producedAt, <, thisUpdate)) { + PORT_SetError(SEC_ERROR_OCSP_FUTURE_RESPONSE); + return SECFailure; + } + if (single->nextUpdate != NULL) { + rv = DER_GeneralizedTimeToTime(&nextUpdate, single->nextUpdate); + if (rv != SECSuccess) + return rv; + + LL_ADD(tmp, tmp, nextUpdate); + if (LL_CMP(tmp, <, now) || LL_CMP(producedAt, >, nextUpdate)) + return ocsp_HandleOldSingleResponse(single, now); + } else if (ocsp_TimeIsRecent(thisUpdate) != PR_TRUE) { + return ocsp_HandleOldSingleResponse(single, now); + } + + return SECSuccess; +} + +/* + * FUNCTION: CERT_GetOCSPAuthorityInfoAccessLocation + * Get the value of the URI of the OCSP responder for the given cert. + * This is found in the (optional) Authority Information Access extension + * in the cert. + * INPUTS: + * CERTCertificate *cert + * The certificate being examined. + * RETURN: + * char * + * A copy of the URI for the OCSP method, if found. If either the + * extension is not present or it does not contain an entry for OCSP, + * SEC_ERROR_CERT_BAD_ACCESS_LOCATION will be set and a NULL returned. + * Any other error will also result in a NULL being returned. + * + * This result should be freed (via PORT_Free) when no longer in use. + */ +char * +CERT_GetOCSPAuthorityInfoAccessLocation(const CERTCertificate *cert) +{ + CERTGeneralName *locname = NULL; + SECItem *location = NULL; + SECItem *encodedAuthInfoAccess = NULL; + CERTAuthInfoAccess **authInfoAccess = NULL; + char *locURI = NULL; + PLArenaPool *arena = NULL; + SECStatus rv; + int i; + + /* + * Allocate this one from the heap because it will get filled in + * by CERT_FindCertExtension which will also allocate from the heap, + * and we can free the entire thing on our way out. + */ + encodedAuthInfoAccess = SECITEM_AllocItem(NULL, NULL, 0); + if (encodedAuthInfoAccess == NULL) + goto loser; + + rv = CERT_FindCertExtension(cert, SEC_OID_X509_AUTH_INFO_ACCESS, + encodedAuthInfoAccess); + if (rv == SECFailure) { + PORT_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION); + goto loser; + } + + /* + * The rest of the things allocated in the routine will come out of + * this arena, which is temporary just for us to decode and get at the + * AIA extension. The whole thing will be destroyed on our way out, + * after we have copied the location string (url) itself (if found). + */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) + goto loser; + + authInfoAccess = CERT_DecodeAuthInfoAccessExtension(arena, + encodedAuthInfoAccess); + if (authInfoAccess == NULL) + goto loser; + + for (i = 0; authInfoAccess[i] != NULL; i++) { + if (SECOID_FindOIDTag(&authInfoAccess[i]->method) == SEC_OID_PKIX_OCSP) + locname = authInfoAccess[i]->location; + } + + /* + * If we found an AIA extension, but it did not include an OCSP method, + * that should look to our caller as if we did not find the extension + * at all, because it is only an OCSP method that we care about. + * So set the same error that would be set if the AIA extension was + * not there at all. + */ + if (locname == NULL) { + PORT_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION); + goto loser; + } + + /* + * The following is just a pointer back into locname (i.e. not a copy); + * thus it should not be freed. + */ + location = CERT_GetGeneralNameByType(locname, certURI, PR_FALSE); + if (location == NULL) { + /* + * XXX Appears that CERT_GetGeneralNameByType does not set an + * error if there is no name by that type. For lack of anything + * better, act as if the extension was not found. In the future + * this should probably be something more like the extension was + * badly formed. + */ + PORT_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION); + goto loser; + } + + /* + * That location is really a string, but it has a specified length + * without a null-terminator. We need a real string that does have + * a null-terminator, and we need a copy of it anyway to return to + * our caller -- so allocate and copy. + */ + locURI = PORT_Alloc(location->len + 1); + if (locURI == NULL) { + goto loser; + } + PORT_Memcpy(locURI, location->data, location->len); + locURI[location->len] = '\0'; + +loser: + if (arena != NULL) + PORT_FreeArena(arena, PR_FALSE); + + if (encodedAuthInfoAccess != NULL) + SECITEM_FreeItem(encodedAuthInfoAccess, PR_TRUE); + + return locURI; +} + +/* + * Figure out where we should go to find out the status of the given cert + * via OCSP. If allowed to use a default responder uri and a default + * responder is set up, then that is our answer. + * If not, see if the certificate has an Authority Information Access (AIA) + * extension for OCSP, and return the value of that. Otherwise return NULL. + * We also let our caller know whether or not the responder chosen was + * a default responder or not through the output variable isDefault; + * its value has no meaning unless a good (non-null) value is returned + * for the location. + * + * The result needs to be freed (PORT_Free) when no longer in use. + */ +char * +ocsp_GetResponderLocation(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool canUseDefault, PRBool *isDefault) +{ + ocspCheckingContext *ocspcx = NULL; + char *ocspUrl = NULL; + + if (canUseDefault) { + ocspcx = ocsp_GetCheckingContext(handle); + } + if (ocspcx != NULL && ocspcx->useDefaultResponder) { + /* + * A default responder wins out, if specified. + * XXX Someday this may be a more complicated determination based + * on the cert's issuer. (That is, we could have different default + * responders configured for different issuers.) + */ + PORT_Assert(ocspcx->defaultResponderURI != NULL); + *isDefault = PR_TRUE; + return (PORT_Strdup(ocspcx->defaultResponderURI)); + } + + /* + * No default responder set up, so go see if we can find an AIA + * extension that has a value for OCSP, and get the url from that. + */ + *isDefault = PR_FALSE; + ocspUrl = CERT_GetOCSPAuthorityInfoAccessLocation(cert); + if (!ocspUrl) { + CERT_StringFromCertFcn altFcn; + + PR_EnterMonitor(OCSP_Global.monitor); + altFcn = OCSP_Global.alternateOCSPAIAFcn; + PR_ExitMonitor(OCSP_Global.monitor); + if (altFcn) { + ocspUrl = (*altFcn)(cert); + if (ocspUrl) + *isDefault = PR_TRUE; + } + } + return ocspUrl; +} + +/* + * Return SECSuccess if the cert was revoked *after* "time", + * SECFailure otherwise. + */ +static SECStatus +ocsp_CertRevokedAfter(ocspRevokedInfo *revokedInfo, PRTime time) +{ + PRTime revokedTime; + SECStatus rv; + + rv = DER_GeneralizedTimeToTime(&revokedTime, &revokedInfo->revocationTime); + if (rv != SECSuccess) + return rv; + + /* + * Set the error even if we will return success; someone might care. + */ + PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE); + + if (LL_CMP(revokedTime, >, time)) + return SECSuccess; + + return SECFailure; +} + +/* + * See if the cert represented in the single response had a good status + * at the specified time. + */ +SECStatus +ocsp_CertHasGoodStatus(ocspCertStatus *status, PRTime time) +{ + SECStatus rv; + switch (status->certStatusType) { + case ocspCertStatus_good: + rv = SECSuccess; + break; + case ocspCertStatus_revoked: + rv = ocsp_CertRevokedAfter(status->certStatusInfo.revokedInfo, time); + break; + case ocspCertStatus_unknown: + PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT); + rv = SECFailure; + break; + case ocspCertStatus_other: + default: + PORT_Assert(0); + PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE); + rv = SECFailure; + break; + } + return rv; +} + +static SECStatus +ocsp_SingleResponseCertHasGoodStatus(CERTOCSPSingleResponse *single, + PRTime time) +{ + return ocsp_CertHasGoodStatus(single->certStatus, time); +} + +/* SECFailure means the arguments were invalid. + * On SECSuccess, the out parameters contain the OCSP status. + * rvOcsp contains the overall result of the OCSP operation. + * Depending on input parameter ignoreGlobalOcspFailureSetting, + * a soft failure might be converted into *rvOcsp=SECSuccess. + * If the cached attempt to obtain OCSP information had resulted + * in a failure, missingResponseError shows the error code of + * that failure. + * cacheFreshness is ocspMissing if no entry was found, + * ocspFresh if a fresh entry was found, or + * ocspStale if a stale entry was found. + */ +SECStatus +ocsp_GetCachedOCSPResponseStatus(CERTOCSPCertID *certID, + PRTime time, + PRBool ignoreGlobalOcspFailureSetting, + SECStatus *rvOcsp, + SECErrorCodes *missingResponseError, + OCSPFreshness *cacheFreshness) +{ + OCSPCacheItem *cacheItem = NULL; + + if (!certID || !missingResponseError || !rvOcsp || !cacheFreshness) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + *rvOcsp = SECFailure; + *missingResponseError = 0; + *cacheFreshness = ocspMissing; + + PR_EnterMonitor(OCSP_Global.monitor); + cacheItem = ocsp_FindCacheEntry(&OCSP_Global.cache, certID); + if (cacheItem) { + *cacheFreshness = ocsp_IsCacheItemFresh(cacheItem) ? ocspFresh + : ocspStale; + /* having an arena means, we have a cached certStatus */ + if (cacheItem->certStatusArena) { + *rvOcsp = ocsp_CertHasGoodStatus(&cacheItem->certStatus, time); + if (*rvOcsp != SECSuccess) { + *missingResponseError = PORT_GetError(); + } + } else { + /* + * No status cached, the previous attempt failed. + * If OCSP is required, we never decide based on a failed attempt + * However, if OCSP is optional, a recent OCSP failure is + * an allowed good state. + */ + if (*cacheFreshness == ocspFresh && + !ignoreGlobalOcspFailureSetting && + OCSP_Global.ocspFailureMode == + ocspMode_FailureIsNotAVerificationFailure) { + *rvOcsp = SECSuccess; + } + *missingResponseError = cacheItem->missingResponseError; + } + } + PR_ExitMonitor(OCSP_Global.monitor); + return SECSuccess; +} + +PRBool +ocsp_FetchingFailureIsVerificationFailure(void) +{ + PRBool isFailure; + + PR_EnterMonitor(OCSP_Global.monitor); + isFailure = + OCSP_Global.ocspFailureMode == ocspMode_FailureIsVerificationFailure; + PR_ExitMonitor(OCSP_Global.monitor); + return isFailure; +} + +/* + * FUNCTION: CERT_CheckOCSPStatus + * Checks the status of a certificate via OCSP. Will only check status for + * a certificate that has an AIA (Authority Information Access) extension + * for OCSP *or* when a "default responder" is specified and enabled. + * (If no AIA extension for OCSP and no default responder in place, the + * cert is considered to have a good status and SECSuccess is returned.) + * INPUTS: + * CERTCertDBHandle *handle + * certificate DB of the cert that is being checked + * CERTCertificate *cert + * the certificate being checked + * XXX in the long term also need a boolean parameter that specifies + * whether to check the cert chain, as well; for now we check only + * the leaf (the specified certificate) + * PRTime time + * time for which status is to be determined + * void *pwArg + * argument for password prompting, if needed + * RETURN: + * Returns SECSuccess if an approved OCSP responder "knows" the cert + * *and* returns a non-revoked status for it; SECFailure otherwise, + * with an error set describing the reason: + * + * SEC_ERROR_OCSP_BAD_HTTP_RESPONSE + * SEC_ERROR_OCSP_FUTURE_RESPONSE + * SEC_ERROR_OCSP_MALFORMED_REQUEST + * SEC_ERROR_OCSP_MALFORMED_RESPONSE + * SEC_ERROR_OCSP_OLD_RESPONSE + * SEC_ERROR_OCSP_REQUEST_NEEDS_SIG + * SEC_ERROR_OCSP_SERVER_ERROR + * SEC_ERROR_OCSP_TRY_SERVER_LATER + * SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST + * SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE + * SEC_ERROR_OCSP_UNKNOWN_CERT + * SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS + * SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE + * + * SEC_ERROR_BAD_SIGNATURE + * SEC_ERROR_CERT_BAD_ACCESS_LOCATION + * SEC_ERROR_INVALID_TIME + * SEC_ERROR_REVOKED_CERTIFICATE + * SEC_ERROR_UNKNOWN_ISSUER + * SEC_ERROR_UNKNOWN_SIGNER + * + * Other errors are any of the many possible failures in cert verification + * (e.g. SEC_ERROR_REVOKED_CERTIFICATE, SEC_ERROR_UNTRUSTED_ISSUER) when + * verifying the signer's cert, or low-level problems (error allocating + * memory, error performing ASN.1 decoding, etc.). + */ +SECStatus +CERT_CheckOCSPStatus(CERTCertDBHandle *handle, CERTCertificate *cert, + PRTime time, void *pwArg) +{ + CERTOCSPCertID *certID; + PRBool certIDWasConsumed = PR_FALSE; + SECStatus rv; + SECStatus rvOcsp; + SECErrorCodes cachedErrorCode; + OCSPFreshness cachedResponseFreshness; + + OCSP_TRACE_CERT(cert); + OCSP_TRACE_TIME("## requested validity time:", time); + + certID = CERT_CreateOCSPCertID(cert, time); + if (!certID) + return SECFailure; + rv = ocsp_GetCachedOCSPResponseStatus( + certID, time, PR_FALSE, /* ignoreGlobalOcspFailureSetting */ + &rvOcsp, &cachedErrorCode, &cachedResponseFreshness); + if (rv != SECSuccess) { + CERT_DestroyOCSPCertID(certID); + return SECFailure; + } + if (cachedResponseFreshness == ocspFresh) { + CERT_DestroyOCSPCertID(certID); + if (rvOcsp != SECSuccess) { + PORT_SetError(cachedErrorCode); + } + return rvOcsp; + } + + rv = ocsp_GetOCSPStatusFromNetwork(handle, certID, cert, time, pwArg, + &certIDWasConsumed, + &rvOcsp); + if (rv != SECSuccess) { + PRErrorCode err = PORT_GetError(); + if (ocsp_FetchingFailureIsVerificationFailure()) { + PORT_SetError(err); + rvOcsp = SECFailure; + } else if (cachedResponseFreshness == ocspStale && + (cachedErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT || + cachedErrorCode == SEC_ERROR_REVOKED_CERTIFICATE)) { + /* If we couldn't get a response for a certificate that the OCSP + * responder previously told us was bad, then assume it is still + * bad until we hear otherwise, as it is very unlikely that the + * certificate status has changed from "revoked" to "good" and it + * is also unlikely that the certificate status has changed from + * "unknown" to "good", except for some buggy OCSP responders. + */ + PORT_SetError(cachedErrorCode); + rvOcsp = SECFailure; + } else { + rvOcsp = SECSuccess; + } + } + if (!certIDWasConsumed) { + CERT_DestroyOCSPCertID(certID); + } + return rvOcsp; +} + +/* + * FUNCTION: CERT_CacheOCSPResponseFromSideChannel + * First, this function checks the OCSP cache to see if a good response + * for the given certificate already exists. If it does, then the function + * returns successfully. + * + * If not, then it validates that the given OCSP response is a valid, + * good response for the given certificate and inserts it into the + * cache. + * + * This function is intended for use when OCSP responses are provided via a + * side-channel, i.e. TLS OCSP stapling (a.k.a. the status_request extension). + * + * INPUTS: + * CERTCertDBHandle *handle + * certificate DB of the cert that is being checked + * CERTCertificate *cert + * the certificate being checked + * PRTime time + * time for which status is to be determined + * SECItem *encodedResponse + * the DER encoded bytes of the OCSP response + * void *pwArg + * argument for password prompting, if needed + * RETURN: + * SECSuccess if the cert was found in the cache, or if the OCSP response was + * found to be valid and inserted into the cache. SECFailure otherwise. + */ +SECStatus +CERT_CacheOCSPResponseFromSideChannel(CERTCertDBHandle *handle, + CERTCertificate *cert, + PRTime time, + const SECItem *encodedResponse, + void *pwArg) +{ + CERTOCSPCertID *certID = NULL; + PRBool certIDWasConsumed = PR_FALSE; + SECStatus rv = SECFailure; + SECStatus rvOcsp = SECFailure; + SECErrorCodes dummy_error_code; /* we ignore this */ + CERTOCSPResponse *decodedResponse = NULL; + CERTOCSPSingleResponse *singleResponse = NULL; + OCSPFreshness freshness; + + /* The OCSP cache can be in three states regarding this certificate: + * + Good (cached, timely, 'good' response, or revoked in the future) + * + Revoked (cached, timely, but doesn't fit in the last category) + * + Miss (no knowledge) + * + * Likewise, the side-channel information can be + * + Good (timely, 'good' response, or revoked in the future) + * + Revoked (timely, but doesn't fit in the last category) + * + Invalid (bad syntax, bad signature, not timely etc) + * + * The common case is that the cache result is Good and so is the + * side-channel information. We want to save processing time in this case + * so we say that any time we see a Good result from the cache we return + * early. + * + * Cache result + * | Good Revoked Miss + * ---+-------------------------------------------- + * G | noop Cache more Cache it + * S | recent result + * i | + * d | + * e | + * R | noop Cache more Cache it + * C | recent result + * h | + * a | + * n | + * n I | noop Noop Noop + * e | + * l | + * + * When we fetch from the network we might choose to cache a negative + * result when the response is invalid. This saves us hammering, uselessly, + * at a broken responder. However, side channels are commonly attacker + * controlled and so we must not cache a negative result for an Invalid + * side channel. + */ + + if (!cert || !encodedResponse) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + certID = CERT_CreateOCSPCertID(cert, time); + if (!certID) + return SECFailure; + + /* We pass PR_TRUE for ignoreGlobalOcspFailureSetting so that a cached + * error entry is not interpreted as being a 'Good' entry here. + */ + rv = ocsp_GetCachedOCSPResponseStatus( + certID, time, PR_TRUE, /* ignoreGlobalOcspFailureSetting */ + &rvOcsp, &dummy_error_code, &freshness); + if (rv == SECSuccess && rvOcsp == SECSuccess && freshness == ocspFresh) { + /* The cached value is good. We don't want to waste time validating + * this OCSP response. This is the first column in the table above. */ + CERT_DestroyOCSPCertID(certID); + return rv; + } + + /* The logic for caching the more recent response is handled in + * ocsp_CacheSingleResponse. */ + + rv = ocsp_GetDecodedVerifiedSingleResponseForID(handle, certID, cert, + time, pwArg, + encodedResponse, + &decodedResponse, + &singleResponse); + if (rv == SECSuccess) { + rvOcsp = ocsp_SingleResponseCertHasGoodStatus(singleResponse, time); + /* Cache any valid singleResponse, regardless of status. */ + ocsp_CacheSingleResponse(certID, singleResponse, &certIDWasConsumed); + } + if (decodedResponse) { + CERT_DestroyOCSPResponse(decodedResponse); + } + if (!certIDWasConsumed) { + CERT_DestroyOCSPCertID(certID); + } + return rv == SECSuccess ? rvOcsp : rv; +} + +/* + * Status in *certIDWasConsumed will always be correct, regardless of + * return value. + */ +static SECStatus +ocsp_GetOCSPStatusFromNetwork(CERTCertDBHandle *handle, + CERTOCSPCertID *certID, + CERTCertificate *cert, + PRTime time, + void *pwArg, + PRBool *certIDWasConsumed, + SECStatus *rv_ocsp) +{ + char *location = NULL; + PRBool locationIsDefault; + SECItem *encodedResponse = NULL; + CERTOCSPRequest *request = NULL; + SECStatus rv = SECFailure; + + CERTOCSPResponse *decodedResponse = NULL; + CERTOCSPSingleResponse *singleResponse = NULL; + enum { stageGET, + stagePOST } currentStage; + PRBool retry = PR_FALSE; + + if (!certIDWasConsumed || !rv_ocsp) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + *certIDWasConsumed = PR_FALSE; + *rv_ocsp = SECFailure; + + if (!OCSP_Global.monitor) { + PORT_SetError(SEC_ERROR_NOT_INITIALIZED); + return SECFailure; + } + PR_EnterMonitor(OCSP_Global.monitor); + if (OCSP_Global.forcePost) { + currentStage = stagePOST; + } else { + currentStage = stageGET; + } + PR_ExitMonitor(OCSP_Global.monitor); + + /* + * The first thing we need to do is find the location of the responder. + * This will be the value of the default responder (if enabled), else + * it will come out of the AIA extension in the cert (if present). + * If we have no such location, then this cert does not "deserve" to + * be checked -- that is, we consider it a success and just return. + * The way we tell that is by looking at the error number to see if + * the problem was no AIA extension was found; any other error was + * a true failure that we unfortunately have to treat as an overall + * failure here. + */ + location = ocsp_GetResponderLocation(handle, cert, PR_TRUE, + &locationIsDefault); + if (location == NULL) { + int err = PORT_GetError(); + if (err == SEC_ERROR_EXTENSION_NOT_FOUND || + err == SEC_ERROR_CERT_BAD_ACCESS_LOCATION) { + PORT_SetError(0); + *rv_ocsp = SECSuccess; + return SECSuccess; + } + return SECFailure; + } + + /* + * XXX In the fullness of time, we will want/need to handle a + * certificate chain. This will be done either when a new parameter + * tells us to, or some configuration variable tells us to. In any + * case, handling it is complicated because we may need to send as + * many requests (and receive as many responses) as we have certs + * in the chain. If we are going to talk to a default responder, + * and we only support one default responder, we can put all of the + * certs together into one request. Otherwise, we must break them up + * into multiple requests. (Even if all of the requests will go to + * the same location, the signature on each response will be different, + * because each issuer is different. Carefully read the OCSP spec + * if you do not understand this.) + */ + + /* + * XXX If/when signing of requests is supported, that second NULL + * should be changed to be the signer certificate. Not sure if that + * should be passed into this function or retrieved via some operation + * on the handle/context. + */ + + do { + const char *method; + PRBool validResponseWithAccurateInfo = PR_FALSE; + retry = PR_FALSE; + *rv_ocsp = SECFailure; + + if (currentStage == stageGET) { + method = "GET"; + } else { + PORT_Assert(currentStage == stagePOST); + method = "POST"; + } + + encodedResponse = + ocsp_GetEncodedOCSPResponseForSingleCert(NULL, certID, cert, + location, method, + time, locationIsDefault, + pwArg, &request); + + if (encodedResponse) { + rv = ocsp_GetDecodedVerifiedSingleResponseForID(handle, certID, cert, + time, pwArg, + encodedResponse, + &decodedResponse, + &singleResponse); + if (rv == SECSuccess) { + switch (singleResponse->certStatus->certStatusType) { + case ocspCertStatus_good: + case ocspCertStatus_revoked: + validResponseWithAccurateInfo = PR_TRUE; + break; + default: + break; + } + *rv_ocsp = ocsp_SingleResponseCertHasGoodStatus(singleResponse, time); + } + } + + if (currentStage == stageGET) { + /* only accept GET response if good or revoked */ + if (validResponseWithAccurateInfo) { + ocsp_CacheSingleResponse(certID, singleResponse, + certIDWasConsumed); + } else { + retry = PR_TRUE; + currentStage = stagePOST; + } + } else { + /* cache the POST respone, regardless of status */ + if (!singleResponse) { + cert_RememberOCSPProcessingFailure(certID, certIDWasConsumed); + } else { + ocsp_CacheSingleResponse(certID, singleResponse, + certIDWasConsumed); + } + } + + if (encodedResponse) { + SECITEM_FreeItem(encodedResponse, PR_TRUE); + encodedResponse = NULL; + } + if (request) { + CERT_DestroyOCSPRequest(request); + request = NULL; + } + if (decodedResponse) { + CERT_DestroyOCSPResponse(decodedResponse); + decodedResponse = NULL; + } + singleResponse = NULL; + + } while (retry); + + PORT_Free(location); + return rv; +} + +/* + * FUNCTION: ocsp_GetDecodedVerifiedSingleResponseForID + * This function decodes an OCSP response and checks for a valid response + * concerning the given certificate. + * + * Note: a 'valid' response is one that parses successfully, is not an OCSP + * exception (see RFC 2560 Section 2.3), is correctly signed and is current. + * A 'good' response is a valid response that attests that the certificate + * is not currently revoked (see RFC 2560 Section 2.2). + * + * INPUTS: + * CERTCertDBHandle *handle + * certificate DB of the cert that is being checked + * CERTOCSPCertID *certID + * the cert ID corresponding to |cert| + * CERTCertificate *cert + * the certificate being checked + * PRTime time + * time for which status is to be determined + * void *pwArg + * the opaque argument to the password prompting function. + * SECItem *encodedResponse + * the DER encoded bytes of the OCSP response + * CERTOCSPResponse **pDecodedResponse + * (output) The caller must ALWAYS check for this output parameter, + * and if it's non-null, must destroy it using CERT_DestroyOCSPResponse. + * CERTOCSPSingleResponse **pSingle + * (output) on success, this points to the single response that corresponds + * to the certID parameter. Points to the inside of pDecodedResponse. + * It isn't a copy, don't free it. + * RETURN: + * SECSuccess iff the response is valid. + */ +static SECStatus +ocsp_GetDecodedVerifiedSingleResponseForID(CERTCertDBHandle *handle, + CERTOCSPCertID *certID, + CERTCertificate *cert, + PRTime time, + void *pwArg, + const SECItem *encodedResponse, + CERTOCSPResponse **pDecodedResponse, + CERTOCSPSingleResponse **pSingle) +{ + CERTCertificate *signerCert = NULL; + CERTCertificate *issuerCert = NULL; + SECStatus rv = SECFailure; + + if (!pSingle || !pDecodedResponse) { + return SECFailure; + } + *pSingle = NULL; + *pDecodedResponse = CERT_DecodeOCSPResponse(encodedResponse); + if (!*pDecodedResponse) { + return SECFailure; + } + + /* + * Okay, we at least have a response that *looks* like a response! + * Now see if the overall response status value is good or not. + * If not, we set an error and give up. (It means that either the + * server had a problem, or it didn't like something about our + * request. Either way there is nothing to do but give up.) + * Otherwise, we continue to find the actual per-cert status + * in the response. + */ + if (CERT_GetOCSPResponseStatus(*pDecodedResponse) != SECSuccess) { + goto loser; + } + + /* + * If we've made it this far, we expect a response with a good signature. + * So, check for that. + */ + issuerCert = CERT_FindCertIssuer(cert, time, certUsageAnyCA); + rv = CERT_VerifyOCSPResponseSignature(*pDecodedResponse, handle, pwArg, + &signerCert, issuerCert); + if (rv != SECSuccess) { + goto loser; + } + + PORT_Assert(signerCert != NULL); /* internal consistency check */ + /* XXX probably should set error, return failure if signerCert is null */ + + /* + * Again, we are only doing one request for one cert. + * XXX When we handle cert chains, the following code will obviously + * have to be modified, in coordation with the code above that will + * have to determine how to make multiple requests, etc. + */ + rv = ocsp_GetVerifiedSingleResponseForCertID(handle, *pDecodedResponse, certID, + signerCert, time, pSingle); +loser: + if (issuerCert != NULL) + CERT_DestroyCertificate(issuerCert); + if (signerCert != NULL) + CERT_DestroyCertificate(signerCert); + return rv; +} + +/* + * FUNCTION: ocsp_CacheSingleResponse + * This function requires that the caller has checked that the response + * is valid and verified. + * The (positive or negative) valid response will be used to update the cache. + * INPUTS: + * CERTOCSPCertID *certID + * the cert ID corresponding to |cert| + * PRBool *certIDWasConsumed + * (output) on return, this is true iff |certID| was consumed by this + * function. + */ +void +ocsp_CacheSingleResponse(CERTOCSPCertID *certID, + CERTOCSPSingleResponse *single, + PRBool *certIDWasConsumed) +{ + if (single != NULL) { + PR_EnterMonitor(OCSP_Global.monitor); + if (OCSP_Global.maxCacheEntries >= 0) { + ocsp_CreateOrUpdateCacheEntry(&OCSP_Global.cache, certID, single, + certIDWasConsumed); + /* ignore cache update failures */ + } + PR_ExitMonitor(OCSP_Global.monitor); + } +} + +SECStatus +ocsp_GetVerifiedSingleResponseForCertID(CERTCertDBHandle *handle, + CERTOCSPResponse *response, + CERTOCSPCertID *certID, + CERTCertificate *signerCert, + PRTime time, + CERTOCSPSingleResponse + **pSingleResponse) +{ + SECStatus rv; + ocspResponseData *responseData; + PRTime producedAt; + CERTOCSPSingleResponse *single; + + /* + * The ResponseData part is the real guts of the response. + */ + responseData = ocsp_GetResponseData(response, NULL); + if (responseData == NULL) { + rv = SECFailure; + goto loser; + } + + /* + * There is one producedAt time for the entire response (and a separate + * thisUpdate time for each individual single response). We need to + * compare them, so get the overall time to pass into the check of each + * single response. + */ + rv = DER_GeneralizedTimeToTime(&producedAt, &responseData->producedAt); + if (rv != SECSuccess) + goto loser; + + single = ocsp_GetSingleResponseForCertID(responseData->responses, + handle, certID); + if (single == NULL) { + rv = SECFailure; + goto loser; + } + + rv = ocsp_VerifySingleResponse(single, handle, signerCert, producedAt); + if (rv != SECSuccess) + goto loser; + *pSingleResponse = single; + +loser: + return rv; +} + +SECStatus +CERT_GetOCSPStatusForCertID(CERTCertDBHandle *handle, + CERTOCSPResponse *response, + CERTOCSPCertID *certID, + CERTCertificate *signerCert, + PRTime time) +{ + /* + * We do not update the cache, because: + * + * CERT_GetOCSPStatusForCertID is an old exported API that was introduced + * before the OCSP cache got implemented. + * + * The implementation of helper function cert_ProcessOCSPResponse + * requires the ability to transfer ownership of the the given certID to + * the cache. The external API doesn't allow us to prevent the caller from + * destroying the certID. We don't have the original certificate available, + * therefore we are unable to produce another certID object (that could + * be stored in the cache). + * + * Should we ever implement code to produce a deep copy of certID, + * then this could be changed to allow updating the cache. + * The duplication would have to be done in + * cert_ProcessOCSPResponse, if the out parameter to indicate + * a transfer of ownership is NULL. + */ + return cert_ProcessOCSPResponse(handle, response, certID, + signerCert, time, + NULL, NULL); +} + +/* + * The first 5 parameters match the definition of CERT_GetOCSPStatusForCertID. + */ +SECStatus +cert_ProcessOCSPResponse(CERTCertDBHandle *handle, + CERTOCSPResponse *response, + CERTOCSPCertID *certID, + CERTCertificate *signerCert, + PRTime time, + PRBool *certIDWasConsumed, + SECStatus *cacheUpdateStatus) +{ + SECStatus rv; + SECStatus rv_cache = SECSuccess; + CERTOCSPSingleResponse *single = NULL; + + rv = ocsp_GetVerifiedSingleResponseForCertID(handle, response, certID, + signerCert, time, &single); + if (rv == SECSuccess) { + /* + * Check whether the status says revoked, and if so + * how that compares to the time value passed into this routine. + */ + rv = ocsp_SingleResponseCertHasGoodStatus(single, time); + } + + if (certIDWasConsumed) { + /* + * We don't have copy-of-certid implemented. In order to update + * the cache, the caller must supply an out variable + * certIDWasConsumed, allowing us to return ownership status. + */ + + PR_EnterMonitor(OCSP_Global.monitor); + if (OCSP_Global.maxCacheEntries >= 0) { + /* single == NULL means: remember response failure */ + rv_cache = + ocsp_CreateOrUpdateCacheEntry(&OCSP_Global.cache, certID, + single, certIDWasConsumed); + } + PR_ExitMonitor(OCSP_Global.monitor); + if (cacheUpdateStatus) { + *cacheUpdateStatus = rv_cache; + } + } + + return rv; +} + +SECStatus +cert_RememberOCSPProcessingFailure(CERTOCSPCertID *certID, + PRBool *certIDWasConsumed) +{ + SECStatus rv = SECSuccess; + PR_EnterMonitor(OCSP_Global.monitor); + if (OCSP_Global.maxCacheEntries >= 0) { + rv = ocsp_CreateOrUpdateCacheEntry(&OCSP_Global.cache, certID, NULL, + certIDWasConsumed); + } + PR_ExitMonitor(OCSP_Global.monitor); + return rv; +} + +/* + * Disable status checking and destroy related structures/data. + */ +static SECStatus +ocsp_DestroyStatusChecking(CERTStatusConfig *statusConfig) +{ + ocspCheckingContext *statusContext; + + /* + * Disable OCSP checking + */ + statusConfig->statusChecker = NULL; + + statusContext = statusConfig->statusContext; + PORT_Assert(statusContext != NULL); + if (statusContext == NULL) + return SECFailure; + + if (statusContext->defaultResponderURI != NULL) + PORT_Free(statusContext->defaultResponderURI); + if (statusContext->defaultResponderNickname != NULL) + PORT_Free(statusContext->defaultResponderNickname); + + PORT_Free(statusContext); + statusConfig->statusContext = NULL; + + PORT_Free(statusConfig); + + return SECSuccess; +} + +/* + * FUNCTION: CERT_DisableOCSPChecking + * Turns off OCSP checking for the given certificate database. + * This routine disables OCSP checking. Though it will return + * SECFailure if OCSP checking is not enabled, it is "safe" to + * call it that way and just ignore the return value, if it is + * easier to just call it than to "remember" whether it is enabled. + * INPUTS: + * CERTCertDBHandle *handle + * Certificate database for which OCSP checking will be disabled. + * RETURN: + * Returns SECFailure if an error occurred (usually means that OCSP + * checking was not enabled or status contexts were not initialized -- + * error set will be SEC_ERROR_OCSP_NOT_ENABLED); SECSuccess otherwise. + */ +SECStatus +CERT_DisableOCSPChecking(CERTCertDBHandle *handle) +{ + CERTStatusConfig *statusConfig; + ocspCheckingContext *statusContext; + + if (handle == NULL) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + statusConfig = CERT_GetStatusConfig(handle); + statusContext = ocsp_GetCheckingContext(handle); + if (statusContext == NULL) + return SECFailure; + + if (statusConfig->statusChecker != CERT_CheckOCSPStatus) { + /* + * Status configuration is present, but either not currently + * enabled or not for OCSP. + */ + PORT_SetError(SEC_ERROR_OCSP_NOT_ENABLED); + return SECFailure; + } + + /* cache no longer necessary */ + CERT_ClearOCSPCache(); + + /* + * This is how we disable status checking. Everything else remains + * in place in case we are enabled again. + */ + statusConfig->statusChecker = NULL; + + return SECSuccess; +} + +/* + * Allocate and initialize the informational structures for status checking. + * This is done when some configuration of OCSP is being done or when OCSP + * checking is being turned on, whichever comes first. + */ +static SECStatus +ocsp_InitStatusChecking(CERTCertDBHandle *handle) +{ + CERTStatusConfig *statusConfig = NULL; + ocspCheckingContext *statusContext = NULL; + + PORT_Assert(CERT_GetStatusConfig(handle) == NULL); + if (CERT_GetStatusConfig(handle) != NULL) { + /* XXX or call statusConfig->statusDestroy and continue? */ + return SECFailure; + } + + statusConfig = PORT_ZNew(CERTStatusConfig); + if (statusConfig == NULL) + goto loser; + + statusContext = PORT_ZNew(ocspCheckingContext); + if (statusContext == NULL) + goto loser; + + statusConfig->statusDestroy = ocsp_DestroyStatusChecking; + statusConfig->statusContext = statusContext; + + CERT_SetStatusConfig(handle, statusConfig); + + return SECSuccess; + +loser: + if (statusConfig != NULL) + PORT_Free(statusConfig); + return SECFailure; +} + +/* + * FUNCTION: CERT_EnableOCSPChecking + * Turns on OCSP checking for the given certificate database. + * INPUTS: + * CERTCertDBHandle *handle + * Certificate database for which OCSP checking will be enabled. + * RETURN: + * Returns SECFailure if an error occurred (likely only problem + * allocating memory); SECSuccess otherwise. + */ +SECStatus +CERT_EnableOCSPChecking(CERTCertDBHandle *handle) +{ + CERTStatusConfig *statusConfig; + + SECStatus rv; + + if (handle == NULL) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + statusConfig = CERT_GetStatusConfig(handle); + if (statusConfig == NULL) { + rv = ocsp_InitStatusChecking(handle); + if (rv != SECSuccess) + return rv; + + /* Get newly established value */ + statusConfig = CERT_GetStatusConfig(handle); + PORT_Assert(statusConfig != NULL); + } + + /* + * Setting the checker function is what really enables the checking + * when each cert verification is done. + */ + statusConfig->statusChecker = CERT_CheckOCSPStatus; + + return SECSuccess; +} + +/* + * FUNCTION: CERT_SetOCSPDefaultResponder + * Specify the location and cert of the default responder. + * If OCSP checking is already enabled *and* use of a default responder + * is also already enabled, all OCSP checking from now on will go directly + * to the specified responder. If OCSP checking is not enabled, or if + * it is but use of a default responder is not enabled, the information + * will be recorded and take effect whenever both are enabled. + * INPUTS: + * CERTCertDBHandle *handle + * Cert database on which OCSP checking should use the default responder. + * char *url + * The location of the default responder (e.g. "http://foo.com:80/ocsp") + * Note that the location will not be tested until the first attempt + * to send a request there. + * char *name + * The nickname of the cert to trust (expected) to sign the OCSP responses. + * If the corresponding cert cannot be found, SECFailure is returned. + * RETURN: + * Returns SECFailure if an error occurred; SECSuccess otherwise. + * The most likely error is that the cert for "name" could not be found + * (probably SEC_ERROR_UNKNOWN_CERT). Other errors are low-level (no memory, + * bad database, etc.). + */ +SECStatus +CERT_SetOCSPDefaultResponder(CERTCertDBHandle *handle, + const char *url, const char *name) +{ + CERTCertificate *cert; + ocspCheckingContext *statusContext; + char *url_copy = NULL; + char *name_copy = NULL; + SECStatus rv; + + if (handle == NULL || url == NULL || name == NULL) { + /* + * XXX When interface is exported, probably want better errors; + * perhaps different one for each parameter. + */ + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* + * Find the certificate for the specified nickname. Do this first + * because it seems the most likely to fail. + * + * XXX Shouldn't need that cast if the FindCertByNickname interface + * used const to convey that it does not modify the name. Maybe someday. + */ + cert = CERT_FindCertByNickname(handle, (char *)name); + if (cert == NULL) { + /* + * look for the cert on an external token. + */ + cert = PK11_FindCertFromNickname((char *)name, NULL); + } + if (cert == NULL) + return SECFailure; + + /* + * Make a copy of the url and nickname. + */ + url_copy = PORT_Strdup(url); + name_copy = PORT_Strdup(name); + if (url_copy == NULL || name_copy == NULL) { + rv = SECFailure; + goto loser; + } + + statusContext = ocsp_GetCheckingContext(handle); + + /* + * Allocate and init the context if it doesn't already exist. + */ + if (statusContext == NULL) { + rv = ocsp_InitStatusChecking(handle); + if (rv != SECSuccess) + goto loser; + + statusContext = ocsp_GetCheckingContext(handle); + PORT_Assert(statusContext != NULL); /* extreme paranoia */ + } + + /* + * Note -- we do not touch the status context until after all of + * the steps which could cause errors. If something goes wrong, + * we want to leave things as they were. + */ + + /* + * Get rid of old url and name if there. + */ + if (statusContext->defaultResponderNickname != NULL) + PORT_Free(statusContext->defaultResponderNickname); + if (statusContext->defaultResponderURI != NULL) + PORT_Free(statusContext->defaultResponderURI); + + /* + * And replace them with the new ones. + */ + statusContext->defaultResponderURI = url_copy; + statusContext->defaultResponderNickname = name_copy; + + /* + * If there was already a cert in place, get rid of it and replace it. + * Otherwise, we are not currently enabled, so we don't want to save it; + * it will get re-found and set whenever use of a default responder is + * enabled. + */ + if (statusContext->defaultResponderCert != NULL) { + CERT_DestroyCertificate(statusContext->defaultResponderCert); + statusContext->defaultResponderCert = cert; + /*OCSP enabled, switching responder: clear cache*/ + CERT_ClearOCSPCache(); + } else { + PORT_Assert(statusContext->useDefaultResponder == PR_FALSE); + CERT_DestroyCertificate(cert); + /*OCSP currently not enabled, no need to clear cache*/ + } + + return SECSuccess; + +loser: + CERT_DestroyCertificate(cert); + if (url_copy != NULL) + PORT_Free(url_copy); + if (name_copy != NULL) + PORT_Free(name_copy); + return rv; +} + +/* + * FUNCTION: CERT_EnableOCSPDefaultResponder + * Turns on use of a default responder when OCSP checking. + * If OCSP checking is already enabled, this will make subsequent checks + * go directly to the default responder. (The location of the responder + * and the nickname of the responder cert must already be specified.) + * If OCSP checking is not enabled, this will be recorded and take effect + * whenever it is enabled. + * INPUTS: + * CERTCertDBHandle *handle + * Cert database on which OCSP checking should use the default responder. + * RETURN: + * Returns SECFailure if an error occurred; SECSuccess otherwise. + * No errors are especially likely unless the caller did not previously + * perform a successful call to SetOCSPDefaultResponder (in which case + * the error set will be SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER). + */ +SECStatus +CERT_EnableOCSPDefaultResponder(CERTCertDBHandle *handle) +{ + ocspCheckingContext *statusContext; + CERTCertificate *cert; + SECStatus rv; + SECCertificateUsage usage; + + if (handle == NULL) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + statusContext = ocsp_GetCheckingContext(handle); + + if (statusContext == NULL) { + /* + * Strictly speaking, the error already set is "correct", + * but cover over it with one more helpful in this context. + */ + PORT_SetError(SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER); + return SECFailure; + } + + if (statusContext->defaultResponderURI == NULL) { + PORT_SetError(SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER); + return SECFailure; + } + + if (statusContext->defaultResponderNickname == NULL) { + PORT_SetError(SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER); + return SECFailure; + } + + /* + * Find the cert for the nickname. + */ + cert = CERT_FindCertByNickname(handle, + statusContext->defaultResponderNickname); + if (cert == NULL) { + cert = PK11_FindCertFromNickname(statusContext->defaultResponderNickname, + NULL); + } + /* + * We should never have trouble finding the cert, because its + * existence should have been proven by SetOCSPDefaultResponder. + */ + PORT_Assert(cert != NULL); + if (cert == NULL) + return SECFailure; + + /* + * Supplied cert should at least have a signing capability in order for us + * to use it as a trusted responder cert. Ability to sign is guaranteed if + * cert is validated to have any set of the usages below. + */ + rv = CERT_VerifyCertificateNow(handle, cert, PR_TRUE, + certificateUsageCheckAllUsages, + NULL, &usage); + if (rv != SECSuccess || (usage & (certificateUsageSSLClient | certificateUsageSSLServer | certificateUsageSSLServerWithStepUp | certificateUsageEmailSigner | certificateUsageObjectSigner | certificateUsageStatusResponder | certificateUsageSSLCA)) == 0) { + PORT_SetError(SEC_ERROR_OCSP_RESPONDER_CERT_INVALID); + return SECFailure; + } + + /* + * And hang onto it. + */ + statusContext->defaultResponderCert = cert; + + /* we don't allow a mix of cache entries from different responders */ + CERT_ClearOCSPCache(); + + /* + * Finally, record the fact that we now have a default responder enabled. + */ + statusContext->useDefaultResponder = PR_TRUE; + return SECSuccess; +} + +/* + * FUNCTION: CERT_DisableOCSPDefaultResponder + * Turns off use of a default responder when OCSP checking. + * (Does nothing if use of a default responder is not enabled.) + * INPUTS: + * CERTCertDBHandle *handle + * Cert database on which OCSP checking should stop using a default + * responder. + * RETURN: + * Returns SECFailure if an error occurred; SECSuccess otherwise. + * Errors very unlikely (like random memory corruption...). + */ +SECStatus +CERT_DisableOCSPDefaultResponder(CERTCertDBHandle *handle) +{ + CERTStatusConfig *statusConfig; + ocspCheckingContext *statusContext; + CERTCertificate *tmpCert; + + if (handle == NULL) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + statusConfig = CERT_GetStatusConfig(handle); + if (statusConfig == NULL) + return SECSuccess; + + statusContext = ocsp_GetCheckingContext(handle); + PORT_Assert(statusContext != NULL); + if (statusContext == NULL) + return SECFailure; + + tmpCert = statusContext->defaultResponderCert; + if (tmpCert) { + statusContext->defaultResponderCert = NULL; + CERT_DestroyCertificate(tmpCert); + /* we don't allow a mix of cache entries from different responders */ + CERT_ClearOCSPCache(); + } + + /* + * Finally, record the fact. + */ + statusContext->useDefaultResponder = PR_FALSE; + return SECSuccess; +} + +SECStatus +CERT_ForcePostMethodForOCSP(PRBool forcePost) +{ + if (!OCSP_Global.monitor) { + PORT_SetError(SEC_ERROR_NOT_INITIALIZED); + return SECFailure; + } + + PR_EnterMonitor(OCSP_Global.monitor); + OCSP_Global.forcePost = forcePost; + PR_ExitMonitor(OCSP_Global.monitor); + + return SECSuccess; +} + +SECStatus +CERT_GetOCSPResponseStatus(CERTOCSPResponse *response) +{ + PORT_Assert(response); + if (response->statusValue == ocspResponse_successful) + return SECSuccess; + + switch (response->statusValue) { + case ocspResponse_malformedRequest: + PORT_SetError(SEC_ERROR_OCSP_MALFORMED_REQUEST); + break; + case ocspResponse_internalError: + PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR); + break; + case ocspResponse_tryLater: + PORT_SetError(SEC_ERROR_OCSP_TRY_SERVER_LATER); + break; + case ocspResponse_sigRequired: + /* XXX We *should* retry with a signature, if possible. */ + PORT_SetError(SEC_ERROR_OCSP_REQUEST_NEEDS_SIG); + break; + case ocspResponse_unauthorized: + PORT_SetError(SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST); + break; + case ocspResponse_unused: + default: + PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS); + break; + } + return SECFailure; +} diff --git a/security/nss/lib/certhigh/ocsp.h b/security/nss/lib/certhigh/ocsp.h new file mode 100644 index 0000000000..1b94aec2e3 --- /dev/null +++ b/security/nss/lib/certhigh/ocsp.h @@ -0,0 +1,723 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Interface to the OCSP implementation. + */ + +#ifndef _OCSP_H_ +#define _OCSP_H_ + +#include "plarena.h" +#include "seccomon.h" +#include "secoidt.h" +#include "keythi.h" +#include "certt.h" +#include "ocspt.h" + +/************************************************************************/ +SEC_BEGIN_PROTOS + +/* + * This function registers the HttpClient with whose functions the + * HttpClientFcn structure has been populated as the default Http + * client. + * + * The function table must be a global object. + * The caller must ensure that NSS will be able to call + * the registered functions for the lifetime of the process. + */ +extern SECStatus +SEC_RegisterDefaultHttpClient(const SEC_HttpClientFcn *fcnTable); + +/* + * This function obtains the HttpClient which has been registered + * by an earlier call to SEC_RegisterDefaultHttpClient. + */ +extern const SEC_HttpClientFcn * +SEC_GetRegisteredHttpClient(void); + +/* + * Sets parameters that control NSS' internal OCSP cache. + * maxCacheEntries, special varlues are: + * -1 disable cache + * 0 unlimited cache entries + * minimumSecondsToNextFetchAttempt: + * whenever an OCSP request was attempted or completed over the network, + * wait at least this number of seconds before trying to fetch again. + * maximumSecondsToNextFetchAttempt: + * this is the maximum age of a cached response we allow, until we try + * to fetch an updated response, even if the OCSP responder expects + * that newer information update will not be available yet. + */ +extern SECStatus +CERT_OCSPCacheSettings(PRInt32 maxCacheEntries, + PRUint32 minimumSecondsToNextFetchAttempt, + PRUint32 maximumSecondsToNextFetchAttempt); + +/* + * Set the desired behaviour on OCSP failures. + * See definition of ocspFailureMode for allowed choices. + */ +extern SECStatus +CERT_SetOCSPFailureMode(SEC_OcspFailureMode ocspFailureMode); + +/* + * Configure the maximum time NSS will wait for an OCSP response. + */ +extern SECStatus +CERT_SetOCSPTimeout(PRUint32 seconds); + +/* + * Removes all items currently stored in the OCSP cache. + */ +extern SECStatus +CERT_ClearOCSPCache(void); + +/* + * FUNCTION: CERT_EnableOCSPChecking + * Turns on OCSP checking for the given certificate database. + * INPUTS: + * CERTCertDBHandle *handle + * Certificate database for which OCSP checking will be enabled. + * RETURN: + * Returns SECFailure if an error occurred (likely only problem + * allocating memory); SECSuccess otherwise. + */ +extern SECStatus +CERT_EnableOCSPChecking(CERTCertDBHandle *handle); + +/* + * FUNCTION: CERT_DisableOCSPChecking + * Turns off OCSP checking for the given certificate database. + * This routine disables OCSP checking. Though it will return + * SECFailure if OCSP checking is not enabled, it is "safe" to + * call it that way and just ignore the return value, if it is + * easier to just call it than to "remember" whether it is enabled. + * INPUTS: + * CERTCertDBHandle *handle + * Certificate database for which OCSP checking will be disabled. + * RETURN: + * Returns SECFailure if an error occurred (usually means that OCSP + * checking was not enabled or status contexts were not initialized -- + * error set will be SEC_ERROR_OCSP_NOT_ENABLED); SECSuccess otherwise. + */ +extern SECStatus +CERT_DisableOCSPChecking(CERTCertDBHandle *handle); + +/* + * FUNCTION: CERT_SetOCSPDefaultResponder + * Specify the location and cert of the default responder. + * If OCSP checking is already enabled *and* use of a default responder + * is also already enabled, all OCSP checking from now on will go directly + * to the specified responder. If OCSP checking is not enabled, or if + * it is but use of a default responder is not enabled, the information + * will be recorded and take effect whenever both are enabled. + * INPUTS: + * CERTCertDBHandle *handle + * Cert database on which OCSP checking should use the default responder. + * const char *url + * The location of the default responder (e.g. "http://foo.com:80/ocsp") + * Note that the location will not be tested until the first attempt + * to send a request there. + * const char *name + * The nickname of the cert to trust (expected) to sign the OCSP responses. + * If the corresponding cert cannot be found, SECFailure is returned. + * RETURN: + * Returns SECFailure if an error occurred; SECSuccess otherwise. + * The most likely error is that the cert for "name" could not be found + * (probably SEC_ERROR_UNKNOWN_CERT). Other errors are low-level (no memory, + * bad database, etc.). + */ +extern SECStatus +CERT_SetOCSPDefaultResponder(CERTCertDBHandle *handle, + const char *url, const char *name); + +/* + * FUNCTION: CERT_EnableOCSPDefaultResponder + * Turns on use of a default responder when OCSP checking. + * If OCSP checking is already enabled, this will make subsequent checks + * go directly to the default responder. (The location of the responder + * and the nickname of the responder cert must already be specified.) + * If OCSP checking is not enabled, this will be recorded and take effect + * whenever it is enabled. + * INPUTS: + * CERTCertDBHandle *handle + * Cert database on which OCSP checking should use the default responder. + * RETURN: + * Returns SECFailure if an error occurred; SECSuccess otherwise. + * No errors are especially likely unless the caller did not previously + * perform a successful call to SetOCSPDefaultResponder (in which case + * the error set will be SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER). + */ +extern SECStatus +CERT_EnableOCSPDefaultResponder(CERTCertDBHandle *handle); + +/* + * FUNCTION: CERT_DisableOCSPDefaultResponder + * Turns off use of a default responder when OCSP checking. + * (Does nothing if use of a default responder is not enabled.) + * INPUTS: + * CERTCertDBHandle *handle + * Cert database on which OCSP checking should stop using a default + * responder. + * RETURN: + * Returns SECFailure if an error occurred; SECSuccess otherwise. + * Errors very unlikely (like random memory corruption...). + */ +extern SECStatus +CERT_DisableOCSPDefaultResponder(CERTCertDBHandle *handle); + +/* If forcePost is set, OCSP requests will only be sent using the HTTP POST + * method. When forcePost is not set, OCSP requests will be sent using the + * HTTP GET method, with a fallback to POST when we fail to receive a response + * and/or when we receive an uncacheable response like "Unknown." + * + * The default is to use GET and fallback to POST. + */ +extern SECStatus CERT_ForcePostMethodForOCSP(PRBool forcePost); + +/* + * ------------------------------------------------------- + * The Functions above are those expected to be used by a client + * providing OCSP status checking along with every cert verification. + * The functions below are for OCSP testing, debugging, or clients + * or servers performing more specialized OCSP tasks. + * ------------------------------------------------------- + */ + +/* + * FUNCTION: CERT_CreateOCSPRequest + * Creates a CERTOCSPRequest, requesting the status of the certs in + * the given list. + * INPUTS: + * CERTCertList *certList + * A list of certs for which status will be requested. + * Note that all of these certificates should have the same issuer, + * or it's expected the response will be signed by a trusted responder. + * If the certs need to be broken up into multiple requests, that + * must be handled by the caller (and thus by having multiple calls + * to this routine), who knows about where the request(s) are being + * sent and whether there are any trusted responders in place. + * PRTime time + * Indicates the time for which the certificate status is to be + * determined -- this may be used in the search for the cert's issuer + * but has no effect on the request itself. + * PRBool addServiceLocator + * If true, the Service Locator extension should be added to the + * single request(s) for each cert. + * CERTCertificate *signerCert + * If non-NULL, means sign the request using this cert. Otherwise, + * do not sign. + * XXX note that request signing is not yet supported; see comment in code + * RETURN: + * A pointer to a CERTOCSPRequest structure containing an OCSP request + * for the cert list. On error, null is returned, with an error set + * indicating the reason. This is likely SEC_ERROR_UNKNOWN_ISSUER. + * (The issuer is needed to create a request for the certificate.) + * Other errors are low-level problems (no memory, bad database, etc.). + */ +extern CERTOCSPRequest * +CERT_CreateOCSPRequest(CERTCertList *certList, PRTime time, + PRBool addServiceLocator, + CERTCertificate *signerCert); + +/* + * FUNCTION: CERT_AddOCSPAcceptableResponses + * Add the AcceptableResponses extension to an OCSP Request. + * INPUTS: + * CERTOCSPRequest *request + * The request to which the extension should be added. + * SECOidTag responseType0, ... + * A list (of one or more) of SECOidTag -- each of the response types + * to be added. The last OID *must* be SEC_OID_PKIX_OCSP_BASIC_RESPONSE. + * (This marks the end of the list, and it must be specified because a + * client conforming to the OCSP standard is required to handle the basic + * response type.) The OIDs are not checked in any way. + * RETURN: + * SECSuccess if the extension is added; SECFailure if anything goes wrong. + * All errors are internal or low-level problems (e.g. no memory). + */ +extern SECStatus +CERT_AddOCSPAcceptableResponses(CERTOCSPRequest *request, + SECOidTag responseType0, ...); + +/* + * FUNCTION: CERT_EncodeOCSPRequest + * DER encodes an OCSP Request, possibly adding a signature as well. + * XXX Signing is not yet supported, however; see comments in code. + * INPUTS: + * PLArenaPool *arena + * The return value is allocated from here. + * If a NULL is passed in, allocation is done from the heap instead. + * CERTOCSPRequest *request + * The request to be encoded. + * void *pwArg + * Pointer to argument for password prompting, if needed. (Definitely + * not needed if not signing.) + * RETURN: + * Returns a NULL on error and a pointer to the SECItem with the + * encoded value otherwise. Any error is likely to be low-level + * (e.g. no memory). + */ +extern SECItem * +CERT_EncodeOCSPRequest(PLArenaPool *arena, CERTOCSPRequest *request, + void *pwArg); + +/* + * FUNCTION: CERT_DecodeOCSPRequest + * Decode a DER encoded OCSP Request. + * INPUTS: + * SECItem *src + * Pointer to a SECItem holding DER encoded OCSP Request. + * RETURN: + * Returns a pointer to a CERTOCSPRequest containing the decoded request. + * On error, returns NULL. Most likely error is trouble decoding + * (SEC_ERROR_OCSP_MALFORMED_REQUEST), or low-level problem (no memory). + */ +extern CERTOCSPRequest * +CERT_DecodeOCSPRequest(const SECItem *src); + +/* + * FUNCTION: CERT_DestroyOCSPRequest + * Frees an OCSP Request structure. + * INPUTS: + * CERTOCSPRequest *request + * Pointer to CERTOCSPRequest to be freed. + * RETURN: + * No return value; no errors. + */ +extern void +CERT_DestroyOCSPRequest(CERTOCSPRequest *request); + +/* + * FUNCTION: CERT_DecodeOCSPResponse + * Decode a DER encoded OCSP Response. + * INPUTS: + * SECItem *src + * Pointer to a SECItem holding DER encoded OCSP Response. + * RETURN: + * Returns a pointer to a CERTOCSPResponse (the decoded OCSP Response); + * the caller is responsible for destroying it. Or NULL if error (either + * response could not be decoded (SEC_ERROR_OCSP_MALFORMED_RESPONSE), + * it was of an unexpected type (SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE), + * or a low-level or internal error occurred). + */ +extern CERTOCSPResponse * +CERT_DecodeOCSPResponse(const SECItem *src); + +/* + * FUNCTION: CERT_DestroyOCSPResponse + * Frees an OCSP Response structure. + * INPUTS: + * CERTOCSPResponse *request + * Pointer to CERTOCSPResponse to be freed. + * RETURN: + * No return value; no errors. + */ +extern void +CERT_DestroyOCSPResponse(CERTOCSPResponse *response); + +/* + * FUNCTION: CERT_GetEncodedOCSPResponse + * Creates and sends a request to an OCSP responder, then reads and + * returns the (encoded) response. + * INPUTS: + * PLArenaPool *arena + * Pointer to arena from which return value will be allocated. + * If NULL, result will be allocated from the heap (and thus should + * be freed via SECITEM_FreeItem). + * CERTCertList *certList + * A list of certs for which status will be requested. + * Note that all of these certificates should have the same issuer, + * or it's expected the response will be signed by a trusted responder. + * If the certs need to be broken up into multiple requests, that + * must be handled by the caller (and thus by having multiple calls + * to this routine), who knows about where the request(s) are being + * sent and whether there are any trusted responders in place. + * const char *location + * The location of the OCSP responder (a URL). + * PRTime time + * Indicates the time for which the certificate status is to be + * determined -- this may be used in the search for the cert's issuer + * but has no other bearing on the operation. + * PRBool addServiceLocator + * If true, the Service Locator extension should be added to the + * single request(s) for each cert. + * CERTCertificate *signerCert + * If non-NULL, means sign the request using this cert. Otherwise, + * do not sign. + * void *pwArg + * Pointer to argument for password prompting, if needed. (Definitely + * not needed if not signing.) + * OUTPUTS: + * CERTOCSPRequest **pRequest + * Pointer in which to store the OCSP request created for the given + * list of certificates. It is only filled in if the entire operation + * is successful and the pointer is not null -- and in that case the + * caller is then reponsible for destroying it. + * RETURN: + * Returns a pointer to the SECItem holding the response. + * On error, returns null with error set describing the reason: + * SEC_ERROR_UNKNOWN_ISSUER + * SEC_ERROR_CERT_BAD_ACCESS_LOCATION + * SEC_ERROR_OCSP_BAD_HTTP_RESPONSE + * Other errors are low-level problems (no memory, bad database, etc.). + */ +extern SECItem * +CERT_GetEncodedOCSPResponse(PLArenaPool *arena, CERTCertList *certList, + const char *location, PRTime time, + PRBool addServiceLocator, + CERTCertificate *signerCert, void *pwArg, + CERTOCSPRequest **pRequest); + +/* + * FUNCTION: CERT_VerifyOCSPResponseSignature + * Check the signature on an OCSP Response. Will also perform a + * verification of the signer's certificate. Note, however, that a + * successful verification does not make any statement about the + * signer's *authority* to provide status for the certificate(s), + * that must be checked individually for each certificate. + * INPUTS: + * CERTOCSPResponse *response + * Pointer to response structure with signature to be checked. + * CERTCertDBHandle *handle + * Pointer to CERTCertDBHandle for certificate DB to use for verification. + * void *pwArg + * Pointer to argument for password prompting, if needed. + * CERTCertificate *issuerCert + * Issuer of the certificate that generated the OCSP request. + * OUTPUTS: + * CERTCertificate **pSignerCert + * Pointer in which to store signer's certificate; only filled-in if + * non-null. + * RETURN: + * Returns SECSuccess when signature is valid, anything else means invalid. + * Possible errors set: + * SEC_ERROR_OCSP_MALFORMED_RESPONSE - unknown type of ResponderID + * SEC_ERROR_INVALID_TIME - bad format of "ProducedAt" time + * SEC_ERROR_UNKNOWN_SIGNER - signer's cert could not be found + * SEC_ERROR_BAD_SIGNATURE - the signature did not verify + * Other errors are any of the many possible failures in cert verification + * (e.g. SEC_ERROR_REVOKED_CERTIFICATE, SEC_ERROR_UNTRUSTED_ISSUER) when + * verifying the signer's cert, or low-level problems (no memory, etc.) + */ +extern SECStatus +CERT_VerifyOCSPResponseSignature(CERTOCSPResponse *response, + CERTCertDBHandle *handle, void *pwArg, + CERTCertificate **pSignerCert, + CERTCertificate *issuerCert); + +/* + * FUNCTION: CERT_GetOCSPAuthorityInfoAccessLocation + * Get the value of the URI of the OCSP responder for the given cert. + * This is found in the (optional) Authority Information Access extension + * in the cert. + * INPUTS: + * CERTCertificate *cert + * The certificate being examined. + * RETURN: + * char * + * A copy of the URI for the OCSP method, if found. If either the + * extension is not present or it does not contain an entry for OCSP, + * SEC_ERROR_EXTENSION_NOT_FOUND will be set and a NULL returned. + * Any other error will also result in a NULL being returned. + * + * This result should be freed (via PORT_Free) when no longer in use. + */ +extern char * +CERT_GetOCSPAuthorityInfoAccessLocation(const CERTCertificate *cert); + +/* + * FUNCTION: CERT_RegisterAlternateOCSPAIAInfoCallBack + * This function serves two purposes. + * 1) It registers the address of a callback function that will be + * called for certs that have no OCSP AIA extension, to see if the + * callback wishes to supply an alternative URL for such an OCSP inquiry. + * 2) It outputs the previously registered function's address to the + * address supplied by the caller, unless that is NULL. + * The registered callback function returns NULL, or an allocated string + * that may be subsequently freed by calling PORT_Free(). + * RETURN: + * SECSuccess or SECFailure (if the library is not yet intialized) + */ +extern SECStatus +CERT_RegisterAlternateOCSPAIAInfoCallBack( + CERT_StringFromCertFcn newCallback, + CERT_StringFromCertFcn *oldCallback); + +/* + * FUNCTION: CERT_ParseURL + * Parse a URI into hostname, port, and path. The scheme in the URI must + * be "http". + * INPUTS: + * const char *url + * The URI to be parsed + * OUTPUTS: + * char **pHostname + * Pointer to store the hostname obtained from the URI. + * This result should be freed (via PORT_Free) when no longer in use. + * PRUint16 *pPort + * Pointer to store the port number obtained from the URI. + * char **pPath + * Pointer to store the path obtained from the URI. + * This result should be freed (via PORT_Free) when no longer in use. + * RETURN: + * Returns SECSuccess when parsing was successful. Returns SECFailure when + * problems were encountered. + */ +extern SECStatus +CERT_ParseURL(const char *url, char **pHostname, PRUint16 *pPort, char **pPath); + +/* + * FUNCTION: CERT_CheckOCSPStatus + * Checks the status of a certificate via OCSP. Will only check status for + * a certificate that has an AIA (Authority Information Access) extension + * for OCSP *or* when a "default responder" is specified and enabled. + * (If no AIA extension for OCSP and no default responder in place, the + * cert is considered to have a good status and SECSuccess is returned.) + * INPUTS: + * CERTCertDBHandle *handle + * certificate DB of the cert that is being checked + * CERTCertificate *cert + * the certificate being checked + * XXX in the long term also need a boolean parameter that specifies + * whether to check the cert chain, as well; for now we check only + * the leaf (the specified certificate) + * PRTime time + * time for which status is to be determined + * void *pwArg + * argument for password prompting, if needed + * RETURN: + * Returns SECSuccess if an approved OCSP responder "knows" the cert + * *and* returns a non-revoked status for it; SECFailure otherwise, + * with an error set describing the reason: + * + * SEC_ERROR_OCSP_BAD_HTTP_RESPONSE + * SEC_ERROR_OCSP_FUTURE_RESPONSE + * SEC_ERROR_OCSP_MALFORMED_REQUEST + * SEC_ERROR_OCSP_MALFORMED_RESPONSE + * SEC_ERROR_OCSP_OLD_RESPONSE + * SEC_ERROR_OCSP_REQUEST_NEEDS_SIG + * SEC_ERROR_OCSP_SERVER_ERROR + * SEC_ERROR_OCSP_TRY_SERVER_LATER + * SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST + * SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE + * SEC_ERROR_OCSP_UNKNOWN_CERT + * SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS + * SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE + * + * SEC_ERROR_BAD_SIGNATURE + * SEC_ERROR_CERT_BAD_ACCESS_LOCATION + * SEC_ERROR_INVALID_TIME + * SEC_ERROR_REVOKED_CERTIFICATE + * SEC_ERROR_UNKNOWN_ISSUER + * SEC_ERROR_UNKNOWN_SIGNER + * + * Other errors are any of the many possible failures in cert verification + * (e.g. SEC_ERROR_REVOKED_CERTIFICATE, SEC_ERROR_UNTRUSTED_ISSUER) when + * verifying the signer's cert, or low-level problems (error allocating + * memory, error performing ASN.1 decoding, etc.). + */ +extern SECStatus +CERT_CheckOCSPStatus(CERTCertDBHandle *handle, CERTCertificate *cert, + PRTime time, void *pwArg); + +/* + * FUNCTION: CERT_CacheOCSPResponseFromSideChannel + * First, this function checks the OCSP cache to see if a good response + * for the given certificate already exists. If it does, then the function + * returns successfully. + * + * If not, then it validates that the given OCSP response is a valid, + * good response for the given certificate and inserts it into the + * cache. + * + * This function is intended for use when OCSP responses are provided via a + * side-channel, i.e. TLS OCSP stapling (a.k.a. the status_request extension). + * + * INPUTS: + * CERTCertDBHandle *handle + * certificate DB of the cert that is being checked + * CERTCertificate *cert + * the certificate being checked + * PRTime time + * time for which status is to be determined + * SECItem *encodedResponse + * the DER encoded bytes of the OCSP response + * void *pwArg + * argument for password prompting, if needed + * RETURN: + * SECSuccess if the cert was found in the cache, or if the OCSP response was + * found to be valid and inserted into the cache. SECFailure otherwise. + */ +extern SECStatus +CERT_CacheOCSPResponseFromSideChannel(CERTCertDBHandle *handle, + CERTCertificate *cert, + PRTime time, + const SECItem *encodedResponse, + void *pwArg); + +/* + * FUNCTION: CERT_GetOCSPStatusForCertID + * Returns the OCSP status contained in the passed in parameter response + * that corresponds to the certID passed in. + * INPUTS: + * CERTCertDBHandle *handle + * certificate DB of the cert that is being checked + * CERTOCSPResponse *response + * the OCSP response we want to retrieve status from. + * CERTOCSPCertID *certID + * the ID we want to look for from the response. + * CERTCertificate *signerCert + * the certificate that was used to sign the OCSP response. + * must be obtained via a call to CERT_VerifyOCSPResponseSignature. + * PRTime time + * The time at which we're checking the status for. + * RETURN: + * Return values are the same as those for CERT_CheckOCSPStatus + */ +extern SECStatus +CERT_GetOCSPStatusForCertID(CERTCertDBHandle *handle, + CERTOCSPResponse *response, + CERTOCSPCertID *certID, + CERTCertificate *signerCert, + PRTime time); + +/* + * FUNCTION CERT_GetOCSPResponseStatus + * Returns the response status for the response passed. + * INPUTS: + * CERTOCSPResponse *response + * The response to query for status + * RETURN: + * Returns SECSuccess if the response has a successful status value. + * Otherwise it returns SECFailure and sets one of the following error + * codes via PORT_SetError + * SEC_ERROR_OCSP_MALFORMED_REQUEST + * SEC_ERROR_OCSP_SERVER_ERROR + * SEC_ERROR_OCSP_TRY_SERVER_LATER + * SEC_ERROR_OCSP_REQUEST_NEEDS_SIG + * SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST + * SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS + */ +extern SECStatus +CERT_GetOCSPResponseStatus(CERTOCSPResponse *response); + +/* + * FUNCTION CERT_CreateOCSPCertID + * Returns the OCSP certID for the certificate passed in. + * INPUTS: + * CERTCertificate *cert + * The certificate for which to create the certID for. + * PRTime time + * The time at which the id is requested for. This is used + * to determine the appropriate issuer for the cert since + * the issuing CA may be an older expired certificate. + * RETURN: + * A new copy of a CERTOCSPCertID*. The memory for this certID + * should be freed by calling CERT_DestroyOCSPCertID when the + * certID is no longer necessary. + */ +extern CERTOCSPCertID * +CERT_CreateOCSPCertID(CERTCertificate *cert, PRTime time); + +/* + * FUNCTION: CERT_DestroyOCSPCertID + * Frees the memory associated with the certID passed in. + * INPUTS: + * CERTOCSPCertID* certID + * The certID that the caller no longer needs and wants to + * free the associated memory. + * RETURN: + * SECSuccess if freeing the memory was successful. Returns + * SECFailure if the memory passed in was not allocated with + * a call to CERT_CreateOCSPCertID. + */ +extern SECStatus +CERT_DestroyOCSPCertID(CERTOCSPCertID *certID); + +extern CERTOCSPSingleResponse * +CERT_CreateOCSPSingleResponseGood(PLArenaPool *arena, + CERTOCSPCertID *id, + PRTime thisUpdate, + const PRTime *nextUpdate); + +extern CERTOCSPSingleResponse * +CERT_CreateOCSPSingleResponseUnknown(PLArenaPool *arena, + CERTOCSPCertID *id, + PRTime thisUpdate, + const PRTime *nextUpdate); + +extern CERTOCSPSingleResponse * +CERT_CreateOCSPSingleResponseRevoked( + PLArenaPool *arena, + CERTOCSPCertID *id, + PRTime thisUpdate, + const PRTime *nextUpdate, + PRTime revocationTime, + const CERTCRLEntryReasonCode *revocationReason); + +extern SECItem * +CERT_CreateEncodedOCSPSuccessResponse( + PLArenaPool *arena, + CERTCertificate *responderCert, + CERTOCSPResponderIDType responderIDType, + PRTime producedAt, + CERTOCSPSingleResponse **responses, + void *wincx); + +/* + * FUNCTION: CERT_CreateEncodedOCSPErrorResponse + * Creates an encoded OCSP response with an error response status. + * INPUTS: + * PLArenaPool *arena + * The return value is allocated from here. + * If a NULL is passed in, allocation is done from the heap instead. + * int error + * An NSS error code indicating an error response status. The error + * code is mapped to an OCSP response status as follows: + * SEC_ERROR_OCSP_MALFORMED_REQUEST -> malformedRequest + * SEC_ERROR_OCSP_SERVER_ERROR -> internalError + * SEC_ERROR_OCSP_TRY_SERVER_LATER -> tryLater + * SEC_ERROR_OCSP_REQUEST_NEEDS_SIG -> sigRequired + * SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST -> unauthorized + * where the OCSP response status is an enumerated type defined in + * RFC 2560: + * OCSPResponseStatus ::= ENUMERATED { + * successful (0), --Response has valid confirmations + * malformedRequest (1), --Illegal confirmation request + * internalError (2), --Internal error in issuer + * tryLater (3), --Try again later + * --(4) is not used + * sigRequired (5), --Must sign the request + * unauthorized (6) --Request unauthorized + * } + * RETURN: + * Returns a pointer to the SECItem holding the response. + * On error, returns null with error set describing the reason: + * SEC_ERROR_INVALID_ARGS + * Other errors are low-level problems (no memory, bad database, etc.). + */ +extern SECItem * +CERT_CreateEncodedOCSPErrorResponse(PLArenaPool *arena, int error); + +/* Sends an OCSP request using the HTTP POST method to the location addressed + * by the URL in |location| parameter. The request body will be + * |encodedRequest|, which must be a valid encoded OCSP request. On success, + * the server's response is returned and the caller must free it using + * SECITEM_FreeItem. On failure, NULL is returned. No parsing or validation of + * the HTTP response is done. + * + * If a default HTTP client has been registered with + * SEC_RegisterDefaultHttpClient then that client is used. Otherwise, an + * internal HTTP client is used. + */ +SECItem *CERT_PostOCSPRequest(PLArenaPool *arena, const char *location, + const SECItem *encodedRequest); + +/************************************************************************/ +SEC_END_PROTOS + +#endif /* _OCSP_H_ */ diff --git a/security/nss/lib/certhigh/ocspi.h b/security/nss/lib/certhigh/ocspi.h new file mode 100644 index 0000000000..c946d9f51c --- /dev/null +++ b/security/nss/lib/certhigh/ocspi.h @@ -0,0 +1,166 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* + * ocspi.h - NSS internal interfaces to OCSP code + */ + +#ifndef _OCSPI_H_ +#define _OCSPI_H_ + +SECStatus OCSP_InitGlobal(void); +SECStatus OCSP_ShutdownGlobal(void); + +ocspResponseData * +ocsp_GetResponseData(CERTOCSPResponse *response, SECItem **tbsResponseDataDER); + +ocspSignature * +ocsp_GetResponseSignature(CERTOCSPResponse *response); + +SECItem * +ocsp_DigestValue(PLArenaPool *arena, SECOidTag digestAlg, + SECItem *fill, const SECItem *src); + +PRBool +ocsp_CertIsOCSPDefaultResponder(CERTCertDBHandle *handle, CERTCertificate *cert); + +CERTCertificate * +ocsp_GetSignerCertificate(CERTCertDBHandle *handle, ocspResponseData *tbsData, + ocspSignature *signature, CERTCertificate *issuer); + +SECStatus +ocsp_VerifyResponseSignature(CERTCertificate *signerCert, + ocspSignature *signature, + SECItem *tbsResponseDataDER, + void *pwArg); + +CERTOCSPRequest * +cert_CreateSingleCertOCSPRequest(CERTOCSPCertID *certID, + CERTCertificate *singleCert, + PRTime time, + PRBool addServiceLocator, + CERTCertificate *signerCert); + +typedef enum { ocspMissing, + ocspFresh, + ocspStale } OCSPFreshness; + +SECStatus +ocsp_GetCachedOCSPResponseStatus(CERTOCSPCertID *certID, + PRTime time, + PRBool ignoreOcspFailureMode, + SECStatus *rvOcsp, + SECErrorCodes *missingResponseError, + OCSPFreshness *freshness); + +/* + * FUNCTION: cert_ProcessOCSPResponse + * Same behavior and basic parameters as CERT_GetOCSPStatusForCertID. + * In addition it can update the OCSP cache (using information + * available internally to this function). + * INPUTS: + * CERTCertDBHandle *handle + * certificate DB of the cert that is being checked + * CERTOCSPResponse *response + * the OCSP response we want to retrieve status from. + * CERTOCSPCertID *certID + * the ID we want to look for from the response. + * CERTCertificate *signerCert + * the certificate that was used to sign the OCSP response. + * must be obtained via a call to CERT_VerifyOCSPResponseSignature. + * PRTime time + * The time at which we're checking the status for. + * PRBool *certIDWasConsumed + * In and Out parameter. + * If certIDWasConsumed is NULL on input, + * this function might produce a deep copy of cert ID + * for storing it in the cache. + * If out value is true, ownership of parameter certID was + * transferred to the OCSP cache. + * SECStatus *cacheUpdateStatus + * This optional out parameter will contain the result + * of the cache update operation (if requested). + * RETURN: + * The return value is not influenced by the cache operation, + * it matches the documentation for CERT_CheckOCSPStatus + */ + +SECStatus +cert_ProcessOCSPResponse(CERTCertDBHandle *handle, + CERTOCSPResponse *response, + CERTOCSPCertID *certID, + CERTCertificate *signerCert, + PRTime time, + PRBool *certIDWasConsumed, + SECStatus *cacheUpdateStatus); + +/* + * FUNCTION: cert_RememberOCSPProcessingFailure + * If an application notices a failure during OCSP processing, + * it should finally call this function. The failure will be recorded + * in the OCSP cache in order to avoid repetitive failures. + * INPUTS: + * CERTOCSPCertID *certID + * the ID that was used for the failed OCSP processing + * PRBool *certIDWasConsumed + * Out parameter, if set to true, ownership of parameter certID was + * transferred to the OCSP cache. + * RETURN: + * Status of the cache update operation. + */ + +SECStatus +cert_RememberOCSPProcessingFailure(CERTOCSPCertID *certID, + PRBool *certIDWasConsumed); + +/* + * FUNCTION: ocsp_GetResponderLocation + * Check ocspx context for user-designated responder URI first. If not + * found, checks cert AIA extension. + * INPUTS: + * CERTCertDBHandle *handle + * certificate DB of the cert that is being checked + * CERTCertificate *cert + * The certificate being examined. + * PRBool *certIDWasConsumed + * Out parameter, if set to true, URI of default responder is + * returned. + * RETURN: + * Responder URI. + */ +char * +ocsp_GetResponderLocation(CERTCertDBHandle *handle, + CERTCertificate *cert, + PRBool canUseDefaultLocation, + PRBool *isDefault); + +/* FUNCTION: ocsp_FetchingFailureIsVerificationFailure + * The function checks the global ocsp settings and + * tells how to treat an ocsp response fetching failure. + * RETURNS: + * if PR_TRUE is returned, then treat fetching as a + * revoked cert status. + */ +PRBool +ocsp_FetchingFailureIsVerificationFailure(void); + +size_t +ocsp_UrlEncodeBase64Buf(const char *base64Buf, char *outputBuf); + +SECStatus +ocsp_GetVerifiedSingleResponseForCertID(CERTCertDBHandle *handle, + CERTOCSPResponse *response, + CERTOCSPCertID *certID, + CERTCertificate *signerCert, + PRTime time, + CERTOCSPSingleResponse **pSingleResponse); + +SECStatus +ocsp_CertHasGoodStatus(ocspCertStatus *status, PRTime time); + +void +ocsp_CacheSingleResponse(CERTOCSPCertID *certID, + CERTOCSPSingleResponse *single, + PRBool *certIDWasConsumed); + +#endif /* _OCSPI_H_ */ diff --git a/security/nss/lib/certhigh/ocspsig.c b/security/nss/lib/certhigh/ocspsig.c new file mode 100644 index 0000000000..94606baf56 --- /dev/null +++ b/security/nss/lib/certhigh/ocspsig.c @@ -0,0 +1,597 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "plarena.h" + +#include "seccomon.h" +#include "secitem.h" +#include "secasn1.h" +#include "secder.h" +#include "cert.h" +#include "secerr.h" +#include "secoid.h" +#include "sechash.h" +#include "keyhi.h" +#include "cryptohi.h" +#include "ocsp.h" +#include "ocspti.h" +#include "ocspi.h" +#include "pk11pub.h" + +extern const SEC_ASN1Template ocsp_ResponderIDByNameTemplate[]; +extern const SEC_ASN1Template ocsp_ResponderIDByKeyTemplate[]; +extern const SEC_ASN1Template ocsp_OCSPResponseTemplate[]; + +ocspCertStatus * +ocsp_CreateCertStatus(PLArenaPool *arena, + ocspCertStatusType status, + PRTime revocationTime) +{ + ocspCertStatus *cs; + + if (!arena) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + switch (status) { + case ocspCertStatus_good: + case ocspCertStatus_unknown: + case ocspCertStatus_revoked: + break; + default: + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + cs = PORT_ArenaZNew(arena, ocspCertStatus); + if (!cs) + return NULL; + cs->certStatusType = status; + switch (status) { + case ocspCertStatus_good: + cs->certStatusInfo.goodInfo = SECITEM_AllocItem(arena, NULL, 0); + if (!cs->certStatusInfo.goodInfo) + return NULL; + break; + case ocspCertStatus_unknown: + cs->certStatusInfo.unknownInfo = SECITEM_AllocItem(arena, NULL, 0); + if (!cs->certStatusInfo.unknownInfo) + return NULL; + break; + case ocspCertStatus_revoked: + cs->certStatusInfo.revokedInfo = + PORT_ArenaZNew(arena, ocspRevokedInfo); + if (!cs->certStatusInfo.revokedInfo) + return NULL; + cs->certStatusInfo.revokedInfo->revocationReason = + SECITEM_AllocItem(arena, NULL, 0); + if (!cs->certStatusInfo.revokedInfo->revocationReason) + return NULL; + if (DER_TimeToGeneralizedTimeArena(arena, + &cs->certStatusInfo.revokedInfo->revocationTime, + revocationTime) != + SECSuccess) + return NULL; + break; + default: + PORT_Assert(PR_FALSE); + } + return cs; +} + +static const SEC_ASN1Template mySEC_EnumeratedTemplate[] = { + { SEC_ASN1_ENUMERATED, 0, NULL, sizeof(SECItem) } +}; + +static const SEC_ASN1Template mySEC_PointerToEnumeratedTemplate[] = { + { SEC_ASN1_POINTER, 0, mySEC_EnumeratedTemplate } +}; + +static const SEC_ASN1Template ocsp_EncodeRevokedInfoTemplate[] = { + { SEC_ASN1_GENERALIZED_TIME, + offsetof(ocspRevokedInfo, revocationTime) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(ocspRevokedInfo, revocationReason), + mySEC_PointerToEnumeratedTemplate }, + { 0 } +}; + +static const SEC_ASN1Template ocsp_PointerToEncodeRevokedInfoTemplate[] = { + { SEC_ASN1_POINTER, 0, + ocsp_EncodeRevokedInfoTemplate } +}; + +static const SEC_ASN1Template mySEC_NullTemplate[] = { + { SEC_ASN1_NULL, 0, NULL, sizeof(SECItem) } +}; + +static const SEC_ASN1Template ocsp_CertStatusTemplate[] = { + { SEC_ASN1_CHOICE, offsetof(ocspCertStatus, certStatusType), + 0, sizeof(ocspCertStatus) }, + { SEC_ASN1_CONTEXT_SPECIFIC | 0, + 0, mySEC_NullTemplate, ocspCertStatus_good }, + { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | + SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(ocspCertStatus, certStatusInfo.revokedInfo), + ocsp_PointerToEncodeRevokedInfoTemplate, ocspCertStatus_revoked }, + { SEC_ASN1_CONTEXT_SPECIFIC | 2, + 0, mySEC_NullTemplate, ocspCertStatus_unknown }, + { 0 } +}; + +static const SEC_ASN1Template mySECOID_AlgorithmIDTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(SECAlgorithmID) }, + { SEC_ASN1_OBJECT_ID, + offsetof(SECAlgorithmID, algorithm) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY, + offsetof(SECAlgorithmID, parameters) }, + { 0 } +}; + +static const SEC_ASN1Template mySEC_AnyTemplate[] = { + { SEC_ASN1_ANY | SEC_ASN1_MAY_STREAM, 0, NULL, sizeof(SECItem) } +}; + +static const SEC_ASN1Template mySEC_SequenceOfAnyTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, 0, mySEC_AnyTemplate } +}; + +static const SEC_ASN1Template mySEC_PointerToSequenceOfAnyTemplate[] = { + { SEC_ASN1_POINTER, 0, mySEC_SequenceOfAnyTemplate } +}; + +static const SEC_ASN1Template mySEC_IntegerTemplate[] = { + { SEC_ASN1_INTEGER, 0, NULL, sizeof(SECItem) } +}; + +static const SEC_ASN1Template mySEC_PointerToIntegerTemplate[] = { + { SEC_ASN1_POINTER, 0, mySEC_IntegerTemplate } +}; + +static const SEC_ASN1Template mySEC_GeneralizedTimeTemplate[] = { + { SEC_ASN1_GENERALIZED_TIME | SEC_ASN1_MAY_STREAM, 0, NULL, sizeof(SECItem) } +}; + +static const SEC_ASN1Template mySEC_PointerToGeneralizedTimeTemplate[] = { + { SEC_ASN1_POINTER, 0, mySEC_GeneralizedTimeTemplate } +}; + +static const SEC_ASN1Template ocsp_myCertIDTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTOCSPCertID) }, + { SEC_ASN1_INLINE, + offsetof(CERTOCSPCertID, hashAlgorithm), + mySECOID_AlgorithmIDTemplate }, + { SEC_ASN1_OCTET_STRING, + offsetof(CERTOCSPCertID, issuerNameHash) }, + { SEC_ASN1_OCTET_STRING, + offsetof(CERTOCSPCertID, issuerKeyHash) }, + { SEC_ASN1_INTEGER, + offsetof(CERTOCSPCertID, serialNumber) }, + { 0 } +}; + +static const SEC_ASN1Template myCERT_CertExtensionTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTCertExtension) }, + { SEC_ASN1_OBJECT_ID, + offsetof(CERTCertExtension, id) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN, /* XXX DER_DEFAULT */ + offsetof(CERTCertExtension, critical) }, + { SEC_ASN1_OCTET_STRING, + offsetof(CERTCertExtension, value) }, + { 0 } +}; + +static const SEC_ASN1Template myCERT_SequenceOfCertExtensionTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, 0, myCERT_CertExtensionTemplate } +}; + +static const SEC_ASN1Template myCERT_PointerToSequenceOfCertExtensionTemplate[] = { + { SEC_ASN1_POINTER, 0, myCERT_SequenceOfCertExtensionTemplate } +}; + +static const SEC_ASN1Template ocsp_mySingleResponseTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTOCSPSingleResponse) }, + { SEC_ASN1_POINTER, + offsetof(CERTOCSPSingleResponse, certID), + ocsp_myCertIDTemplate }, + { SEC_ASN1_ANY, + offsetof(CERTOCSPSingleResponse, derCertStatus) }, + { SEC_ASN1_GENERALIZED_TIME, + offsetof(CERTOCSPSingleResponse, thisUpdate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(CERTOCSPSingleResponse, nextUpdate), + mySEC_PointerToGeneralizedTimeTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(CERTOCSPSingleResponse, singleExtensions), + myCERT_PointerToSequenceOfCertExtensionTemplate }, + { 0 } +}; + +static const SEC_ASN1Template ocsp_myResponseDataTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(ocspResponseData) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | /* XXX DER_DEFAULT */ + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(ocspResponseData, version), + mySEC_PointerToIntegerTemplate }, + { SEC_ASN1_ANY, + offsetof(ocspResponseData, derResponderID) }, + { SEC_ASN1_GENERALIZED_TIME, + offsetof(ocspResponseData, producedAt) }, + { SEC_ASN1_SEQUENCE_OF, + offsetof(ocspResponseData, responses), + ocsp_mySingleResponseTemplate }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, + offsetof(ocspResponseData, responseExtensions), + myCERT_PointerToSequenceOfCertExtensionTemplate }, + { 0 } +}; + +static const SEC_ASN1Template ocsp_EncodeBasicOCSPResponseTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(ocspBasicOCSPResponse) }, + { SEC_ASN1_POINTER, + offsetof(ocspBasicOCSPResponse, tbsResponseData), + ocsp_myResponseDataTemplate }, + { SEC_ASN1_INLINE, + offsetof(ocspBasicOCSPResponse, responseSignature.signatureAlgorithm), + mySECOID_AlgorithmIDTemplate }, + { SEC_ASN1_BIT_STRING, + offsetof(ocspBasicOCSPResponse, responseSignature.signature) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, + offsetof(ocspBasicOCSPResponse, responseSignature.derCerts), + mySEC_PointerToSequenceOfAnyTemplate }, + { 0 } +}; + +static CERTOCSPSingleResponse * +ocsp_CreateSingleResponse(PLArenaPool *arena, + CERTOCSPCertID *id, ocspCertStatus *status, + PRTime thisUpdate, const PRTime *nextUpdate) +{ + CERTOCSPSingleResponse *sr; + + if (!arena || !id || !status) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + sr = PORT_ArenaZNew(arena, CERTOCSPSingleResponse); + if (!sr) + return NULL; + sr->arena = arena; + sr->certID = id; + sr->certStatus = status; + if (DER_TimeToGeneralizedTimeArena(arena, &sr->thisUpdate, thisUpdate) != + SECSuccess) + return NULL; + sr->nextUpdate = NULL; + if (nextUpdate) { + sr->nextUpdate = SECITEM_AllocItem(arena, NULL, 0); + if (!sr->nextUpdate) + return NULL; + if (DER_TimeToGeneralizedTimeArena(arena, sr->nextUpdate, *nextUpdate) != + SECSuccess) + return NULL; + } + + sr->singleExtensions = PORT_ArenaNewArray(arena, CERTCertExtension *, 1); + if (!sr->singleExtensions) + return NULL; + + sr->singleExtensions[0] = NULL; + + if (!SEC_ASN1EncodeItem(arena, &sr->derCertStatus, + status, ocsp_CertStatusTemplate)) + return NULL; + + return sr; +} + +CERTOCSPSingleResponse * +CERT_CreateOCSPSingleResponseGood(PLArenaPool *arena, + CERTOCSPCertID *id, + PRTime thisUpdate, + const PRTime *nextUpdate) +{ + ocspCertStatus *cs; + if (!arena) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + cs = ocsp_CreateCertStatus(arena, ocspCertStatus_good, 0); + if (!cs) + return NULL; + return ocsp_CreateSingleResponse(arena, id, cs, thisUpdate, nextUpdate); +} + +CERTOCSPSingleResponse * +CERT_CreateOCSPSingleResponseUnknown(PLArenaPool *arena, + CERTOCSPCertID *id, + PRTime thisUpdate, + const PRTime *nextUpdate) +{ + ocspCertStatus *cs; + if (!arena) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + cs = ocsp_CreateCertStatus(arena, ocspCertStatus_unknown, 0); + if (!cs) + return NULL; + return ocsp_CreateSingleResponse(arena, id, cs, thisUpdate, nextUpdate); +} + +CERTOCSPSingleResponse * +CERT_CreateOCSPSingleResponseRevoked( + PLArenaPool *arena, + CERTOCSPCertID *id, + PRTime thisUpdate, + const PRTime *nextUpdate, + PRTime revocationTime, + const CERTCRLEntryReasonCode *revocationReason) +{ + ocspCertStatus *cs; + /* revocationReason is not yet supported, so it must be NULL. */ + if (!arena || revocationReason) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + cs = ocsp_CreateCertStatus(arena, ocspCertStatus_revoked, revocationTime); + if (!cs) + return NULL; + return ocsp_CreateSingleResponse(arena, id, cs, thisUpdate, nextUpdate); +} + +/* responderCert == 0 means: + * create a response with an invalid signature (for testing purposes) */ +SECItem * +CERT_CreateEncodedOCSPSuccessResponse( + PLArenaPool *arena, + CERTCertificate *responderCert, + CERTOCSPResponderIDType responderIDType, + PRTime producedAt, + CERTOCSPSingleResponse **responses, + void *wincx) +{ + PLArenaPool *tmpArena; + ocspResponseData *rd = NULL; + ocspResponderID *rid = NULL; + const SEC_ASN1Template *responderIDTemplate = NULL; + ocspBasicOCSPResponse *br = NULL; + ocspResponseBytes *rb = NULL; + CERTOCSPResponse *response = NULL; + + SECOidTag algID; + SECOidData *od = NULL; + SECKEYPrivateKey *privKey = NULL; + SECItem *result = NULL; + + if (!arena || !responses) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + if (responderIDType != ocspResponderID_byName && + responderIDType != ocspResponderID_byKey) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!tmpArena) + return NULL; + + rd = PORT_ArenaZNew(tmpArena, ocspResponseData); + if (!rd) + goto done; + rid = PORT_ArenaZNew(tmpArena, ocspResponderID); + if (!rid) + goto done; + br = PORT_ArenaZNew(tmpArena, ocspBasicOCSPResponse); + if (!br) + goto done; + rb = PORT_ArenaZNew(tmpArena, ocspResponseBytes); + if (!rb) + goto done; + response = PORT_ArenaZNew(tmpArena, CERTOCSPResponse); + if (!response) + goto done; + + rd->version.data = NULL; + rd->version.len = 0; + rd->responseExtensions = NULL; + rd->responses = responses; + if (DER_TimeToGeneralizedTimeArena(tmpArena, &rd->producedAt, producedAt) != + SECSuccess) + goto done; + + if (!responderCert) { + /* use invalid signature for testing purposes */ + unsigned char dummyChar = 'd'; + SECItem dummy; + + dummy.len = 1; + dummy.data = &dummyChar; + + /* it's easier to produdce a keyHash out of nowhere, + * than to produce an encoded subject, + * so for our dummy response we always use byKey + */ + + rid->responderIDType = ocspResponderID_byKey; + if (!ocsp_DigestValue(tmpArena, SEC_OID_SHA1, &rid->responderIDValue.keyHash, + &dummy)) + goto done; + + if (!SEC_ASN1EncodeItem(tmpArena, &rd->derResponderID, rid, + ocsp_ResponderIDByKeyTemplate)) + goto done; + + br->tbsResponseData = rd; + + if (!SEC_ASN1EncodeItem(tmpArena, &br->tbsResponseDataDER, br->tbsResponseData, + ocsp_myResponseDataTemplate)) + goto done; + + br->responseSignature.derCerts = PORT_ArenaNewArray(tmpArena, SECItem *, 1); + if (!br->responseSignature.derCerts) + goto done; + br->responseSignature.derCerts[0] = NULL; + + algID = SEC_GetSignatureAlgorithmOidTag(rsaKey, SEC_OID_SHA1); + if (algID == SEC_OID_UNKNOWN) + goto done; + + /* match the regular signature code, which doesn't use the arena */ + if (!SECITEM_AllocItem(NULL, &br->responseSignature.signature, 1)) + goto done; + PORT_Memcpy(br->responseSignature.signature.data, &dummyChar, 1); + + /* convert len-in-bytes to len-in-bits */ + br->responseSignature.signature.len = br->responseSignature.signature.len << 3; + } else { + rid->responderIDType = responderIDType; + if (responderIDType == ocspResponderID_byName) { + responderIDTemplate = ocsp_ResponderIDByNameTemplate; + if (CERT_CopyName(tmpArena, &rid->responderIDValue.name, + &responderCert->subject) != SECSuccess) + goto done; + } else { + responderIDTemplate = ocsp_ResponderIDByKeyTemplate; + if (!CERT_GetSubjectPublicKeyDigest(tmpArena, responderCert, + SEC_OID_SHA1, &rid->responderIDValue.keyHash)) + goto done; + } + + if (!SEC_ASN1EncodeItem(tmpArena, &rd->derResponderID, rid, + responderIDTemplate)) + goto done; + + br->tbsResponseData = rd; + + if (!SEC_ASN1EncodeItem(tmpArena, &br->tbsResponseDataDER, br->tbsResponseData, + ocsp_myResponseDataTemplate)) + goto done; + + br->responseSignature.derCerts = PORT_ArenaNewArray(tmpArena, SECItem *, 1); + if (!br->responseSignature.derCerts) + goto done; + br->responseSignature.derCerts[0] = NULL; + + privKey = PK11_FindKeyByAnyCert(responderCert, wincx); + if (!privKey) + goto done; + + algID = SEC_GetSignatureAlgorithmOidTag(privKey->keyType, SEC_OID_SHA1); + if (algID == SEC_OID_UNKNOWN) + goto done; + + if (SEC_SignData(&br->responseSignature.signature, + br->tbsResponseDataDER.data, br->tbsResponseDataDER.len, + privKey, algID) != + SECSuccess) + goto done; + + /* convert len-in-bytes to len-in-bits */ + br->responseSignature.signature.len = br->responseSignature.signature.len << 3; + + /* br->responseSignature.signature wasn't allocated from arena, + * we must free it when done. */ + } + + if (SECOID_SetAlgorithmID(tmpArena, &br->responseSignature.signatureAlgorithm, algID, 0) != + SECSuccess) + goto done; + + if (!SEC_ASN1EncodeItem(tmpArena, &rb->response, br, + ocsp_EncodeBasicOCSPResponseTemplate)) + goto done; + + rb->responseTypeTag = SEC_OID_PKIX_OCSP_BASIC_RESPONSE; + + od = SECOID_FindOIDByTag(rb->responseTypeTag); + if (!od) + goto done; + + rb->responseType = od->oid; + rb->decodedResponse.basic = br; + + response->arena = tmpArena; + response->responseBytes = rb; + response->statusValue = ocspResponse_successful; + + if (!SEC_ASN1EncodeInteger(tmpArena, &response->responseStatus, + response->statusValue)) + goto done; + + result = SEC_ASN1EncodeItem(arena, NULL, response, ocsp_OCSPResponseTemplate); + +done: + if (privKey) + SECKEY_DestroyPrivateKey(privKey); + if (br && br->responseSignature.signature.data) + SECITEM_FreeItem(&br->responseSignature.signature, PR_FALSE); + PORT_FreeArena(tmpArena, PR_FALSE); + + return result; +} + +static const SEC_ASN1Template ocsp_OCSPErrorResponseTemplate[] = { + { SEC_ASN1_SEQUENCE, + 0, NULL, sizeof(CERTOCSPResponse) }, + { SEC_ASN1_ENUMERATED, + offsetof(CERTOCSPResponse, responseStatus) }, + { 0, 0, + mySEC_NullTemplate }, + { 0 } +}; + +SECItem * +CERT_CreateEncodedOCSPErrorResponse(PLArenaPool *arena, int error) +{ + CERTOCSPResponse response; + SECItem *result = NULL; + + switch (error) { + case SEC_ERROR_OCSP_MALFORMED_REQUEST: + response.statusValue = ocspResponse_malformedRequest; + break; + case SEC_ERROR_OCSP_SERVER_ERROR: + response.statusValue = ocspResponse_internalError; + break; + case SEC_ERROR_OCSP_TRY_SERVER_LATER: + response.statusValue = ocspResponse_tryLater; + break; + case SEC_ERROR_OCSP_REQUEST_NEEDS_SIG: + response.statusValue = ocspResponse_sigRequired; + break; + case SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST: + response.statusValue = ocspResponse_unauthorized; + break; + default: + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + if (!SEC_ASN1EncodeInteger(NULL, &response.responseStatus, + response.statusValue)) + return NULL; + + result = SEC_ASN1EncodeItem(arena, NULL, &response, + ocsp_OCSPErrorResponseTemplate); + + SECITEM_FreeItem(&response.responseStatus, PR_FALSE); + + return result; +} diff --git a/security/nss/lib/certhigh/ocspt.h b/security/nss/lib/certhigh/ocspt.h new file mode 100644 index 0000000000..db429ff058 --- /dev/null +++ b/security/nss/lib/certhigh/ocspt.h @@ -0,0 +1,301 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Public header for exported OCSP types. + */ + +#ifndef _OCSPT_H_ +#define _OCSPT_H_ + +/* + * The following are all opaque types. If someone needs to get at + * a field within, then we need to fix the API. Try very hard not + * make the type available to them. + */ +typedef struct CERTOCSPRequestStr CERTOCSPRequest; +typedef struct CERTOCSPResponseStr CERTOCSPResponse; + +/* + * XXX I think only those first two above should need to be exported, + * but until I know for certain I am leaving the rest of these here, too. + */ +typedef struct CERTOCSPCertIDStr CERTOCSPCertID; +typedef struct CERTOCSPSingleResponseStr CERTOCSPSingleResponse; + +/* + * This interface is described in terms of an HttpClient which + * supports at least a specified set of functions. (An implementer may + * provide HttpClients with additional functionality accessible only to + * users with a particular implementation in mind.) The basic behavior + * is provided by defining a set of functions, listed in an + * SEC_HttpServerFcnStruct. If the implementor of a SpecificHttpClient + * registers his SpecificHttpClient as the default HttpClient, then his + * functions will be called by the user of an HttpClient, such as an + * OCSPChecker. + * + * The implementer of a specific HttpClient (e.g., the NSS-provided + * DefaultHttpClient), populates an SEC_HttpClientFcnStruct, uses it to + * register his client, and waits for his functions to be called. + * + * For future expandability, the SEC_HttpClientFcnStruct is defined as a + * union, with the version field acting as a selector. The proposed + * initial version of the structure is given following the definition + * of the union. The HttpClientState structure is implementation- + * dependent, and should be opaque to the user. + */ + +typedef void *SEC_HTTP_SERVER_SESSION; +typedef void *SEC_HTTP_REQUEST_SESSION; + +/* + * This function creates a SEC_HTTP_SERVER_SESSION object. The implementer of a + * specific HttpClient will allocate the necessary space, when this + * function is called, and will free it when the corresponding FreeFcn + * is called. The SEC_HTTP_SERVER_SESSION object is passed, as an opaque object, + * to subsequent calls. + * + * If the function returns SECSuccess, the returned SEC_HTTP_SERVER_SESSION + * must be cleaned up with a call to SEC_HttpServer_FreeSession, + * after processing is finished. + */ +typedef SECStatus (*SEC_HttpServer_CreateSessionFcn)( + const char *host, + PRUint16 portnum, + SEC_HTTP_SERVER_SESSION *pSession); + +/* + * This function is called to allow the implementation to attempt to keep + * the connection alive. Depending on the underlying platform, it might + * immediately return SECSuccess without having performed any operations. + * (If a connection has not been kept alive, a subsequent call to + * SEC_HttpRequest_TrySendAndReceiveFcn should reopen the connection + * automatically.) + * + * If the connection uses nonblocking I/O, this function may return + * SECWouldBlock and store a nonzero value at "pPollDesc". In that case + * the caller may wait on the poll descriptor, and should call this function + * again until SECSuccess (and a zero value at "pPollDesc") is obtained. + */ +typedef SECStatus (*SEC_HttpServer_KeepAliveSessionFcn)( + SEC_HTTP_SERVER_SESSION session, + PRPollDesc **pPollDesc); + +/* + * This function frees the client SEC_HTTP_SERVER_SESSION object, closes all + * SEC_HTTP_REQUEST_SESSIONs created for that server, discards all partial results, + * frees any memory that was allocated by the client, and invalidates any + * response pointers that might have been returned by prior server or request + * functions. + */ +typedef SECStatus (*SEC_HttpServer_FreeSessionFcn)( + SEC_HTTP_SERVER_SESSION session); + +/* + * This function creates a SEC_HTTP_REQUEST_SESSION object. The implementer of a + * specific HttpClient will allocate the necessary space, when this + * function is called, and will free it when the corresponding FreeFcn + * is called. The SEC_HTTP_REQUEST_SESSION object is passed, as an opaque object, + * to subsequent calls. + * + * An implementation that does not support the requested protocol variant + * (usually "http", but could eventually allow "https") or request method + * should return SECFailure. + * + * Timeout values may include the constants PR_INTERVAL_NO_TIMEOUT (wait + * forever) or PR_INTERVAL_NO_WAIT (nonblocking I/O). + * + * If the function returns SECSuccess, the returned SEC_HTTP_REQUEST_SESSION + * must be cleaned up with a call to SEC_HttpRequest_FreeSession, + * after processing is finished. + */ +typedef SECStatus (*SEC_HttpRequest_CreateFcn)( + SEC_HTTP_SERVER_SESSION session, + const char *http_protocol_variant, /* usually "http" */ + const char *path_and_query_string, + const char *http_request_method, + const PRIntervalTime timeout, + SEC_HTTP_REQUEST_SESSION *pRequest); + +/* + * This function sets data to be sent to the server for an HTTP request + * of http_request_method == POST. If a particular implementation + * supports it, the details for the POST request can be set by calling + * this function, prior to activating the request with TrySendAndReceiveFcn. + * + * An implementation that does not support the POST method should + * implement a SetPostDataFcn function that returns immediately. + * + * Setting http_content_type is optional, the parameter may + * by NULL or the empty string. + */ +typedef SECStatus (*SEC_HttpRequest_SetPostDataFcn)( + SEC_HTTP_REQUEST_SESSION request, + const char *http_data, + const PRUint32 http_data_len, + const char *http_content_type); + +/* + * This function sets an additional HTTP protocol request header. + * If a particular implementation supports it, one or multiple headers + * can be added to the request by calling this function once or multiple + * times, prior to activating the request with TryFcn. + * + * An implementation that does not support setting additional headers + * should implement an AddRequestHeaderFcn function that returns immediately. + */ +typedef SECStatus (*SEC_HttpRequest_AddHeaderFcn)( + SEC_HTTP_REQUEST_SESSION request, + const char *http_header_name, + const char *http_header_value); + +/* + * This function initiates or continues an HTTP request. After + * parameters have been set with the Create function and, optionally, + * modified or enhanced with the AddParams function, this call creates + * the socket connection and initiates the communication. + * + * If a timeout value of zero is specified, indicating non-blocking + * I/O, the client creates a non-blocking socket, and returns a status + * of SECWouldBlock and a non-NULL PRPollDesc if the operation is not + * complete. In that case all other return parameters are undefined. + * The caller is expected to repeat the call, possibly after using + * PRPoll to determine that a completion has occurred, until a return + * value of SECSuccess (and a NULL value for pPollDesc) or a return + * value of SECFailure (indicating failure on the network level) + * is obtained. + * + * http_response_data_len is both input and output parameter. + * If a pointer to a PRUint32 is supplied, the http client is + * expected to check the given integer value and always set an out + * value, even on failure. + * An input value of zero means, the caller will accept any response len. + * A different input value indicates the maximum response value acceptable + * to the caller. + * If data is successfully read and the size is acceptable to the caller, + * the function will return SECSuccess and set http_response_data_len to + * the size of the block returned in http_response_data. + * If the data read from the http server is larger than the acceptable + * size, the function will return SECFailure. + * http_response_data_len will be set to a value different from zero to + * indicate the reason of the failure. + * An out value of "0" means, the failure was unrelated to the + * acceptable size. + * An out value of "1" means, the result data is larger than the + * accpeptable size, but the real size is not yet known to the http client + * implementation and it stopped retrieving it, + * Any other out value combined with a return value of SECFailure + * will indicate the actual size of the server data. + * + * The caller is permitted to provide NULL values for any of the + * http_response arguments, indicating the caller is not interested in + * those values. If the caller does provide an address, the HttpClient + * stores at that address a pointer to the corresponding argument, at + * the completion of the operation. + * + * All returned pointers will be owned by the the HttpClient + * implementation and will remain valid until the call to + * SEC_HttpRequest_FreeFcn. + */ +typedef SECStatus (*SEC_HttpRequest_TrySendAndReceiveFcn)( + SEC_HTTP_REQUEST_SESSION request, + PRPollDesc **pPollDesc, + PRUint16 *http_response_code, + const char **http_response_content_type, + const char **http_response_headers, + const char **http_response_data, + PRUint32 *http_response_data_len); + +/* + * Calling CancelFcn asks for premature termination of the request. + * + * Future calls to SEC_HttpRequest_TrySendAndReceive should + * by avoided, but in this case the HttpClient implementation + * is expected to return immediately with SECFailure. + * + * After calling CancelFcn, a separate call to SEC_HttpRequest_FreeFcn + * is still necessary to free resources. + */ +typedef SECStatus (*SEC_HttpRequest_CancelFcn)( + SEC_HTTP_REQUEST_SESSION request); + +/* + * Before calling this function, it must be assured the request + * has been completed, i.e. either SEC_HttpRequest_TrySendAndReceiveFcn has + * returned SECSuccess, or the request has been canceled with + * a call to SEC_HttpRequest_CancelFcn. + * + * This function frees the client state object, closes all sockets, + * discards all partial results, frees any memory that was allocated + * by the client, and invalidates all response pointers that might + * have been returned by SEC_HttpRequest_TrySendAndReceiveFcn + */ +typedef SECStatus (*SEC_HttpRequest_FreeFcn)( + SEC_HTTP_REQUEST_SESSION request); + +typedef struct SEC_HttpClientFcnV1Struct { + SEC_HttpServer_CreateSessionFcn createSessionFcn; + SEC_HttpServer_KeepAliveSessionFcn keepAliveSessionFcn; + SEC_HttpServer_FreeSessionFcn freeSessionFcn; + SEC_HttpRequest_CreateFcn createFcn; + SEC_HttpRequest_SetPostDataFcn setPostDataFcn; + SEC_HttpRequest_AddHeaderFcn addHeaderFcn; + SEC_HttpRequest_TrySendAndReceiveFcn trySendAndReceiveFcn; + SEC_HttpRequest_CancelFcn cancelFcn; + SEC_HttpRequest_FreeFcn freeFcn; +} SEC_HttpClientFcnV1; + +typedef struct SEC_HttpClientFcnStruct { + PRInt16 version; + union { + SEC_HttpClientFcnV1 ftable1; + /* SEC_HttpClientFcnV2 ftable2; */ + /* ... */ + } fcnTable; +} SEC_HttpClientFcn; + +/* + * ocspMode_FailureIsVerificationFailure: + * This is the classic behaviour of NSS. + * Any OCSP failure is a verification failure (classic mode, default). + * Without a good response, OCSP networking will be retried each time + * it is required for verifying a cert. + * + * ocspMode_FailureIsNotAVerificationFailure: + * If we fail to obtain a valid OCSP response, consider the + * cert as good. + * Failed OCSP attempts might get cached and not retried until + * minimumSecondsToNextFetchAttempt. + * If we are able to obtain a valid response, the cert + * will be considered good, if either status is "good" + * or the cert was not yet revoked at verification time. + * + * Additional failure modes might be added in the future. + */ +typedef enum { + ocspMode_FailureIsVerificationFailure = 0, + ocspMode_FailureIsNotAVerificationFailure = 1 +} SEC_OcspFailureMode; + +/* + * A ResponderID identifies the responder -- or more correctly, the + * signer of the response. The ASN.1 definition of a ResponderID is: + * + * ResponderID ::= CHOICE { + * byName [1] EXPLICIT Name, + * byKey [2] EXPLICIT KeyHash } + * + * Because it is CHOICE, the type of identification used and the + * identification itself are actually encoded together. To represent + * this same information internally, we explicitly define a type and + * save it, along with the value, into a data structure. + */ + +typedef enum { + ocspResponderID_other = -1, /* unknown kind of responderID */ + ocspResponderID_byName = 1, + ocspResponderID_byKey = 2 +} CERTOCSPResponderIDType; + +#endif /* _OCSPT_H_ */ diff --git a/security/nss/lib/certhigh/ocspti.h b/security/nss/lib/certhigh/ocspti.h new file mode 100644 index 0000000000..d9297dba6a --- /dev/null +++ b/security/nss/lib/certhigh/ocspti.h @@ -0,0 +1,356 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Private header defining OCSP types. + */ + +#ifndef _OCSPTI_H_ +#define _OCSPTI_H_ + +#include "ocspt.h" + +#include "certt.h" +#include "plarena.h" +#include "seccomon.h" +#include "secoidt.h" + +/* + * Some notes about naming conventions... + * + * The public data types all start with "CERTOCSP" (e.g. CERTOCSPRequest). + * (Even the public types are opaque, however. Only their names are + * "exported".) + * + * Internal-only data types drop the "CERT" prefix and use only the + * lower-case "ocsp" (e.g. ocspTBSRequest), for brevity sake. + * + * In either case, the base/suffix of the type name usually matches the + * name as defined in the OCSP specification. The exceptions to this are: + * - When there is overlap between the "OCSP" or "ocsp" prefix and + * the name used in the standard. That is, you cannot strip off the + * "CERTOCSP" or "ocsp" prefix and necessarily get the name of the + * type as it is defined in the standard; the "real" name will be + * *either* "OCSPSuffix" or just "Suffix". + * - When the name in the standard was a little too generic. (e.g. The + * standard defines "Request" but we call it a "SingleRequest".) + * In this case a comment above the type definition calls attention + * to the difference. + * + * The definitions laid out in this header file are intended to follow + * the same order as the definitions in the OCSP specification itself. + * With the OCSP standard in hand, you should be able to move through + * this file and follow along. To future modifiers of this file: please + * try to keep it that way. The only exceptions are the few cases where + * we need to define a type before it is referenced (e.g. enumerations), + * whereas in the OCSP specification these are usually defined the other + * way around (reference before definition). + */ + +/* + * Forward-declarations of internal-only data structures. + * + * These are in alphabetical order (case-insensitive); please keep it that way! + */ +typedef struct ocspBasicOCSPResponseStr ocspBasicOCSPResponse; +typedef struct ocspCertStatusStr ocspCertStatus; +typedef struct ocspResponderIDStr ocspResponderID; +typedef struct ocspResponseBytesStr ocspResponseBytes; +typedef struct ocspResponseDataStr ocspResponseData; +typedef struct ocspRevokedInfoStr ocspRevokedInfo; +typedef struct ocspServiceLocatorStr ocspServiceLocator; +typedef struct ocspSignatureStr ocspSignature; +typedef struct ocspSingleRequestStr ocspSingleRequest; +typedef struct ocspSingleResponseStr ocspSingleResponse; +typedef struct ocspTBSRequestStr ocspTBSRequest; + +/* + * An OCSPRequest; this is what is sent (encoded) to an OCSP responder. + */ +struct CERTOCSPRequestStr { + PLArenaPool *arena; /* local; not part of encoding */ + ocspTBSRequest *tbsRequest; + ocspSignature *optionalSignature; +}; + +/* + * A TBSRequest; when an OCSPRequest is signed, the encoding of this + * is what the signature is actually applied to. ("TBS" == To Be Signed) + * Whether signed or not, however, this structure will be present, and + * is the "meat" of the OCSPRequest. + * + * Note that the "requestorName" field cannot be encoded/decoded in the + * same pass as the entire request -- it needs to be handled with a special + * call to convert to/from our internal form of a GeneralName. Thus the + * "derRequestorName" field, which is the actual DER-encoded bytes. + * + * The "extensionHandle" field is used on creation only; it holds + * in-progress extensions as they are optionally added to the request. + */ +struct ocspTBSRequestStr { + SECItem version; /* an INTEGER */ + SECItem *derRequestorName; /* encoded GeneralName; see above */ + CERTGeneralNameList *requestorName; /* local; not part of encoding */ + ocspSingleRequest **requestList; + CERTCertExtension **requestExtensions; + void *extensionHandle; /* local; not part of encoding */ +}; + +/* + * This is the actual signature information for an OCSPRequest (applied to + * the TBSRequest structure) or for a BasicOCSPResponse (applied to a + * ResponseData structure). + * + * Note that the "signature" field itself is a BIT STRING; operations on + * it need to keep that in mind, converting the length to bytes as needed + * and back again afterward (so that the length is usually expressing bits). + * + * The "cert" field is the signer's certificate. In the case of a received + * signature, it will be filled in when the signature is verified. In the + * case of a created signature, it is filled in on creation and will be the + * cert used to create the signature when the signing-and-encoding occurs, + * as well as the cert (and its chain) to fill in derCerts if requested. + * + * The extra fields cache information about the signature after we have + * attempted a verification. "wasChecked", if true, means the signature + * has been checked against the appropriate data and thus that "status" + * contains the result of that verification. If "status" is not SECSuccess, + * "failureReason" is a copy of the error code that was set at the time; + * presumably it tells why the signature verification failed. + */ +struct ocspSignatureStr { + SECAlgorithmID signatureAlgorithm; + SECItem signature; /* a BIT STRING */ + SECItem **derCerts; /* a SEQUENCE OF Certificate */ + CERTCertificate *cert; /* local; not part of encoding */ + PRBool wasChecked; /* local; not part of encoding */ + SECStatus status; /* local; not part of encoding */ + int failureReason; /* local; not part of encoding */ +}; + +/* + * An OCSPRequest contains a SEQUENCE OF these, one for each certificate + * whose status is being checked. + * + * Note that in the OCSP specification this is just called "Request", + * but since that seemed confusing (vs. an OCSPRequest) and to be more + * consistent with the parallel type "SingleResponse", I called it a + * "SingleRequest". + * + * XXX figure out how to get rid of that arena -- there must be a way + */ +struct ocspSingleRequestStr { + PLArenaPool *arena; /* just a copy of the response arena, + * needed here for extension handling + * routines, on creation only */ + CERTOCSPCertID *reqCert; + CERTCertExtension **singleRequestExtensions; +}; + +/* + * A CertID is the means of identifying a certificate, used both in requests + * and in responses. + * + * When in a SingleRequest it specifies the certificate to be checked. + * When in a SingleResponse it is the cert whose status is being given. + */ +struct CERTOCSPCertIDStr { + SECAlgorithmID hashAlgorithm; + SECItem issuerNameHash; /* an OCTET STRING */ + SECItem issuerKeyHash; /* an OCTET STRING */ + SECItem serialNumber; /* an INTEGER */ + SECItem issuerSHA1NameHash; /* keep other hashes around when */ + SECItem issuerMD5NameHash; /* we have them */ + SECItem issuerMD2NameHash; + SECItem issuerSHA1KeyHash; /* keep other hashes around when */ + SECItem issuerMD5KeyHash; /* we have them */ + SECItem issuerMD2KeyHash; + PLArenaPool *poolp; +}; + +/* + * This describes the value of the responseStatus field in an OCSPResponse. + * The corresponding ASN.1 definition is: + * + * OCSPResponseStatus ::= ENUMERATED { + * successful (0), --Response has valid confirmations + * malformedRequest (1), --Illegal confirmation request + * internalError (2), --Internal error in issuer + * tryLater (3), --Try again later + * --(4) is not used + * sigRequired (5), --Must sign the request + * unauthorized (6), --Request unauthorized + * } + */ +typedef enum { + ocspResponse_min = 0, + ocspResponse_successful = 0, + ocspResponse_malformedRequest = 1, + ocspResponse_internalError = 2, + ocspResponse_tryLater = 3, + ocspResponse_unused = 4, + ocspResponse_sigRequired = 5, + ocspResponse_unauthorized = 6, + ocspResponse_max = 6 /* Please update max when adding values. + * Remember to also update arrays, e.g. + * "responseStatusNames" in ocspclnt.c + * and potentially other places. */ +} ocspResponseStatus; + +/* + * An OCSPResponse is what is sent (encoded) by an OCSP responder. + * + * The field "responseStatus" is the ASN.1 encoded value; the field + * "statusValue" is simply that same value translated into our local + * type ocspResponseStatus. + */ +struct CERTOCSPResponseStr { + PLArenaPool *arena; /* local; not part of encoding */ + SECItem responseStatus; /* an ENUMERATED, see above */ + ocspResponseStatus statusValue; /* local; not part of encoding */ + ocspResponseBytes *responseBytes; /* only when status is successful */ +}; + +/* + * A ResponseBytes (despite appearances) is what contains the meat + * of a successful response -- but still in encoded form. The type + * given as "responseType" tells you how to decode the string. + * + * We look at the OID and translate it into our local OID representation + * "responseTypeTag", and use that value to tell us how to decode the + * actual response itself. For now the only kind of OCSP response we + * know about is a BasicOCSPResponse. However, the intention in the + * OCSP specification is to allow for other response types, so we are + * building in that flexibility from the start and thus put a pointer + * to that data structure inside of a union. Whenever OCSP adds more + * response types, just add them to the union. + */ +struct ocspResponseBytesStr { + SECItem responseType; /* an OBJECT IDENTIFIER */ + SECOidTag responseTypeTag; /* local; not part of encoding */ + SECItem response; /* an OCTET STRING */ + union { + ocspBasicOCSPResponse *basic; /* when type is id-pkix-ocsp-basic */ + } decodedResponse; /* local; not part of encoding */ +}; + +/* + * A BasicOCSPResponse -- when the responseType in a ResponseBytes is + * id-pkix-ocsp-basic, the "response" OCTET STRING above is the DER + * encoding of one of these. + * + * Note that in the OCSP specification, the signature fields are not + * part of a separate sub-structure. But since they are the same fields + * as we define for the signature in a request, it made sense to share + * the C data structure here and in some shared code to operate on them. + */ +struct ocspBasicOCSPResponseStr { + SECItem tbsResponseDataDER; + ocspResponseData *tbsResponseData; /* "tbs" == To Be Signed */ + ocspSignature responseSignature; +}; + +/* + * A ResponseData is the part of a BasicOCSPResponse that is signed + * (after it is DER encoded). It contains the real details of the response + * (a per-certificate status). + */ +struct ocspResponseDataStr { + SECItem version; /* an INTEGER */ + SECItem derResponderID; + ocspResponderID *responderID; /* local; not part of encoding */ + SECItem producedAt; /* a GeneralizedTime */ + CERTOCSPSingleResponse **responses; + CERTCertExtension **responseExtensions; +}; + +struct ocspResponderIDStr { + CERTOCSPResponderIDType responderIDType; /* local; not part of encoding */ + union { + CERTName name; /* when ocspResponderID_byName */ + SECItem keyHash; /* when ocspResponderID_byKey */ + SECItem other; /* when ocspResponderID_other */ + } responderIDValue; +}; + +/* + * The ResponseData in a BasicOCSPResponse contains a SEQUENCE OF + * SingleResponse -- one for each certificate whose status is being supplied. + * + * XXX figure out how to get rid of that arena -- there must be a way + */ +struct CERTOCSPSingleResponseStr { + PLArenaPool *arena; /* just a copy of the response arena, + * needed here for extension handling + * routines, on creation only */ + CERTOCSPCertID *certID; + SECItem derCertStatus; + ocspCertStatus *certStatus; /* local; not part of encoding */ + SECItem thisUpdate; /* a GeneralizedTime */ + SECItem *nextUpdate; /* a GeneralizedTime */ + CERTCertExtension **singleExtensions; +}; + +/* + * A CertStatus is the actual per-certificate status. Its ASN.1 definition: + * + * CertStatus ::= CHOICE { + * good [0] IMPLICIT NULL, + * revoked [1] IMPLICIT RevokedInfo, + * unknown [2] IMPLICIT UnknownInfo } + * + * (where for now UnknownInfo is defined to be NULL but in the + * future may be replaced with an enumeration). + * + * Because it is CHOICE, the status value and its associated information + * (if any) are actually encoded together. To represent this same + * information internally, we explicitly define a type and save it, + * along with the value, into a data structure. + */ + +typedef enum { + ocspCertStatus_good, /* cert is not revoked */ + ocspCertStatus_revoked, /* cert is revoked */ + ocspCertStatus_unknown, /* cert was unknown to the responder */ + ocspCertStatus_other /* status was not an expected value */ +} ocspCertStatusType; + +/* + * This is the actual per-certificate status. + * + * The "goodInfo" and "unknownInfo" items are only place-holders for a NULL. + * (Though someday OCSP may replace UnknownInfo with an enumeration that + * gives more detailed information.) + */ +struct ocspCertStatusStr { + ocspCertStatusType certStatusType; /* local; not part of encoding */ + union { + SECItem *goodInfo; /* when ocspCertStatus_good */ + ocspRevokedInfo *revokedInfo; /* when ocspCertStatus_revoked */ + SECItem *unknownInfo; /* when ocspCertStatus_unknown */ + SECItem *otherInfo; /* when ocspCertStatus_other */ + } certStatusInfo; +}; + +/* + * A RevokedInfo gives information about a revoked certificate -- when it + * was revoked and why. + */ +struct ocspRevokedInfoStr { + SECItem revocationTime; /* a GeneralizedTime */ + SECItem *revocationReason; /* a CRLReason; ignored for now */ +}; + +/* + * ServiceLocator can be included as one of the singleRequestExtensions. + * When added, it specifies the (name of the) issuer of the cert being + * checked, and optionally the value of the AuthorityInfoAccess extension + * if the cert has one. + */ +struct ocspServiceLocatorStr { + CERTName *issuer; + SECItem locator; /* DER encoded authInfoAccess extension from cert */ +}; + +#endif /* _OCSPTI_H_ */ diff --git a/security/nss/lib/certhigh/xcrldist.c b/security/nss/lib/certhigh/xcrldist.c new file mode 100644 index 0000000000..4f74cdb259 --- /dev/null +++ b/security/nss/lib/certhigh/xcrldist.c @@ -0,0 +1,212 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Code for dealing with x.509 v3 CRL Distribution Point extension. + */ +#include "genname.h" +#include "certt.h" +#include "secerr.h" + +SEC_ASN1_MKSUB(SEC_AnyTemplate) +SEC_ASN1_MKSUB(SEC_BitStringTemplate) + +extern void PrepareBitStringForEncoding(SECItem *bitMap, SECItem *value); + +static const SEC_ASN1Template FullNameTemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | 0, + offsetof(CRLDistributionPoint, derFullName), + CERT_GeneralNamesTemplate } +}; + +static const SEC_ASN1Template RelativeNameTemplate[] = { + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | 1, + offsetof(CRLDistributionPoint, distPoint.relativeName), + CERT_RDNTemplate } +}; + +static const SEC_ASN1Template DistributionPointNameTemplate[] = { + { SEC_ASN1_CHOICE, + offsetof(CRLDistributionPoint, distPointType), NULL, + sizeof(CRLDistributionPoint) }, + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | 0, + offsetof(CRLDistributionPoint, derFullName), + CERT_GeneralNamesTemplate, generalName }, + { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | 1, + offsetof(CRLDistributionPoint, distPoint.relativeName), + CERT_RDNTemplate, relativeDistinguishedName }, + { 0 } +}; + +static const SEC_ASN1Template CRLDistributionPointTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CRLDistributionPoint) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | + SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | SEC_ASN1_XTRN | 0, + offsetof(CRLDistributionPoint, derDistPoint), + SEC_ASN1_SUB(SEC_AnyTemplate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1, + offsetof(CRLDistributionPoint, bitsmap), + SEC_ASN1_SUB(SEC_BitStringTemplate) }, + { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | + SEC_ASN1_CONSTRUCTED | 2, + offsetof(CRLDistributionPoint, derCrlIssuer), + CERT_GeneralNamesTemplate }, + { 0 } +}; + +const SEC_ASN1Template CERTCRLDistributionPointsTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, 0, CRLDistributionPointTemplate } +}; + +SECStatus +CERT_EncodeCRLDistributionPoints(PLArenaPool *arena, + CERTCrlDistributionPoints *value, + SECItem *derValue) +{ + CRLDistributionPoint **pointList, *point; + PLArenaPool *ourPool = NULL; + SECStatus rv = SECSuccess; + + PORT_Assert(derValue); + PORT_Assert(value && value->distPoints); + + do { + ourPool = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if (ourPool == NULL) { + rv = SECFailure; + break; + } + + pointList = value->distPoints; + while (*pointList) { + point = *pointList; + point->derFullName = NULL; + point->derDistPoint.data = NULL; + + switch (point->distPointType) { + case generalName: + point->derFullName = cert_EncodeGeneralNames(ourPool, point->distPoint.fullName); + + if (!point->derFullName || + !SEC_ASN1EncodeItem(ourPool, &point->derDistPoint, + point, FullNameTemplate)) + rv = SECFailure; + break; + + case relativeDistinguishedName: + if (!SEC_ASN1EncodeItem(ourPool, &point->derDistPoint, + point, RelativeNameTemplate)) + rv = SECFailure; + break; + + default: + PORT_SetError(SEC_ERROR_EXTENSION_VALUE_INVALID); + rv = SECFailure; + break; + } + + if (rv != SECSuccess) + break; + + if (point->reasons.data) + PrepareBitStringForEncoding(&point->bitsmap, &point->reasons); + + if (point->crlIssuer) { + point->derCrlIssuer = cert_EncodeGeneralNames(ourPool, point->crlIssuer); + if (!point->derCrlIssuer) { + rv = SECFailure; + break; + } + } + ++pointList; + } + if (rv != SECSuccess) + break; + if (!SEC_ASN1EncodeItem(arena, derValue, value, + CERTCRLDistributionPointsTemplate)) { + rv = SECFailure; + break; + } + } while (0); + PORT_FreeArena(ourPool, PR_FALSE); + return rv; +} + +CERTCrlDistributionPoints * +CERT_DecodeCRLDistributionPoints(PLArenaPool *arena, SECItem *encodedValue) +{ + CERTCrlDistributionPoints *value = NULL; + CRLDistributionPoint **pointList, *point; + SECStatus rv = SECSuccess; + SECItem newEncodedValue; + + PORT_Assert(arena); + do { + value = PORT_ArenaZNew(arena, CERTCrlDistributionPoints); + if (value == NULL) { + rv = SECFailure; + break; + } + + /* copy the DER into the arena, since Quick DER returns data that points + into the DER input, which may get freed by the caller */ + rv = SECITEM_CopyItem(arena, &newEncodedValue, encodedValue); + if (rv != SECSuccess) + break; + + rv = SEC_QuickDERDecodeItem(arena, &value->distPoints, + CERTCRLDistributionPointsTemplate, &newEncodedValue); + if (rv != SECSuccess) + break; + + pointList = value->distPoints; + while (NULL != (point = *pointList)) { + + /* get the data if the distributionPointName is not omitted */ + if (point->derDistPoint.data != NULL) { + rv = SEC_QuickDERDecodeItem(arena, point, + DistributionPointNameTemplate, &(point->derDistPoint)); + if (rv != SECSuccess) + break; + + switch (point->distPointType) { + case generalName: + point->distPoint.fullName = + cert_DecodeGeneralNames(arena, point->derFullName); + rv = point->distPoint.fullName ? SECSuccess : SECFailure; + break; + + case relativeDistinguishedName: + break; + + default: + PORT_SetError(SEC_ERROR_EXTENSION_VALUE_INVALID); + rv = SECFailure; + break; + } /* end switch */ + if (rv != SECSuccess) + break; + } /* end if */ + + /* Get the reason code if it's not omitted in the encoding */ + if (point->bitsmap.data != NULL) { + SECItem bitsmap = point->bitsmap; + DER_ConvertBitString(&bitsmap); + rv = SECITEM_CopyItem(arena, &point->reasons, &bitsmap); + if (rv != SECSuccess) + break; + } + + /* Get the crl issuer name if it's not omitted in the encoding */ + if (point->derCrlIssuer != NULL) { + point->crlIssuer = cert_DecodeGeneralNames(arena, + point->derCrlIssuer); + if (!point->crlIssuer) + break; + } + ++pointList; + } /* end while points remain */ + } while (0); + return (rv == SECSuccess ? value : NULL); +} diff --git a/security/nss/lib/ckfw/Makefile b/security/nss/lib/ckfw/Makefile new file mode 100644 index 0000000000..effa9abceb --- /dev/null +++ b/security/nss/lib/ckfw/Makefile @@ -0,0 +1,39 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +include manifest.mn + +include $(CORE_DEPTH)/coreconf/config.mk + +ifdef BUILD_IDG +DEFINES += -DNSSDEBUG +endif + +ifdef NOTDEF # was ifdef MOZILLA_CLIENT +NSS_BUILD_CAPI = 1 +endif + +# This'll need some help from a build person. + +# The generated files are checked in, and differ from what ckapi.perl +# will produce. ckapi.perl is currently newer than the targets, so +# these rules are invoked, causing the wrong files to be generated. +# Turning off to fix builds. +# +# nssckepv.h: ck.api ckapi.perl +# nssckft.h: ck.api ckapi.perl +# nssckg.h: ck.api ckapi.perl +# nssck.api: ck.api ckapi.perl +# $(PERL) ckapi.perl ck.api + + +# can't do this in manifest.mn because OS_TARGET isn't defined there. +ifeq (,$(filter-out WINNT WIN95,$(OS_TARGET))) +ifdef NSS_BUILD_CAPI +DIRS += capi +endif +endif + +include $(CORE_DEPTH)/coreconf/rules.mk diff --git a/security/nss/lib/ckfw/builtins/Makefile b/security/nss/lib/ckfw/builtins/Makefile new file mode 100644 index 0000000000..2a633d2892 --- /dev/null +++ b/security/nss/lib/ckfw/builtins/Makefile @@ -0,0 +1,70 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +include manifest.mn +include $(CORE_DEPTH)/coreconf/config.mk + +ifdef BUILD_IDG +DEFINES += -DNSSDEBUG +endif + +# Needed for compilation of $(OBJDIR)/certdata.c +INCLUDES += -I. + +# +# To create a loadable module on Darwin, we must use -bundle. +# +ifeq ($(OS_TARGET),Darwin) +DSO_LDOPTS = -bundle +endif + +ifdef USE_GCOV +DSO_LDOPTS += --coverage +endif + +EXTRA_LIBS = \ + $(DIST)/lib/$(LIB_PREFIX)nssckfw.$(LIB_SUFFIX) \ + $(DIST)/lib/$(LIB_PREFIX)nssb.$(LIB_SUFFIX) \ + $(NULL) + +# can't do this in manifest.mn because OS_TARGET isn't defined there. +ifeq (,$(filter-out WIN%,$(OS_TARGET))) + +ifdef NS_USE_GCC +EXTRA_SHARED_LIBS += \ + -L$(NSPR_LIB_DIR) \ + -lplc4 \ + -lplds4 \ + -lnspr4 \ + $(NULL) +else +EXTRA_SHARED_LIBS += \ + $(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plc4.lib \ + $(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plds4.lib \ + $(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)nspr4.lib \ + $(NULL) +endif # NS_USE_GCC +else + +EXTRA_SHARED_LIBS += \ + -L$(NSPR_LIB_DIR) \ + -lplc4 \ + -lplds4 \ + -lnspr4 \ + $(NULL) +endif + + +include $(CORE_DEPTH)/coreconf/rules.mk + +# Generate certdata.c. + +# By default, use the unmodified certdata.txt. +ifndef NSS_CERTDATA_TXT +NSS_CERTDATA_TXT = certdata.txt +endif + +$(OBJDIR)/certdata.c: $(NSS_CERTDATA_TXT) certdata.perl | $$(@D)/d + $(PERL) certdata.perl $(NSS_CERTDATA_TXT) $@ diff --git a/security/nss/lib/ckfw/builtins/README b/security/nss/lib/ckfw/builtins/README new file mode 100644 index 0000000000..11f5c2c9a7 --- /dev/null +++ b/security/nss/lib/ckfw/builtins/README @@ -0,0 +1,106 @@ +This README file explains how to add a builtin root CA certificate to NSS +or remove a builtin root CA certificate from NSS. + +The builtin root CA certificates in NSS are stored in the nssckbi PKCS #11 +module. The sources to the nssckbi module are in this directory. + +I. Adding a Builtin Root CA Certificate + +You need to use the addbuiltin command-line tool to add a root CA certificate +to the nssckbi module. In the procedure described below, we assume that the +new root CA certificate is distributed in DER format in the file newroot.der. + +1. Add the directory where the addbuiltin executable resides to your PATH +environment variable. Then, add the directory where the NSPR and NSS shared +libraries (DLLs) reside to the platform-specific environment variable that +specifies your shared library search path: LD_LIBRARY_PATH (most Unix +variants), SHLIB_PATH (32-bit HP-UX), LIBPATH (AIX), or PATH (Windows). + +2. Copy newroot.der to this directory. + +3. In this directory, run addbuiltin to add the new root certificate. The +argument to the -n option should be replaced by the nickname of the root +certificate. + + % addbuiltin -n "Nickname of the Root Certificate" -t C,C,C < newroot.der \ + >> certdata.txt + +4. Edit nssckbi.h to bump the version of the module. + +5. Run gmake in this directory to build the nssckbi module. + +6. After you verify that the new nssckbi module is correct, check in +certdata.txt and nssckbi.h. + +II. Removing a Builtin Root CA Certificate + +1. Change directory to this directory. + +2. Edit certdata.txt and remove the root CA certificate. + +3. Edit nssckbi.h to bump the version of the module. + +4. Run gmake in this directory to build the nssckbi module. + +5. After you verify that the new nssckbi module is correct, check in +certdata.txt and nssckbi.h. + +III. Scheduling a Distrust date for Server/TLS or Email certificates issued +by a CA + +For each Builtin Root CA Certificate we have the Trust Bits to know what kind +of certificates issued by this CA are trusted: Server/TLS, E-mail or S/MIME. +Sometimes a CA discontinues support for a particular kind of certificate, +but will still issue other kinds. For instance, they might cease support for +email certificates but continue to provide server certificates. In this +scenario, we have to disable the Trust Bit for this kind of certificate when +the last issued certificate expires. +Between the last expired certificate date and the change and propagation of +this respective Trust Bit, could have a undesired gap. + +So, in these situations we can set a Distrust Date for this Builtin Root CA +Certificate. Clients should check the distrust date in certificates to avoid +trusting a CA for service they have ceased to support. + +A distrust date is a timestamp in unix epoch, encoded in DER format and saved +in certdata.txt. These fields are defined at the "Certificate" entries of +certdata.txt, in a MULTILINE_OCTAL format. By default, for readability purpose, +these fields are set as a boolean CK_FALSE and will be ignored when read. + +1. Create the timestamp for the desired distrust date. An easy and practical way +to do this is using the date command. + % date -d "2019-07-01 00:00:00 UTC" +%s + The result should be something like: 1561939200 + +2. Then, run the addbuiltin -d to verify the timestamp and do the right +conversions. + The -d option takes the timestamp as an argument, which is interpreted as + seconds since unix epoch. The addbuiltin command will show the result in the + stdout, as it should be inserted in certdata.txt. + % addbuiltin -d 1561939200 + The result should be something like this: + + The timestamp represents this date: Mon Jul 01 00:00:00 2019 + Locate the entry of the desired certificate in certdata.txt + Erase the CKA_NSS_[SERVER|EMAIL]_DISTRUST_AFTER CK_BBOOL CK_FALSE + And override with the following respective entry: + + # For Server Distrust After: Mon Jul 01 00:00:00 2019 + CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL + \061\071\060\067\060\061\060\060\060\060\060\060\132 + END + # For Email Distrust After: Mon Jul 01 00:00:00 2019 + CKA_NSS_EMAIL_DISTRUST_AFTER MULTILINE_OCTAL + \061\071\060\067\060\061\060\060\060\060\060\060\132 + END + +3. Edit the certdata.txt, overriding the desired entry for the desired CA, as +the instructions generated by the previous command. + +4. If necessary, increment the version counter +NSS_BUILTINS_LIBRARY_VERSION_MINOR in nssckbi.h. + +5. Build the nssckbi module. + +6. A good way to test is with certutil: + % certutil -L -d $DBDIR -n "Builtin Object Token:" diff --git a/security/nss/lib/ckfw/builtins/anchor.c b/security/nss/lib/ckfw/builtins/anchor.c new file mode 100644 index 0000000000..af21c6a0bf --- /dev/null +++ b/security/nss/lib/ckfw/builtins/anchor.c @@ -0,0 +1,17 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * builtins/anchor.c + * + * This file "anchors" the actual cryptoki entry points in this module's + * shared library, which is required for dynamic loading. See the + * comments in nssck.api for more information. + */ + +#include "builtins.h" + +#define MODULE_NAME builtins +#define INSTANCE_NAME (NSSCKMDInstance *)&nss_builtins_mdInstance +#include "nssck.api" diff --git a/security/nss/lib/ckfw/builtins/bfind.c b/security/nss/lib/ckfw/builtins/bfind.c new file mode 100644 index 0000000000..3e5da1a558 --- /dev/null +++ b/security/nss/lib/ckfw/builtins/bfind.c @@ -0,0 +1,261 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BUILTINS_H +#include "builtins.h" +#endif /* BUILTINS_H */ + +/* + * builtins/find.c + * + * This file implements the NSSCKMDFindObjects object for the + * "builtin objects" cryptoki module. + */ + +struct builtinsFOStr { + NSSArena *arena; + CK_ULONG n; + CK_ULONG i; + builtinsInternalObject **objs; +}; + +static void +builtins_mdFindObjects_Final( + NSSCKMDFindObjects *mdFindObjects, + NSSCKFWFindObjects *fwFindObjects, + NSSCKMDSession *mdSession, + NSSCKFWSession *fwSession, + NSSCKMDToken *mdToken, + NSSCKFWToken *fwToken, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance) +{ + struct builtinsFOStr *fo = (struct builtinsFOStr *)mdFindObjects->etc; + NSSArena *arena = fo->arena; + + nss_ZFreeIf(fo->objs); + nss_ZFreeIf(fo); + nss_ZFreeIf(mdFindObjects); + if ((NSSArena *)NULL != arena) { + NSSArena_Destroy(arena); + } + + return; +} + +static NSSCKMDObject * +builtins_mdFindObjects_Next( + NSSCKMDFindObjects *mdFindObjects, + NSSCKFWFindObjects *fwFindObjects, + NSSCKMDSession *mdSession, + NSSCKFWSession *fwSession, + NSSCKMDToken *mdToken, + NSSCKFWToken *fwToken, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance, + NSSArena *arena, + CK_RV *pError) +{ + struct builtinsFOStr *fo = (struct builtinsFOStr *)mdFindObjects->etc; + builtinsInternalObject *io; + + if (fo->i == fo->n) { + *pError = CKR_OK; + return (NSSCKMDObject *)NULL; + } + + io = fo->objs[fo->i]; + fo->i++; + + return nss_builtins_CreateMDObject(arena, io, pError); +} + +static int +builtins_derUnwrapInt(unsigned char *src, int size, unsigned char **dest) +{ + unsigned char *start = src; + int len = 0; + + if (*src++ != 2) { + return 0; + } + len = *src++; + if (len & 0x80) { + int count = len & 0x7f; + len = 0; + + if (count + 2 > size) { + return 0; + } + while (count-- > 0) { + len = (len << 8) | *src++; + } + } + if (len + (src - start) != size) { + return 0; + } + *dest = src; + return len; +} + +static CK_BBOOL +builtins_attrmatch( + CK_ATTRIBUTE_PTR a, + const NSSItem *b) +{ + PRBool prb; + + if (a->ulValueLen != b->size) { + /* match a decoded serial number */ + if ((a->type == CKA_SERIAL_NUMBER) && (a->ulValueLen < b->size)) { + int len; + unsigned char *data = NULL; + + len = builtins_derUnwrapInt(b->data, b->size, &data); + if (data && + (len == a->ulValueLen) && + nsslibc_memequal(a->pValue, data, len, (PRStatus *)NULL)) { + return CK_TRUE; + } + } + return CK_FALSE; + } + + prb = nsslibc_memequal(a->pValue, b->data, b->size, (PRStatus *)NULL); + + if (PR_TRUE == prb) { + return CK_TRUE; + } else { + return CK_FALSE; + } +} + +static CK_BBOOL +builtins_match( + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + builtinsInternalObject *o) +{ + CK_ULONG i; + + for (i = 0; i < ulAttributeCount; i++) { + CK_ULONG j; + + for (j = 0; j < o->n; j++) { + if (o->types[j] == pTemplate[i].type) { + if (CK_FALSE == builtins_attrmatch(&pTemplate[i], &o->items[j])) { + return CK_FALSE; + } else { + break; + } + } + } + + if (j == o->n) { + /* Loop ran to the end: no matching attribute */ + return CK_FALSE; + } + } + + /* Every attribute passed */ + return CK_TRUE; +} + +NSS_IMPLEMENT NSSCKMDFindObjects * +nss_builtins_FindObjectsInit( + NSSCKFWSession *fwSession, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_RV *pError) +{ + /* This could be made more efficient. I'm rather rushed. */ + NSSArena *arena; + NSSCKMDFindObjects *rv = (NSSCKMDFindObjects *)NULL; + struct builtinsFOStr *fo = (struct builtinsFOStr *)NULL; + +/* + * 99% of the time we get 0 or 1 matches. So we start with a small + * stack-allocated array to hold the matches and switch to a heap-allocated + * array later if the number of matches exceeds STACK_BUF_LENGTH. + */ +#define STACK_BUF_LENGTH 1 + builtinsInternalObject *stackTemp[STACK_BUF_LENGTH]; + builtinsInternalObject **temp = stackTemp; + PRBool tempIsHeapAllocated = PR_FALSE; + PRUint32 i; + + arena = NSSArena_Create(); + if ((NSSArena *)NULL == arena) { + goto loser; + } + + rv = nss_ZNEW(arena, NSSCKMDFindObjects); + if ((NSSCKMDFindObjects *)NULL == rv) { + *pError = CKR_HOST_MEMORY; + goto loser; + } + + fo = nss_ZNEW(arena, struct builtinsFOStr); + if ((struct builtinsFOStr *)NULL == fo) { + *pError = CKR_HOST_MEMORY; + goto loser; + } + + fo->arena = arena; + /* fo->n and fo->i are already zero */ + + rv->etc = (void *)fo; + rv->Final = builtins_mdFindObjects_Final; + rv->Next = builtins_mdFindObjects_Next; + rv->null = (void *)NULL; + + for (i = 0; i < nss_builtins_nObjects; i++) { + builtinsInternalObject *o = (builtinsInternalObject *)&nss_builtins_data[i]; + + if (CK_TRUE == builtins_match(pTemplate, ulAttributeCount, o)) { + if (fo->n == STACK_BUF_LENGTH) { + /* Switch from the small stack array to a heap-allocated array large + * enough to handle matches in all remaining cases. */ + temp = nss_ZNEWARRAY((NSSArena *)NULL, builtinsInternalObject *, + fo->n + nss_builtins_nObjects - i); + if ((builtinsInternalObject **)NULL == temp) { + *pError = + CKR_HOST_MEMORY; + goto loser; + } + tempIsHeapAllocated = PR_TRUE; + (void)nsslibc_memcpy(temp, stackTemp, + sizeof(builtinsInternalObject *) * fo->n); + } + + temp[fo->n] = o; + fo->n++; + } + } + + fo->objs = nss_ZNEWARRAY(arena, builtinsInternalObject *, fo->n); + if ((builtinsInternalObject **)NULL == fo->objs) { + *pError = CKR_HOST_MEMORY; + goto loser; + } + + (void)nsslibc_memcpy(fo->objs, temp, sizeof(builtinsInternalObject *) * fo->n); + if (tempIsHeapAllocated) { + nss_ZFreeIf(temp); + temp = (builtinsInternalObject **)NULL; + } + + return rv; + +loser: + if (tempIsHeapAllocated) { + nss_ZFreeIf(temp); + } + nss_ZFreeIf(fo); + nss_ZFreeIf(rv); + if ((NSSArena *)NULL != arena) { + NSSArena_Destroy(arena); + } + return (NSSCKMDFindObjects *)NULL; +} diff --git a/security/nss/lib/ckfw/builtins/binst.c b/security/nss/lib/ckfw/builtins/binst.c new file mode 100644 index 0000000000..ca1dac89cd --- /dev/null +++ b/security/nss/lib/ckfw/builtins/binst.c @@ -0,0 +1,87 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "builtins.h" + +/* + * builtins/instance.c + * + * This file implements the NSSCKMDInstance object for the + * "builtin objects" cryptoki module. + */ + +/* + * NSSCKMDInstance methods + */ + +static CK_ULONG +builtins_mdInstance_GetNSlots( + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance, + CK_RV *pError) +{ + return (CK_ULONG)1; +} + +static CK_VERSION +builtins_mdInstance_GetCryptokiVersion( + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance) +{ + return nss_builtins_CryptokiVersion; +} + +static NSSUTF8 * +builtins_mdInstance_GetManufacturerID( + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance, + CK_RV *pError) +{ + return (NSSUTF8 *)nss_builtins_ManufacturerID; +} + +static NSSUTF8 * +builtins_mdInstance_GetLibraryDescription( + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance, + CK_RV *pError) +{ + return (NSSUTF8 *)nss_builtins_LibraryDescription; +} + +static CK_VERSION +builtins_mdInstance_GetLibraryVersion( + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance) +{ +#define NSS_VERSION_VARIABLE __nss_builtins_version +#include "verref.h" + return nss_builtins_LibraryVersion; +} + +static CK_RV +builtins_mdInstance_GetSlots( + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance, + NSSCKMDSlot *slots[]) +{ + slots[0] = (NSSCKMDSlot *)&nss_builtins_mdSlot; + return CKR_OK; +} + +const NSSCKMDInstance + nss_builtins_mdInstance = { + (void *)NULL, /* etc */ + NULL, /* Initialize */ + NULL, /* Finalize */ + builtins_mdInstance_GetNSlots, + builtins_mdInstance_GetCryptokiVersion, + builtins_mdInstance_GetManufacturerID, + builtins_mdInstance_GetLibraryDescription, + builtins_mdInstance_GetLibraryVersion, + NULL, /* ModuleHandlesSessionObjects -- defaults to false */ + builtins_mdInstance_GetSlots, + NULL, /* WaitForSlotEvent */ + (void *)NULL /* null terminator */ + }; diff --git a/security/nss/lib/ckfw/builtins/bobject.c b/security/nss/lib/ckfw/builtins/bobject.c new file mode 100644 index 0000000000..1c0babdd66 --- /dev/null +++ b/security/nss/lib/ckfw/builtins/bobject.c @@ -0,0 +1,206 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "builtins.h" + +/* + * builtins/object.c + * + * This file implements the NSSCKMDObject object for the + * "builtin objects" cryptoki module. + */ + +/* + * Finalize - unneeded + * Destroy - CKR_SESSION_READ_ONLY + * IsTokenObject - CK_TRUE + * GetAttributeCount + * GetAttributeTypes + * GetAttributeSize + * GetAttribute + * SetAttribute - unneeded + * GetObjectSize + */ + +static CK_RV +builtins_mdObject_Destroy( + NSSCKMDObject *mdObject, + NSSCKFWObject *fwObject, + NSSCKMDSession *mdSession, + NSSCKFWSession *fwSession, + NSSCKMDToken *mdToken, + NSSCKFWToken *fwToken, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance) +{ + return CKR_SESSION_READ_ONLY; +} + +static CK_BBOOL +builtins_mdObject_IsTokenObject( + NSSCKMDObject *mdObject, + NSSCKFWObject *fwObject, + NSSCKMDSession *mdSession, + NSSCKFWSession *fwSession, + NSSCKMDToken *mdToken, + NSSCKFWToken *fwToken, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance) +{ + return CK_TRUE; +} + +static CK_ULONG +builtins_mdObject_GetAttributeCount( + NSSCKMDObject *mdObject, + NSSCKFWObject *fwObject, + NSSCKMDSession *mdSession, + NSSCKFWSession *fwSession, + NSSCKMDToken *mdToken, + NSSCKFWToken *fwToken, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance, + CK_RV *pError) +{ + builtinsInternalObject *io = (builtinsInternalObject *)mdObject->etc; + return io->n; +} + +static CK_RV +builtins_mdObject_GetAttributeTypes( + NSSCKMDObject *mdObject, + NSSCKFWObject *fwObject, + NSSCKMDSession *mdSession, + NSSCKFWSession *fwSession, + NSSCKMDToken *mdToken, + NSSCKFWToken *fwToken, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance, + CK_ATTRIBUTE_TYPE_PTR typeArray, + CK_ULONG ulCount) +{ + builtinsInternalObject *io = (builtinsInternalObject *)mdObject->etc; + CK_ULONG i; + + if (io->n != ulCount) { + return CKR_BUFFER_TOO_SMALL; + } + + for (i = 0; i < io->n; i++) { + typeArray[i] = io->types[i]; + } + + return CKR_OK; +} + +static CK_ULONG +builtins_mdObject_GetAttributeSize( + NSSCKMDObject *mdObject, + NSSCKFWObject *fwObject, + NSSCKMDSession *mdSession, + NSSCKFWSession *fwSession, + NSSCKMDToken *mdToken, + NSSCKFWToken *fwToken, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance, + CK_ATTRIBUTE_TYPE attribute, + CK_RV *pError) +{ + builtinsInternalObject *io = (builtinsInternalObject *)mdObject->etc; + CK_ULONG i; + + for (i = 0; i < io->n; i++) { + if (attribute == io->types[i]) { + return (CK_ULONG)(io->items[i].size); + } + } + + *pError = CKR_ATTRIBUTE_TYPE_INVALID; + return 0; +} + +static NSSCKFWItem +builtins_mdObject_GetAttribute( + NSSCKMDObject *mdObject, + NSSCKFWObject *fwObject, + NSSCKMDSession *mdSession, + NSSCKFWSession *fwSession, + NSSCKMDToken *mdToken, + NSSCKFWToken *fwToken, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance, + CK_ATTRIBUTE_TYPE attribute, + CK_RV *pError) +{ + NSSCKFWItem mdItem; + builtinsInternalObject *io = (builtinsInternalObject *)mdObject->etc; + CK_ULONG i; + + mdItem.needsFreeing = PR_FALSE; + mdItem.item = (NSSItem *)NULL; + + for (i = 0; i < io->n; i++) { + if (attribute == io->types[i]) { + mdItem.item = (NSSItem *)&io->items[i]; + return mdItem; + } + } + + *pError = CKR_ATTRIBUTE_TYPE_INVALID; + return mdItem; +} + +static CK_ULONG +builtins_mdObject_GetObjectSize( + NSSCKMDObject *mdObject, + NSSCKFWObject *fwObject, + NSSCKMDSession *mdSession, + NSSCKFWSession *fwSession, + NSSCKMDToken *mdToken, + NSSCKFWToken *fwToken, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance, + CK_RV *pError) +{ + builtinsInternalObject *io = (builtinsInternalObject *)mdObject->etc; + CK_ULONG i; + CK_ULONG rv = sizeof(CK_ULONG); + + for (i = 0; i < io->n; i++) { + rv += sizeof(CK_ATTRIBUTE_TYPE) + sizeof(NSSItem) + io->items[i].size; + } + + return rv; +} + +static const NSSCKMDObject + builtins_prototype_mdObject = { + (void *)NULL, /* etc */ + NULL, /* Finalize */ + builtins_mdObject_Destroy, + builtins_mdObject_IsTokenObject, + builtins_mdObject_GetAttributeCount, + builtins_mdObject_GetAttributeTypes, + builtins_mdObject_GetAttributeSize, + builtins_mdObject_GetAttribute, + NULL, /* FreeAttribute */ + NULL, /* SetAttribute */ + builtins_mdObject_GetObjectSize, + (void *)NULL /* null terminator */ + }; + +NSS_IMPLEMENT NSSCKMDObject * +nss_builtins_CreateMDObject( + NSSArena *arena, + builtinsInternalObject *io, + CK_RV *pError) +{ + if ((void *)NULL == io->mdObject.etc) { + (void)nsslibc_memcpy(&io->mdObject, &builtins_prototype_mdObject, + sizeof(builtins_prototype_mdObject)); + io->mdObject.etc = (void *)io; + } + + return &io->mdObject; +} diff --git a/security/nss/lib/ckfw/builtins/bsession.c b/security/nss/lib/ckfw/builtins/bsession.c new file mode 100644 index 0000000000..6828a49aff --- /dev/null +++ b/security/nss/lib/ckfw/builtins/bsession.c @@ -0,0 +1,71 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "builtins.h" + +/* + * builtins/session.c + * + * This file implements the NSSCKMDSession object for the + * "builtin objects" cryptoki module. + */ + +static NSSCKMDFindObjects * +builtins_mdSession_FindObjectsInit( + NSSCKMDSession *mdSession, + NSSCKFWSession *fwSession, + NSSCKMDToken *mdToken, + NSSCKFWToken *fwToken, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_RV *pError) +{ + return nss_builtins_FindObjectsInit(fwSession, pTemplate, ulAttributeCount, pError); +} + +NSS_IMPLEMENT NSSCKMDSession * +nss_builtins_CreateSession( + NSSCKFWSession *fwSession, + CK_RV *pError) +{ + NSSArena *arena; + NSSCKMDSession *rv; + + arena = NSSCKFWSession_GetArena(fwSession, pError); + if ((NSSArena *)NULL == arena) { + return (NSSCKMDSession *)NULL; + } + + rv = nss_ZNEW(arena, NSSCKMDSession); + if ((NSSCKMDSession *)NULL == rv) { + *pError = CKR_HOST_MEMORY; + return (NSSCKMDSession *)NULL; + } + + /* + * rv was zeroed when allocated, so we only + * need to set the non-zero members. + */ + + rv->etc = (void *)fwSession; + /* rv->Close */ + /* rv->GetDeviceError */ + /* rv->Login */ + /* rv->Logout */ + /* rv->InitPIN */ + /* rv->SetPIN */ + /* rv->GetOperationStateLen */ + /* rv->GetOperationState */ + /* rv->SetOperationState */ + /* rv->CreateObject */ + /* rv->CopyObject */ + rv->FindObjectsInit = builtins_mdSession_FindObjectsInit; + /* rv->SeedRandom */ + /* rv->GetRandom */ + /* rv->null */ + + return rv; +} diff --git a/security/nss/lib/ckfw/builtins/bslot.c b/security/nss/lib/ckfw/builtins/bslot.c new file mode 100644 index 0000000000..f2ef1efb92 --- /dev/null +++ b/security/nss/lib/ckfw/builtins/bslot.c @@ -0,0 +1,81 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "builtins.h" + +/* + * builtins/slot.c + * + * This file implements the NSSCKMDSlot object for the + * "builtin objects" cryptoki module. + */ + +static NSSUTF8 * +builtins_mdSlot_GetSlotDescription( + NSSCKMDSlot *mdSlot, + NSSCKFWSlot *fwSlot, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance, + CK_RV *pError) +{ + return (NSSUTF8 *)nss_builtins_SlotDescription; +} + +static NSSUTF8 * +builtins_mdSlot_GetManufacturerID( + NSSCKMDSlot *mdSlot, + NSSCKFWSlot *fwSlot, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance, + CK_RV *pError) +{ + return (NSSUTF8 *)nss_builtins_ManufacturerID; +} + +static CK_VERSION +builtins_mdSlot_GetHardwareVersion( + NSSCKMDSlot *mdSlot, + NSSCKFWSlot *fwSlot, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance) +{ + return nss_builtins_HardwareVersion; +} + +static CK_VERSION +builtins_mdSlot_GetFirmwareVersion( + NSSCKMDSlot *mdSlot, + NSSCKFWSlot *fwSlot, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance) +{ + return nss_builtins_FirmwareVersion; +} + +static NSSCKMDToken * +builtins_mdSlot_GetToken( + NSSCKMDSlot *mdSlot, + NSSCKFWSlot *fwSlot, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance, + CK_RV *pError) +{ + return (NSSCKMDToken *)&nss_builtins_mdToken; +} + +const NSSCKMDSlot + nss_builtins_mdSlot = { + (void *)NULL, /* etc */ + NULL, /* Initialize */ + NULL, /* Destroy */ + builtins_mdSlot_GetSlotDescription, + builtins_mdSlot_GetManufacturerID, + NULL, /* GetTokenPresent -- defaults to true */ + NULL, /* GetRemovableDevice -- defaults to false */ + NULL, /* GetHardwareSlot -- defaults to false */ + builtins_mdSlot_GetHardwareVersion, + builtins_mdSlot_GetFirmwareVersion, + builtins_mdSlot_GetToken, + (void *)NULL /* null terminator */ + }; diff --git a/security/nss/lib/ckfw/builtins/btoken.c b/security/nss/lib/ckfw/builtins/btoken.c new file mode 100644 index 0000000000..ae1e1380bd --- /dev/null +++ b/security/nss/lib/ckfw/builtins/btoken.c @@ -0,0 +1,135 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "builtins.h" + +/* + * builtins/token.c + * + * This file implements the NSSCKMDToken object for the + * "builtin objects" cryptoki module. + */ + +static NSSUTF8 * +builtins_mdToken_GetLabel( + NSSCKMDToken *mdToken, + NSSCKFWToken *fwToken, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance, + CK_RV *pError) +{ + return (NSSUTF8 *)nss_builtins_TokenLabel; +} + +static NSSUTF8 * +builtins_mdToken_GetManufacturerID( + NSSCKMDToken *mdToken, + NSSCKFWToken *fwToken, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance, + CK_RV *pError) +{ + return (NSSUTF8 *)nss_builtins_ManufacturerID; +} + +static NSSUTF8 * +builtins_mdToken_GetModel( + NSSCKMDToken *mdToken, + NSSCKFWToken *fwToken, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance, + CK_RV *pError) +{ + return (NSSUTF8 *)nss_builtins_TokenModel; +} + +static NSSUTF8 * +builtins_mdToken_GetSerialNumber( + NSSCKMDToken *mdToken, + NSSCKFWToken *fwToken, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance, + CK_RV *pError) +{ + return (NSSUTF8 *)nss_builtins_TokenSerialNumber; +} + +static CK_BBOOL +builtins_mdToken_GetIsWriteProtected( + NSSCKMDToken *mdToken, + NSSCKFWToken *fwToken, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance) +{ + return CK_TRUE; +} + +static CK_VERSION +builtins_mdToken_GetHardwareVersion( + NSSCKMDToken *mdToken, + NSSCKFWToken *fwToken, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance) +{ + return nss_builtins_HardwareVersion; +} + +static CK_VERSION +builtins_mdToken_GetFirmwareVersion( + NSSCKMDToken *mdToken, + NSSCKFWToken *fwToken, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance) +{ + return nss_builtins_FirmwareVersion; +} + +static NSSCKMDSession * +builtins_mdToken_OpenSession( + NSSCKMDToken *mdToken, + NSSCKFWToken *fwToken, + NSSCKMDInstance *mdInstance, + NSSCKFWInstance *fwInstance, + NSSCKFWSession *fwSession, + CK_BBOOL rw, + CK_RV *pError) +{ + return nss_builtins_CreateSession(fwSession, pError); +} + +const NSSCKMDToken + nss_builtins_mdToken = { + (void *)NULL, /* etc */ + NULL, /* Setup */ + NULL, /* Invalidate */ + NULL, /* InitToken -- default errs */ + builtins_mdToken_GetLabel, + builtins_mdToken_GetManufacturerID, + builtins_mdToken_GetModel, + builtins_mdToken_GetSerialNumber, + NULL, /* GetHasRNG -- default is false */ + builtins_mdToken_GetIsWriteProtected, + NULL, /* GetLoginRequired -- default is false */ + NULL, /* GetUserPinInitialized -- default is false */ + NULL, /* GetRestoreKeyNotNeeded -- irrelevant */ + NULL, /* GetHasClockOnToken -- default is false */ + NULL, /* GetHasProtectedAuthenticationPath -- default is false */ + NULL, /* GetSupportsDualCryptoOperations -- default is false */ + NULL, /* GetMaxSessionCount -- default is CK_UNAVAILABLE_INFORMATION */ + NULL, /* GetMaxRwSessionCount -- default is CK_UNAVAILABLE_INFORMATION */ + NULL, /* GetMaxPinLen -- irrelevant */ + NULL, /* GetMinPinLen -- irrelevant */ + NULL, /* GetTotalPublicMemory -- default is CK_UNAVAILABLE_INFORMATION */ + NULL, /* GetFreePublicMemory -- default is CK_UNAVAILABLE_INFORMATION */ + NULL, /* GetTotalPrivateMemory -- default is CK_UNAVAILABLE_INFORMATION */ + NULL, /* GetFreePrivateMemory -- default is CK_UNAVAILABLE_INFORMATION */ + builtins_mdToken_GetHardwareVersion, + builtins_mdToken_GetFirmwareVersion, + NULL, /* GetUTCTime -- no clock */ + builtins_mdToken_OpenSession, + NULL, /* GetMechanismCount -- default is zero */ + NULL, /* GetMechanismTypes -- irrelevant */ + NULL, /* GetMechanism -- irrelevant */ + (void *)NULL /* null terminator */ + }; diff --git a/security/nss/lib/ckfw/builtins/builtins.gyp b/security/nss/lib/ckfw/builtins/builtins.gyp new file mode 100644 index 0000000000..5f3c5e321c --- /dev/null +++ b/security/nss/lib/ckfw/builtins/builtins.gyp @@ -0,0 +1,63 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +{ + 'includes': [ + '../../../coreconf/config.gypi' + ], + 'targets': [ + { + 'target_name': 'nssckbi', + 'type': 'shared_library', + 'sources': [ + 'anchor.c', + 'bfind.c', + 'binst.c', + 'bobject.c', + 'bsession.c', + 'bslot.c', + 'btoken.c', + 'ckbiver.c', + 'constants.c', + '<(certdata_c)', + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_exports', + '<(DEPTH)/lib/ckfw/ckfw.gyp:nssckfw', + '<(DEPTH)/lib/base/base.gyp:nssb' + ], + 'actions': [ + { + 'msvs_cygwin_shell': 0, + 'action': [ + '<(python)', + 'certdata.py', + 'certdata.txt', + '<@(_outputs)', + ], + 'inputs': [ + 'certdata.py', + 'certdata.perl', + 'certdata.txt' + ], + 'outputs': [ + '<(certdata_c)' + ], + 'action_name': 'generate_certdata_c' + } + ], + 'variables': { + 'mapfile': 'nssckbi.def', + 'certdata_c': '<(INTERMEDIATE_DIR)/certdata.c', + } + } + ], + 'target_defaults': { + 'include_dirs': [ + '.' + ] + }, + 'variables': { + 'module': 'nss', + } +} diff --git a/security/nss/lib/ckfw/builtins/builtins.h b/security/nss/lib/ckfw/builtins/builtins.h new file mode 100644 index 0000000000..a1693c29ca --- /dev/null +++ b/security/nss/lib/ckfw/builtins/builtins.h @@ -0,0 +1,66 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nssckmdt.h" +#include "nssckfw.h" + +/* + * I'm including this for access to the arena functions. + * Looks like we should publish that API. + */ +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +/* + * This is where the Netscape extensions live, at least for now. + */ +#ifndef CKT_H +#include "ckt.h" +#endif /* CKT_H */ + +struct builtinsInternalObjectStr { + CK_ULONG n; + const CK_ATTRIBUTE_TYPE *types; + const NSSItem *items; + NSSCKMDObject mdObject; +}; +typedef struct builtinsInternalObjectStr builtinsInternalObject; + +extern builtinsInternalObject nss_builtins_data[]; +extern const PRUint32 nss_builtins_nObjects; + +extern const CK_VERSION nss_builtins_CryptokiVersion; +extern const CK_VERSION nss_builtins_LibraryVersion; +extern const CK_VERSION nss_builtins_HardwareVersion; +extern const CK_VERSION nss_builtins_FirmwareVersion; + +extern const NSSUTF8 nss_builtins_ManufacturerID[]; +extern const NSSUTF8 nss_builtins_LibraryDescription[]; +extern const NSSUTF8 nss_builtins_SlotDescription[]; +extern const NSSUTF8 nss_builtins_TokenLabel[]; +extern const NSSUTF8 nss_builtins_TokenModel[]; +extern const NSSUTF8 nss_builtins_TokenSerialNumber[]; + +extern const NSSCKMDInstance nss_builtins_mdInstance; +extern const NSSCKMDSlot nss_builtins_mdSlot; +extern const NSSCKMDToken nss_builtins_mdToken; + +NSS_EXTERN NSSCKMDSession * +nss_builtins_CreateSession( + NSSCKFWSession *fwSession, + CK_RV *pError); + +NSS_EXTERN NSSCKMDFindObjects * +nss_builtins_FindObjectsInit( + NSSCKFWSession *fwSession, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_RV *pError); + +NSS_EXTERN NSSCKMDObject * +nss_builtins_CreateMDObject( + NSSArena *arena, + builtinsInternalObject *io, + CK_RV *pError); diff --git a/security/nss/lib/ckfw/builtins/certdata.perl b/security/nss/lib/ckfw/builtins/certdata.perl new file mode 100644 index 0000000000..502dfb0c5b --- /dev/null +++ b/security/nss/lib/ckfw/builtins/certdata.perl @@ -0,0 +1,192 @@ +#!perl -w +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +use strict; + +my %constants; +my $count = 0; +my $o; +my @objects = (); +my @objsize; + +$constants{CK_TRUE} = "static const CK_BBOOL ck_true = CK_TRUE;\n"; +$constants{CK_FALSE} = "static const CK_BBOOL ck_false = CK_FALSE;\n"; + +if( scalar @ARGV == 0 ) { + print STDERR "Usage: $0 [output-file]\n"; + exit 1; +} + +open(STDIN, '<', $ARGV[0]) + or die "Could not open input file '$ARGV[0]' $!"; +if( scalar @ARGV > 1 ) { + open(STDOUT, '>', $ARGV[1]) + or die "Could not open output file '$ARGV[1]' $!"; +} + +while(<>) { + my @fields = (); + my $size; + + s/^((?:[^"#]+|"[^"]*")*)(\s*#.*$)/$1/; + next if (/^\s*$/); + + # This was taken from the perl faq #4. + my $text = $_; + push(@fields, $+) while $text =~ m{ + "([^\"\\]*(?:\\.[^\"\\]*)*)"\s? # groups the phrase inside the quotes + | ([^\s]+)\s? + | \s + }gx; + push(@fields, undef) if substr($text,-1,1) eq '\s'; + + if( $fields[0] =~ /BEGINDATA/ ) { + next; + } + + if( $fields[1] =~ /MULTILINE/ ) { + $fields[2] = ""; + while(<>) { + last if /END/; + chomp; + $fields[2] .= "\"$_\"\n"; + } + } + + if( $fields[1] =~ /UTF8/ ) { + if( $fields[2] =~ /^"/ ) { + ; + } else { + $fields[2] = "\"" . $fields[2] . "\""; + } + + my $scratch = eval($fields[2]); + + $size = length($scratch) + 1; # null terminate + } + + if( $fields[1] =~ /OCTAL/ ) { + if( $fields[2] =~ /^"/ ) { + ; + } else { + $fields[2] = "\"" . $fields[2] . "\""; + } + + my $scratch = $fields[2]; + $size = $scratch =~ tr/\\//; + # no null termination + } + + if( $fields[1] =~ /^CK_/ ) { + my $lcv = $fields[2]; + $lcv =~ tr/A-Z/a-z/; + if( !defined($constants{$fields[2]}) ) { + $constants{$fields[2]} = "static const $fields[1] $lcv = $fields[2];\n"; + } + + $size = "sizeof($fields[1])"; + $fields[2] = "&$lcv"; + } + + if( $fields[0] =~ /CKA_CLASS/ ) { + $count++; + $objsize[$count] = 0; + } + + @{$objects[$count][$objsize[$count]++]} = ( "$fields[0]", $fields[2], "$size" ); + + # print "$fields[0] | $fields[1] | $size | $fields[2]\n"; +} + +doprint(); + +sub dudump { +my $i; +for( $i = 1; $i <= $count; $i++ ) { + print "\n"; + $o = $objects[$i]; + my @ob = @{$o}; + my $l; + my $j; + for( $j = 0; $j < @ob; $j++ ) { + $l = $ob[$j]; + my @a = @{$l}; + print "$a[0] ! $a[1] ! $a[2]\n"; + } +} + +} + +sub doprint { +my $i; + +print <