summaryrefslogtreecommitdiffstats
path: root/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_encoding.c
diff options
context:
space:
mode:
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.c809
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;
+}
+