src/common/cert_vfy.c | 44 +++++++++++++++++++++++++++++++++++++++++--- src/common/cert_vfy.h | 3 +++ src/common/pkcs11_lib.c | 33 ++++++++++++++++++++++++++------- src/common/rsaref/pkcs11t.h | 20 ++++++++++++++++++++ src/pam_pkcs11/pam_config.c | 8 ++++++++ src/pam_pkcs11/pam_pkcs11.c | 7 ++++++- 6 files changed, 104 insertions(+), 11 deletions(-) diff --git a/src/common/cert_vfy.c b/src/common/cert_vfy.c index 7efb0cb..41d3b64 100644 --- a/src/common/cert_vfy.c +++ b/src/common/cert_vfy.c @@ -481,16 +481,28 @@ int verify_signature(X509 * x509, unsigned char *data, int data_length, int rv; EVP_PKEY *pubkey; EVP_MD_CTX *md_ctx = NULL; - + const EVP_MD* md; + int nid; /* get the public-key */ pubkey = X509_get_pubkey(x509); if (pubkey == NULL) { set_error("X509_get_pubkey() failed: %s", ERR_error_string(ERR_get_error(), NULL)); return -1; } - md_ctx = EVP_MD_CTX_new(); + + nid = OBJ_obj2nid(x509->cert_info->key->algor->algorithm); + if( NID_id_GostR3410_2001 == nid ) + md = EVP_get_digestbyname("md_gost94"); + else + md = EVP_sha1(); + if (!md) { + set_error("unsupported key algorithm, nid: %d", nid); + return -1; + } + + md_ctx = EVP_MD_CTX_create(); /* verify the signature */ - EVP_VerifyInit(md_ctx, EVP_sha1()); + 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); @@ -502,4 +514,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) == extobj->length + && 0 == memcmp(extobj->data, 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 657b212..335bc44 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, int 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 41b55d1..fd79837 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. @@ -1671,6 +1671,7 @@ getlist_error: int get_private_key(pkcs11_handle_t *h, cert_object_t *cert) { CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY; CK_BBOOL key_sign = CK_TRUE; + CK_ATTRIBUTE attr; CK_ATTRIBUTE key_template[] = { {CKA_CLASS, &key_class, sizeof(key_class)} , @@ -1718,7 +1719,16 @@ int get_private_key(pkcs11_handle_t *h, cert_object_t *cert) { } cert->private_key = object; - cert->key_type = CKK_RSA; + attr.type = CKA_KEY_TYPE; + attr.ulValueLen = sizeof(cert->key_type); + attr.pValue = &(cert->key_type); + rv = h->fl->C_GetAttributeValue(h->session, object, &attr,1); + if (rv != CKR_OK) { + set_error("C_GetAttributeValue() failed: 0x%08lX", rv); + return -1; + } + + DBG1("C_GetAttributeValue keytype: %x",cert->key_type); return 0; @@ -1759,29 +1769,38 @@ int sign_value(pkcs11_handle_t *h, cert_object_t *cert, CK_BYTE *data, case CKK_RSA: mechanism.mechanism = CKM_RSA_PKCS; break; + case CKK_GOSTR3410: + mechanism.mechanism = CKM_GOSTR3410_WITH_GOSTR3411; + break; default: set_error("unsupported key type %d", cert->type); return -1; } /* compute hash-value */ - SHA1(data, length, &hash[15]); - DBG5("hash[%ld] = [...:%02x:%02x:%02x:...:%02x]", sizeof(hash), - hash[15], hash[16], hash[17], hash[sizeof(hash) - 1]); + if( CKK_RSA == cert->key_type ) { + SHA1(data, length, &hash[15]); + DBG5("hash[%ld] = [...:%02x:%02x:%02x:...:%02x]", sizeof(hash), + hash[15], hash[16], hash[17], hash[sizeof(hash) - 1]); + } /* sign the token */ + DBG2("C_SignInit: mech: %x, keytype: %x", 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: 0x%08lX", rv); return -1; } *signature = NULL; - *signature_length = 128; + *signature_length = 256; while (*signature == NULL) { *signature = malloc(*signature_length); if (*signature == NULL) { set_error("not enough free memory available"); return -1; } - rv = h->fl->C_Sign(h->session, hash, sizeof(hash), *signature, signature_length); + if( CKK_RSA == cert->key_type ) + rv = h->fl->C_Sign(h->session, hash, sizeof(hash), *signature, signature_length); + else + rv = h->fl->C_Sign(h->session, data, length, *signature, signature_length); if (rv == CKR_BUFFER_TOO_SMALL) { /* increase signature length as long as it it to short */ free(*signature); diff --git a/src/common/rsaref/pkcs11t.h b/src/common/rsaref/pkcs11t.h index a80482b..37375ea 100644 --- a/src/common/rsaref/pkcs11t.h +++ b/src/common/rsaref/pkcs11t.h @@ -383,6 +383,11 @@ typedef CK_ULONG CK_KEY_TYPE; #define CKK_CDMF 0x0000001E #define CKK_AES 0x0000001F +/* Elvis */ +#define CKK_GOSTR3410 0x00000030 +#define CKK_GOSTR3411 0x00000031 +#define CKK_GOST28147 0x00000032 + #define CKK_VENDOR_DEFINED 0x80000000 @@ -774,6 +779,21 @@ 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_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 9d2ad40..b8169c5 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("crl_policy %d",configuration.policy.crl_policy); DBG1("signature_policy %d",configuration.policy.signature_policy); DBG1("ocsp_policy %d",configuration.policy.ocsp_policy); + DBG1("eku_sc_logon_policy %d",configuration.policy.eku_sc_logon_policy); DBG1("err_display_time %d", configuration.err_display_time); DBG1("ask_pin %d",configuration.ask_pin); } @@ -190,6 +191,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; @@ -203,6 +205,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); } @@ -336,6 +340,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; @@ -355,6 +360,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 3cadabb..3503d1f 100644 --- a/src/pam_pkcs11/pam_pkcs11.c +++ b/src/pam_pkcs11/pam_pkcs11.c @@ -532,7 +532,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) {