/* * 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. * * The SECACERT is the Signed Execution Trust Anchor that the * Cryptographic Framework uses for FIPS-140 validation of non-crypto * binaries */ static const char _PATH_CRYPTO_CACERT[] = CRYPTO_CERTS_DIR "/CA"; static const char _PATH_CRYPTO_OBJCACERT[] = CRYPTO_CERTS_DIR "/SUNWObjectCA"; static const char _PATH_CRYPTO_SECACERT[] = ETC_CERTS_DIR "/SUNWSolarisCA"; static ELFCert_t CACERT = NULL; static ELFCert_t OBJCACERT = NULL; static ELFCert_t SECACERT = 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_ATTRIBUTE attrlist[8]; int numattr; 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); } if (SECACERT == NULL) { (void) elfcertlib_getcert(ess, (char *)_PATH_CRYPTO_SECACERT, NULL, &SECACERT, ES_GET_FIPS140); } (void) pthread_mutex_unlock(&ca_mutex); if (CACERT != NULL) { numattr = 0; kmf_set_attr_at_index(attrlist, numattr++, KMF_CERT_DATA_ATTR, &cert->c_cert.certificate, sizeof (KMF_DATA)); kmf_set_attr_at_index(attrlist, numattr++, KMF_SIGNER_CERT_DATA_ATTR, &CACERT->c_cert.certificate, sizeof (KMF_DATA)); rv = kmf_verify_cert(ess->es_kmfhandle, numattr, attrlist); 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) { numattr = 0; kmf_set_attr_at_index(attrlist, numattr++, KMF_CERT_DATA_ATTR, &cert->c_cert.certificate, sizeof (KMF_DATA)); kmf_set_attr_at_index(attrlist, numattr++, KMF_SIGNER_CERT_DATA_ATTR, &OBJCACERT->c_cert.certificate, sizeof (KMF_DATA)); rv = kmf_verify_cert(ess->es_kmfhandle, numattr, attrlist); 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); } } if (SECACERT != NULL) { rv = KMF_VerifyCertWithCert(ess->es_kmfhandle, (const KMF_DATA *)&cert->c_cert, (const KMF_DATA *)&SECACERT->c_cert.certificate); if (rv == KMF_OK) { if (ess->es_certCAcallback != NULL) (ess->es_certvercallback)(ess->es_callbackctx, cert, SECACERT); 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_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++) { KMF_ATTRIBUTE attrlist[8]; KMF_KEYSTORE_TYPE kstype; KMF_CERT_VALIDITY certvalidity; int numattr; kstype = KMF_KEYSTORE_OPENSSL; certvalidity = KMF_ALL_CERTS; ncerts = 2; numattr = 0; kmf_set_attr_at_index(attrlist, numattr++, KMF_KEYSTORE_TYPE_ATTR, &kstype, sizeof (kstype)); kmf_set_attr_at_index(attrlist, numattr++, KMF_X509_DER_CERT_ATTR, certbuf, sizeof (KMF_X509_DER_CERT)); kmf_set_attr_at_index(attrlist, numattr++, KMF_COUNT_ATTR, &ncerts, sizeof (uint32_t)); if (signer_DN != NULL) { kmf_set_attr_at_index(attrlist, numattr++, KMF_SUBJECT_NAME_ATTR, signer_DN, strlen(signer_DN)); } kmf_set_attr_at_index(attrlist, numattr++, KMF_CERT_VALIDITY_ATTR, &certvalidity, sizeof (KMF_CERT_VALIDITY)); kmf_set_attr_at_index(attrlist, numattr++, KMF_CERT_FILENAME_ATTR, *plp, strlen (*plp)); rv = kmf_find_cert(ess->es_kmfhandle, numattr, attrlist); if (rv != KMF_OK) continue; /* found one */ cert->c_cert = certbuf[0]; if (ncerts > 1) { /* release any extras */ kmf_free_kmf_cert(ess->es_kmfhandle, &certbuf[1]); if (signer_DN == NULL) { /* There can be only one */ cryptodebug("elfcertlib_getcert: " "too many certificates found in %s", cert_pathname); goto cleanup; } } /* cache subject and issuer */ rv = kmf_get_cert_subject_str(ess->es_kmfhandle, &cert->c_cert.certificate, &cert->c_subject); if (rv != KMF_OK) goto cleanup; rv = kmf_get_cert_issuer_str(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 || strcmp(cert_pathname, _PATH_CRYPTO_SECACERT) == 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; KMF_KEY_HANDLE keybuf[2]; KMF_ATTRIBUTE attrlist[16]; uint32_t nkeys; KMF_KEYSTORE_TYPE kstype; KMF_KEY_ALG keytype; KMF_KEY_CLASS keyclass; KMF_ENCODE_FORMAT format; int numattr; kstype = KMF_KEYSTORE_OPENSSL; nkeys = 2; keytype = KMF_KEYALG_NONE; keyclass = KMF_ASYM_PRI; format = KMF_FORMAT_UNDEF; numattr = 0; kmf_set_attr_at_index(attrlist, numattr++, KMF_KEYSTORE_TYPE_ATTR, &kstype, sizeof (kstype)); kmf_set_attr_at_index(attrlist, numattr++, KMF_KEY_HANDLE_ATTR, keybuf, sizeof (KMF_KEY_HANDLE)); kmf_set_attr_at_index(attrlist, numattr++, KMF_COUNT_ATTR, &nkeys, sizeof (uint32_t)); kmf_set_attr_at_index(attrlist, numattr++, KMF_KEYALG_ATTR, &keytype, sizeof (keytype)); kmf_set_attr_at_index(attrlist, numattr++, KMF_KEYCLASS_ATTR, &keyclass, sizeof (keyclass)); kmf_set_attr_at_index(attrlist, numattr++, KMF_ENCODE_FORMAT_ATTR, &format, sizeof (format)); kmf_set_attr_at_index(attrlist, numattr++, KMF_KEY_FILENAME_ATTR, (char *)pathname, strlen(pathname)); rv = kmf_find_key(ess->es_kmfhandle, numattr, attrlist); 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; char *idstr = NULL; char *kmferr; KMF_ATTRIBUTE attrlist[16]; uint32_t nkeys; KMF_KEYSTORE_TYPE kstype; KMF_KEY_ALG keytype; KMF_KEY_CLASS keyclass; KMF_ENCODE_FORMAT format; KMF_CREDENTIAL pincred; boolean_t tokenbool, privatebool; int numattr; /* * 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_get_cert_id_str(&cert->c_cert.certificate, &idstr); if (rv != KMF_OK) { (void) kmf_get_kmf_error_str(rv, &kmferr); cryptodebug("Error getting ID from cert: %s\n", (kmferr ? kmferr : "Unrecognized KMF error")); free(kmferr); return (B_FALSE); } kstype = KMF_KEYSTORE_PK11TOKEN; nkeys = 1; keytype = KMF_KEYALG_NONE; keyclass = KMF_ASYM_PRI; format = KMF_FORMAT_UNDEF; pincred.cred = (char *)pin; pincred.credlen = strlen(pin); tokenbool = B_FALSE; privatebool = B_TRUE; numattr = 0; kmf_set_attr_at_index(attrlist, numattr++, KMF_KEYSTORE_TYPE_ATTR, &kstype, sizeof (kstype)); kmf_set_attr_at_index(attrlist, numattr++, KMF_KEY_HANDLE_ATTR, &cert->c_privatekey, sizeof (KMF_KEY_HANDLE)); kmf_set_attr_at_index(attrlist, numattr++, KMF_COUNT_ATTR, &nkeys, sizeof (uint32_t)); kmf_set_attr_at_index(attrlist, numattr++, KMF_KEYALG_ATTR, &keytype, sizeof (keytype)); kmf_set_attr_at_index(attrlist, numattr++, KMF_KEYCLASS_ATTR, &keyclass, sizeof (keyclass)); kmf_set_attr_at_index(attrlist, numattr++, KMF_ENCODE_FORMAT_ATTR, &format, sizeof (format)); kmf_set_attr_at_index(attrlist, numattr++, KMF_IDSTR_ATTR, idstr, strlen(idstr)); kmf_set_attr_at_index(attrlist, numattr++, KMF_CREDENTIAL_ATTR, &pincred, sizeof (KMF_CREDENTIAL)); kmf_set_attr_at_index(attrlist, numattr++, KMF_TOKEN_BOOL_ATTR, &tokenbool, sizeof (tokenbool)); kmf_set_attr_at_index(attrlist, numattr++, KMF_PRIVATE_BOOL_ATTR, &privatebool, sizeof (privatebool)); rv = kmf_find_key(ess->es_kmfhandle, numattr, attrlist); free(idstr); if (rv != KMF_OK) { (void) kmf_get_kmf_error_str(rv, &kmferr); cryptodebug("Error finding private key: %s\n", (kmferr ? kmferr : "Unrecognized KMF error")); free(kmferr); return (B_FALSE); } if (nkeys != 1) { cryptodebug("Error finding private key: No key found\n"); return (B_FALSE); } cryptodebug("key found in %s", token_label); cryptodebug("elfcertlib_loadprivatekey = 0x%.8X", &cert->c_privatekey); 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_DATA tobesigned; KMF_DATA signature; uchar_t der_data[sizeof (MD5_DER_PREFIX) + MD5_DIGEST_LENGTH]; KMF_ATTRIBUTE attrlist[8]; int numattr; 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; numattr = 0; kmf_set_attr_at_index(attrlist, numattr++, KMF_KEYSTORE_TYPE_ATTR, &(cert->c_privatekey.kstype), sizeof (KMF_KEYSTORE_TYPE)); kmf_set_attr_at_index(attrlist, numattr++, KMF_KEY_HANDLE_ATTR, &cert->c_privatekey, sizeof (KMF_KEY_HANDLE)); kmf_set_attr_at_index(attrlist, numattr++, KMF_OID_ATTR, (KMF_OID *)&KMFOID_RSA, sizeof (KMF_OID)); kmf_set_attr_at_index(attrlist, numattr++, KMF_DATA_ATTR, &tobesigned, sizeof (KMF_DATA)); kmf_set_attr_at_index(attrlist, numattr++, KMF_OUT_DATA_ATTR, &signature, sizeof (KMF_DATA)); ret = kmf_sign_data(ess->es_kmfhandle, numattr, attrlist); if (ret != KMF_OK) { char *kmferr; (void) kmf_get_kmf_error_str(ret, &kmferr); cryptodebug("Error signing data: %s\n", (kmferr ? kmferr : "Unrecognized KMF error")); free(kmferr); *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; KMF_ATTRIBUTE attrlist[8]; KMF_KEYSTORE_TYPE kstype; int numattr; 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. */ kstype = KMF_KEYSTORE_PK11TOKEN; numattr = 0; kmf_set_attr_at_index(attrlist, numattr++, KMF_KEYSTORE_TYPE_ATTR, &kstype, sizeof (kstype)); kmf_set_attr_at_index(attrlist, numattr++, KMF_DATA_ATTR, &indata, sizeof (KMF_DATA)); kmf_set_attr_at_index(attrlist, numattr++, KMF_IN_SIGN_ATTR, &insig, sizeof (KMF_DATA)); kmf_set_attr_at_index(attrlist, numattr++, KMF_SIGNER_CERT_DATA_ATTR, (KMF_DATA *)(&cert->c_cert.certificate), sizeof (KMF_DATA)); kmf_set_attr_at_index(attrlist, numattr++, KMF_ALGORITHM_INDEX_ATTR, &algid, sizeof (algid)); rv = kmf_verify_data(ess->es_kmfhandle, numattr, attrlist); 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_ATTRIBUTE attrlist[8]; KMF_KEYSTORE_TYPE kstype; boolean_t readonly; int numattr; kstype = KMF_KEYSTORE_PK11TOKEN; readonly = B_TRUE; numattr = 0; kmf_set_attr_at_index(attrlist, numattr++, KMF_KEYSTORE_TYPE_ATTR, &kstype, sizeof (kstype)); kmf_set_attr_at_index(attrlist, numattr++, KMF_TOKEN_LABEL_ATTR, token, strlen(token)); kmf_set_attr_at_index(attrlist, numattr++, KMF_READONLY_ATTR, &readonly, sizeof (readonly)); rv = kmf_configure_keystore(ess->es_kmfhandle, numattr, attrlist); 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_free_kmf_cert(ess->es_kmfhandle, &cert->c_cert); kmf_free_kmf_key(ess->es_kmfhandle, &cert->c_privatekey); free(cert); }