diff --git a/src/common/cert_vfy.c b/src/common/cert_vfy.c index 82c5d0cb..ccd66066 100644 --- a/src/common/cert_vfy.c +++ b/src/common/cert_vfy.c @@ -519,9 +519,10 @@ int verify_signature(X509 * x509, unsigned char *data, int data_length, int rv; EVP_PKEY *pubkey; EVP_MD_CTX *md_ctx = NULL; - ECDSA_SIG* ec_sig; - int rs_len; - unsigned char *p = NULL; + const EVP_MD* md = NULL; + const char* md_name = NULL; + ASN1_OBJECT *algorithm; + int nid; /* get the public-key */ pubkey = X509_get_pubkey(x509); @@ -533,39 +534,76 @@ int verify_signature(X509 * x509, unsigned char *data, int data_length, DBG1("public key type: 0x%08x", EVP_PKEY_base_id(pubkey)); DBG1("public key bits: 0x%08x", EVP_PKEY_bits(pubkey)); - if (EVP_PKEY_base_id(pubkey) == EVP_PKEY_EC) { - // FIXME: Why not to use d2i_ECDSA_SIG() ??? - rs_len = *signature_length / 2; - ec_sig = ECDSA_SIG_new(); - BIGNUM *r = BN_bin2bn(*signature, rs_len, NULL); - BIGNUM *s = BN_bin2bn(*signature + rs_len, rs_len, NULL); - if (!r || !s) { - set_error("Unable to parse r+s EC signature numbers: %s", - ERR_error_string(ERR_get_error(), NULL)); - return -1; - } - if (1 != ECDSA_SIG_set0(ec_sig, r, s)) { - set_error("Unable to write r+s numbers to the signature structure: %s", - ERR_error_string(ERR_get_error(), NULL)); - return -1; - } - *signature_length = i2d_ECDSA_SIG(ec_sig, &p); - free(*signature); - *signature = malloc(*signature_length); - p = *signature; - *signature_length = i2d_ECDSA_SIG(ec_sig, &p); - ECDSA_SIG_free(ec_sig); + X509_PUBKEY_get0_param(&algorithm, NULL, NULL, NULL, X509_get_X509_PUBKEY(x509)); + nid = OBJ_obj2nid(algorithm); + + switch ( nid ) { + case NID_id_GostR3410_2001: + md_name = SN_id_GostR3411_94; + break; + case NID_id_GostR3410_2012_256: + md_name = SN_id_GostR3411_2012_256; + break; + case NID_id_GostR3410_2012_512: + md_name = SN_id_GostR3411_2012_512; + break; } - md_ctx = EVP_MD_CTX_new(); - /* verify the signature */ + if (md_name) { + md = EVP_get_digestbyname(md_name); + if (!md) { + set_error("unsupported digest %s", md_name); + return -1; + } + DBG1("hashing with %s", md_name); + } else { #ifdef USE_HASH_SHA1 - DBG("hashing with SHA1"); - EVP_VerifyInit(md_ctx, EVP_sha1()); + DBG("hashing with SHA1"); + md = EVP_sha1(); #else - DBG("hashing with SHA256"); - EVP_VerifyInit(md_ctx, EVP_sha256()); + DBG("hashing with SHA256"); + md = EVP_sha256(); #endif + } + + if (!md) { + set_error("unsupported key algorithm, nid: %d", nid); + return -1; + } + + if (EVP_PKEY_base_id(pubkey) == EVP_PKEY_EC) { + // FIXME: Why not to use d2i_ECDSA_SIG() ??? + ECDSA_SIG* ec_sig; + int rs_len; + unsigned char *p = NULL; + + rs_len = *signature_length / 2; + ec_sig = ECDSA_SIG_new(); + + BIGNUM *r = BN_bin2bn(*signature, rs_len, NULL); + BIGNUM *s = BN_bin2bn(*signature + rs_len, rs_len, NULL); + if (!r || !s) { + set_error("Unable to parse r+s EC signature numbers: %s", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + if (1 != ECDSA_SIG_set0(ec_sig, r, s)) { + set_error("Unable to write r+s numbers to the signature structure: %s", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + *signature_length = i2d_ECDSA_SIG(ec_sig, &p); + free(*signature); + *signature = malloc(*signature_length); + p = *signature; + *signature_length = i2d_ECDSA_SIG(ec_sig, &p); + ECDSA_SIG_free(ec_sig); + } + + /* verify the signature */ + md_ctx = EVP_MD_CTX_new(); + EVP_VerifyInit(md_ctx, md); EVP_VerifyUpdate(md_ctx, data, data_length); rv = EVP_VerifyFinal(md_ctx, *signature, *signature_length, pubkey); EVP_PKEY_free(pubkey); @@ -577,4 +615,30 @@ int verify_signature(X509 * x509, unsigned char *data, int data_length, DBG("signature is valid"); return 0; } + +int verify_eku_sc_logon(X509 * x509) +{ + static unsigned char id_kp_sc_logon[] = {0x2b, 6, 1, 4, 1, 0x82, 0x37, 20, 2, 2}; // 1.3.6.1.4.1.311.20.2.2 + int rv = 0; + EXTENDED_KEY_USAGE* eku = X509_get_ext_d2i(x509, NID_ext_key_usage, NULL, NULL); + if( NULL != eku ) + { + int i = 0, n = sk_ASN1_OBJECT_num(eku); + for( ; i < n; ++i ) + { + ASN1_OBJECT* extobj = sk_ASN1_OBJECT_value( eku, i ); + if( NULL == extobj ) + continue; + if( sizeof(id_kp_sc_logon) == OBJ_length(extobj) + && 0 == memcmp(OBJ_get0_data(extobj), id_kp_sc_logon, sizeof(id_kp_sc_logon)) ) + { + rv = 1; + break; + } + } + EXTENDED_KEY_USAGE_free(eku); + } + return rv; +} + #endif diff --git a/src/common/cert_vfy.h b/src/common/cert_vfy.h index c068558e..8a7e10f4 100644 --- a/src/common/cert_vfy.h +++ b/src/common/cert_vfy.h @@ -53,6 +53,7 @@ struct cert_policy_st { const char *crl_dir; const char *nss_dir; int ocsp_policy; + int eku_sc_logon_policy; }; #ifndef __CERT_VFY_C @@ -80,6 +81,8 @@ CERTVFY_EXTERN int verify_certificate(X509 * x509, cert_policy *policy); */ CERTVFY_EXTERN int verify_signature(X509 * x509, unsigned char *data, int data_length, unsigned char **signature, unsigned long *signature_length); +CERTVFY_EXTERN int verify_eku_sc_logon(X509 * x509); + #undef CERTVFY_EXTERN #endif /* __CERT_VFY_H_ */ diff --git a/src/common/pkcs11_lib.c b/src/common/pkcs11_lib.c index 2377fdaf..b0a99afc 100644 --- a/src/common/pkcs11_lib.c +++ b/src/common/pkcs11_lib.c @@ -32,7 +32,7 @@ #include "error.h" #include "cert_info.h" #include "pkcs11_lib.h" - +#include /* * this functions is completely common between both implementation. @@ -1757,74 +1757,166 @@ X509 *get_X509_certificate(cert_object_t *cert) return cert->x509; } +static int get_pubkey_algo(X509 *x509) +{ + ASN1_OBJECT *algorithm = NULL; + int nid; + + /* get the public-key */ + EVP_PKEY *pubkey = X509_get_pubkey(x509); + if (pubkey == NULL) { + set_error("X509_get_pubkey() failed: %s", ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + DBG1("public key type: 0x%08x", EVP_PKEY_base_id(pubkey)); + DBG1("public key bits: 0x%08x", EVP_PKEY_bits(pubkey)); + + X509_PUBKEY_get0_param(&algorithm, NULL, NULL, NULL, X509_get_X509_PUBKEY(x509)); + nid = OBJ_obj2nid(algorithm); + EVP_PKEY_free(pubkey); + + return nid; +} + #define MAX_SIGNATURE_LENGTH 65536 int sign_value(pkcs11_handle_t *h, cert_object_t *cert, CK_BYTE *data, CK_ULONG length, CK_BYTE **signature, CK_ULONG *signature_length) { - int rv; - int h_offset = 0; -#ifdef USE_HASH_SHA1 - CK_BYTE hash[15 + SHA_DIGEST_LENGTH] = - "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14"; -#else - CK_BYTE hash[19 + SHA256_DIGEST_LENGTH] = - "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20"; -#endif + int rv = -1; + unsigned char *h_prefix = NULL; + unsigned long h_offset = 0; CK_MECHANISM mechanism = { 0, NULL, 0 }; - + const EVP_MD* md = NULL; + const char *md_name = NULL; + EVP_MD_CTX *md_ctx = NULL; + unsigned char* hash = NULL; if (get_private_key(h, cert) == -1) { set_error("Couldn't find private key for certificate"); return -1; } + *signature_length = 256; + /* set mechanism */ switch (cert->key_type) { case CKK_RSA: mechanism.mechanism = CKM_RSA_PKCS; - break; - case CKK_ECDSA: - mechanism.mechanism = CKM_ECDSA; #ifdef USE_HASH_SHA1 + h_prefix = "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14"; h_offset = 15; #else + h_prefix = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20"; h_offset = 19; #endif break; + case CKK_GOSTR3410: { + mechanism.mechanism = CKM_GOSTR3410; + *signature_length = 64; + int nid = get_pubkey_algo(get_X509_certificate(cert)); + if (nid < 0) { + set_error("unable to get the public key algorithm"); + return -1; + } + switch (nid) { + case NID_id_GostR3410_2001: + md_name = SN_id_GostR3411_94; + break; + case NID_id_GostR3410_2012_256: + md_name = SN_id_GostR3411_2012_256; + break; + default: + set_error("unexpected public key algorithm: 0x%08X", nid); + return -1; + } + } + break; + case CKK_GOSTR3410_512: + mechanism.mechanism = CKM_GOSTR3410_512; + *signature_length = 128; + md_name = SN_id_GostR3411_2012_512; + break; + case CKK_ECDSA: + mechanism.mechanism = CKM_ECDSA; + break; default: set_error("unsupported private key type 0x%08X", cert->key_type); return -1; } - /* compute hash-value */ + + if (!md_name) { #ifdef USE_HASH_SHA1 - DBG("hashing with SHA1"); - SHA1(data, length, &hash[15]); - DBG5("hash[%ld] = [...:%02x:%02x:%02x:...:%02x]", sizeof(hash), - hash[15], hash[16], hash[17], hash[sizeof(hash) - 1]); + DBG("hashing with SHA1"); + md = EVP_sha1(); #else - SHA256(data, length, &hash[19]); - DBG5("hash[%ld] = [...:%02x:%02x:%02x:...:%02x]", sizeof(hash), - hash[19], hash[20], hash[21], hash[sizeof(hash) - 1]); + DBG("hashing with SHA256"); + md = EVP_sha256(); #endif - /* sign the token */ + } else { + md = EVP_get_digestbyname(md_name); + if (!md) { + set_error("unsupported digest %s", md_name); + return -1; + } + DBG1("hashing with %s", md_name); + } + + /* compute hash-value */ + md_ctx = EVP_MD_CTX_new(); + if (!md_ctx) { + set_error("unable to create a digest context"); + goto error; + } + if (1 != EVP_DigestInit (md_ctx, md)) { + set_error("unable to initialize the digest context"); + goto error; + } + if (1 != EVP_DigestUpdate (md_ctx, data, length)) { + set_error("unable to update the digest context"); + goto error; + } + + unsigned int md_size = EVP_MD_size(md); + hash = malloc(h_offset + md_size); + + if (!hash) { + set_error("unable to allocate the digest buffer"); + goto error; + } + if (1 != EVP_DigestFinal_ex(md_ctx, hash + h_offset, &md_size)) { + set_error("unable to calculate the digest"); + goto error; + } + + if (h_prefix) + memcpy(hash, h_prefix, h_offset); + + DBG5("hash[%u] = [%02x:%02x:%02x:...:%02x]", md_size, + hash[h_offset], hash[h_offset+1], hash[h_offset+2], + hash[h_offset + md_size - 1]); + + /* sign the hash */ + DBG2("C_SignInit: mech: %lx, keytype: %lx", mechanism.mechanism, cert->key_type); rv = h->fl->C_SignInit(h->session, &mechanism, cert->private_key); if (rv != CKR_OK) { set_error("C_SignInit() failed: %i", rv); - return -1; + goto error; } + *signature = NULL; *signature_length = 1024; while (*signature == NULL) { CK_ULONG current_signature_length = *signature_length; *signature = malloc(*signature_length); if (*signature == NULL) { - set_error("not enough free memory available"); - return -1; + set_error("not enough free memory available"); + goto error; } - rv = h->fl->C_Sign(h->session, hash + h_offset, sizeof(hash) - h_offset, *signature, signature_length); + rv = h->fl->C_Sign(h->session, hash, h_offset + md_size, *signature, signature_length); if (rv == CKR_BUFFER_TOO_SMALL) { - /* increase signature length as long as it it to short */ + /* FIXME: Is *signature_length already increased by C_Sign()? */ free(*signature); *signature = NULL; if (current_signature_length >= *signature_length) { @@ -1841,11 +1933,18 @@ int sign_value(pkcs11_handle_t *h, cert_object_t *cert, CK_BYTE *data, free(*signature); *signature = NULL; set_error("C_Sign() failed: %i", rv); - return -1; + goto error; } } DBG5("signature[%ld] = [%02x:%02x:%02x:...:%02x]", *signature_length, (*signature)[0], (*signature)[1], (*signature)[2], (*signature)[*signature_length - 1]); - return 0; + + rv = 0; + + error: + EVP_MD_CTX_free(md_ctx); + free(hash); + + return rv; } #endif /* HAVE_NSS */ diff --git a/src/common/rsaref/pkcs11t.h b/src/common/rsaref/pkcs11t.h index a80482bf..0fe1798b 100644 --- a/src/common/rsaref/pkcs11t.h +++ b/src/common/rsaref/pkcs11t.h @@ -383,6 +383,15 @@ typedef CK_ULONG CK_KEY_TYPE; #define CKK_CDMF 0x0000001E #define CKK_AES 0x0000001F +#define CK_VENDOR_PKCS11_RU_TEAM_TC26 0xD4321000 /* 0x80000000 | 0x54321000 */ + +/* Elvis, BaseALT */ +#define CKK_GOSTR3410 0x00000030 +#define CKK_GOSTR3411 0x00000031 +#define CKK_GOST28147 0x00000032 +#define CKK_GOSTR3410_256 CKK_GOSTR3410 +#define CKK_GOSTR3410_512 (CK_VENDOR_PKCS11_RU_TEAM_TC26 | 0x003) + #define CKK_VENDOR_DEFINED 0x80000000 @@ -774,6 +783,22 @@ typedef CK_ULONG CK_MECHANISM_TYPE; #define CKM_DH_PKCS_PARAMETER_GEN 0x00002001 #define CKM_X9_42_DH_PARAMETER_GEN 0x00002002 +/* Elvis */ +#define CKM_GOSTR3410_KEY_PAIR_GEN 0x00001200 +#define CKM_GOSTR3410 0x00001201 +#define CKM_GOSTR3410_WITH_GOSTR3411 0x00001202 +#define CKM_GOSTR3410_KEY_WRAP 0x00001203 +#define CKM_GOSTR3410_DERIVE 0x00001204 +#define CKM_GOSTR3411 0x00001210 +#define CKM_GOSTR3411_HMAC 0x00001211 +#define CKM_GOST28147_KEY_GEN 0x00001220 +#define CKM_GOST28147_ECB 0x00001221 +#define CKM_GOST28147 0x00001222 +#define CKM_GOST28147_MAC 0x00001223 +#define CKM_GOST28147_KEY_WRAP 0x00001224 +#define CKM_GOSTR3410_512 (CK_VENDOR_PKCS11_RU_TEAM_TC26 | 0x006) + + #define CKM_VENDOR_DEFINED 0x80000000 typedef CK_MECHANISM_TYPE CK_PTR CK_MECHANISM_TYPE_PTR; diff --git a/src/pam_pkcs11/pam_config.c b/src/pam_pkcs11/pam_config.c index 96e2a3f6..e462a74b 100644 --- a/src/pam_pkcs11/pam_config.c +++ b/src/pam_pkcs11/pam_config.c @@ -90,6 +90,7 @@ static void display_config (void) { DBG1("signature_policy %d",configuration.policy.signature_policy); DBG1("ocsp_policy %d",configuration.policy.ocsp_policy); DBG1("err_display_time %d", configuration.err_display_time); + DBG1("eku_sc_logon_policy %d",configuration.policy.eku_sc_logon_policy); } #endif @@ -186,6 +187,7 @@ static void parse_config_file(void) { configuration.policy.ocsp_policy=OCSP_NONE; configuration.policy.ca_policy=0; configuration.policy.signature_policy=0; + configuration.policy.eku_sc_logon_policy=0; break; } else if ( !strcmp(policy_list->data,"crl_auto") ) { configuration.policy.crl_policy=CRLP_AUTO; @@ -199,6 +201,8 @@ static void parse_config_file(void) { configuration.policy.ca_policy=1; } else if ( !strcmp(policy_list->data,"signature") ) { configuration.policy.signature_policy=1; + } else if ( !strcmp(policy_list->data,"eku_sclogon") ) { + configuration.policy.eku_sc_logon_policy=1; } else { DBG1("Invalid CRL policy: %s",policy_list->data); } @@ -328,6 +332,7 @@ struct configuration_st *pk_configure( int argc, const char **argv ) { configuration.policy.ca_policy=0; configuration.policy.signature_policy=0; configuration.policy.ocsp_policy=OCSP_NONE; + configuration.policy.eku_sc_logon_policy=0; } if (strstr(argv[i],"crl_online")) { configuration.policy.crl_policy=CRLP_ONLINE; @@ -347,6 +352,9 @@ struct configuration_st *pk_configure( int argc, const char **argv ) { if (strstr(argv[i],"signature")) { configuration.policy.signature_policy=1; } + if (strstr(argv[i],"eku_sclogon")) { + configuration.policy.eku_sc_logon_policy=1; + } continue; } diff --git a/src/pam_pkcs11/pam_pkcs11.c b/src/pam_pkcs11/pam_pkcs11.c index e4bc49ad..88e60404 100644 --- a/src/pam_pkcs11/pam_pkcs11.c +++ b/src/pam_pkcs11/pam_pkcs11.c @@ -533,7 +533,12 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, cons if (!configuration->quiet) { pam_prompt(pamh, PAM_TEXT_INFO, NULL, _("verifying certificate")); } - + if (configuration->policy.eku_sc_logon_policy) { + if (!verify_eku_sc_logon(x509)) { + DBG("Certificate does not contain EKU Smart Card Logon"); + continue; /* try next certificate */ + } + } /* verify certificate (date, signature, CRL, ...) */ rv = verify_certificate(x509,&configuration->policy); if (rv < 0) {