| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592 |
- /** BEGIN COPYRIGHT BLOCK
- * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
- * Copyright (C) 2015 Red Hat, Inc.
- * All rights reserved.
- *
- * License: GPL (version 3 or any later version).
- * See LICENSE for details.
- * END COPYRIGHT BLOCK **/
- #ifdef HAVE_CONFIG_H
- # include <config.h>
- #endif
- #include <string.h>
- #include <stdio.h>
- #include <ldap.h>
- #include <nspr.h>
- #include <nss.h>
- #include <secmod.h>
- /*
- #include <secasn1.h>
- #include <secpkcs7.h>
- */
- #include <key.h>
- #include <certdb.h>
- #include <cert.h>
- #include <svrcore.h>
- #include <secmodt.h>
- #include <prtypes.h>
- #include <seccomon.h>
- #include <pk11func.h>
- #define NEED_TOK_PBE /* see slap.h - defines tokPBE and ptokPBE */
- #include "rever.h"
- #include <slap.h>
- #include "slapi-plugin.h"
- #include <uuid.h>
- #include <plbase64.h>
- struct pk11MechItem
- {
- CK_MECHANISM_TYPE type;
- const char *mechName;
- };
- static const struct pk11MechItem DESmech = { CKM_DES_CBC, "DES CBC encryption" };
- static const struct pk11MechItem AESmech = { CKM_AES_CBC, "AES CBC encryption" };
- static Slapi_Mutex *pbe_lock = NULL;
- struct pk11ContextStore
- {
- PK11SlotInfo *slot;
- const struct pk11MechItem *mech;
- PK11SymKey *key;
- SECItem *params;
- int length;
- unsigned char *crypt;
- char *algid_base64;
- };
- /*
- * der_algid converting functions:
- *
- * SECStatus ATOB_ConvertAsciiToItem(SECItem *binary_item, const char *ascii);
- * char * BTOA_ConvertItemToAscii(SECItem *binary_item);
- *
- */
- static int encode_path(char *inPlain, char **outCipher, char *path, int mech);
- static int decode_path(char *inCipher, char **outPlain, char *path, int mech, char *algid);
- static SVRCOREError genKey(struct pk11ContextStore **out, const char *token, char *path, int mech, PRArenaPool *arena, char *algid);
- static SVRCOREError cryptPassword(struct pk11ContextStore *store, char * clear, unsigned char **out);
- static SVRCOREError decryptPassword(struct pk11ContextStore *store, unsigned char *cipher, char **out, int len);
- static void freePBE(struct pk11ContextStore *store);
- void
- init_pbe_plugin()
- {
- if(!pbe_lock){
- pbe_lock = slapi_new_mutex();
- }
- }
- int
- encode(char *inPlain, char **outCipher, int mech)
- {
- return encode_path(inPlain, outCipher, NULL, mech);
- }
- static int
- encode_path(char *inPlain, char **outCipher, char *path, int mech)
- {
- struct pk11ContextStore *context = NULL;
- PRArenaPool *arena = NULL;
- unsigned char *cipher = NULL;
- char *tmp = NULL;
- char *base = NULL;
- int len = 0;
- int err;
- *outCipher = NULL;
- err = 1;
- if ( genKey(&context, tokPBE, path, mech, arena, NULL) == SVRCORE_Success ){
- /* Try an encryption */
- if ( cryptPassword(context, inPlain, &cipher) == SVRCORE_Success ){
- base = BTOA_DataToAscii(cipher, context->length);
- if ( base != NULL ){
- const char *scheme;
- if (mech == AES_MECH){
- scheme = AES_REVER_SCHEME_NAME;
- len = 3 + strlen(scheme)+ strlen(context->algid_base64) + strlen(base) + 1;
- if( (tmp = slapi_ch_malloc( len )) ){
- /*
- * {AES-<BASE64_ALG_ID>}<ENCODED PASSWORD>
- */
- sprintf( tmp, "%c%s-%s%c%s", PWD_HASH_PREFIX_START, scheme,
- context->algid_base64,PWD_HASH_PREFIX_END, base);
- }
- } else {
- /* Old school DES */
- scheme = DES_REVER_SCHEME_NAME;
- if((tmp = slapi_ch_malloc( 3 + strlen(scheme) + strlen(base)))){
- sprintf( tmp, "%c%s%c%s", PWD_HASH_PREFIX_START, scheme,
- PWD_HASH_PREFIX_END, base);
- }
- }
- if ( tmp != NULL ){
- *outCipher = tmp;
- tmp = NULL;
- err = 0;
- }
- PORT_Free(base);
- }
- }
- }
- freePBE(context);
- return(err);
- }
- int
- decode(char *inCipher, char **outPlain, int mech, char *algid)
- {
- return decode_path(inCipher, outPlain, NULL, mech, algid);
- }
- static int
- decode_path(char *inCipher, char **outPlain, char *path, int mech, char *algid)
- {
- struct pk11ContextStore *context = NULL;
- PRArenaPool *arena = NULL;
- unsigned char *base = NULL;
- char *plain = NULL;
- int err;
- int len = 0;
- *outPlain = NULL;
- err = 1;
- if ( genKey(&context, tokPBE, path, mech, arena, algid) == SVRCORE_Success ){
- /* it seems that there is memory leak in that function: bug 400170 */
- base = ATOB_AsciiToData(inCipher, (unsigned int*)&len);
- if ( base != NULL ){
- if ( decryptPassword(context, base, &plain, len) == SVRCORE_Success ){
- *outPlain = plain;
- err = 0;
- }
- }
- }
- slapi_ch_free_string(&algid);
- PORT_Free(base);
- PORT_FreeArena(arena, PR_TRUE);
- freePBE(context);
- return(err);
- }
- static void
- freePBE(struct pk11ContextStore *store)
- {
- if (store){
- if (store->slot)
- slapd_pk11_freeSlot(store->slot);
- if (store->key)
- slapd_pk11_freeSymKey(store->key);
- if (store->params)
- SECITEM_FreeItem(store->params, PR_TRUE);
- slapi_ch_free((void **)&store->crypt);
- slapi_ch_free_string(&store->algid_base64);
- slapi_ch_free((void **)&store);
- }
- }
- static SVRCOREError
- genKey(struct pk11ContextStore **out, const char *token, char *path, int mech, PRArenaPool *arena, char *alg)
- {
- SVRCOREError err = SVRCORE_Success;
- struct pk11ContextStore *store = NULL;
- SECItem *pwitem = NULL;
- SECItem *result = NULL;
- SECItem *salt = NULL;
- SECItem der_algid;
- SECAlgorithmID *algid = NULL;
- SECOidTag algoid;
- CK_MECHANISM pbeMech;
- CK_MECHANISM cryptoMech;
- SECAlgorithmID my_algid;
- char *configdir = NULL;
- char *der_ascii = NULL;
- char *iv = NULL;
- int free_it = 0;
- arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
- store = (struct pk11ContextStore*)slapi_ch_calloc(1, sizeof(*store));
- if (store == NULL){
- err = SVRCORE_NoMemory_Error;
- goto done;
- }
- *out = store;
- /* Use the tokenName to find a PKCS11 slot */
- store->slot = slapd_pk11_findSlotByName((char *)token);
- if (store->slot == NULL){
- err = SVRCORE_NoSuchToken_Error;
- goto done;
- }
- /* Generate a key and parameters to do the encryption */
- if(mech == AES_MECH){
- store->mech = &AESmech;
- algoid = SEC_OID_AES_256_CBC;
- } else {
- store->mech = &DESmech;
- algoid = SEC_OID_PKCS5_PBE_WITH_MD2_AND_DES_CBC;
- }
- /* Generate a unique id, used as salt for the key generation */
- if ( path == NULL ){
- configdir = config_get_configdir();
- if ( configdir == NULL ){
- err = SVRCORE_System_Error;
- goto done;
- }
- } else {
- configdir = slapi_ch_strdup(path);
- }
- if ( slapi_uniqueIDGenerateFromNameString (&iv, NULL, configdir, strlen(configdir)) != UID_SUCCESS ){
- err = SVRCORE_System_Error;
- goto done;
- }
- pwitem = (SECItem *) PORT_Alloc(sizeof(SECItem));
- if (pwitem == NULL){
- err = SVRCORE_NoMemory_Error;
- goto done;
- }
- pwitem->type = siBuffer;
- pwitem->data = (unsigned char *)PORT_Alloc(strlen(iv)+1);
- if (pwitem->data == NULL){
- err = SVRCORE_NoMemory_Error;
- goto done;
- }
- strcpy((char*)pwitem->data, iv);
- pwitem->len = strlen(iv) + 1;
- salt = (SECItem *) PORT_Alloc(sizeof(SECItem));
- if (salt == NULL){
- err = SVRCORE_NoMemory_Error;
- goto done;
- }
- salt->type = siBuffer;
- salt->data = (unsigned char *)PORT_Alloc(strlen(iv)+1);
- if ( salt->data == NULL ){
- err = SVRCORE_NoMemory_Error;
- goto done;
- }
- strcpy((char*)salt->data, iv);
- salt->len = strlen(iv) + 1;
- PORT_Memset(&der_algid, 0, sizeof(der_algid));
- if(!alg){
- /*
- * This is DES, or we are encoding AES - the process is the same.
- */
- algid = slapd_pk11_createPBEAlgorithmID(algoid, 2, salt);
- free_it = 1; /* we need to free this algid */
- /*
- * The following is only need for AES - we need to store
- * algid for future decodings(unlike with DES). So convert
- * algid to its DER encoding. Then convert the DER to ascii,
- * and finally convert the DER ascii to base64 so we can store
- * it in the cipher prefix.
- */
- SEC_ASN1EncodeItem(arena, &der_algid, algid, SEC_ASN1_GET(SECOID_AlgorithmIDTemplate));
- der_ascii = BTOA_ConvertItemToAscii(&der_algid);
- store->algid_base64 = PL_Base64Encode(der_ascii,strlen(der_ascii), NULL);
- slapi_ch_free_string(&der_ascii);
- } else {
- /*
- * We are decoding AES - use the supplied algid
- */
- PORT_Memset(&my_algid, 0, sizeof(my_algid));
- /* Decode the base64 der encoding */
- der_ascii = PL_Base64Decode(alg, strlen(alg), NULL);
- /* convert the der ascii to the SEC item */
- ATOB_ConvertAsciiToItem(&der_algid, der_ascii);
- SEC_ASN1DecodeItem(arena, &my_algid, SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), &der_algid);
- SECITEM_FreeItem(&der_algid, PR_FALSE);
- algid = &my_algid;
- slapi_ch_free_string(&der_ascii);
- }
- slapi_lock_mutex(pbe_lock);
- store->key = slapd_pk11_pbeKeyGen(store->slot, algid, pwitem, 0, 0);
- if (store->key == 0){
- slapi_unlock_mutex(pbe_lock);
- err = SVRCORE_System_Error;
- goto done;
- }
- slapi_unlock_mutex(pbe_lock);
- if(mech == AES_MECH)
- {
- cryptoMech.mechanism = PK11_GetPBECryptoMechanism(algid, &store->params, pwitem);
- if (cryptoMech.mechanism == CKM_INVALID_MECHANISM) {
- err = SVRCORE_System_Error;
- goto done;
- }
- }
- else
- {
- /* DES */
- pbeMech.mechanism = slapd_pk11_algtagToMechanism(algoid);
- result = slapd_pk11_paramFromAlgid(algid);
- if(result){
- pbeMech.pParameter = result->data;
- pbeMech.ulParameterLen = result->len;
- }
- if(slapd_pk11_mapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, pwitem,
- PR_FALSE) != CKR_OK){
- err = SVRCORE_System_Error;
- goto done;
- }
- store->params = (SECItem *) PORT_Alloc(sizeof(SECItem));
- if (store->params == NULL){
- err = SVRCORE_System_Error;
- goto done;
- }
- store->params->type = store->mech->type;
- store->params->data = (unsigned char *)PORT_Alloc(cryptoMech.ulParameterLen);
- if (store->params->data == NULL){
- err = SVRCORE_System_Error;
- goto done;
- }
- memcpy(store->params->data, (unsigned char *)cryptoMech.pParameter, cryptoMech.ulParameterLen);
- store->params->len = cryptoMech.ulParameterLen;
- PORT_Free(cryptoMech.pParameter);
- }
- done:
- SECITEM_FreeItem(result, PR_TRUE);
- SECITEM_FreeItem(pwitem, PR_TRUE);
- SECITEM_FreeItem(salt, PR_TRUE);
- if(free_it){
- secoid_destroyAlgorithmID(algid, PR_TRUE);
- }
- slapi_ch_free_string(&configdir);
- slapi_ch_free_string(&iv);
- if (arena) {
- PORT_FreeArena(arena, PR_TRUE);
- }
- return (err);
- }
- static SVRCOREError
- decryptPassword(struct pk11ContextStore *store, unsigned char *cipher, char **out, int len)
- {
- SVRCOREError err = SVRCORE_Success;
- unsigned char *plain = NULL;
- unsigned char *cipher_with_padding = NULL;
- SECStatus rv;
- PK11Context *ctx = NULL;
- int outLen = 0;
- int blocksize = 0;
- blocksize = slapd_pk11_getBlockSize(store->mech->type, 0);
- store->length = len;
- /*
- * store->length is the max. length of the returned clear text -
- * must be >= length of crypted bytes - also must be a multiple
- * of blocksize
- */
- if (blocksize != 0){
- store->length += blocksize - (store->length % blocksize);
- }
- /* plain will hold the returned clear text */
- plain = (unsigned char *)slapi_ch_calloc(sizeof(unsigned char),
- store->length+1);
- if (!plain){
- err = SVRCORE_NoMemory_Error;
- goto done;
- }
- /*
- * create a buffer holding the original cipher bytes, padded with
- * zeros to a multiple of blocksize - do not need +1 since buffer is not
- * a string
- */
- cipher_with_padding = (unsigned char *)slapi_ch_calloc(sizeof(unsigned char),
- store->length);
- if (!cipher_with_padding){
- err = SVRCORE_NoMemory_Error;
- goto done;
- }
- memcpy(cipher_with_padding, cipher, len);
- ctx = slapd_pk11_createContextBySymKey(store->mech->type, CKA_DECRYPT,
- store->key, store->params);
- if (!ctx) {
- err = SVRCORE_System_Error;
- goto done;
- }
- /*
- * Warning - there is a purify UMR in the NSS des code - you may see it when the
- * password is not a multiple of 8 bytes long
- */
- rv = slapd_pk11_cipherOp(ctx, plain, &outLen, store->length,
- cipher_with_padding, store->length);
- if (rv){
- err = SVRCORE_System_Error;
- }
- rv = slapd_pk11_finalize(ctx);
- /*
- * We must do the finalize, but we only want to set the err return
- * code if it is not already set
- */
- if (rv && (SVRCORE_Success == err))
- err = SVRCORE_System_Error;
- done:
- if (err == SVRCORE_Success){
- *out = (char *)plain;
- } else {
- slapi_ch_free((void **)&plain);
- }
- slapi_ch_free((void **)&cipher_with_padding);
- /* We should free the PK11Context... Something like : */
- if (ctx){
- slapd_pk11_destroyContext(ctx, PR_TRUE);
- }
- return err;
- }
- static SVRCOREError
- cryptPassword(struct pk11ContextStore *store, char * clear, unsigned char **out)
- {
- SVRCOREError err = SVRCORE_Success;
- SECStatus rv;
- PK11Context *ctx = NULL;
- unsigned char *clear_with_padding = NULL; /* clear with padding up to blocksize */
- int blocksize = 0;
- int outLen = 0;
- blocksize = slapd_pk11_getBlockSize(store->mech->type, 0);
- store->length = strlen(clear);
- /*
- * The size of the clear text buffer passed to the encryption functions
- * must be a multiple of blocksize (usually 8 bytes) - we allocate a buffer
- * of this size, copy the clear text password into it, and pad the rest with
- * zeros.
- */
- if (blocksize != 0){
- store->length += blocksize - (store->length % blocksize);
- }
- /*
- * store->crypt will hold the crypted password - it must be >= clear length
- * store->crypt is freed in NSS; let's not use slapi_ch_calloc
- */
- store->crypt = (unsigned char *)calloc(sizeof(unsigned char),
- store->length+1);
- if (!store->crypt) {
- err = SVRCORE_NoMemory_Error;
- goto done;
- }
- /* Create a buffer big enough to hold the clear text password and padding */
- clear_with_padding = (unsigned char *)slapi_ch_calloc(sizeof(unsigned char),
- store->length+1);
- if (!clear_with_padding){
- err = SVRCORE_NoMemory_Error;
- goto done;
- }
- /*
- * Copy the clear text password into the buffer - the calloc insures the
- * remainder is zero padded .
- */
- strcpy((char *)clear_with_padding, clear);
- ctx = slapd_pk11_createContextBySymKey(store->mech->type, CKA_ENCRYPT,
- store->key, store->params);
- if (!ctx) {
- err = SVRCORE_System_Error;
- goto done;
- }
- rv = slapd_pk11_cipherOp(ctx, store->crypt, &outLen, store->length,
- clear_with_padding, store->length);
- if (rv) {
- err = SVRCORE_System_Error;
- }
- rv = slapd_pk11_finalize(ctx);
- /*
- * We must do the finalize, but we only want to set the err return
- * code if it is not already set
- */
- if (rv && (SVRCORE_Success == err)){
- err = SVRCORE_System_Error;
- }
- done:
- if (err == SVRCORE_Success){
- *out = store->crypt;
- }
- slapi_ch_free((void **)&clear_with_padding);
- /* We should free the PK11Context... Something like : */
- if (ctx){
- slapd_pk11_destroyContext(ctx, PR_TRUE);
- }
- return err;
- }
- /*
- * The UUID name based generator was broken on x86 platforms. We use
- * this to generate the password encryption key. During migration,
- * we have to fix this so we can use the fixed generator. The env.
- * var USE_BROKEN_UUID tells the uuid generator to use the old
- * broken method to create the UUID. That will allow us to decrypt
- * the password to the correct clear text, then we can turn off
- * the broken method and use the fixed method to encrypt the
- * password.
- */
- char *
- migrateCredentials(char *oldpath, char *newpath, char *oldcred)
- {
- static char *useBrokenUUID = "USE_BROKEN_UUID=1";
- static char *disableBrokenUUID = "USE_BROKEN_UUID=0";
- char *plain = NULL;
- char *cipher = NULL;
- init_pbe_plugin();
- slapd_pk11_configurePKCS11(NULL, NULL, tokPBE, ptokPBE, NULL, NULL, NULL, NULL, 0, 0 );
- NSS_NoDB_Init(NULL);
- if (getenv("MIGRATE_BROKEN_PWD")) {
- putenv(useBrokenUUID);
- }
- if ( decode_path(oldcred, &plain, oldpath, DES_MECH, NULL) == 0 ){
- if (getenv("MIGRATE_BROKEN_PWD")) {
- putenv(disableBrokenUUID);
- }
- if ( encode_path(plain, &cipher, newpath, AES_MECH) != 0 ){
- return(NULL);
- } else {
- return(cipher);
- }
- } else {
- return(NULL);
- }
- }
|