From e1af3090e6c247d6a314fdb2d68882a37eca71dd Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Tue, 5 Oct 2010 21:30:30 -0400 Subject: [PATCH 047/150] - thread password prompts through the places they need to go - broken attempts at using generic encoding types --- src/plugins/preauth/pkinit/pkinit_crypto_nss.c | 296 ++++++++++++++++-------- 1 files changed, 197 insertions(+), 99 deletions(-) diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_nss.c b/src/plugins/preauth/pkinit/pkinit_crypto_nss.c index baa8cd7..f267432 100644 --- a/src/plugins/preauth/pkinit/pkinit_crypto_nss.c +++ b/src/plugins/preauth/pkinit/pkinit_crypto_nss.c @@ -39,6 +39,7 @@ #include "pkinit_crypto.h" #include "krb5.h" +#include #include #include #include @@ -54,32 +55,16 @@ * trust settings. */ #define CONFIGDIR "/home/nalin/projects/krb5/pkinit/src/plugins/preauth/pkinit" /* FIXME */ -#define NULLCX NULL /* FIXME */ #define DEBUG_DER "derdump" -#ifndef FIXME -SECStatus NSS_CMSContentInfo_SetDontStream(NSSCMSContentInfo *i, PRBool b); -SECStatus NSS_CMSType_RegisterContentType(SECOidTag tag, - const SEC_ASN1Template *, - size_t size, - void *, - void *, - void *, - void *, - void *, - void *, - void *, - void *, - PRBool data); -#endif - /* Forward declarations. */ static krb5_error_code cert_retrieve_cert_sans(krb5_context context, CERTCertificate *cert, krb5_principal **pkinit_sans, krb5_principal **upn_sans, unsigned char ***kdc_hostname); -static int cert_load_default_identity(pkinit_identity_crypto_context id); +static int cert_load_default_identity(krb5_context context, + pkinit_identity_crypto_context id); /* Plugin and request state. */ struct _pkinit_plg_crypto_context { @@ -95,13 +80,17 @@ struct _pkinit_req_crypto_context { struct _pkinit_identity_crypto_context { PLArenaPool *pool; + PRBool loaded; PK11SlotList *slots; PK11SlotInfo *slot; CERTCertList *id_certs, *ca_certs, *other_certs; SECKEYPrivateKeyList *id_keys; CERTCertificate *id_cert; - krb5_prompter_fct prompter; - void *prompter_data; + struct { + krb5_context context; + krb5_prompter_fct prompter; + void *prompter_data; + } pwcb_args; }; struct _pkinit_cert_info { /* aka _pkinit_cert_handle */ @@ -413,7 +402,7 @@ static SECItem pkinit_nt_upn = { }; static unsigned char oid_pkinit_auth_data_bytes[] = {0x2b, 0x06, 0x01, 0x05, 0x02, 0x03, 0x01}; -static const SECOidData +static SECOidData oid_pkinit_auth_data = { .oid = { .data = oid_pkinit_auth_data_bytes, @@ -424,7 +413,7 @@ oid_pkinit_auth_data = { .mechanism = CKM_INVALID_MECHANISM, .supportedExtension = UNSUPPORTED_CERT_EXTENSION, }; -static const SECOidTag +static SECOidTag get_pkinit_data_auth_data_tag(void) { static SECOidTag tag = SEC_OID_UNKNOWN; @@ -433,9 +422,32 @@ get_pkinit_data_auth_data_tag(void) } return tag; } + +struct wrapped_data { + NSSCMSGenericWrapperData parent; + SECItem *wrapped_data; +}; + +static SEC_ASN1Template +wrapped_data_template[] = { + { + .kind = SEC_ASN1_SEQUENCE, + .offset = 0, + .sub = NULL, + .size = sizeof(struct wrapped_data), + }, + { + .kind = SEC_ASN1_ANY | SEC_ASN1_POINTER, + .offset = offsetof(struct wrapped_data, wrapped_data), + .sub = &SEC_AnyTemplate, + .size = sizeof(SECItem *), + }, + {0, 0, NULL, 0}, +}; + static unsigned char oid_pkinit_rkey_data_bytes[] = {0x2b, 0x06, 0x01, 0x05, 0x02, 0x03, 0x03}; -static const SECOidData +static SECOidData oid_pkinit_rkey_data = { .oid = { .data = oid_pkinit_rkey_data_bytes, @@ -446,7 +458,7 @@ oid_pkinit_rkey_data = { .mechanism = CKM_INVALID_MECHANISM, .supportedExtension = UNSUPPORTED_CERT_EXTENSION, }; -static const SECOidTag +static SECOidTag get_pkinit_data_rkey_data_tag(void) { static SECOidTag tag = SEC_OID_UNKNOWN; @@ -457,7 +469,7 @@ get_pkinit_data_rkey_data_tag(void) } static unsigned char oid_pkinit_dhkey_data_bytes[] = {0x2b, 0x06, 0x01, 0x05, 0x02, 0x03, 0x02}; -static const SECOidData +static SECOidData oid_pkinit_dhkey_data = { .oid = { .data = oid_pkinit_dhkey_data_bytes, @@ -469,7 +481,7 @@ oid_pkinit_dhkey_data = { .supportedExtension = UNSUPPORTED_CERT_EXTENSION, }; -static const SECOidTag +static SECOidTag get_pkinit_data_dhkey_data_tag(void) { static SECOidTag tag = SEC_OID_UNKNOWN; @@ -491,6 +503,77 @@ get_oid_from_tag(SECOidTag tag) } } +/* A password-prompt callback for NSS that calls the libkrb5 callback. */ +static char * +crypto_pwcb(PK11SlotInfo *slot, PRBool retry, void *arg) +{ + int ret; + pkinit_identity_crypto_context id; + krb5_prompt prompt; + krb5_prompt_type prompt_types[2]; + krb5_data reply; + char *name, *text, *answer; + void *data; + + /* We only want to be called once. */ + if (retry) { + return NULL; + } + /* We need our callback arguments. */ + if (arg == NULL) { + return NULL; + } + id = arg; + if (id->pwcb_args.prompter == NULL) { + return NULL; + } + + /* Set up the prompt. */ + name = PK11_GetTokenName(slot); + text = PORT_ArenaZAlloc(id->pool, strlen(name) + 100); + snprintf(text, strlen(name) + 100, "Password for %s", + PK11_GetTokenName(slot)); + memset(&prompt, 0, sizeof(prompt)); + prompt.prompt = text; + prompt.hidden = 1; + prompt.reply = &reply; + reply.length = 256; + data = malloc(reply.length); + reply.data = data; + name = NULL; + answer = NULL; + + /* Call the prompter callback. */ + prompt_types[0] = KRB5_PROMPT_TYPE_PREAUTH; + prompt_types[1] = 0; + (*k5int_set_prompt_types)(id->pwcb_args.context, prompt_types); + ret = (*id->pwcb_args.prompter)(id->pwcb_args.context, + id->pwcb_args.prompter_data, + name, answer, 1, &prompt); + answer = NULL; + if ((ret == 0) && (reply.data != NULL)) { + answer = PR_Malloc(reply.length + 1); + memcpy(answer, reply.data, reply.length); + answer[reply.length] = '\0'; + answer[strcspn(answer, "\r\n")] = '\0'; + } + + if (reply.data == data) { + free(reply.data); + } + + return answer; +} + +static void * +crypto_pwcb_prep(pkinit_identity_crypto_context id_cryptoctx, + krb5_context context) +{ + PK11_SetPasswordFunc(crypto_pwcb); + id_cryptoctx->pwcb_args.context = context; + return id_cryptoctx; +} + krb5_error_code pkinit_init_identity_crypto(pkinit_identity_crypto_context *id_cryptoctx) { @@ -508,7 +591,6 @@ pkinit_init_identity_crypto(pkinit_identity_crypto_context *id_cryptoctx) ((*id_cryptoctx)->id_keys != NULL) && ((*id_cryptoctx)->ca_certs != NULL) && ((*id_cryptoctx)->other_certs != NULL)) { - cert_load_default_identity(*id_cryptoctx); return 0; } if ((*id_cryptoctx)->other_certs != NULL) { @@ -539,10 +621,37 @@ pkinit_fini_identity_crypto(pkinit_identity_crypto_context id_cryptoctx) PORT_FreeArena(id_cryptoctx->pool, PR_TRUE); } +static SECStatus +return_success(NSSCMSGenericWrapperData *data) +{ + return SECSuccess; +} + +static SECStatus +crypto_register_any(SECOidTag tag) +{ + static NSSCMSGenericWrapperDataCallback success = return_success; + if (NSS_CMSType_RegisterContentType(tag, + wrapped_data_template, + sizeof(struct wrapped_data), + NULL, + &success, + &success, + &success, + &success, + &success, + &success, + PR_FALSE) != SECSuccess) { + return ENOMEM; + } + return 0; +} + krb5_error_code pkinit_init_plg_crypto(pkinit_plg_crypto_context *plg_cryptoctx) { PLArenaPool *pool; + SECOidTag tag; pool = PORT_NewArena(sizeof(double)); if (pool != NULL) { *plg_cryptoctx = PORT_ArenaZAlloc(pool, @@ -556,48 +665,18 @@ pkinit_init_plg_crypto(pkinit_plg_crypto_context *plg_cryptoctx) NULL, 0); if ((*plg_cryptoctx)->ncontext != NULL) { - if (NSS_CMSType_RegisterContentType(get_pkinit_data_auth_data_tag(), - SEC_AnyTemplate, - sizeof(SECItem), - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - PR_FALSE) != SECSuccess) { + tag = get_pkinit_data_auth_data_tag(); + if (crypto_register_any(tag) != SECSuccess) { PORT_FreeArena(pool, PR_TRUE); return ENOMEM; } - if (NSS_CMSType_RegisterContentType(get_pkinit_data_rkey_data_tag(), - SEC_AnyTemplate, - sizeof(SECItem), - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - PR_FALSE) != SECSuccess) { + tag = get_pkinit_data_rkey_data_tag(); + if (crypto_register_any(tag) != SECSuccess) { PORT_FreeArena(pool, PR_TRUE); return ENOMEM; } - if (NSS_CMSType_RegisterContentType(get_pkinit_data_dhkey_data_tag(), - SEC_AnyTemplate, - sizeof(SECItem), - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - PR_FALSE) != SECSuccess) { + tag = get_pkinit_data_dhkey_data_tag(); + if (crypto_register_any(tag) != SECSuccess) { PORT_FreeArena(pool, PR_TRUE); return ENOMEM; } @@ -1071,7 +1150,8 @@ client_create_dh(krb5_context context, dh_param.base = params->g; /* Generate a public value and a private key. */ - slot = PK11_GetBestSlot(CKM_DH_PKCS_KEY_PAIR_GEN, NULLCX); + slot = PK11_GetBestSlot(CKM_DH_PKCS_KEY_PAIR_GEN, + crypto_pwcb_prep(id_cryptoctx, context)); if (slot == NULL) { PORT_FreeArena(pool, PR_TRUE); pkiDebug("%s: error selecting slot\n", __FUNCTION__); @@ -1080,7 +1160,7 @@ client_create_dh(krb5_context context, pub = NULL; priv = PK11_GenerateKeyPair(slot, CKM_DH_PKCS_KEY_PAIR_GEN, &dh_param, &pub, PR_FALSE, PR_FALSE, - NULLCX); + crypto_pwcb_prep(id_cryptoctx, context)); /* Finish building the return values. */ memset(&encoded, 0, sizeof(encoded)); @@ -1159,7 +1239,8 @@ client_process_dh(krb5_context context, /* Generate the shared value using our private key and the KDC's * public key. */ - slot = PK11_GetBestSlot(CKM_DH_PKCS_KEY_PAIR_GEN, NULLCX); + slot = PK11_GetBestSlot(CKM_DH_PKCS_KEY_PAIR_GEN, + crypto_pwcb_prep(id_cryptoctx, context)); if (slot == NULL) { SECKEY_DestroyPublicKey(pub); PORT_FreeArena(pool, PR_TRUE); @@ -1171,7 +1252,7 @@ client_process_dh(krb5_context context, CKM_TLS_MASTER_KEY_DERIVE_DH, CKA_DERIVE, 0, - NULLCX); + crypto_pwcb_prep(id_cryptoctx, context)); if (sym == NULL) { SECKEY_DestroyPublicKey(pub); PK11_FreeSlot(slot); @@ -1320,7 +1401,8 @@ server_process_dh(krb5_context context, dh_params.base = params.g; /* Generate a public value and a private key. */ - slot = PK11_GetBestSlot(CKM_DH_PKCS_KEY_PAIR_GEN, NULLCX); + slot = PK11_GetBestSlot(CKM_DH_PKCS_KEY_PAIR_GEN, + crypto_pwcb_prep(id_cryptoctx, context)); if (slot == NULL) { SECKEY_DestroySubjectPublicKeyInfo(spki); PORT_FreeArena(pool, PR_TRUE); @@ -1329,7 +1411,7 @@ server_process_dh(krb5_context context, pub = NULL; priv = PK11_GenerateKeyPair(slot, CKM_DH_PKCS_KEY_PAIR_GEN, &dh_params, &pub, PR_FALSE, PR_FALSE, - NULLCX); + crypto_pwcb_prep(id_cryptoctx, context)); if (priv == NULL) { PK11_FreeSlot(slot); SECKEY_DestroySubjectPublicKeyInfo(spki); @@ -1360,7 +1442,7 @@ server_process_dh(krb5_context context, CKM_TLS_MASTER_KEY_DERIVE_DH, CKA_DERIVE, 0, - NULLCX); + crypto_pwcb_prep(id_cryptoctx, context)); if (sym == NULL) { SECKEY_DestroyPrivateKey(priv); SECKEY_DestroyPublicKey(pub); @@ -1641,6 +1723,10 @@ crypto_cert_get_count(krb5_context context, int *cert_count) { CERTCertListNode *node; + if (!id_cryptoctx->loaded) { + cert_load_default_identity(context, id_cryptoctx); + id_cryptoctx->loaded = PR_TRUE; + } *cert_count = 0; if (!CERT_LIST_EMPTY(id_cryptoctx->id_certs)) { for (node = CERT_LIST_HEAD(id_cryptoctx->id_certs); @@ -1729,7 +1815,8 @@ crypto_cert_iteration_next(krb5_context context, } static int -cert_load_certs_with_keys_from_slot(pkinit_identity_crypto_context id, +cert_load_certs_with_keys_from_slot(krb5_context context, + pkinit_identity_crypto_context id, PK11SlotInfo *slot) { CERTCertificate *cert; @@ -1754,6 +1841,7 @@ cert_load_certs_with_keys_from_slot(pkinit_identity_crypto_context id, /* Walk the list of certs, and for each one for which we can * find the matching private key, add it and the keys to the * lists. */ + status = SECSuccess; for (cnode = CERT_LIST_HEAD(clist); (cnode != NULL) && (cnode->cert != NULL) && @@ -1763,7 +1851,8 @@ cert_load_certs_with_keys_from_slot(pkinit_identity_crypto_context id, __FUNCTION__, cnode->cert->nickname ? cnode->cert->nickname : "(no name)"); - key = PK11_FindPrivateKeyFromCert(slot, cnode->cert, NULLCX); + key = PK11_FindPrivateKeyFromCert(slot, cnode->cert, + crypto_pwcb_prep(id, context)); if (key == NULL) { pkiDebug("%s: no key for \"%s\"\n", __FUNCTION__, @@ -1793,7 +1882,8 @@ cert_load_certs_with_keys_from_slot(pkinit_identity_crypto_context id, /* Load all of the certificates in all of the tokens for which we also have * matching secret keys. */ static int -cert_load_default_identity(pkinit_identity_crypto_context id) +cert_load_default_identity(krb5_context context, + pkinit_identity_crypto_context id) { PK11SlotList *slist; PK11SlotListElement *sle; @@ -1801,7 +1891,8 @@ cert_load_default_identity(pkinit_identity_crypto_context id) /* Get the list of tokens. All of them. */ slist = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, - PR_FALSE, NULLCX); + PR_FALSE, + crypto_pwcb_prep(id, context)); if (slist == NULL) { return ENOENT; } @@ -1810,7 +1901,8 @@ cert_load_default_identity(pkinit_identity_crypto_context id) status = 0; for (sle = slist->head; sle != NULL; sle = sle->next) { /* Skip over slots we don't want to use. */ - if (!PK11_IsLoggedIn(sle->slot, NULLCX) && + if (!PK11_IsLoggedIn(sle->slot, + crypto_pwcb_prep(id, context)) && PK11_NeedLogin(sle->slot)) { pkiDebug("%s: skipping token \"%s\"\n", __FUNCTION__, PK11_GetTokenName(sle->slot)); @@ -1818,7 +1910,8 @@ cert_load_default_identity(pkinit_identity_crypto_context id) } /* Get the list of certs, and skip the slot if it doesn't have * any. */ - status = cert_load_certs_with_keys_from_slot(id, sle->slot); + status = cert_load_certs_with_keys_from_slot(context, id, + sle->slot); if (status != 0) { PK11_FreeSlotList(slist); return status; @@ -1827,21 +1920,24 @@ cert_load_default_identity(pkinit_identity_crypto_context id) /* Now try logging in to the slots that still require it. */ for (sle = slist->head; sle != NULL; sle = sle->next) { /* Skip over slots we've already used. */ - if (PK11_IsLoggedIn(sle->slot, NULLCX) || + if (PK11_IsLoggedIn(sle->slot, + crypto_pwcb_prep(id, context)) || !PK11_NeedLogin(sle->slot)) { pkiDebug("%s: skipping token \"%s\"\n", __FUNCTION__, PK11_GetTokenName(sle->slot)); continue; } if (PK11_Authenticate(sle->slot, PR_TRUE, - NULLCX) != SECSuccess) { + crypto_pwcb_prep(id, + context)) != SECSuccess) { pkiDebug("%s: error logging into \"%s\", skipping\n", __FUNCTION__, PK11_GetTokenName(sle->slot)); continue; } /* Get the list of certs, and skip the slot if it doesn't have * any. */ - status = cert_load_certs_with_keys_from_slot(id, sle->slot); + status = cert_load_certs_with_keys_from_slot(context, id, + sle->slot); if (status != 0) { PK11_FreeSlotList(slist); return status; @@ -2139,7 +2235,7 @@ pkinit_create_td_dh_parameters(krb5_context context, ids[j] = NULL; /* Pass it back up. */ data = NULL; - code = k5int_encode_krb5_td_dh_parameters(ids, &data); + code = (*k5int_encode_krb5_td_dh_parameters)(ids, &data); if (code != 0) { return code; } @@ -2149,7 +2245,7 @@ pkinit_create_td_dh_parameters(krb5_context context, typed_datum.data = (unsigned char *) data->data; typed_data[0] = &typed_datum; typed_data[1] = NULL; - code = k5int_encode_krb5_typed_data(typed_data, edata); + code = (*k5int_encode_krb5_typed_data)(typed_data, edata); krb5_free_data(context, data); return code; } @@ -2228,7 +2324,7 @@ pkinit_create_td_invalid_certificate(krb5_context context, /* Pass it back up. */ data = NULL; - code = k5int_encode_krb5_td_trusted_certifiers(ids, &data); + code = (*k5int_encode_krb5_td_trusted_certifiers)(ids, &data); if (code != 0) { return code; } @@ -2238,7 +2334,7 @@ pkinit_create_td_invalid_certificate(krb5_context context, typed_datum.data = (unsigned char *) data->data; typed_data[0] = &typed_datum; typed_data[1] = NULL; - code = k5int_encode_krb5_typed_data(typed_data, edata); + code = (*k5int_encode_krb5_typed_data)(typed_data, edata); krb5_free_data(context, data); return code; } @@ -2275,7 +2371,8 @@ pkinit_create_td_trusted_certifiers(krb5_context context, /* Get the list of tokens. All of them. */ slist = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, - PR_FALSE, NULLCX); + PR_FALSE, + crypto_pwcb_prep(id_cryptoctx, context)); if (slist == NULL) { return ENOENT; } @@ -2285,7 +2382,8 @@ pkinit_create_td_trusted_certifiers(krb5_context context, status = SECSuccess; for (sle = slist->head; sle != NULL; sle = sle->next) { /* Skip over slots we would need to log in to use. */ - if (!PK11_IsLoggedIn(sle->slot, NULLCX) && + if (!PK11_IsLoggedIn(sle->slot, + crypto_pwcb_prep(id_cryptoctx, context)) && PK11_NeedLogin(sle->slot)) { pkiDebug("%s: skipping token \"%s\"\n", __FUNCTION__, PK11_GetTokenName(sle->slot)); @@ -2379,7 +2477,7 @@ pkinit_create_td_trusted_certifiers(krb5_context context, /* Pass the list back up. */ data = NULL; - code = k5int_encode_krb5_td_trusted_certifiers(ids, &data); + code = (*k5int_encode_krb5_td_trusted_certifiers)(ids, &data); CERT_DestroyCertList(clist); if (code != 0) { return code; @@ -2390,7 +2488,7 @@ pkinit_create_td_trusted_certifiers(krb5_context context, typed_datum.data = (unsigned char *) data->data; typed_data[0] = &typed_datum; typed_data[1] = NULL; - code = k5int_encode_krb5_typed_data(typed_data, edata); + code = (*k5int_encode_krb5_typed_data)(typed_data, edata); krb5_free_data(context, data); return code; } @@ -2462,8 +2560,8 @@ pkinit_identity_set_prompter(pkinit_identity_crypto_context id_cryptoctx, krb5_prompter_fct prompter, void *prompter_data) { - id_cryptoctx->prompter = prompter; - id_cryptoctx->prompter_data = prompter_data; + id_cryptoctx->pwcb_args.prompter = prompter; + id_cryptoctx->pwcb_args.prompter_data = prompter_data; return 0; } @@ -2892,7 +2990,7 @@ cms_contentinfo_create(krb5_context context, } memset(&encoded, 0, sizeof(encoded)); - if (NSS_CMSDEREncode(msg, NULL, &encoded, pool) != SECSuccess) { + if (NSS_CMSDEREncode(msg, &plain, &encoded, pool) != SECSuccess) { PORT_FreeArena(pool, PR_TRUE); pkiDebug("%s: error %d encoding data\n", __FUNCTION__, encapsulated_tag); @@ -3126,6 +3224,7 @@ cms_signeddata_create(krb5_context context, NSSCMSSignerInfo *signer; PLArenaPool *pool; SECItem plain, encoded; + struct wrapped_data wrapped_plain; SECOidTag digest, encapsulated_tag; PRBool add_signed_attributes; @@ -3184,11 +3283,6 @@ cms_signeddata_create(krb5_context context, PORT_FreeArena(pool, PR_TRUE); return ENOMEM; } - if (NSS_CMSContentInfo_SetDontStream(info, PR_TRUE) != SECSuccess) { - pkiDebug("%s: error turning off streaming\n", __FUNCTION__); - PORT_FreeArena(pool, PR_TRUE); - return ENOMEM; - } if (NSS_CMSContentInfo_SetContent_SignedData(msg, info, sdata) != SECSuccess) { PORT_FreeArena(pool, PR_TRUE); @@ -3225,16 +3319,20 @@ cms_signeddata_create(krb5_context context, } } - /* Set the raw data as the contents for the signed-data pointer. */ + memset(&plain, 0, sizeof(plain)); + plain.data = payload; + plain.len = payload_len; + + /* Set the data as the contents of the signed-data. */ info = NSS_CMSSignedData_GetContentInfo(sdata); if (info == NULL) { PORT_FreeArena(pool, PR_TRUE); return ENOMEM; } - plain.data = payload; - plain.len = payload_len; + memset(&wrapped_plain, 0, sizeof(wrapped_plain)); + wrapped_plain.wrapped_data = &plain; if (NSS_CMSContentInfo_SetContent(msg, info, encapsulated_tag, - &plain) != SECSuccess) { + &wrapped_plain) != SECSuccess) { pkiDebug("%s: error setting encapsulated content type\n", __FUNCTION__); PORT_FreeArena(pool, PR_TRUE); -- 1.7.6.4