diff --git a/security/nss/cmd/pk12util/pk12util.c b/security/nss/cmd/pk12util/pk12util.c index 2ea30bc4664..20bdc1c8fc9 100644 --- a/security/nss/cmd/pk12util/pk12util.c +++ b/security/nss/cmd/pk12util/pk12util.c @@ -59,6 +59,9 @@ Usage(char *progName) progName); FPS "\t\t [-k slotpwfile | -K slotpw] [-w p12filepwfile | -W p12filepw]\n"); FPS "\t\t [-v]\n"); + FPS "Usage: %s -l listfile [-d certdir] [-P dbprefix] [-h tokenname]\n", + progName); + FPS "\t\t [-k slotpwfile | -K slotpw] [-w p12filepwfile | -W p12filepw]\n"); FPS "Usage: %s -o exportfile -n certname [-d certdir] [-P dbprefix]\n", progName); FPS "\t\t [-k slotpwfile | -K slotpw] [-w p12filepwfile | -W p12filepw]\n"); FPS "\t\t [-v]\n"); @@ -686,6 +689,140 @@ loser: return; } + +PRIntn +P12U_ListPKCS12File(char *in_file, PK11SlotInfo *slot, + secuPWData *slotPw, secuPWData *p12FilePw) +{ + p12uContext *p12cxt = NULL; + SEC_PKCS12DecoderContext *p12dcx = NULL; + SECItem *pwitem = NULL, uniPwitem = { 0 }; + SECItem p12file = { 0 }; + SECStatus rv = SECFailure; + PRBool swapUnicode = PR_FALSE; + const SEC_PKCS12DecoderItem *dip; + + int error; + +#ifdef IS_LITTLE_ENDIAN + swapUnicode = PR_TRUE; +#endif + + p12cxt = p12u_InitContext(PR_TRUE, in_file); + if(!p12cxt) { + SECU_PrintError(progName,"File Open failed: %s", in_file); + pk12uErrno = PK12UERR_INIT_FILE; + goto loser; + } + + /* get the password */ + pwitem = P12U_GetP12FilePassword(PR_FALSE, p12FilePw); + if (!pwitem) { + pk12uErrno = PK12UERR_USER_CANCELLED; + goto loser; + } + + if(P12U_UnicodeConversion(NULL, &uniPwitem, pwitem, PR_TRUE, + swapUnicode) != SECSuccess) { + SECU_PrintError(progName,"Unicode conversion failed"); + pk12uErrno = PK12UERR_UNICODECONV; + goto loser; + } + + /* init the decoder context */ + p12dcx = SEC_PKCS12DecoderStart(&uniPwitem, slot, slotPw, + NULL, NULL, NULL, NULL, NULL); + if(!p12dcx) { + SECU_PrintError(progName,"PKCS12 decoder start failed"); + pk12uErrno = PK12UERR_PK12DECODESTART; + goto loser; + } + + /* read the item */ + rv = SECU_FileToItem(&p12file, p12cxt->file); + PR_Close(p12cxt->file); + p12cxt->file = NULL; + + if (rv != SECSuccess) { + SECU_PrintError(progName,"Failed to read from import file"); + goto loser; + } + + rv = SEC_PKCS12DecoderUpdate(p12dcx, p12file.data, p12file.len); + if(rv != SECSuccess) { + error = PR_GetError(); + if(error == SEC_ERROR_DECRYPTION_DISALLOWED) { + PR_SetError(error, 0); + goto loser; + } + SECU_PrintError(progName,"PKCS12 decoding failed"); + pk12uErrno = PK12UERR_DECODE; + } + + /* does the blob authenticate properly? */ + if(SEC_PKCS12DecoderVerify(p12dcx) != SECSuccess) { + SECU_PrintError(progName,"PKCS12 decode not verified"); + pk12uErrno = PK12UERR_DECODEVERIFY; + rv = SECFailure; + } + else if (SEC_PKCS12DecoderIterateInit(p12dcx) != SECSuccess) { + SECU_PrintError(progName,"PKCS12 decode iterate bags failed"); + pk12uErrno = PK12UERR_DECODEIMPTBAGS; + rv = SECFailure; + } + else { + while (SEC_PKCS12DecoderIterateNext(p12dcx, &dip) == SECSuccess) { + switch (dip->type) { + case SEC_OID_PKCS12_V1_CERT_BAG_ID: + printf("Certificate"); + if (SECU_PrintSignedData(stdout, dip->der, + (dip->hasKey) ? "(has private key)" : "", + 0, SECU_PrintCertificate) != 0) { + SECU_PrintError(progName,"PKCS12 print cert bag failed"); + } + if (dip->friendlyName != NULL) { + printf(" Friendly Name: %s\n\n", + dip->friendlyName->data); + } + break; + case SEC_OID_PKCS12_V1_KEY_BAG_ID: + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: + printf("Key"); + if (dip->type == SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID) + printf("(shrouded)"); + printf(":\n"); + if (dip->friendlyName != NULL) { + printf(" Friendly Name: %s\n\n", + dip->friendlyName->data); + } + break; + default: + printf("unknown bag type(%d): %s\n\n", dip->type, + SECOID_FindOIDTagDescription(dip->type)); + break; + } + } + rv = SECSuccess; + } + +loser: + + if (p12dcx) { + SEC_PKCS12DecoderFinish(p12dcx); + } + p12u_DestroyContext(&p12cxt, PR_FALSE); + + if (uniPwitem.data) { + SECITEM_ZfreeItem(&uniPwitem, PR_FALSE); + } + + if (pwitem) { + SECITEM_ZfreeItem(pwitem, PR_TRUE); + } + + return rv; +} + static void p12u_EnableAllCiphers() { @@ -699,13 +836,18 @@ p12u_EnableAllCiphers() } static PRUintn -P12U_Init(char *dir, char *dbprefix) +P12U_Init(char *dir, char *dbprefix, PRBool listonly) { SECStatus rv; PK11_SetPasswordFunc(SECU_GetModulePassword); PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); - rv = NSS_Initialize(dir,dbprefix,dbprefix,"secmod.db",0); + if (listonly && NSS_NoDB_Init("") == SECSuccess) { + rv = SECSuccess; + } + else { + rv = NSS_Initialize(dir,dbprefix,dbprefix,"secmod.db",0); + } if (rv != SECSuccess) { SECU_PrintPRandOSError(progName); exit(-1); @@ -726,6 +868,7 @@ enum { opt_Import, opt_SlotPWFile, opt_SlotPW, + opt_List, opt_Nickname, opt_Export, opt_P12FilePWFile, @@ -741,6 +884,7 @@ static secuCommandFlag pk12util_options[] = { /* opt_Import */ 'i', PR_TRUE, 0, PR_FALSE }, { /* opt_SlotPWFile */ 'k', PR_TRUE, 0, PR_FALSE }, { /* opt_SlotPW */ 'K', PR_TRUE, 0, PR_FALSE }, + { /* opt_List */ 'l', PR_TRUE, 0, PR_FALSE }, { /* opt_Nickname */ 'n', PR_TRUE, 0, PR_FALSE }, { /* opt_Export */ 'o', PR_TRUE, 0, PR_FALSE }, { /* opt_P12FilePWFile */ 'w', PR_TRUE, 0, PR_FALSE }, @@ -778,8 +922,9 @@ main(int argc, char **argv) pk12_debugging = pk12util.options[opt_Debug].activated; - if (pk12util.options[opt_Import].activated + - pk12util.options[opt_Export].activated != 1) { + if ((pk12util.options[opt_Import].activated + + pk12util.options[opt_Export].activated + + pk12util.options[opt_List].activated) != 1) { Usage(progName); } @@ -789,7 +934,10 @@ main(int argc, char **argv) } slotname = SECU_GetOptionArg(&pk12util, opt_TokenName); - import_file = SECU_GetOptionArg(&pk12util, opt_Import); + + import_file = (pk12util.options[opt_List].activated) ? + SECU_GetOptionArg(&pk12util, opt_List) : + SECU_GetOptionArg(&pk12util, opt_Import); export_file = SECU_GetOptionArg(&pk12util, opt_Export); if (pk12util.options[opt_P12FilePWFile].activated) { @@ -818,7 +966,8 @@ main(int argc, char **argv) if (pk12util.options[opt_DBPrefix].activated) { dbprefix = pk12util.options[opt_DBPrefix].arg; } - P12U_Init(SECU_ConfigDirectory(NULL),dbprefix); + P12U_Init(SECU_ConfigDirectory(NULL), dbprefix, + pk12util.options[opt_List].activated); if (!slotname || PL_strcmp(slotname, "internal") == 0) slot = PK11_GetInternalKeySlot(); @@ -832,14 +981,16 @@ main(int argc, char **argv) } if (pk12util.options[opt_Import].activated) { - - if ((ret = P12U_ImportPKCS12Object(import_file, slot, &slotPw, - &p12FilePw)) != 0) - goto done; + P12U_ImportPKCS12Object(import_file, slot, &slotPw, + &p12FilePw); } else if (pk12util.options[opt_Export].activated) { P12U_ExportPKCS12Object(pk12util.options[opt_Nickname].arg, export_file, slot, &slotPw, &p12FilePw); + + } else if (pk12util.options[opt_List].activated) { + P12U_ListPKCS12File(import_file, slot, &slotPw, &p12FilePw); + } else { Usage(progName); pk12uErrno = PK12UERR_USAGE; diff --git a/security/nss/lib/pkcs12/p12.h b/security/nss/lib/pkcs12/p12.h index 155b171e1d3..6eb6ecfedea 100644 --- a/security/nss/lib/pkcs12/p12.h +++ b/security/nss/lib/pkcs12/p12.h @@ -80,6 +80,7 @@ typedef int (PR_CALLBACK *digestIOFn)(void *arg, unsigned char *buf, typedef struct SEC_PKCS12ExportContextStr SEC_PKCS12ExportContext; typedef struct SEC_PKCS12SafeInfoStr SEC_PKCS12SafeInfo; typedef struct SEC_PKCS12DecoderContextStr SEC_PKCS12DecoderContext; +typedef struct SEC_PKCS12DecoderItemStr SEC_PKCS12DecoderItem; struct sec_PKCS12PasswordModeInfo { SECItem *password; @@ -93,6 +94,14 @@ struct sec_PKCS12PublicKeyModeInfo { int keySize; }; +struct SEC_PKCS12DecoderItemStr { + SECItem *der; + SECOidTag type; + PRBool hasKey; + SECItem *friendlyName; /* UTF-8 string */ +}; + + SEC_BEGIN_PROTOS SEC_PKCS12SafeInfo * @@ -187,6 +196,13 @@ SEC_PKCS12DecoderImportBags(SEC_PKCS12DecoderContext *p12dcx); CERTCertList * SEC_PKCS12DecoderGetCerts(SEC_PKCS12DecoderContext *p12dcx); +SECStatus +SEC_PKCS12DecoderIterateInit(SEC_PKCS12DecoderContext *p12dcx); + +SECStatus +SEC_PKCS12DecoderIterateNext(SEC_PKCS12DecoderContext *p12dcx, + const SEC_PKCS12DecoderItem **ipp); + SEC_END_PROTOS #endif diff --git a/security/nss/lib/pkcs12/p12d.c b/security/nss/lib/pkcs12/p12d.c index c6a1c51abe5..67d01ea19b6 100644 --- a/security/nss/lib/pkcs12/p12d.c +++ b/security/nss/lib/pkcs12/p12d.c @@ -146,6 +146,9 @@ struct SEC_PKCS12DecoderContextStr { PRInt32 allocated; /* total buffer size allocated */ PRInt32 currentpos; /* position counter */ SECPKCS12TargetTokenCAs tokenCAs; + sec_PKCS12SafeBag **keyList;/* used by ...IterateNext() */ + unsigned int iteration; + SEC_PKCS12DecoderItem decitem; }; @@ -1233,6 +1236,13 @@ SEC_PKCS12DecoderStart(SECItem *pwitem, PK11SlotInfo *slot, void *wincx, p12dcx->dClose = dClose; p12dcx->dRead = dRead; p12dcx->dArg = dArg; + + p12dcx->keyList = NULL; + p12dcx->decitem.type = 0; + p12dcx->decitem.der = NULL; + p12dcx->decitem.hasKey = PR_FALSE; + p12dcx->decitem.friendlyName = NULL; + p12dcx->iteration = 0; return p12dcx; @@ -1523,6 +1533,13 @@ SEC_PKCS12DecoderFinish(SEC_PKCS12DecoderContext *p12dcx) SEC_ASN1DecoderFinish(p12dcx->hmacDcx); p12dcx->hmacDcx = NULL; } + + if (p12dcx->decitem.type != 0 && p12dcx->decitem.der != NULL) { + SECITEM_FreeItem(p12dcx->decitem.der, PR_TRUE); + } + if (p12dcx->decitem.friendlyName != NULL) { + SECITEM_FreeItem(p12dcx->decitem.friendlyName, PR_TRUE); + } if(p12dcx->slot) { PK11_FreeSlot(p12dcx->slot); @@ -2908,6 +2925,132 @@ SEC_PKCS12DecoderImportBags(SEC_PKCS12DecoderContext *p12dcx) return sec_pkcs12_install_bags(p12dcx->safeBags, p12dcx->wincx); } +PRBool +sec_pkcs12_bagHasKey(SEC_PKCS12DecoderContext *p12dcx, sec_PKCS12SafeBag *bag) +{ + int i; + SECItem *keyId; + SECItem *certKeyId; + + certKeyId = sec_pkcs12_get_attribute_value(bag, SEC_OID_PKCS9_LOCAL_KEY_ID); + if (certKeyId == NULL) { + return PR_FALSE; + } + + for (i=0; p12dcx->keyList && p12dcx->keyList[i]; i++) { + keyId = sec_pkcs12_get_attribute_value(p12dcx->keyList[i], + SEC_OID_PKCS9_LOCAL_KEY_ID); + if(!keyId) { + continue; + } + if(SECITEM_CompareItem(certKeyId, keyId) == SECEqual) { + return PR_TRUE; + } + } + return PR_FALSE; +} + +SECItem * +sec_pkcs12_get_friendlyName(sec_PKCS12SafeBag *bag) +{ + SECItem *friendlyName; + SECItem *tempnm; + + tempnm = sec_pkcs12_get_attribute_value(bag, SEC_OID_PKCS9_FRIENDLY_NAME); + friendlyName = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if (friendlyName) { + if (!sec_pkcs12_convert_item_to_unicode(NULL, friendlyName, + tempnm, PR_TRUE, PR_FALSE, PR_FALSE)) { + SECITEM_FreeItem(friendlyName, PR_TRUE); + friendlyName = NULL; + } + } + return friendlyName; +} + +/* Following two functions provide access to selected portions of the safe bags. + * Iteration is implemented per decoder context and may be accessed after + * SEC_PKCS12DecoderVerify() returns success. + * When ...DecoderIterateNext() returns SUCCESS a decoder item has been returned + * where item.type is always set; item.friendlyName is set if it is non-null; + * item.der, item.hasKey are set only for SEC_OID_PKCS12_V1_CERT_BAG_ID items. + * ...DecoderIterateNext() returns FAILURE when the list is exhausted or when + * arguments are invalid; PORT_GetError() is 0 at end-of-list. + * Caller has read-only access to decoder items. Any SECItems generated are + * owned by the decoder context and are freed by ...DecoderFinish(). + */ +SECStatus +SEC_PKCS12DecoderIterateInit(SEC_PKCS12DecoderContext *p12dcx) +{ + if(!p12dcx || p12dcx->error) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + p12dcx->iteration = 0; + return SECSuccess; +} + +SECStatus +SEC_PKCS12DecoderIterateNext(SEC_PKCS12DecoderContext *p12dcx, + const SEC_PKCS12DecoderItem **ipp) +{ + sec_PKCS12SafeBag *bag; + SECItem *tempnm; + + if(!p12dcx || p12dcx->error) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (p12dcx->decitem.type != 0 && p12dcx->decitem.der != NULL) { + SECITEM_FreeItem(p12dcx->decitem.der, PR_TRUE); + } + if (p12dcx->decitem.friendlyName != NULL) { + SECITEM_FreeItem(p12dcx->decitem.friendlyName, PR_TRUE); + } + p12dcx->decitem.type = 0; + p12dcx->decitem.der = NULL; + p12dcx->decitem.friendlyName = NULL; + p12dcx->decitem.hasKey = PR_FALSE; + *ipp = NULL; + if (p12dcx->keyList == NULL) { + p12dcx->keyList = sec_pkcs12_get_key_bags(p12dcx->safeBags); + } + + + for (; p12dcx->iteration < p12dcx->safeBagCount; p12dcx->iteration++) { + bag = p12dcx->safeBags[p12dcx->iteration]; + if(bag == NULL || bag->problem) { + continue; + } + p12dcx->decitem.type = SECOID_FindOIDTag(&(bag->safeBagType)); + switch(p12dcx->decitem.type) { + case SEC_OID_PKCS12_V1_CERT_BAG_ID: + p12dcx->decitem.der = sec_pkcs12_get_der_cert(bag); + p12dcx->decitem.friendlyName = sec_pkcs12_get_friendlyName(bag); + p12dcx->decitem.hasKey = sec_pkcs12_bagHasKey(p12dcx, bag); + break; + case SEC_OID_PKCS12_V1_KEY_BAG_ID: + case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: + p12dcx->decitem.friendlyName = sec_pkcs12_get_friendlyName(bag); + break; + default: + /* return these even though we don't expect them */ + break; + case SEC_OID_UNKNOWN: + /* ignore these */ + continue; + } + *ipp = &p12dcx->decitem; + p12dcx->iteration++; + break; /* end for() */ + } + + PORT_SetError(0); /* end-of-list is SECFailure with no PORT error */ + return ((p12dcx->decitem.type == 0) ? SECFailure : SECSuccess); +} + static SECStatus sec_pkcs12_decoder_append_bag_to_context(SEC_PKCS12DecoderContext *p12dcx, sec_PKCS12SafeBag *bag) diff --git a/security/nss/lib/smime/smime.def b/security/nss/lib/smime/smime.def index 3350cfa3b6a..ecb2dcff47b 100644 --- a/security/nss/lib/smime/smime.def +++ b/security/nss/lib/smime/smime.def @@ -260,3 +260,10 @@ SEC_PKCS7EncodeItem; ;+ local: ;+ *; ;+}; +;+NSS_3.10 { # NSS 3.10 release +;+ global: +SEC_PKCS12DecoderIterateInit; +SEC_PKCS12DecoderIterateNext; +;+ local: +;+ *; +;+}; diff --git a/security/nss/tests/tools/tools.sh b/security/nss/tests/tools/tools.sh index d4a16eec4f9..73d8173661e 100644 --- a/security/nss/tests/tools/tools.sh +++ b/security/nss/tests/tools/tools.sh @@ -118,6 +118,13 @@ tools_p12() ret=$? html_msg $ret 0 "Importing Alice's email cert & key (pk12util -i)" check_tmpfile + + echo "$SCRIPTNAME: Listing Alice's pk12 file -----------------" + echo "pk12util -l Alice.p12 -w ${R_PWFILE}" + pk12util -l Alice.p12 -w ${R_PWFILE} 2>&1 + ret=$? + html_msg $ret 0 "Listing Alice's pk12 file (pk12util -l)" + check_tmpfile } ############################## tools_sign ##############################