Split ipa_pwd_extop plugin in multiple files

The plugin was getting difficult to read and maintain.
Split it (and apply cosmetic cleanups to some functions) in smaller
pieces that perform specific tasks.
This commit is contained in:
Simo Sorce 2010-09-29 17:00:41 -04:00
parent 58f1026e34
commit 3127df2aee
6 changed files with 3487 additions and 3215 deletions

View File

@ -20,6 +20,9 @@ plugin_LTLIBRARIES = \
$(NULL)
libipa_pwd_extop_la_SOURCES = \
ipapwd_common.c \
ipapwd_encoding.c \
ipapwd_prepost.c \
ipa_pwd_extop.c \
$(NULL)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,162 @@
/** 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 <prio.h>
#include <ssl.h>
#include <dirsrv/slapi-plugin.h>
#define KRB5_PRIVATE 1
#include <krb5.h>
#include <lber.h>
#include <time.h>
#include <iconv.h>
#include <openssl/des.h>
#include <openssl/md4.h>
#define IPAPWD_PLUGIN_NAME "ipa-pwd-extop"
#define IPAPWD_FEATURE_DESC "IPA Password Manager"
#define IPAPWD_PLUGIN_DESC "IPA Password Extended Operation plugin"
#define IPAPWD_CHECK_CONN_SECURE 0x00000001
#define IPAPWD_CHECK_DN 0x00000002
#define IPA_CHANGETYPE_NORMAL 0
#define IPA_CHANGETYPE_ADMIN 1
#define IPA_CHANGETYPE_DSMGR 2
struct ipapwd_data {
Slapi_Entry *target;
char *dn;
char *password;
time_t timeNow;
time_t lastPwChange;
time_t expireTime;
int changetype;
int pwHistoryLen;
};
struct ipapwd_operation {
struct ipapwd_data pwdata;
int pwd_op;
int is_krb;
};
#define GENERALIZED_TIME_LENGTH 15
#define IPAPWD_POLICY_MASK 0x0FF
#define IPAPWD_POLICY_ERROR 0x100
#define IPAPWD_POLICY_OK 0
/* from ipapwd_common.c */
struct ipapwd_encsalt {
krb5_int32 enc_type;
krb5_int32 salt_type;
};
struct ipapwd_krbcfg {
krb5_context krbctx;
char *realm;
krb5_keyblock *kmkey;
int num_supp_encsalts;
struct ipapwd_encsalt *supp_encsalts;
int num_pref_encsalts;
struct ipapwd_encsalt *pref_encsalts;
char **passsync_mgrs;
int num_passsync_mgrs;
};
int ipapwd_entry_checks(Slapi_PBlock *pb, struct slapi_entry *e,
int *is_root, int *is_krb, int *is_smb,
char *attr, int access);
int ipapwd_gen_checks(Slapi_PBlock *pb, char **errMesg,
struct ipapwd_krbcfg **config, int check_flags);
int ipapwd_CheckPolicy(struct ipapwd_data *data);
int ipapwd_getEntry(const char *dn, Slapi_Entry **e2, char **attrlist);
int ipapwd_get_cur_kvno(Slapi_Entry *target);
int ipapwd_SetPassword(struct ipapwd_krbcfg *krbcfg,
struct ipapwd_data *data, int is_krb);
Slapi_Value **ipapwd_setPasswordHistory(Slapi_Mods *smods,
struct ipapwd_data *data);
int ipapwd_apply_mods(const char *dn, Slapi_Mods *mods);
void ipapwd_free_slapi_value_array(Slapi_Value ***svals);
void free_ipapwd_krbcfg(struct ipapwd_krbcfg **cfg);
/* from ipapwd_encoding.c */
struct ipapwd_krbkeydata {
int32_t type;
struct berval value;
};
struct ipapwd_krbkey {
struct ipapwd_krbkeydata *salt;
struct ipapwd_krbkeydata *ekey;
struct berval s2kparams;
};
struct ipapwd_keyset {
uint16_t major_vno;
uint16_t minor_vno;
uint32_t kvno;
uint32_t mkvno;
struct ipapwd_krbkey *keys;
int num_keys;
};
struct berval *encode_keys(struct ipapwd_keyset *kset);
void ipapwd_keyset_free(struct ipapwd_keyset **pkset);
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);
/* from ipapwd_prepost.c */
int ipapwd_ext_init(void);
int ipapwd_pre_init(Slapi_PBlock *pb);
int ipapwd_post_init(Slapi_PBlock *pb);

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

File diff suppressed because it is too large Load Diff