diff options
Diffstat (limited to 'daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_encoding.c')
-rw-r--r-- | daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_encoding.c | 809 |
1 files changed, 809 insertions, 0 deletions
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_encoding.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_encoding.c new file mode 100644 index 00000000..e6468a51 --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_encoding.c @@ -0,0 +1,809 @@ +/** BEGIN COPYRIGHT BLOCK + * This Program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; version 2 of the License. + * + * This Program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details + * + * You should have received a copy of the GNU General Public License along with + * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA. + * + * In addition, as a special exception, Red Hat, Inc. gives You the additional + * right to link the code of this Program with code not covered under the GNU + * General Public License ("Non-GPL Code") and to distribute linked combinations + * including the two, subject to the limitations in this paragraph. Non-GPL Code + * permitted under this exception must only link to the code of this Program + * through those well defined interfaces identified in the file named EXCEPTION + * found in the source code files (the "Approved Interfaces"). The files of + * Non-GPL Code may instantiate templates or use macros or inline functions from + * the Approved Interfaces without causing the resulting work to be covered by + * the GNU General Public License. Only Red Hat, Inc. may make changes or + * additions to the list of Approved Interfaces. You must obey the GNU General + * Public License in all respects for all of the Program code and other code + * used in conjunction with the Program except the Non-GPL Code covered by this + * exception. If you modify this file, you may extend this exception to your + * version of the file, but you are not obligated to do so. If you do not wish + * to provide this exception without modification, you must delete this + * exception statement from your version and license this file solely under the + * GPL without exception. + * + * Authors: + * Simo Sorce <ssorce@redhat.com> + * + * Copyright (C) 2007-2010 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +#include <dirsrv/slapi-plugin.h> +#include <lber.h> +#include <time.h> + +#include "ipapwd.h" + +/* krbTicketFlags */ +#define KTF_DISALLOW_POSTDATED 0x00000001 +#define KTF_DISALLOW_FORWARDABLE 0x00000002 +#define KTF_DISALLOW_TGT_BASED 0x00000004 +#define KTF_DISALLOW_RENEWABLE 0x00000008 +#define KTF_DISALLOW_PROXIABLE 0x00000010 +#define KTF_DISALLOW_DUP_SKEY 0x00000020 +#define KTF_DISALLOW_ALL_TIX 0x00000040 +#define KTF_REQUIRES_PRE_AUTH 0x00000080 +#define KTF_REQUIRES_HW_AUTH 0x00000100 +#define KTF_REQUIRES_PWCHANGE 0x00000200 +#define KTF_DISALLOW_SVR 0x00001000 +#define KTF_PWCHANGE_SERVICE 0x00002000 + +/* Salt types */ +#define KRB5_KDB_SALTTYPE_NORMAL 0 +#define KRB5_KDB_SALTTYPE_V4 1 +#define KRB5_KDB_SALTTYPE_NOREALM 2 +#define KRB5_KDB_SALTTYPE_ONLYREALM 3 +#define KRB5_KDB_SALTTYPE_SPECIAL 4 +#define KRB5_KDB_SALTTYPE_AFS3 5 +#define KRB5P_SALT_SIZE 16 + +void krb5int_c_free_keyblock_contents(krb5_context context, + register krb5_keyblock *key); + +/* Novell key-format scheme: + + KrbKeySet ::= SEQUENCE { + attribute-major-vno [0] UInt16, + attribute-minor-vno [1] UInt16, + kvno [2] UInt32, + mkvno [3] UInt32 OPTIONAL, + keys [4] SEQUENCE OF KrbKey, + ... + } + + KrbKey ::= SEQUENCE { + salt [0] KrbSalt OPTIONAL, + key [1] EncryptionKey, + s2kparams [2] OCTET STRING OPTIONAL, + ... + } + + KrbSalt ::= SEQUENCE { + type [0] Int32, + salt [1] OCTET STRING OPTIONAL + } + + EncryptionKey ::= SEQUENCE { + keytype [0] Int32, + keyvalue [1] OCTET STRING + } + + */ + +struct berval *encode_keys(struct ipapwd_keyset *kset) +{ + BerElement *be = NULL; + struct berval *bval = NULL; + int ret, i; + + be = ber_alloc_t(LBER_USE_DER); + + if (!be) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "memory allocation failed\n"); + return NULL; + } + + ret = ber_printf(be, "{t[i]t[i]t[i]t[i]t[{", + (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), + kset->major_vno, + (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), + kset->minor_vno, + (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 2), + kset->kvno, + (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 3), + kset->mkvno, + (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 4)); + if (ret == -1) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "encoding asn1 vno info failed\n"); + goto done; + } + + for (i = 0; i < kset->num_keys; i++) { + + ret = ber_printf(be, "{"); + if (ret == -1) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "encoding asn1 EncryptionKey failed\n"); + goto done; + } + + if (kset->keys[i].salt) { + ret = ber_printf(be, "t[{t[i]", + (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), + (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), + kset->keys[i].salt->type); + if ((ret != -1) && kset->keys[i].salt->value.bv_len) { + ret = ber_printf(be, "t[o]", + (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), + kset->keys[i].salt->value.bv_val, + kset->keys[i].salt->value.bv_len); + } + if (ret != -1) { + ret = ber_printf(be, "}]"); + } + if (ret == -1) { + goto done; + } + } + + ret = ber_printf(be, "t[{t[i]t[o]}]", + (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), + (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), + kset->keys[i].ekey->type, + (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), + kset->keys[i].ekey->value.bv_val, + kset->keys[i].ekey->value.bv_len); + if (ret == -1) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "encoding asn1 EncryptionKey failed\n"); + goto done; + } + + /* FIXME: s2kparams not supported yet */ + + ret = ber_printf(be, "}"); + if (ret == -1) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "encoding asn1 EncryptionKey failed\n"); + goto done; + } + } + + ret = ber_printf(be, "}]}"); + if (ret == -1) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "encoding asn1 end of sequences failed\n"); + goto done; + } + + ret = ber_flatten(be, &bval); + if (ret == -1) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "flattening asn1 failed\n"); + goto done; + } +done: + ber_free(be, 1); + + return bval; +} + +void ipapwd_keyset_free(struct ipapwd_keyset **pkset) +{ + struct ipapwd_keyset *kset = *pkset; + int i; + + if (!kset) return; + + for (i = 0; i < kset->num_keys; i++) { + if (kset->keys[i].salt) { + free(kset->keys[i].salt->value.bv_val); + free(kset->keys[i].salt); + } + if (kset->keys[i].ekey) { + free(kset->keys[i].ekey->value.bv_val); + free(kset->keys[i].ekey); + } + free(kset->keys[i].s2kparams.bv_val); + } + free(kset->keys); + free(kset); + *pkset = NULL; +} + + + +static inline void encode_int16(unsigned int val, unsigned char *p) +{ + p[1] = (val >> 8) & 0xff; + p[0] = (val ) & 0xff; +} + +static Slapi_Value **encrypt_encode_key(struct ipapwd_krbcfg *krbcfg, + struct ipapwd_data *data, + char **errMesg) +{ + krb5_context krbctx; + char *krbPrincipalName = NULL; + uint32_t krbMaxTicketLife; + int kvno, i; + int krbTicketFlags; + struct berval *bval = NULL; + Slapi_Value **svals = NULL; + krb5_principal princ; + krb5_error_code krberr; + krb5_data pwd; + struct ipapwd_keyset *kset = NULL; + + krbctx = krbcfg->krbctx; + + svals = (Slapi_Value **)calloc(2, sizeof(Slapi_Value *)); + if (!svals) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "memory allocation failed\n"); + return NULL; + } + + kvno = ipapwd_get_cur_kvno(data->target); + + krbPrincipalName = slapi_entry_attr_get_charptr(data->target, + "krbPrincipalName"); + if (!krbPrincipalName) { + *errMesg = "no krbPrincipalName present in this entry\n"; + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, *errMesg); + return NULL; + } + + krberr = krb5_parse_name(krbctx, krbPrincipalName, &princ); + if (krberr) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "krb5_parse_name failed [%s]\n", + krb5_get_error_message(krbctx, krberr)); + goto enc_error; + } + + krbMaxTicketLife = slapi_entry_attr_get_uint(data->target, + "krbMaxTicketLife"); + if (krbMaxTicketLife == 0) { + /* FIXME: retrieve the default from config (max_life from kdc.conf) */ + krbMaxTicketLife = 86400; /* just set the default 24h for now */ + } + + krbTicketFlags = slapi_entry_attr_get_int(data->target, + "krbTicketFlags"); + + pwd.data = (char *)data->password; + pwd.length = strlen(data->password); + + kset = malloc(sizeof(struct ipapwd_keyset)); + if (!kset) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "malloc failed!\n"); + goto enc_error; + } + + /* this encoding assumes all keys have the same kvno */ + /* major-vno = 1 and minor-vno = 1 */ + kset->major_vno = 1; + kset->minor_vno = 1; + /* increment kvno (will be 1 if this is a new entry) */ + kset->kvno = kvno + 1; + /* we also assum mkvno is 0 */ + kset->mkvno = 0; + + kset->num_keys = krbcfg->num_pref_encsalts; + kset->keys = calloc(kset->num_keys, sizeof(struct ipapwd_krbkey)); + if (!kset->keys) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "malloc failed!\n"); + goto enc_error; + } + + for (i = 0; i < kset->num_keys; i++) { + krb5_keyblock key; + krb5_data salt; + krb5_octet *ptr; + krb5_data plain; + krb5_enc_data cipher; + size_t len; + const char *p; + + salt.data = NULL; + + switch (krbcfg->pref_encsalts[i].salt_type) { + + case KRB5_KDB_SALTTYPE_ONLYREALM: + + p = strchr(krbPrincipalName, '@'); + if (!p) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "Invalid principal name, no realm found!\n"); + goto enc_error; + } + p++; + salt.data = strdup(p); + if (!salt.data) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "memory allocation failed\n"); + goto enc_error; + } + salt.length = strlen(salt.data); /* final \0 omitted on purpose */ + break; + + case KRB5_KDB_SALTTYPE_NOREALM: + + krberr = krb5_principal2salt_norealm(krbctx, princ, &salt); + if (krberr) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "krb5_principal2salt failed [%s]\n", + krb5_get_error_message(krbctx, krberr)); + goto enc_error; + } + break; + + case KRB5_KDB_SALTTYPE_NORMAL: + + /* If pre auth is required we can set a random salt, otherwise + * we have to use a more conservative approach and set the salt + * to be REALMprincipal (the concatenation of REALM and principal + * name without any separator) */ +#if 0 + if (krbTicketFlags & KTF_REQUIRES_PRE_AUTH) { + salt.length = KRB5P_SALT_SIZE; + salt.data = malloc(KRB5P_SALT_SIZE); + if (!salt.data) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "memory allocation failed\n"); + goto enc_error; + } + krberr = krb5_c_random_make_octets(krbctx, &salt); + if (krberr) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "krb5_c_random_make_octets failed [%s]\n", + krb5_get_error_message(krbctx, krberr)); + goto enc_error; + } + } else { +#endif + krberr = krb5_principal2salt(krbctx, princ, &salt); + if (krberr) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "krb5_principal2salt failed [%s]\n", + krb5_get_error_message(krbctx, krberr)); + goto enc_error; + } +#if 0 + } +#endif + break; + + case KRB5_KDB_SALTTYPE_V4: + salt.length = 0; + break; + + case KRB5_KDB_SALTTYPE_AFS3: + + p = strchr(krbPrincipalName, '@'); + if (!p) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "Invalid principal name, no realm found!\n"); + goto enc_error; + } + p++; + salt.data = strdup(p); + if (!salt.data) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "memory allocation failed\n"); + goto enc_error; + } + salt.length = SALT_TYPE_AFS_LENGTH; /* special value */ + break; + + default: + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "Invalid salt type [%d]\n", + krbcfg->pref_encsalts[i].salt_type); + goto enc_error; + } + + /* need to build the key now to manage the AFS salt.length + * special case */ + krberr = krb5_c_string_to_key(krbctx, + krbcfg->pref_encsalts[i].enc_type, + &pwd, &salt, &key); + if (krberr) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "krb5_c_string_to_key failed [%s]\n", + krb5_get_error_message(krbctx, krberr)); + krb5_free_data_contents(krbctx, &salt); + goto enc_error; + } + if (salt.length == SALT_TYPE_AFS_LENGTH) { + salt.length = strlen(salt.data); + } + + krberr = krb5_c_encrypt_length(krbctx, + krbcfg->kmkey->enctype, + key.length, &len); + if (krberr) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "krb5_c_string_to_key failed [%s]\n", + krb5_get_error_message(krbctx, krberr)); + krb5int_c_free_keyblock_contents(krbctx, &key); + krb5_free_data_contents(krbctx, &salt); + goto enc_error; + } + + if ((ptr = (krb5_octet *) malloc(2 + len)) == NULL) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "memory allocation failed\n"); + krb5int_c_free_keyblock_contents(krbctx, &key); + krb5_free_data_contents(krbctx, &salt); + goto enc_error; + } + + encode_int16(key.length, ptr); + + plain.length = key.length; + plain.data = (char *)key.contents; + + cipher.ciphertext.length = len; + cipher.ciphertext.data = (char *)ptr+2; + + krberr = krb5_c_encrypt(krbctx, krbcfg->kmkey, 0, 0, &plain, &cipher); + if (krberr) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "krb5_c_encrypt failed [%s]\n", + krb5_get_error_message(krbctx, krberr)); + krb5int_c_free_keyblock_contents(krbctx, &key); + krb5_free_data_contents(krbctx, &salt); + free(ptr); + goto enc_error; + } + + /* KrbSalt */ + kset->keys[i].salt = malloc(sizeof(struct ipapwd_krbkeydata)); + if (!kset->keys[i].salt) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "malloc failed!\n"); + krb5int_c_free_keyblock_contents(krbctx, &key); + free(ptr); + goto enc_error; + } + + kset->keys[i].salt->type = krbcfg->pref_encsalts[i].salt_type; + + if (salt.length) { + kset->keys[i].salt->value.bv_len = salt.length; + kset->keys[i].salt->value.bv_val = salt.data; + } + + /* EncryptionKey */ + kset->keys[i].ekey = malloc(sizeof(struct ipapwd_krbkeydata)); + if (!kset->keys[i].ekey) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "malloc failed!\n"); + krb5int_c_free_keyblock_contents(krbctx, &key); + free(ptr); + goto enc_error; + } + kset->keys[i].ekey->type = key.enctype; + kset->keys[i].ekey->value.bv_len = len+2; + kset->keys[i].ekey->value.bv_val = malloc(len+2); + if (!kset->keys[i].ekey->value.bv_val) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "malloc failed!\n"); + krb5int_c_free_keyblock_contents(krbctx, &key); + free(ptr); + goto enc_error; + } + memcpy(kset->keys[i].ekey->value.bv_val, ptr, len+2); + + /* make sure we free the memory used now that we are done with it */ + krb5int_c_free_keyblock_contents(krbctx, &key); + free(ptr); + } + + bval = encode_keys(kset); + if (!bval) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "encoding asn1 KrbSalt failed\n"); + goto enc_error; + } + + svals[0] = slapi_value_new_berval(bval); + if (!svals[0]) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "Converting berval to Slapi_Value\n"); + goto enc_error; + } + + ipapwd_keyset_free(&kset); + krb5_free_principal(krbctx, princ); + slapi_ch_free_string(&krbPrincipalName); + ber_bvfree(bval); + return svals; + +enc_error: + *errMesg = "key encryption/encoding failed\n"; + if (kset) ipapwd_keyset_free(&kset); + krb5_free_principal(krbctx, princ); + slapi_ch_free_string(&krbPrincipalName); + if (bval) ber_bvfree(bval); + free(svals); + return NULL; +} + + +#define KTF_LM_HASH 0x01 +#define KTF_NT_HASH 0x02 +#define KTF_DOS_CHARSET "CP850" /* same default as samba */ +#define KTF_UTF8 "UTF-8" +#define KTF_UCS2 "UCS-2LE" + +static const uint8_t parity_table[128] = { + 1, 2, 4, 7, 8, 11, 13, 14, 16, 19, 21, 22, 25, 26, 28, 31, + 32, 35, 37, 38, 41, 42, 44, 47, 49, 50, 52, 55, 56, 59, 61, 62, + 64, 67, 69, 70, 73, 74, 76, 79, 81, 82, 84, 87, 88, 91, 93, 94, + 97, 98,100,103,104,107,109,110,112,115,117,118,121,122,124,127, + 128,131,133,134,137,138,140,143,145,146,148,151,152,155,157,158, + 161,162,164,167,168,171,173,174,176,179,181,182,185,186,188,191, + 193,194,196,199,200,203,205,206,208,211,213,214,217,218,220,223, + 224,227,229,230,233,234,236,239,241,242,244,247,248,251,253,254 +}; + +static void lm_shuffle(uint8_t *out, uint8_t *in) +{ + out[0] = parity_table[in[0]>>1]; + out[1] = parity_table[((in[0]<<6)|(in[1]>>2)) & 0x7F]; + out[2] = parity_table[((in[1]<<5)|(in[2]>>3)) & 0x7F]; + out[3] = parity_table[((in[2]<<4)|(in[3]>>4)) & 0x7F]; + out[4] = parity_table[((in[3]<<3)|(in[4]>>5)) & 0x7F]; + out[5] = parity_table[((in[4]<<2)|(in[5]>>6)) & 0x7F]; + out[6] = parity_table[((in[5]<<1)|(in[6]>>7)) & 0x7F]; + out[7] = parity_table[in[6] & 0x7F]; +} + +struct ntlm_keys { + uint8_t lm[16]; + uint8_t nt[16]; +}; + +/* create the lm and nt hashes + newPassword: the clear text utf8 password + flags: KTF_LM_HASH | KTF_NT_HASH +*/ +static int encode_ntlm_keys(char *newPasswd, + unsigned int flags, + struct ntlm_keys *keys) +{ + int ret = 0; + + /* do lanman first */ + if (flags & KTF_LM_HASH) { + iconv_t cd; + size_t cs, il, ol; + char *inc, *outc; + char *upperPasswd; + char *asciiPasswd; + DES_key_schedule schedule; + DES_cblock deskey; + DES_cblock magic = "KGS!@#$%"; + + /* TODO: must store the dos charset somewhere in the directory */ + cd = iconv_open(KTF_DOS_CHARSET, KTF_UTF8); + if (cd == (iconv_t)(-1)) { + ret = -1; + goto done; + } + + /* the lanman password is upper case */ + upperPasswd = (char *)slapi_utf8StrToUpper((unsigned char *)newPasswd); + if (!upperPasswd) { + ret = -1; + goto done; + } + il = strlen(upperPasswd); + + /* an ascii string can only be smaller than or equal to an utf8 one */ + ol = il; + if (ol < 14) ol = 14; + asciiPasswd = calloc(ol+1, 1); + if (!asciiPasswd) { + slapi_ch_free_string(&upperPasswd); + ret = -1; + goto done; + } + + inc = upperPasswd; + outc = asciiPasswd; + cs = iconv(cd, &inc, &il, &outc, &ol); + if (cs == -1) { + ret = -1; + slapi_ch_free_string(&upperPasswd); + free(asciiPasswd); + iconv_close(cd); + goto done; + } + + /* done with these */ + slapi_ch_free_string(&upperPasswd); + iconv_close(cd); + + /* we are interested only in the first 14 ASCII chars for lanman */ + if (strlen(asciiPasswd) > 14) { + asciiPasswd[14] = '\0'; + } + + /* first half */ + lm_shuffle(deskey, (uint8_t *)asciiPasswd); + + DES_set_key_unchecked(&deskey, &schedule); + DES_ecb_encrypt(&magic, (DES_cblock *)keys->lm, + &schedule, DES_ENCRYPT); + + /* second half */ + lm_shuffle(deskey, (uint8_t *)&asciiPasswd[7]); + + DES_set_key_unchecked(&deskey, &schedule); + DES_ecb_encrypt(&magic, (DES_cblock *)&(keys->lm[8]), + &schedule, DES_ENCRYPT); + + /* done with it */ + free(asciiPasswd); + + } else { + memset(keys->lm, 0, 16); + } + + if (flags & KTF_NT_HASH) { + iconv_t cd; + size_t cs, il, ol, sl; + char *inc, *outc; + char *ucs2Passwd; + MD4_CTX md4ctx; + + /* TODO: must store the dos charset somewhere in the directory */ + cd = iconv_open(KTF_UCS2, KTF_UTF8); + if (cd == (iconv_t)(-1)) { + ret = -1; + goto done; + } + + il = strlen(newPasswd); + + /* an ucs2 string can be at most double than an utf8 one */ + sl = ol = (il+1)*2; + ucs2Passwd = calloc(ol, 1); + if (!ucs2Passwd) { + ret = -1; + goto done; + } + + inc = newPasswd; + outc = ucs2Passwd; + cs = iconv(cd, &inc, &il, &outc, &ol); + if (cs == -1) { + ret = -1; + free(ucs2Passwd); + iconv_close(cd); + goto done; + } + + /* done with it */ + iconv_close(cd); + + /* get the final ucs2 string length */ + sl -= ol; + /* we are interested only in the first 14 wchars for the nt password */ + if (sl > 28) { + sl = 28; + } + + ret = MD4_Init(&md4ctx); + if (ret == 0) { + ret = -1; + free(ucs2Passwd); + goto done; + } + ret = MD4_Update(&md4ctx, ucs2Passwd, sl); + if (ret == 0) { + ret = -1; + free(ucs2Passwd); + goto done; + } + ret = MD4_Final(keys->nt, &md4ctx); + if (ret == 0) { + ret = -1; + free(ucs2Passwd); + goto done; + } + + } else { + memset(keys->nt, 0, 16); + } + + ret = 0; + +done: + return ret; +} + +int ipapwd_gen_hashes(struct ipapwd_krbcfg *krbcfg, + struct ipapwd_data *data, char *userpw, + int is_krb, int is_smb, Slapi_Value ***svals, + char **nthash, char **lmhash, char **errMesg) +{ + int rc; + + if (is_krb) { + + *svals = encrypt_encode_key(krbcfg, data, errMesg); + + if (!*svals) { + /* errMesg should have been set in encrypt_encode_key() */ + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "key encryption/encoding failed\n"); + rc = LDAP_OPERATIONS_ERROR; + goto done; + } + } + + if (is_smb) { + char lm[33], nt[33]; + struct ntlm_keys ntlm; + int ntlm_flags = 0; + int ret; + + /* TODO: retrieve if we want to store the LM hash or not */ + ntlm_flags = KTF_LM_HASH | KTF_NT_HASH; + + ret = encode_ntlm_keys(userpw, ntlm_flags, &ntlm); + if (ret) { + *errMesg = "Failed to generate NT/LM hashes\n"; + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + *errMesg); + rc = LDAP_OPERATIONS_ERROR; + goto done; + } + if (ntlm_flags & KTF_LM_HASH) { + hexbuf(lm, ntlm.lm); + lm[32] = '\0'; + *lmhash = slapi_ch_strdup(lm); + } + if (ntlm_flags & KTF_NT_HASH) { + hexbuf(nt, ntlm.nt); + nt[32] = '\0'; + *nthash = slapi_ch_strdup(nt); + } + } + + rc = LDAP_SUCCESS; + +done: + + return rc; +} + |