/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const char _PATH_ELFSIGN_CRYPTO_CERTS[] = CRYPTO_CERTS_DIR; const char _PATH_ELFSIGN_ETC_CERTS[] = ETC_CERTS_DIR; /* * The CACERT and OBJCACERT are the Cryptographic Trust Anchors * for the Solaris Cryptographic Framework. */ static const char _PATH_CRYPTO_CACERT[] = CRYPTO_CERTS_DIR "/CA"; static const char _PATH_CRYPTO_OBJCACERT[] = CRYPTO_CERTS_DIR "/SUNWObjectCA"; static ELFCert_t CACERT = NULL; static ELFCert_t OBJCACERT = NULL; static pthread_mutex_t ca_mutex = PTHREAD_MUTEX_INITIALIZER; static void elfcertlib_freecert(ELFsign_t, ELFCert_t); static ELFCert_t elfcertlib_allocatecert(void); /* * elfcertlib_verifycert - Verify the Cert with a Trust Anchor * * IN ess - elfsign context structure * cert * OUT NONE * RETURN TRUE/FALSE * * We first setup the Trust Anchor (CA and SUNWObjectCA) certs * if it hasn't been done already. We verify that the files on disk * are those we expected. * * We then verify the given cert using the publickey of a TA. * If the passed in cert is a TA or it has been verified already we * short cut and return TRUE without futher validation. */ /*ARGSUSED*/ boolean_t elfcertlib_verifycert(ELFsign_t ess, ELFCert_t cert) { KMF_RETURN rv; if ((cert->c_verified == E_OK) || (cert->c_verified == E_IS_TA)) { return (B_TRUE); } (void) pthread_mutex_lock(&ca_mutex); if (CACERT == NULL) { (void) elfcertlib_getcert(ess, (char *)_PATH_CRYPTO_CACERT, NULL, &CACERT, ES_GET); } if (OBJCACERT == NULL) { (void) elfcertlib_getcert(ess, (char *)_PATH_CRYPTO_OBJCACERT, NULL, &OBJCACERT, ES_GET); } (void) pthread_mutex_unlock(&ca_mutex); if (CACERT != NULL) { rv = KMF_VerifyCertWithCert(ess->es_kmfhandle, (const KMF_DATA *)&cert->c_cert, (const KMF_DATA *)&CACERT->c_cert.certificate); if (rv == KMF_OK) { if (ess->es_certCAcallback != NULL) (ess->es_certvercallback)(ess->es_callbackctx, cert, CACERT); cert->c_verified = E_OK; return (B_TRUE); } } if (OBJCACERT != NULL) { rv = KMF_VerifyCertWithCert(ess->es_kmfhandle, (const KMF_DATA *)&cert->c_cert, (const KMF_DATA *)&OBJCACERT->c_cert.certificate); if (rv == KMF_OK) { if (ess->es_certCAcallback != NULL) (ess->es_certvercallback)(ess->es_callbackctx, cert, OBJCACERT); cert->c_verified = E_OK; return (B_TRUE); } } return (B_FALSE); } /* * elfcertlib_getcert - Get the certificate for signer_DN * * IN ess - elfsign context structure * cert_pathname - path to cert (May be NULL) * signer_DN - The DN we are looking for (May be NULL) * action - indicates crypto verification call * OUT certp - allocated/loaded ELFCert_t * * If the cert_pathname is passed use it and don't search. * Otherwise, go looking in certificate directories */ boolean_t elfcertlib_getcert(ELFsign_t ess, char *cert_pathname, char *signer_DN, ELFCert_t *certp, enum ES_ACTION action) { KMF_RETURN rv; ELFCert_t cert = NULL; KMF_FINDCERT_PARAMS fcparams; KMF_X509_DER_CERT certbuf[2]; uint32_t ncerts; boolean_t ret = B_FALSE; char *pathlist[3], **plp; cryptodebug("elfcertlib_getcert: path=%s, DN=%s", cert_pathname ? cert_pathname : "-none-", signer_DN ? signer_DN : "-none-"); *certp = NULL; if (cert_pathname == NULL && signer_DN == NULL) { cryptodebug("elfcertlib_getcert: lack of specificity"); return (ret); } plp = pathlist; if (cert_pathname != NULL) { /* look in the specified object */ *plp++ = cert_pathname; } else { /* look in the certificate directories */ *plp++ = (char *)_PATH_ELFSIGN_CRYPTO_CERTS; /* * crypto verifications don't search beyond * _PATH_ELFSIGN_CRYPTO_CERTS */ if (action != ES_GET_CRYPTO) *plp++ = (char *)_PATH_ELFSIGN_ETC_CERTS; } *plp = NULL; if ((cert = elfcertlib_allocatecert()) == NULL) { return (ret); } for (plp = pathlist; *plp; plp++) { (void) memset(&fcparams, 0, sizeof (fcparams)); fcparams.kstype = KMF_KEYSTORE_OPENSSL; fcparams.sslparms.certfile = *plp; fcparams.subject = signer_DN; ncerts = 2; rv = KMF_FindCert(ess->es_kmfhandle, &fcparams, certbuf, &ncerts); if (rv != KMF_OK) continue; if (ncerts > 1 && signer_DN == NULL) { /* There can be only one */ cryptodebug("elfcertlib_getcert: " "too many certificates found in %s", cert_pathname); goto cleanup; } /* found it, cache subject and issuer */ cert->c_cert = certbuf[0]; rv = KMF_GetCertSubjectNameString(ess->es_kmfhandle, &cert->c_cert.certificate, &cert->c_subject); if (rv != KMF_OK) goto cleanup; rv = KMF_GetCertIssuerNameString(ess->es_kmfhandle, &cert->c_cert.certificate, &cert->c_issuer); if (rv != KMF_OK) goto cleanup; break; } if (*plp == NULL) { cryptodebug("elfcertlib_getcert: no certificate found"); goto cleanup; } cert->c_verified = E_UNCHECKED; /* * If the cert we are loading is the trust anchor (ie the CA) then * we mark it as such in cert. This is so that we don't attempt * to verify it later. The CA is always implicitly verified. */ if (cert_pathname != NULL && ( strcmp(cert_pathname, _PATH_CRYPTO_CACERT) == 0 || strcmp(cert_pathname, _PATH_CRYPTO_OBJCACERT) == 0)) { if (ess->es_certCAcallback != NULL) (ess->es_certCAcallback)(ess->es_callbackctx, cert, cert_pathname); cert->c_verified = E_IS_TA; } ret = B_TRUE; cleanup: if (ret) { *certp = cert; } else { if (cert != NULL) elfcertlib_freecert(ess, cert); if (signer_DN != NULL) cryptoerror(LOG_ERR, "unable to find a certificate " "for DN: %s", signer_DN); else cryptoerror(LOG_ERR, "unable to load certificate " "from %s", cert_pathname); } return (ret); } /* * elfcertlib_loadprivatekey - Load the private key from path * * IN ess - elfsign context structure * cert * pathname * OUT cert * RETURNS TRUE/FALSE */ boolean_t elfcertlib_loadprivatekey(ELFsign_t ess, ELFCert_t cert, const char *pathname) { KMF_RETURN rv = KMF_OK; uint32_t nkeys = 2; KMF_FINDKEY_PARAMS fkparams; KMF_KEY_HANDLE keybuf[2]; (void) memset(&fkparams, 0, sizeof (fkparams)); fkparams.keyclass = KMF_ASYM_PRI; fkparams.kstype = KMF_KEYSTORE_OPENSSL; fkparams.sslparms.keyfile = (char *)pathname; rv = KMF_FindKey(ess->es_kmfhandle, &fkparams, keybuf, &nkeys); if (rv != KMF_OK) return (B_FALSE); if (nkeys != 1) { /* lack of specificity */ cryptodebug("found %d keys at %s", nkeys, pathname); return (B_FALSE); } cert->c_privatekey = keybuf[0]; cryptodebug("key %s loaded", pathname); return (B_TRUE); } /* * elfcertlib_loadtokenkey - Load the private key from token * * IN ess - elfsign context structure * cert * token_label * pin * OUT cert * RETURNS TRUE/FALSE */ boolean_t elfcertlib_loadtokenkey(ELFsign_t ess, ELFCert_t cert, const char *token_label, const char *pin) { KMF_RETURN rv = KMF_OK; KMF_FINDKEY_PARAMS fkparams; KMF_CONFIG_PARAMS cfgparams; uint32_t nkeys = 1; char *idstr = NULL; char *err = NULL; (void) memset(&fkparams, 0, sizeof (fkparams)); (void) memset(&cfgparams, 0, sizeof (cfgparams)); cfgparams.kstype = KMF_KEYSTORE_PK11TOKEN; cfgparams.pkcs11config.label = (char *)token_label; cfgparams.pkcs11config.readonly = B_TRUE; rv = KMF_ConfigureKeystore(ess->es_kmfhandle, &cfgparams); if (rv != KMF_OK) { if (KMF_GetKMFErrorString(rv, &err) == KMF_OK) { cryptodebug("Error configuring token access:" " %s\n", err); free(err); } return (B_FALSE); } fkparams.idstr = idstr; fkparams.kstype = KMF_KEYSTORE_PK11TOKEN; fkparams.keyclass = KMF_ASYM_PRI; fkparams.cred.cred = (char *)pin; fkparams.cred.credlen = (pin != NULL ? strlen(pin) : 0); fkparams.pkcs11parms.private = B_TRUE; /* * We will search for the key based on the ID attribute * which was added when the key was created. ID is * a SHA-1 hash of the public modulus shared by the * key and the certificate. */ rv = KMF_GetCertIDString(&cert->c_cert.certificate, &idstr); if (rv != KMF_OK) { if (KMF_GetKMFErrorString(rv, &err) == KMF_OK) { cryptodebug("Error getting ID from cert: %s\n", err); free(err); } return (B_FALSE); } fkparams.idstr = idstr; rv = KMF_FindKey(ess->es_kmfhandle, &fkparams, &cert->c_privatekey, &nkeys); if (rv != KMF_OK || nkeys != 1) { if (KMF_GetKMFErrorString(rv, &err) == KMF_OK) { cryptodebug("Error finding private key: %s\n", err); free(err); } free(idstr); return (B_FALSE); } cryptodebug("key found in %s", token_label); cryptodebug("elfcertlib_loadprivatekey = 0x%.8X", &cert->c_privatekey); free(idstr); return (B_TRUE); } static const CK_BYTE MD5_DER_PREFIX[] = {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10}; /* * elfcertlib_sign - sign the given DATA using the privatekey in cert * * IN ess - elfsign context structure * cert * data * data_len * OUT sig - must be big enough to hold the signature of data * Caller must allocate * sig_len - actual length used; 0 on failure. * RETURNS TRUE/FALSE */ /*ARGSUSED*/ boolean_t elfcertlib_sign(ELFsign_t ess, ELFCert_t cert, const uchar_t *data, size_t data_len, uchar_t *sig, size_t *sig_len) { KMF_RETURN ret = KMF_OK; KMF_DATA tobesigned; KMF_DATA signature; uchar_t der_data[sizeof (MD5_DER_PREFIX) + MD5_DIGEST_LENGTH]; if (ess->es_version <= FILESIG_VERSION2) { /* compatibility: take MD5 hash of SHA1 hash */ size_t derlen = MD5_DIGEST_LENGTH; MD5_CTX ctx; /* * first: digest using software-based methods, don't * rely on the token for hashing. */ MD5Init(&ctx); MD5Update(&ctx, data, data_len); MD5Final(&der_data[sizeof (MD5_DER_PREFIX)], &ctx); /* * second: insert prefix */ (void) memcpy(der_data, MD5_DER_PREFIX, sizeof (MD5_DER_PREFIX)); /* * prepare to sign the local buffer */ tobesigned.Data = (uchar_t *)der_data; tobesigned.Length = sizeof (MD5_DER_PREFIX) + derlen; } else { tobesigned.Data = (uchar_t *)data; tobesigned.Length = data_len; } signature.Data = (uchar_t *)sig; signature.Length = *sig_len; ret = KMF_SignDataWithKey(ess->es_kmfhandle, &cert->c_privatekey, (KMF_OID *)&KMFOID_RSA, &tobesigned, &signature); if (ret != KMF_OK) { char *err; if (KMF_GetKMFErrorString(ret, &err) == KMF_OK && err != NULL) { cryptodebug("Error signing data: %s\n", err); free(err); } *sig_len = 0; return (B_FALSE); } *sig_len = signature.Length; return (B_TRUE); } /* * elfcertlib_verifysig - verify the given DATA using the public key in cert * * IN ess - elfsign context structure * cert * signature * sig_len * data * data_len * OUT N/A * RETURNS TRUE/FALSE */ boolean_t elfcertlib_verifysig(ELFsign_t ess, ELFCert_t cert, const uchar_t *signature, size_t sig_len, const uchar_t *data, size_t data_len) { KMF_RETURN rv; KMF_DATA indata; KMF_DATA insig; KMF_ALGORITHM_INDEX algid; indata.Data = (uchar_t *)data; indata.Length = data_len; insig.Data = (uchar_t *)signature; insig.Length = sig_len; if (ess->es_version <= FILESIG_VERSION2) algid = KMF_ALGID_MD5WithRSA; else algid = KMF_ALGID_RSA; /* * We tell KMF to use the PKCS11 verification APIs * here to prevent the use of OpenSSL and to keep * all validation within the FIPS-140 boundary for * the Cryptographic Framework. */ rv = KMF_VerifyDataWithCert(ess->es_kmfhandle, KMF_KEYSTORE_PK11TOKEN, algid, &indata, &insig, &cert->c_cert.certificate); return ((rv == KMF_OK)); } /* * elfcertlib_getdn * * IN cert * OUT NONE * RETURN dn or NULL */ char * elfcertlib_getdn(ELFCert_t cert) { cryptodebug("elfcertlib_getdn"); return (cert->c_subject); } /* * elfcertlib_getissuer * * IN cert * OUT NONE * RETURN dn or NULL */ char * elfcertlib_getissuer(ELFCert_t cert) { cryptodebug("elfcertlib_issuer"); return (cert->c_issuer); } boolean_t elfcertlib_init(ELFsign_t ess) { boolean_t rc = B_TRUE; KMF_RETURN rv; if (ess->es_kmfhandle == NULL) { rv = KMF_Initialize(&ess->es_kmfhandle, NULL, NULL); if (rv != KMF_OK) { cryptoerror(LOG_ERR, "unable to initialize KMF library"); rc = B_FALSE; } } return (rc); } void elfcertlib_fini(ELFsign_t ess) { (void) KMF_Finalize(ess->es_kmfhandle); } /* * set the token device */ boolean_t elfcertlib_settoken(ELFsign_t ess, char *token) { boolean_t rc = B_TRUE; KMF_RETURN rv; KMF_CONFIG_PARAMS cfgparams; (void) memset(&cfgparams, 0, sizeof (cfgparams)); cfgparams.kstype = KMF_KEYSTORE_PK11TOKEN; cfgparams.pkcs11config.label = token; cfgparams.pkcs11config.readonly = B_TRUE; rv = KMF_ConfigureKeystore(ess->es_kmfhandle, &cfgparams); if (rv != KMF_OK) { cryptoerror(LOG_ERR, "unable to select token\n"); rc = B_FALSE; } return (rc); } /* * set the certificate CA identification callback */ void elfcertlib_setcertCAcallback(ELFsign_t ess, void (*cb)(void *, ELFCert_t, char *)) { ess->es_certCAcallback = cb; } /* * set the certificate verification callback */ void elfcertlib_setcertvercallback(ELFsign_t ess, void (*cb)(void *, ELFCert_t, ELFCert_t)) { ess->es_certvercallback = cb; } /* * elfcertlib_releasecert - release a cert * * IN cert * OUT cert * RETURN N/A * */ void elfcertlib_releasecert(ELFsign_t ess, ELFCert_t cert) { elfcertlib_freecert(ess, cert); } /* * elfcertlib_allocatecert - create a new ELFCert_t * * IN N/A * OUT N/A * RETURN ELFCert_t, NULL on failure. */ static ELFCert_t elfcertlib_allocatecert(void) { ELFCert_t cert = NULL; cert = malloc(sizeof (struct ELFCert_s)); if (cert == NULL) { cryptoerror(LOG_ERR, "elfcertlib_allocatecert: malloc failed %s", strerror(errno)); return (NULL); } (void) memset(cert, 0, sizeof (struct ELFCert_s)); cert->c_verified = E_UNCHECKED; cert->c_subject = NULL; cert->c_issuer = NULL; return (cert); } /* * elfcertlib_freecert - freeup the memory of a cert * * IN cert * OUT cert * RETURN N/A * */ static void elfcertlib_freecert(ELFsign_t ess, ELFCert_t cert) { if (cert == NULL) return; free(cert->c_subject); free(cert->c_issuer); KMF_FreeKMFCert(ess->es_kmfhandle, &cert->c_cert); KMF_FreeKMFKey(ess->es_kmfhandle, &cert->c_privatekey); free(cert); }