2007-06-29 16:21:31 -04:00
|
|
|
/** 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) 2005 Red Hat, Inc.
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
* END COPYRIGHT BLOCK **/
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
# include <config.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Password Modify - LDAP Extended Operation.
|
|
|
|
|
* RFC 3062
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* This plugin implements the "Password Modify - LDAP3"
|
|
|
|
|
* extended operation for LDAP. The plugin function is called by
|
|
|
|
|
* the server if an LDAP client request contains the OID:
|
|
|
|
|
* "1.3.6.1.4.1.4203.1.11.1".
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
|
|
#include <prio.h>
|
|
|
|
|
#include <ssl.h>
|
2007-08-15 19:45:18 -04:00
|
|
|
#include <dirsrv/slapi-plugin.h>
|
2007-06-29 16:21:31 -04:00
|
|
|
#include <krb5.h>
|
|
|
|
|
#include <lber.h>
|
|
|
|
|
#include <time.h>
|
|
|
|
|
#include <iconv.h>
|
|
|
|
|
#include <openssl/des.h>
|
2007-08-20 15:38:47 -04:00
|
|
|
#include <openssl/md4.h>
|
2007-06-29 16:21:31 -04:00
|
|
|
|
|
|
|
|
/* Type of connection for this operation;*/
|
|
|
|
|
#define LDAP_EXTOP_PASSMOD_CONN_SECURE
|
|
|
|
|
|
|
|
|
|
/* Uncomment the following line FOR TESTING: allows non-SSL connections to use the password change extended op */
|
|
|
|
|
/* #undef LDAP_EXTOP_PASSMOD_CONN_SECURE */
|
|
|
|
|
|
|
|
|
|
/* ber tags for the PasswdModifyRequestValue sequence */
|
|
|
|
|
#define LDAP_EXTOP_PASSMOD_TAG_USERID 0x80U
|
|
|
|
|
#define LDAP_EXTOP_PASSMOD_TAG_OLDPWD 0x81U
|
|
|
|
|
#define LDAP_EXTOP_PASSMOD_TAG_NEWPWD 0x82U
|
|
|
|
|
|
|
|
|
|
/* ber tags for the PasswdModifyResponseValue sequence */
|
|
|
|
|
#define LDAP_EXTOP_PASSMOD_TAG_GENPWD 0x80U
|
|
|
|
|
|
|
|
|
|
/* number of bytes used for random password generation */
|
|
|
|
|
#define LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN 8
|
|
|
|
|
|
|
|
|
|
/* number of random bytes needed to generate password */
|
|
|
|
|
#define LDAP_EXTOP_PASSMOD_RANDOM_BYTES 6
|
|
|
|
|
|
|
|
|
|
/* OID of the extended operation handled by this plug-in */
|
|
|
|
|
#define EXOP_PASSWD_OID "1.3.6.1.4.1.4203.1.11.1"
|
|
|
|
|
|
|
|
|
|
/* These are thye default enc:salt ypes if nothing is defined.
|
|
|
|
|
* TODO: retrieve the configure set of ecntypes either from the
|
|
|
|
|
* kfc.conf file or by synchronizing the the file content into
|
|
|
|
|
* the directory */
|
|
|
|
|
|
|
|
|
|
#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
|
|
|
|
|
|
|
|
|
|
struct krb5p_keysalt {
|
|
|
|
|
krb5_int32 enc_type;
|
|
|
|
|
krb5_int32 salt_type;
|
|
|
|
|
};
|
|
|
|
|
|
2007-07-02 10:14:45 -04:00
|
|
|
static void *ipapwd_plugin_id;
|
2007-06-29 16:21:31 -04:00
|
|
|
|
|
|
|
|
krb5_keyblock kmkey;
|
|
|
|
|
|
|
|
|
|
struct krb5p_keysalt *keysalts;
|
|
|
|
|
int n_keysalts;
|
|
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
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(krb5_context krbctx, Slapi_Entry *e, const char *newPasswd)
|
|
|
|
|
{
|
|
|
|
|
struct berval *bval = NULL;
|
|
|
|
|
Slapi_Value **svals = NULL;
|
|
|
|
|
BerElement *be = NULL;
|
|
|
|
|
int num_versions;
|
|
|
|
|
int krbTicketFlags;
|
|
|
|
|
const char *krbPrincipalName;
|
|
|
|
|
krb5_principal princ;
|
|
|
|
|
krb5_error_code krberr;
|
|
|
|
|
krb5_data pwd;
|
|
|
|
|
int ret, i;
|
|
|
|
|
|
|
|
|
|
krbPrincipalName = slapi_entry_attr_get_charptr(e, "krbPrincipalName");
|
|
|
|
|
if (!krbPrincipalName) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "no krbPrincipalName present in this entry\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* TODO: retrieve current kvno and increment it */
|
|
|
|
|
/* TODO: keep previous version */
|
|
|
|
|
num_versions = 1;
|
|
|
|
|
|
|
|
|
|
svals = (Slapi_Value **)calloc(num_versions + 1, sizeof(Slapi_Value *));
|
|
|
|
|
if (!svals) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "memory allocation failed\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
svals[1] = NULL;
|
|
|
|
|
|
|
|
|
|
krberr = krb5_parse_name(krbctx, krbPrincipalName, &princ);
|
|
|
|
|
if (krberr) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"krb5_parse_name failed [%s]\n",
|
|
|
|
|
krb5_get_error_message(krbctx, krberr));
|
|
|
|
|
goto enc_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
krbTicketFlags = slapi_entry_attr_get_int(e, "krbTicketFlags");
|
|
|
|
|
|
|
|
|
|
pwd.data = (char *)newPasswd;
|
|
|
|
|
pwd.length = strlen(newPasswd);
|
|
|
|
|
|
|
|
|
|
be = ber_alloc_t( LBER_USE_DER );
|
|
|
|
|
|
|
|
|
|
if (!be) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"memory allocation failed\n");
|
|
|
|
|
goto enc_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* major-vno = 1 and minor-von = 1 */
|
|
|
|
|
/* this encoding assumes all keys have the same kvno (currently set at 1) */
|
|
|
|
|
/* we also assum mkvno is 0 */
|
|
|
|
|
ret = ber_printf(be, "{t[i]t[i]t[i]t[i]t[{",
|
|
|
|
|
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), 1,
|
|
|
|
|
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), 1,
|
|
|
|
|
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 2), 1,
|
|
|
|
|
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 3), 0,
|
|
|
|
|
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 4));
|
|
|
|
|
if (ret == -1) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"encoding asn1 vno info failed\n");
|
|
|
|
|
goto enc_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < n_keysalts; 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 (keysalts[i].salt_type) {
|
|
|
|
|
|
|
|
|
|
case KRB5_KDB_SALTTYPE_ONLYREALM:
|
|
|
|
|
|
|
|
|
|
p = strchr(krbPrincipalName, '@');
|
|
|
|
|
if (!p) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"Invalid principal name, no realm found!\n");
|
|
|
|
|
goto enc_error;
|
|
|
|
|
}
|
|
|
|
|
p++;
|
|
|
|
|
salt.data = strdup(p);
|
|
|
|
|
if (!salt.data) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"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) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"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 (krbTicketFlags & KTF_REQUIRES_PRE_AUTH) {
|
|
|
|
|
salt.length = KRB5P_SALT_SIZE;
|
|
|
|
|
krberr = krb5_c_random_make_octets(krbctx, &salt);
|
|
|
|
|
if (!krberr) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"krb5_c_random_make_octets failed [%s]\n",
|
|
|
|
|
krb5_get_error_message(krbctx, krberr));
|
|
|
|
|
goto enc_error;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
krberr = krb5_principal2salt(krbctx, princ, &salt);
|
|
|
|
|
if (krberr) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"krb5_principal2salt failed [%s]\n",
|
|
|
|
|
krb5_get_error_message(krbctx, krberr));
|
|
|
|
|
goto enc_error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case KRB5_KDB_SALTTYPE_V4:
|
|
|
|
|
salt.length = 0;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case KRB5_KDB_SALTTYPE_AFS3:
|
|
|
|
|
|
|
|
|
|
p = strchr(krbPrincipalName, '@');
|
|
|
|
|
if (!p) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"Invalid principal name, no realm found!\n");
|
|
|
|
|
goto enc_error;
|
|
|
|
|
}
|
|
|
|
|
p++;
|
|
|
|
|
salt.data = strdup(p);
|
|
|
|
|
if (!salt.data) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"memory allocation failed\n");
|
|
|
|
|
goto enc_error;
|
|
|
|
|
}
|
|
|
|
|
salt.length = SALT_TYPE_AFS_LENGTH; /* special value */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"Invalid salt type [%d]\n", keysalts[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, keysalts[i].enc_type, &pwd, &salt, &key);
|
|
|
|
|
if (krberr) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"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, kmkey.enctype, key.length, &len);
|
|
|
|
|
if (krberr) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"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) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"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;
|
2007-08-15 11:37:03 -04:00
|
|
|
plain.data = (char *)key.contents;
|
2007-06-29 16:21:31 -04:00
|
|
|
|
|
|
|
|
cipher.ciphertext.length = len;
|
2007-08-15 11:37:03 -04:00
|
|
|
cipher.ciphertext.data = (char *)ptr+2;
|
2007-06-29 16:21:31 -04:00
|
|
|
|
|
|
|
|
krberr = krb5_c_encrypt(krbctx, &kmkey, 0, 0, &plain, &cipher);
|
|
|
|
|
if (krberr) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"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 */
|
|
|
|
|
if (salt.length) {
|
|
|
|
|
ret = ber_printf(be, "{t[{t[i]t[o]}]",
|
|
|
|
|
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0),
|
|
|
|
|
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), keysalts[i].salt_type,
|
|
|
|
|
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), salt.data, salt.length);
|
|
|
|
|
} else {
|
|
|
|
|
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), keysalts[i].salt_type);
|
|
|
|
|
}
|
|
|
|
|
if (ret == -1) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"encoding asn1 KrbSalt failed\n");
|
|
|
|
|
krb5int_c_free_keyblock_contents(krbctx, &key);
|
|
|
|
|
krb5_free_data_contents(krbctx, &salt);
|
|
|
|
|
free(ptr);
|
|
|
|
|
goto enc_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* EncryptionKey */
|
|
|
|
|
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), key.enctype,
|
|
|
|
|
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), ptr, len+2);
|
|
|
|
|
if (ret == -1) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"encoding asn1 EncryptionKey failed\n");
|
|
|
|
|
krb5int_c_free_keyblock_contents(krbctx, &key);
|
|
|
|
|
krb5_free_data_contents(krbctx, &salt);
|
|
|
|
|
free(ptr);
|
|
|
|
|
goto enc_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* make sure we free the memory used now that we are done with it */
|
|
|
|
|
krb5int_c_free_keyblock_contents(krbctx, &key);
|
|
|
|
|
krb5_free_data_contents(krbctx, &salt);
|
|
|
|
|
free(ptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = ber_printf(be, "}]}");
|
|
|
|
|
if (ret == -1) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"encoding asn1 end of sequences failed\n");
|
|
|
|
|
goto enc_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = ber_flatten(be, &bval);
|
|
|
|
|
if (ret == -1) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"flattening asn1 failed\n");
|
|
|
|
|
goto enc_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
svals[0] = slapi_value_new_berval(bval);
|
|
|
|
|
if (!svals[0]) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"Converting berval to Slapi_Value\n");
|
|
|
|
|
goto enc_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
krb5_free_principal(krbctx, princ);
|
|
|
|
|
ber_bvfree(bval);
|
|
|
|
|
ber_free(be, 1);
|
|
|
|
|
return svals;
|
|
|
|
|
|
|
|
|
|
enc_error:
|
|
|
|
|
krb5_free_principal(krbctx, princ);
|
|
|
|
|
if (bval) ber_bvfree(bval);
|
|
|
|
|
if (svals) free(svals);
|
|
|
|
|
if (be) ber_free(be, 1);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ntlm_keys {
|
|
|
|
|
uint8_t lm[16];
|
|
|
|
|
uint8_t nt[16];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#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};
|
|
|
|
|
|
2007-08-15 11:37:03 -04:00
|
|
|
static void lm_shuffle(uint8_t *out, uint8_t *in)
|
2007-06-29 16:21:31 -04:00
|
|
|
{
|
2007-08-15 11:37:03 -04:00
|
|
|
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];
|
2007-06-29 16:21:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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 */
|
2007-08-15 11:37:03 -04:00
|
|
|
upperPasswd = (char *)slapi_utf8StrToUpper((unsigned char *)newPasswd);
|
2007-06-29 16:21:31 -04:00
|
|
|
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 */
|
2007-08-15 11:37:03 -04:00
|
|
|
lm_shuffle(deskey, (uint8_t *)asciiPasswd);
|
2007-06-29 16:21:31 -04:00
|
|
|
|
|
|
|
|
DES_set_key_unchecked(&deskey, &schedule);
|
|
|
|
|
DES_ecb_encrypt(&magic, (DES_cblock *)keys->lm, &schedule, DES_ENCRYPT);
|
|
|
|
|
|
|
|
|
|
/* second half */
|
2007-08-15 11:37:03 -04:00
|
|
|
lm_shuffle(deskey, (uint8_t *)&asciiPasswd[7]);
|
2007-06-29 16:21:31 -04:00
|
|
|
|
|
|
|
|
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;
|
2007-08-20 15:38:47 -04:00
|
|
|
MD4_CTX md4ctx;
|
2007-06-29 16:21:31 -04:00
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2007-08-20 15:38:47 -04:00
|
|
|
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) {
|
2007-06-29 16:21:31 -04:00
|
|
|
ret = -1;
|
|
|
|
|
free(ucs2Passwd);
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
memset(keys->nt, 0, 16);
|
|
|
|
|
}
|
|
|
|
|
|
2007-08-20 15:38:47 -04:00
|
|
|
ret = 0;
|
|
|
|
|
|
2007-06-29 16:21:31 -04:00
|
|
|
done:
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Searches the dn in directory,
|
|
|
|
|
* If found : fills in slapi_entry structure and returns 0
|
|
|
|
|
* If NOT found : returns the search result as LDAP_NO_SUCH_OBJECT
|
|
|
|
|
*/
|
|
|
|
|
static int
|
2007-07-02 10:14:45 -04:00
|
|
|
ipapwd_getEntry( const char *dn, Slapi_Entry **e2 ) {
|
2007-06-29 16:21:31 -04:00
|
|
|
int search_result = 0;
|
|
|
|
|
Slapi_DN *sdn;
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipapwd_getEntry\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
|
|
|
|
|
sdn = slapi_sdn_new_dn_byref(dn);
|
|
|
|
|
if ((search_result = slapi_search_internal_get_entry( sdn, NULL, e2,
|
2007-07-02 10:14:45 -04:00
|
|
|
ipapwd_plugin_id)) != LDAP_SUCCESS ){
|
|
|
|
|
slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "ipapwd_getEntry: No such entry-(%s), err (%d)\n",
|
2007-06-29 16:21:31 -04:00
|
|
|
dn, search_result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
slapi_sdn_free( &sdn );
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "<= ipapwd_getEntry: %d\n", search_result);
|
2007-06-29 16:21:31 -04:00
|
|
|
return search_result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Construct Mods pblock and perform the modify operation
|
|
|
|
|
* Sets result of operation in SLAPI_PLUGIN_INTOP_RESULT
|
|
|
|
|
*/
|
2007-07-02 10:14:45 -04:00
|
|
|
static int ipapwd_apply_mods(const char *dn, Slapi_Mods *mods)
|
2007-06-29 16:21:31 -04:00
|
|
|
{
|
|
|
|
|
Slapi_PBlock *pb;
|
|
|
|
|
int ret=0;
|
|
|
|
|
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipapwd_apply_mods\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
|
|
|
|
|
if (mods && (slapi_mods_get_num_mods(mods) > 0))
|
|
|
|
|
{
|
|
|
|
|
pb = slapi_pblock_new();
|
|
|
|
|
slapi_modify_internal_set_pb (pb, dn,
|
|
|
|
|
slapi_mods_get_ldapmods_byref(mods),
|
|
|
|
|
NULL, /* Controls */
|
|
|
|
|
NULL, /* UniqueID */
|
2007-07-02 10:14:45 -04:00
|
|
|
ipapwd_plugin_id, /* PluginID */
|
2007-06-29 16:21:31 -04:00
|
|
|
0); /* Flags */
|
|
|
|
|
|
|
|
|
|
ret = slapi_modify_internal_pb (pb);
|
|
|
|
|
|
|
|
|
|
slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
|
|
|
|
|
|
|
|
|
|
if (ret != LDAP_SUCCESS){
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "WARNING: modify error %d on entry '%s'\n",
|
2007-06-29 16:21:31 -04:00
|
|
|
ret, dn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
slapi_pblock_destroy(pb);
|
|
|
|
|
}
|
|
|
|
|
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "<= ipapwd_apply_mods: %d\n", ret);
|
2007-06-29 16:21:31 -04:00
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ascii hex output of bytes in "in"
|
|
|
|
|
* out len is 32 (preallocated)
|
|
|
|
|
* in len is 16 */
|
|
|
|
|
static const char hexchars[] = "0123456789ABCDEF";
|
|
|
|
|
static void hexbuf(char *out, const uint8_t *in)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
|
|
|
out[i*2] = hexchars[in[i] >> 4];
|
|
|
|
|
out[i*2+1] = hexchars[in[i] & 0x0f];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Modify the userPassword attribute field of the entry */
|
2007-07-02 10:14:45 -04:00
|
|
|
static int ipapwd_userpassword(Slapi_Entry *targetEntry, const char *newPasswd)
|
2007-06-29 16:21:31 -04:00
|
|
|
{
|
|
|
|
|
char *dn = NULL;
|
|
|
|
|
int ret = 0, i = 0;
|
|
|
|
|
Slapi_Mods *smods;
|
|
|
|
|
Slapi_Value **svals;
|
|
|
|
|
time_t curtime;
|
|
|
|
|
struct tm utctime;
|
|
|
|
|
char timestr[16];
|
|
|
|
|
krb5_context krbctx;
|
|
|
|
|
krb5_error_code krberr;
|
|
|
|
|
char lm[33], nt[33];
|
|
|
|
|
struct ntlm_keys ntlm;
|
|
|
|
|
int ntlm_flags = 0;
|
|
|
|
|
Slapi_Value *sambaSamAccount;
|
|
|
|
|
|
|
|
|
|
krberr = krb5_init_context(&krbctx);
|
|
|
|
|
if (krberr) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "krb5_init_context failed\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
return LDAP_OPERATIONS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipapwd_userpassword\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
|
|
|
|
|
smods = slapi_mods_new();
|
|
|
|
|
dn = slapi_entry_get_ndn( targetEntry );
|
|
|
|
|
|
|
|
|
|
/* generate kerberos keys to be put into krbPrincipalKey */
|
|
|
|
|
svals = encrypt_encode_key(krbctx, targetEntry, newPasswd);
|
|
|
|
|
if (!svals) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "key encryption/encoding failed\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
krb5_free_context(krbctx);
|
|
|
|
|
return LDAP_OPERATIONS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
/* done with it */
|
|
|
|
|
krb5_free_context(krbctx);
|
|
|
|
|
|
|
|
|
|
slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE, "krbPrincipalKey", svals);
|
|
|
|
|
|
|
|
|
|
/* change Last Password Change field with the current date */
|
|
|
|
|
curtime = time(NULL);
|
|
|
|
|
if (!gmtime_r(&curtime, &utctime)) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "failed to retrieve current date (buggy gmtime_r ?)\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
return LDAP_OPERATIONS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
if (utctime.tm_year > 8099 || utctime.tm_mon > 11 || utctime.tm_mday > 31 ||
|
|
|
|
|
utctime.tm_hour > 23 || utctime.tm_min > 59 || utctime.tm_sec > 59) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "retrieved a bad date (buggy gmtime_r ?)\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
return LDAP_OPERATIONS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
snprintf(timestr, 16, "%04d%02d%02d%02d%02d%02dZ", utctime.tm_year+1900, utctime.tm_mon+1,
|
|
|
|
|
utctime.tm_mday, utctime.tm_hour, utctime.tm_min, utctime.tm_sec);
|
|
|
|
|
|
|
|
|
|
slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "krbLastPwdChange", timestr);
|
|
|
|
|
/* TODO: krbPasswordExpiration, (krbMaxTicketLife, krbMaxRenewableAge, krbTicketFlags ?) */
|
|
|
|
|
|
|
|
|
|
sambaSamAccount = slapi_value_new_string("sambaSamAccount");
|
|
|
|
|
if (slapi_entry_attr_has_syntax_value(targetEntry, "objectClass", sambaSamAccount)) {
|
|
|
|
|
/* TODO: retrieve if we want to store the LM hash or not */
|
|
|
|
|
ntlm_flags = KTF_LM_HASH | KTF_NT_HASH;
|
|
|
|
|
}
|
|
|
|
|
slapi_value_free(&sambaSamAccount);
|
|
|
|
|
|
|
|
|
|
if (ntlm_flags) {
|
|
|
|
|
if (encode_ntlm_keys((char *)newPasswd, ntlm_flags, &ntlm) != 0) {
|
|
|
|
|
return LDAP_OPERATIONS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
if (ntlm_flags & KTF_LM_HASH) {
|
|
|
|
|
hexbuf(lm, ntlm.lm);
|
|
|
|
|
lm[32] = '\0';
|
|
|
|
|
slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "sambaLMPassword", lm);
|
|
|
|
|
}
|
|
|
|
|
if (ntlm_flags & KTF_NT_HASH) {
|
|
|
|
|
hexbuf(nt, ntlm.nt);
|
|
|
|
|
nt[32] = '\0';
|
|
|
|
|
slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "sambaNTPassword", nt);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-08-08 22:20:07 -04:00
|
|
|
/* TODO !!!
|
|
|
|
|
* instead of replace we should use a delete/add so that we are
|
|
|
|
|
* completely sure nobody else modified the entry meanwhile and
|
|
|
|
|
* fail if that's the case */
|
|
|
|
|
|
2007-06-29 16:21:31 -04:00
|
|
|
/* commit changes */
|
2007-07-02 10:14:45 -04:00
|
|
|
ret = ipapwd_apply_mods(dn, smods);
|
2007-06-29 16:21:31 -04:00
|
|
|
|
|
|
|
|
slapi_mods_free(&smods);
|
|
|
|
|
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "<= ipapwd_userpassword: %d\n", ret);
|
2007-06-29 16:21:31 -04:00
|
|
|
|
|
|
|
|
for (i = 0; svals[i]; i++) {
|
|
|
|
|
slapi_value_free(&svals[i]);
|
|
|
|
|
}
|
|
|
|
|
free(svals);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2007-08-15 11:37:03 -04:00
|
|
|
#if 0 /* Not used right now */
|
|
|
|
|
|
2007-06-29 16:21:31 -04:00
|
|
|
/* Generate a new, basic random password */
|
2007-07-02 10:14:45 -04:00
|
|
|
static int ipapwd_generate_basic_passwd( int passlen, char **genpasswd )
|
2007-06-29 16:21:31 -04:00
|
|
|
{
|
|
|
|
|
unsigned char *data = NULL;
|
|
|
|
|
char *enc = NULL;
|
|
|
|
|
int datalen = LDAP_EXTOP_PASSMOD_RANDOM_BYTES;
|
|
|
|
|
int enclen = LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN + 1;
|
|
|
|
|
|
|
|
|
|
if ( genpasswd == NULL ) {
|
|
|
|
|
return LDAP_OPERATIONS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( passlen > 0 ) {
|
|
|
|
|
datalen = passlen * 3 / 4 + 1;
|
|
|
|
|
enclen = datalen * 4; /* allocate the large enough space */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data = (unsigned char *)slapi_ch_calloc( datalen, 1 );
|
|
|
|
|
enc = (char *)slapi_ch_calloc( enclen, 1 );
|
|
|
|
|
|
|
|
|
|
/* get random bytes from NSS */
|
|
|
|
|
PK11_GenerateRandom( data, datalen );
|
|
|
|
|
|
|
|
|
|
/* b64 encode the random bytes to get a password made up
|
|
|
|
|
* of printable characters. ldif_base64_encode() will
|
|
|
|
|
* zero-terminate the string */
|
|
|
|
|
(void)ldif_base64_encode( data, enc, passlen, -1 );
|
|
|
|
|
|
|
|
|
|
/* This will get freed by the caller */
|
|
|
|
|
*genpasswd = slapi_ch_malloc( 1 + passlen );
|
|
|
|
|
|
|
|
|
|
/* trim the password to the proper length */
|
|
|
|
|
PL_strncpyz( *genpasswd, enc, passlen + 1 );
|
|
|
|
|
|
|
|
|
|
slapi_ch_free( (void **)&data );
|
|
|
|
|
slapi_ch_free_string( &enc );
|
|
|
|
|
|
|
|
|
|
return LDAP_SUCCESS;
|
|
|
|
|
}
|
2007-08-15 11:37:03 -04:00
|
|
|
#endif
|
2007-06-29 16:21:31 -04:00
|
|
|
|
|
|
|
|
/* Password Modify Extended operation plugin function */
|
|
|
|
|
int
|
2007-07-02 10:14:45 -04:00
|
|
|
ipapwd_extop( Slapi_PBlock *pb )
|
2007-06-29 16:21:31 -04:00
|
|
|
{
|
|
|
|
|
char *oid = NULL;
|
|
|
|
|
char *bindDN = NULL;
|
|
|
|
|
char *authmethod = NULL;
|
|
|
|
|
char *dn = NULL;
|
|
|
|
|
char *oldPasswd = NULL;
|
|
|
|
|
char *newPasswd = NULL;
|
|
|
|
|
char *errMesg = NULL;
|
|
|
|
|
int ret=0, rc=0, sasl_ssf=0, is_ssl=0, is_root=0;
|
|
|
|
|
ber_tag_t tag=0;
|
|
|
|
|
ber_len_t len=-1;
|
|
|
|
|
struct berval *extop_value = NULL;
|
|
|
|
|
BerElement *ber = NULL;
|
|
|
|
|
Slapi_Entry *targetEntry=NULL;
|
|
|
|
|
/* Slapi_DN sdn; */
|
|
|
|
|
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipa_pwd_extop\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
|
|
|
|
|
/* Before going any further, we'll make sure that the right extended operation plugin
|
|
|
|
|
* has been called: i.e., the OID shipped whithin the extended operation request must
|
|
|
|
|
* match this very plugin's OID: EXOP_PASSWD_OID. */
|
|
|
|
|
if ( slapi_pblock_get( pb, SLAPI_EXT_OP_REQ_OID, &oid ) != 0 ) {
|
|
|
|
|
errMesg = "Could not get OID value from request.\n";
|
|
|
|
|
rc = LDAP_OPERATIONS_ERROR;
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error( SLAPI_LOG_PLUGIN, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
errMesg );
|
|
|
|
|
goto free_and_return;
|
|
|
|
|
} else {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error( SLAPI_LOG_PLUGIN, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"Received extended operation request with OID %s\n", oid );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( strcasecmp( oid, EXOP_PASSWD_OID ) != 0) {
|
|
|
|
|
errMesg = "Request OID does not match Passwd OID.\n";
|
|
|
|
|
rc = LDAP_OPERATIONS_ERROR;
|
|
|
|
|
goto free_and_return;
|
|
|
|
|
} else {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error( SLAPI_LOG_PLUGIN, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"Password Modify extended operation request confirmed.\n" );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now , at least we know that the request was indeed a Password Modify one. */
|
|
|
|
|
|
|
|
|
|
#ifdef LDAP_EXTOP_PASSMOD_CONN_SECURE
|
|
|
|
|
/* Allow password modify only for SSL/TLS established connections and
|
|
|
|
|
* connections using SASL privacy layers */
|
|
|
|
|
if ( slapi_pblock_get(pb, SLAPI_CONN_SASL_SSF, &sasl_ssf) != 0) {
|
|
|
|
|
errMesg = "Could not get SASL SSF from connection\n";
|
|
|
|
|
rc = LDAP_OPERATIONS_ERROR;
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error( SLAPI_LOG_PLUGIN, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
errMesg );
|
|
|
|
|
goto free_and_return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (slapi_pblock_get(pb, SLAPI_CONN_IS_SSL_SESSION, &is_ssl) != 0) {
|
|
|
|
|
errMesg = "Could not get IS SSL from connection\n";
|
|
|
|
|
rc = LDAP_OPERATIONS_ERROR;
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error( SLAPI_LOG_PLUGIN, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
errMesg );
|
|
|
|
|
goto free_and_return;
|
|
|
|
|
}
|
|
|
|
|
|
2007-08-31 18:40:01 -04:00
|
|
|
if ( (is_ssl == 0) && (sasl_ssf <= 1) ) {
|
2007-06-29 16:21:31 -04:00
|
|
|
errMesg = "Operation requires a secure connection.\n";
|
|
|
|
|
rc = LDAP_CONFIDENTIALITY_REQUIRED;
|
|
|
|
|
goto free_and_return;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Get the ber value of the extended operation */
|
|
|
|
|
slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &extop_value);
|
|
|
|
|
|
|
|
|
|
if ((ber = ber_init(extop_value)) == NULL)
|
|
|
|
|
{
|
|
|
|
|
errMesg = "PasswdModify Request decode failed.\n";
|
|
|
|
|
rc = LDAP_PROTOCOL_ERROR;
|
|
|
|
|
goto free_and_return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Format of request to parse
|
|
|
|
|
*
|
|
|
|
|
* PasswdModifyRequestValue ::= SEQUENCE {
|
|
|
|
|
* userIdentity [0] OCTET STRING OPTIONAL
|
|
|
|
|
* oldPasswd [1] OCTET STRING OPTIONAL
|
|
|
|
|
* newPasswd [2] OCTET STRING OPTIONAL }
|
|
|
|
|
*
|
|
|
|
|
* The request value field is optional. If it is
|
|
|
|
|
* provided, at least one field must be filled in.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* ber parse code */
|
|
|
|
|
if ( ber_scanf( ber, "{") == LBER_ERROR )
|
|
|
|
|
{
|
|
|
|
|
/* The request field wasn't provided. We'll
|
|
|
|
|
* now try to determine the userid and verify
|
|
|
|
|
* knowledge of the old password via other
|
|
|
|
|
* means.
|
|
|
|
|
*/
|
|
|
|
|
goto parse_req_done;
|
|
|
|
|
} else {
|
|
|
|
|
tag = ber_peek_tag( ber, &len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* identify userID field by tags */
|
|
|
|
|
if (tag == LDAP_EXTOP_PASSMOD_TAG_USERID )
|
|
|
|
|
{
|
|
|
|
|
if ( ber_scanf( ber, "a", &dn) == LBER_ERROR )
|
|
|
|
|
{
|
|
|
|
|
slapi_ch_free_string(&dn);
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "ber_scanf failed :{\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
errMesg = "ber_scanf failed at userID parse.\n";
|
|
|
|
|
rc = LDAP_PROTOCOL_ERROR;
|
|
|
|
|
goto free_and_return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tag = ber_peek_tag( ber, &len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* identify oldPasswd field by tags */
|
|
|
|
|
if (tag == LDAP_EXTOP_PASSMOD_TAG_OLDPWD )
|
|
|
|
|
{
|
|
|
|
|
if ( ber_scanf( ber, "a", &oldPasswd ) == LBER_ERROR )
|
|
|
|
|
{
|
|
|
|
|
slapi_ch_free_string(&oldPasswd);
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "ber_scanf failed :{\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
errMesg = "ber_scanf failed at oldPasswd parse.\n";
|
|
|
|
|
rc = LDAP_PROTOCOL_ERROR;
|
|
|
|
|
goto free_and_return;
|
|
|
|
|
}
|
|
|
|
|
tag = ber_peek_tag( ber, &len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* identify newPasswd field by tags */
|
|
|
|
|
if (tag == LDAP_EXTOP_PASSMOD_TAG_NEWPWD )
|
|
|
|
|
{
|
|
|
|
|
if ( ber_scanf( ber, "a", &newPasswd ) == LBER_ERROR )
|
|
|
|
|
{
|
|
|
|
|
slapi_ch_free_string(&newPasswd);
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "ber_scanf failed :{\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
errMesg = "ber_scanf failed at newPasswd parse.\n";
|
|
|
|
|
rc = LDAP_PROTOCOL_ERROR;
|
|
|
|
|
goto free_and_return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parse_req_done:
|
|
|
|
|
/* Uncomment for debugging, otherwise we don't want to leak the password values into the log... */
|
|
|
|
|
/* LDAPDebug( LDAP_DEBUG_ARGS, "passwd: dn (%s), oldPasswd (%s) ,newPasswd (%s)\n",
|
|
|
|
|
dn, oldPasswd, newPasswd); */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Get Bind DN */
|
|
|
|
|
slapi_pblock_get( pb, SLAPI_CONN_DN, &bindDN );
|
|
|
|
|
|
|
|
|
|
/* If the connection is bound anonymously, we must refuse to process this operation. */
|
|
|
|
|
if (bindDN == NULL || *bindDN == '\0') {
|
|
|
|
|
/* Refuse the operation because they're bound anonymously */
|
|
|
|
|
errMesg = "Anonymous Binds are not allowed.\n";
|
|
|
|
|
rc = LDAP_INSUFFICIENT_ACCESS;
|
|
|
|
|
goto free_and_return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* A new password was not supplied in the request, and we do not support
|
|
|
|
|
* password generation yet.
|
|
|
|
|
*/
|
|
|
|
|
if (newPasswd == NULL || *newPasswd == '\0') {
|
|
|
|
|
errMesg = "Password generation not implemented.\n";
|
|
|
|
|
rc = LDAP_UNWILLING_TO_PERFORM;
|
|
|
|
|
goto free_and_return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (oldPasswd == NULL || *oldPasswd == '\0') {
|
|
|
|
|
/* If user is authenticated, they already gave their password during
|
|
|
|
|
the bind operation (or used sasl or client cert auth or OS creds) */
|
|
|
|
|
slapi_pblock_get(pb, SLAPI_CONN_AUTHMETHOD, &authmethod);
|
|
|
|
|
if (!authmethod || !strcmp(authmethod, SLAPD_AUTH_NONE)) {
|
|
|
|
|
errMesg = "User must be authenticated to the directory server.\n";
|
|
|
|
|
rc = LDAP_INSUFFICIENT_ACCESS;
|
|
|
|
|
goto free_and_return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Determine the target DN for this operation */
|
|
|
|
|
/* Did they give us a DN ? */
|
|
|
|
|
if (dn == NULL || *dn == '\0') {
|
|
|
|
|
/* Get the DN from the bind identity on this connection */
|
|
|
|
|
dn = slapi_ch_strdup(bindDN);
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
"Missing userIdentity in request, using the bind DN instead.\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
slapi_pblock_set( pb, SLAPI_ORIGINAL_TARGET, dn );
|
|
|
|
|
|
|
|
|
|
/* Now we have the DN, look for the entry */
|
2007-07-02 10:14:45 -04:00
|
|
|
ret = ipapwd_getEntry(dn, &targetEntry);
|
2007-06-29 16:21:31 -04:00
|
|
|
/* If we can't find the entry, then that's an error */
|
|
|
|
|
if (ret) {
|
|
|
|
|
/* Couldn't find the entry, fail */
|
|
|
|
|
errMesg = "No such Entry exists.\n" ;
|
|
|
|
|
rc = LDAP_NO_SUCH_OBJECT ;
|
|
|
|
|
goto free_and_return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* First thing to do is to ask access control if the bound identity has
|
|
|
|
|
rights to modify the userpassword attribute on this entry. If not, then
|
|
|
|
|
we fail immediately with insufficient access. This means that we don't
|
|
|
|
|
leak any useful information to the client such as current password
|
|
|
|
|
wrong, etc.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
is_root = slapi_dn_isroot(bindDN);
|
|
|
|
|
slapi_pblock_set(pb, SLAPI_REQUESTOR_ISROOT, &is_root);
|
|
|
|
|
|
|
|
|
|
/* In order to perform the access control check , we need to select a backend (even though
|
|
|
|
|
* we don't actually need it otherwise).
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
Slapi_Backend *be = NULL;
|
|
|
|
|
|
|
|
|
|
be = slapi_be_select(slapi_entry_get_sdn(targetEntry));
|
|
|
|
|
if (NULL == be) {
|
|
|
|
|
errMesg = "Failed to find backend for target entry";
|
|
|
|
|
rc = LDAP_OPERATIONS_ERROR;
|
|
|
|
|
goto free_and_return;
|
|
|
|
|
}
|
|
|
|
|
slapi_pblock_set(pb, SLAPI_BACKEND, be);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = slapi_access_allowed ( pb, targetEntry, SLAPI_USERPWD_ATTR, NULL, SLAPI_ACL_WRITE );
|
|
|
|
|
if ( ret != LDAP_SUCCESS ) {
|
|
|
|
|
errMesg = "Insufficient access rights\n";
|
|
|
|
|
rc = LDAP_INSUFFICIENT_ACCESS;
|
|
|
|
|
goto free_and_return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now we have the entry which we want to modify
|
|
|
|
|
* They gave us a password (old), check it against the target entry
|
|
|
|
|
* Is the old password valid ?
|
|
|
|
|
*/
|
|
|
|
|
if (oldPasswd && *oldPasswd) {
|
|
|
|
|
/* If user is authenticated, they already gave their password during
|
|
|
|
|
the bind operation (or used sasl or client cert auth or OS creds) */
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "oldPasswd provided, but we will ignore it");
|
2007-06-29 16:21:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Now we're ready to make actual password change */
|
2007-07-02 10:14:45 -04:00
|
|
|
ret = ipapwd_userpassword(targetEntry, newPasswd);
|
2007-06-29 16:21:31 -04:00
|
|
|
if (ret != LDAP_SUCCESS) {
|
|
|
|
|
/* Failed to modify the password, e.g. because insufficient access allowed */
|
|
|
|
|
errMesg = "Failed to update password\n";
|
|
|
|
|
rc = ret;
|
|
|
|
|
goto free_and_return;
|
|
|
|
|
}
|
|
|
|
|
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "<= ipa_pwd_extop: %d\n", rc);
|
2007-06-29 16:21:31 -04:00
|
|
|
|
|
|
|
|
/* Free anything that we allocated above */
|
|
|
|
|
free_and_return:
|
|
|
|
|
slapi_ch_free_string(&oldPasswd);
|
|
|
|
|
slapi_ch_free_string(&newPasswd);
|
|
|
|
|
/* Either this is the same pointer that we allocated and set above,
|
|
|
|
|
* or whoever used it should have freed it and allocated a new
|
|
|
|
|
* value that we need to free here */
|
|
|
|
|
slapi_pblock_get( pb, SLAPI_ORIGINAL_TARGET, &dn );
|
|
|
|
|
slapi_ch_free_string(&dn);
|
|
|
|
|
slapi_pblock_set( pb, SLAPI_ORIGINAL_TARGET, NULL );
|
|
|
|
|
slapi_ch_free_string(&authmethod);
|
|
|
|
|
|
|
|
|
|
if ( targetEntry != NULL ){
|
|
|
|
|
slapi_entry_free (targetEntry);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( ber != NULL ){
|
|
|
|
|
ber_free(ber, 1);
|
|
|
|
|
ber = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error( SLAPI_LOG_PLUGIN, "ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
errMesg ? errMesg : "success" );
|
2007-08-15 11:37:03 -04:00
|
|
|
slapi_send_ldap_result( pb, rc, NULL, errMesg, 0, NULL );
|
2007-06-29 16:21:31 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
return( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
|
|
|
|
|
|
2007-07-02 10:14:45 -04:00
|
|
|
}/* ipa_pwd_extop */
|
2007-06-29 16:21:31 -04:00
|
|
|
|
|
|
|
|
|
2007-07-02 10:14:45 -04:00
|
|
|
static char *ipapwd_oid_list[] = {
|
2007-06-29 16:21:31 -04:00
|
|
|
EXOP_PASSWD_OID,
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2007-07-02 10:14:45 -04:00
|
|
|
static char *ipapwd_name_list[] = {
|
|
|
|
|
"ipa_pwd_extop",
|
2007-06-29 16:21:31 -04:00
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* will read this from the krbSupportedEncSaltTypes in the krbRealmContainer later on */
|
|
|
|
|
const char *krb_sup_encs[] = {
|
|
|
|
|
"des3-hmac-sha1:normal",
|
|
|
|
|
"arcfour-hmac:normal",
|
|
|
|
|
"des-hmac-sha1:normal",
|
|
|
|
|
"des-cbc-md5:normal",
|
|
|
|
|
"des-cbc-crc:normal",
|
|
|
|
|
"des-cbc-crc:v4",
|
|
|
|
|
"des-cbc-crc:afs3",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define KRBCHECK(ctx, err, fname) do { \
|
|
|
|
|
if (err) { \
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_PLUGIN, "ipapwd_start", \
|
2007-06-29 16:21:31 -04:00
|
|
|
"%s failed [%s]\n", fname, \
|
|
|
|
|
krb5_get_error_message(ctx, err)); \
|
|
|
|
|
return LDAP_OPERATIONS_ERROR; \
|
|
|
|
|
} } while(0)
|
|
|
|
|
|
|
|
|
|
/* Init data structs */
|
|
|
|
|
/* TODO: read input from tree */
|
2007-07-02 10:14:45 -04:00
|
|
|
int ipapwd_start( Slapi_PBlock *pb )
|
2007-06-29 16:21:31 -04:00
|
|
|
{
|
|
|
|
|
int krberr, i;
|
|
|
|
|
krb5_context krbctx;
|
|
|
|
|
char *config_dn;
|
|
|
|
|
Slapi_Entry *config_entry;
|
|
|
|
|
const char *stash_file;
|
|
|
|
|
int fd;
|
|
|
|
|
ssize_t r;
|
|
|
|
|
uint16_t e;
|
|
|
|
|
unsigned int l;
|
2007-08-15 11:37:03 -04:00
|
|
|
unsigned char *o;
|
2007-06-29 16:21:31 -04:00
|
|
|
|
|
|
|
|
krberr = krb5_init_context(&krbctx);
|
|
|
|
|
if (krberr) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "krb5_init_context failed\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
return LDAP_OPERATIONS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; krb_sup_encs[i]; i++) /* count */ ;
|
|
|
|
|
keysalts = (struct krb5p_keysalt *)malloc(sizeof(struct krb5p_keysalt) * (i + 1));
|
|
|
|
|
if (!keysalts) {
|
|
|
|
|
krb5_free_context(krbctx);
|
|
|
|
|
return LDAP_OPERATIONS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0, n_keysalts = 0; krb_sup_encs[i]; i++) {
|
|
|
|
|
char *enc, *salt;
|
|
|
|
|
krb5_int32 tmpenc;
|
|
|
|
|
krb5_int32 tmpsalt;
|
|
|
|
|
krb5_boolean similar;
|
|
|
|
|
int j;
|
|
|
|
|
|
|
|
|
|
enc = strdup(krb_sup_encs[i]);
|
|
|
|
|
if (!enc) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error( SLAPI_LOG_PLUGIN, "ipapwd_start", "Allocation error\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
krb5_free_context(krbctx);
|
|
|
|
|
return LDAP_OPERATIONS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
salt = strchr(enc, ':');
|
|
|
|
|
if (!salt) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error( SLAPI_LOG_PLUGIN, "ipapwd_start", "Invalid krb5 enc string\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
free(enc);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
*salt = '\0'; /* null terminate the enc type */
|
|
|
|
|
salt++; /* skip : */
|
|
|
|
|
|
|
|
|
|
krberr = krb5_string_to_enctype(enc, &tmpenc);
|
|
|
|
|
if (krberr) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error( SLAPI_LOG_PLUGIN, "ipapwd_start", "Invalid krb5 enctype\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
free(enc);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
krberr = krb5_string_to_salttype(salt, &tmpsalt);
|
|
|
|
|
for (j = 0; j < n_keysalts; j++) {
|
|
|
|
|
krb5_c_enctype_compare(krbctx, keysalts[j].enc_type, tmpenc, &similar);
|
|
|
|
|
if (similar && (keysalts[j].salt_type == tmpsalt)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (j == n_keysalts) {
|
|
|
|
|
/* not found */
|
|
|
|
|
keysalts[j].enc_type = tmpenc;
|
|
|
|
|
keysalts[j].salt_type = tmpsalt;
|
|
|
|
|
n_keysalts++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(enc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*retrieve the master key from the stash file */
|
|
|
|
|
if (slapi_pblock_get(pb, SLAPI_TARGET_DN, &config_dn) != 0) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "No config DN?\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
krb5_free_context(krbctx);
|
|
|
|
|
return LDAP_OPERATIONS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2007-07-02 10:14:45 -04:00
|
|
|
if (ipapwd_getEntry(config_dn, &config_entry) != LDAP_SUCCESS) {
|
|
|
|
|
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "No config Entry?\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
krb5_free_context(krbctx);
|
|
|
|
|
return LDAP_OPERATIONS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stash_file = slapi_entry_attr_get_charptr(config_entry, "nsslapd-pluginarg0");
|
|
|
|
|
if (!stash_file) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Missing Master key stash file path configuration entry (nsslapd-pluginarg0)!\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
krb5_free_context(krbctx);
|
|
|
|
|
return LDAP_OPERATIONS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fd = open(stash_file, O_RDONLY);
|
|
|
|
|
if (fd == -1) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Missing Master key stash file!\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
krb5_free_context(krbctx);
|
|
|
|
|
return LDAP_OPERATIONS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r = read(fd, &e, 2); /* read enctype a local endian 16bit value */
|
|
|
|
|
if (r != 2) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Error reading Master key stash file!\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
krb5_free_context(krbctx);
|
|
|
|
|
return LDAP_OPERATIONS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r = read(fd, &l, sizeof(l)); /* read the key length, a horrible sizeof(int) local endian value */
|
|
|
|
|
if (r != sizeof(l)) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Error reading Master key stash file!\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
krb5_free_context(krbctx);
|
|
|
|
|
return LDAP_OPERATIONS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (l == 0 || l > 1024) { /* the maximum key size should be 32 bytes, lets's not accept more than 1k anyway */
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Invalid key lenght, Master key stash file corrupted?\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
krb5_free_context(krbctx);
|
|
|
|
|
return LDAP_OPERATIONS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
o = malloc(l);
|
|
|
|
|
if (!o) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Memory allocation problem!\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
krb5_free_context(krbctx);
|
|
|
|
|
return LDAP_OPERATIONS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r = read(fd, o, l);
|
|
|
|
|
if (r != l) {
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Error reading Master key stash file!\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
krb5_free_context(krbctx);
|
|
|
|
|
return LDAP_OPERATIONS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
|
|
kmkey.magic = KV5M_KEYBLOCK;
|
|
|
|
|
kmkey.enctype = e;
|
|
|
|
|
kmkey.length = l;
|
|
|
|
|
kmkey.contents = o;
|
|
|
|
|
|
|
|
|
|
krb5_free_context(krbctx);
|
|
|
|
|
return LDAP_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Initialization function */
|
2007-07-02 10:14:45 -04:00
|
|
|
int ipapwd_init( Slapi_PBlock *pb )
|
2007-06-29 16:21:31 -04:00
|
|
|
{
|
|
|
|
|
/* Get the arguments appended to the plugin extendedop directive. The first argument
|
|
|
|
|
* (after the standard arguments for the directive) should contain the OID of the
|
|
|
|
|
* extended operation.
|
|
|
|
|
*/
|
2007-07-02 10:14:45 -04:00
|
|
|
if ((slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &ipapwd_plugin_id) != 0)
|
|
|
|
|
|| (ipapwd_plugin_id == NULL)) {
|
|
|
|
|
slapi_log_error( SLAPI_LOG_PLUGIN, "ipapwd_init", "Could not get identity or identity was NULL\n");
|
2007-06-29 16:21:31 -04:00
|
|
|
return( -1 );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Register the plug-in function as an extended operation
|
|
|
|
|
* plug-in function that handles the operation identified by
|
|
|
|
|
* OID 1.3.6.1.4.1.4203.1.11.1 . Also specify the version of the server
|
|
|
|
|
* plug-in */
|
|
|
|
|
if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_pblock_set( pb, SLAPI_PLUGIN_START_FN, (void *) ipapwd_start ) != 0 ||
|
|
|
|
|
slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_FN, (void *) ipapwd_extop ) != 0 ||
|
|
|
|
|
slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_OIDLIST, ipapwd_oid_list ) != 0 ||
|
|
|
|
|
slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_NAMELIST, ipapwd_name_list ) != 0 ) {
|
2007-06-29 16:21:31 -04:00
|
|
|
|
2007-07-02 10:14:45 -04:00
|
|
|
slapi_log_error( SLAPI_LOG_PLUGIN, "ipapwd_init",
|
2007-06-29 16:21:31 -04:00
|
|
|
"Failed to set plug-in version, function, and OID.\n" );
|
|
|
|
|
return( -1 );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return( 0 );
|
|
|
|
|
}
|