From a5f84481e38ffc79043bfbac5f0353856b77b141 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Sat, 5 Mar 2005 01:22:53 +0000 Subject: r5655: Added support for Novell NDS universal password. Code donated by Vince Brimhall - slight tidyup by me to use Samba conventions. Vince - thanks a *lot* for this code - please test to make sure I haven't messed anything up. Jeremy. (This used to be commit 6f5ea963abe8e19d17a1803d4bedd9d87a317e58) --- source3/auth/auth_sam.c | 8 +- source3/configure.in | 2 +- source3/include/passdb.h | 6 +- source3/include/smbldap.h | 25 ++ source3/passdb/pdb_interface.c | 36 ++ source3/passdb/pdb_ldap.c | 136 +++--- source3/passdb/pdb_nds.c | 937 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1092 insertions(+), 58 deletions(-) create mode 100644 source3/passdb/pdb_nds.c (limited to 'source3') diff --git a/source3/auth/auth_sam.c b/source3/auth/auth_sam.c index 35f85f5e60..9da59f220a 100644 --- a/source3/auth/auth_sam.c +++ b/source3/auth/auth_sam.c @@ -232,6 +232,7 @@ static NTSTATUS check_sam_security(const struct auth_context *auth_context, SAM_ACCOUNT *sampass=NULL; BOOL ret; NTSTATUS nt_status; + NTSTATUS update_login_attempts_status; DATA_BLOB user_sess_key = data_blob(NULL, 0); DATA_BLOB lm_sess_key = data_blob(NULL, 0); BOOL updated_autolock = False, updated_badpw = False; @@ -269,7 +270,12 @@ static NTSTATUS check_sam_security(const struct auth_context *auth_context, nt_status = sam_password_ok(auth_context, mem_ctx, sampass, user_info, &user_sess_key, &lm_sess_key); - + + /* Notify passdb backend of login success/failure. If not NT_STATUS_OK the backend doesn't like the login */ + update_login_attempts_status = pdb_update_login_attempts(sampass, NT_STATUS_IS_OK(nt_status)); + if (!NT_STATUS_IS_OK(update_login_attempts_status)) + nt_status = update_login_attempts_status; + if (!NT_STATUS_IS_OK(nt_status)) { if (NT_STATUS_EQUAL(nt_status,NT_STATUS_WRONG_PASSWORD) && pdb_get_acct_ctrl(sampass) &ACB_NORMAL) { diff --git a/source3/configure.in b/source3/configure.in index 7e7c3c2035..7338b879ee 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -4541,7 +4541,7 @@ SMB_MODULE(pdb_pgsql, passdb/pdb_pgsql.o, "bin/pgsql.$SHLIBEXT", PDB, ## end of contributed pdb_modules ########################################################################### -SMB_MODULE(pdb_ldap, passdb/pdb_ldap.o, "bin/ldapsam.$SHLIBEXT", PDB, +SMB_MODULE(pdb_ldap, passdb/pdb_ldap.o passdb/pdb_nds.o, "bin/ldapsam.$SHLIBEXT", PDB, [ PASSDB_LIBS="$PASSDB_LIBS $LDAP_LIBS" ] ) SMB_MODULE(pdb_smbpasswd, passdb/pdb_smbpasswd.o, "bin/smbpasswd.$SHLIBEXT", PDB) SMB_MODULE(pdb_tdbsam, passdb/pdb_tdb.o, "bin/tdbsam.$SHLIBEXT", PDB) diff --git a/source3/include/passdb.h b/source3/include/passdb.h index 3c244e7625..5a70bb45a8 100644 --- a/source3/include/passdb.h +++ b/source3/include/passdb.h @@ -241,7 +241,7 @@ struct acct_info * this SAMBA will load. Increment this if *ANY* changes are made to the interface. */ -#define PASSDB_INTERFACE_VERSION 7 +#define PASSDB_INTERFACE_VERSION 8 typedef struct pdb_context { @@ -267,6 +267,8 @@ typedef struct pdb_context NTSTATUS (*pdb_delete_sam_account)(struct pdb_context *, SAM_ACCOUNT *username); + NTSTATUS (*pdb_update_login_attempts)(struct pdb_context *context, SAM_ACCOUNT *sam_acct, BOOL success); + NTSTATUS (*pdb_getgrsid)(struct pdb_context *context, GROUP_MAP *map, DOM_SID sid); NTSTATUS (*pdb_getgrgid)(struct pdb_context *context, GROUP_MAP *map, gid_t gid); @@ -371,6 +373,8 @@ typedef struct pdb_methods NTSTATUS (*delete_sam_account)(struct pdb_methods *, SAM_ACCOUNT *username); + NTSTATUS (*update_login_attempts)(struct pdb_methods *methods, SAM_ACCOUNT *sam_acct, BOOL success); + NTSTATUS (*getgrsid)(struct pdb_methods *methods, GROUP_MAP *map, DOM_SID sid); NTSTATUS (*getgrgid)(struct pdb_methods *methods, GROUP_MAP *map, gid_t gid); diff --git a/source3/include/smbldap.h b/source3/include/smbldap.h index d005104dea..e6a6a1b7c6 100644 --- a/source3/include/smbldap.h +++ b/source3/include/smbldap.h @@ -155,6 +155,31 @@ struct smbldap_state { struct timeval last_rebind; }; +/* struct used by both pdb_ldap.c and pdb_nds.c */ + +struct ldapsam_privates { + struct smbldap_state *smbldap_state; + + /* Former statics */ + LDAPMessage *result; + LDAPMessage *entry; + int index; + + const char *domain_name; + DOM_SID domain_sid; + + /* configuration items */ + int schema_ver; + + char *domain_dn; + + /* Is this NDS ldap? */ + int is_nds_ldap; + + /* ldap server location parameter */ + char *location; +}; + #endif /* HAVE_LDAP */ struct smbldap_state; diff --git a/source3/passdb/pdb_interface.c b/source3/passdb/pdb_interface.c index 36a575214b..84d398ccd6 100644 --- a/source3/passdb/pdb_interface.c +++ b/source3/passdb/pdb_interface.c @@ -325,6 +325,24 @@ static NTSTATUS context_delete_sam_account(struct pdb_context *context, SAM_ACCO return sam_acct->methods->delete_sam_account(sam_acct->methods, sam_acct); } +static NTSTATUS context_update_login_attempts(struct pdb_context *context, + SAM_ACCOUNT *sam_acct, BOOL success) +{ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + + if (!context) { + DEBUG(0, ("invalid pdb_context specified!\n")); + return ret; + } + + if (!sam_acct || !sam_acct->methods){ + DEBUG(0, ("invalid sam_acct specified\n")); + return ret; + } + + return sam_acct->methods->update_login_attempts(sam_acct->methods, sam_acct, success); +} + static NTSTATUS context_getgrsid(struct pdb_context *context, GROUP_MAP *map, DOM_SID sid) { @@ -749,6 +767,7 @@ static NTSTATUS make_pdb_context(struct pdb_context **context) (*context)->pdb_add_sam_account = context_add_sam_account; (*context)->pdb_update_sam_account = context_update_sam_account; (*context)->pdb_delete_sam_account = context_delete_sam_account; + (*context)->pdb_update_login_attempts = context_update_login_attempts; (*context)->pdb_getgrsid = context_getgrsid; (*context)->pdb_getgrgid = context_getgrgid; (*context)->pdb_getgrnam = context_getgrnam; @@ -992,6 +1011,17 @@ BOOL pdb_delete_sam_account(SAM_ACCOUNT *sam_acct) return NT_STATUS_IS_OK(pdb_context->pdb_delete_sam_account(pdb_context, sam_acct)); } +NTSTATUS pdb_update_login_attempts(SAM_ACCOUNT *sam_acct, BOOL success) +{ + struct pdb_context *pdb_context = pdb_get_static_context(False); + + if (!pdb_context) { + return NT_STATUS_NOT_IMPLEMENTED; + } + + return pdb_context->pdb_update_login_attempts(pdb_context, sam_acct, success); +} + BOOL pdb_getgrsid(GROUP_MAP *map, DOM_SID sid) { struct pdb_context *pdb_context = pdb_get_static_context(False); @@ -1279,6 +1309,11 @@ static NTSTATUS pdb_default_delete_sam_account (struct pdb_methods *methods, SAM return NT_STATUS_NOT_IMPLEMENTED; } +static NTSTATUS pdb_default_update_login_attempts (struct pdb_methods *methods, SAM_ACCOUNT *newpwd, BOOL success) +{ + return NT_STATUS_OK; +} + static NTSTATUS pdb_default_setsampwent(struct pdb_methods *methods, BOOL update, uint16 acb_mask) { return NT_STATUS_NOT_IMPLEMENTED; @@ -1422,6 +1457,7 @@ NTSTATUS make_pdb_methods(TALLOC_CTX *mem_ctx, PDB_METHODS **methods) (*methods)->add_sam_account = pdb_default_add_sam_account; (*methods)->update_sam_account = pdb_default_update_sam_account; (*methods)->delete_sam_account = pdb_default_delete_sam_account; + (*methods)->update_login_attempts = pdb_default_update_login_attempts; (*methods)->getgrsid = pdb_default_getgrsid; (*methods)->getgrgid = pdb_default_getgrgid; diff --git a/source3/passdb/pdb_ldap.c b/source3/passdb/pdb_ldap.c index 0a0426ef2a..7511b67bae 100644 --- a/source3/passdb/pdb_ldap.c +++ b/source3/passdb/pdb_ldap.c @@ -83,28 +83,11 @@ #include "smbldap.h" -struct ldapsam_privates { - struct smbldap_state *smbldap_state; - - /* Former statics */ - LDAPMessage *result; - LDAPMessage *entry; - int index; - - const char *domain_name; - DOM_SID domain_sid; - - /* configuration items */ - int schema_ver; - - char *domain_dn; -}; - /********************************************************************** Free a LDAPMessage (one is stored on the SAM_ACCOUNT). **********************************************************************/ -static void private_data_free_fn(void **result) +void private_data_free_fn(void **result) { ldap_msgfree(*result); *result = NULL; @@ -134,7 +117,7 @@ static const char* get_userattr_key2string( int schema_ver, int key ) Return the list of attribute names given a user schema version. **********************************************************************/ -static const char** get_userattr_list( int schema_ver ) +const char** get_userattr_list( int schema_ver ) { switch ( schema_ver ) { case SCHEMAVER_SAMBAACCOUNT: @@ -199,7 +182,7 @@ static const char* get_objclass_filter( int schema_ver ) Run the search by name. ******************************************************************/ -static int ldapsam_search_suffix_by_name (struct ldapsam_privates *ldap_state, +int ldapsam_search_suffix_by_name(struct ldapsam_privates *ldap_state, const char *user, LDAPMessage ** result, const char **attr) @@ -444,7 +427,7 @@ static time_t ldapsam_get_entry_timestamp( (Based on init_sam_from_buffer in pdb_tdb.c) *********************************************************************/ -static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state, +static BOOL init_sam_from_ldap(struct ldapsam_privates *ldap_state, SAM_ACCOUNT * sampass, LDAPMessage * entry) { @@ -470,6 +453,7 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state, uint32 user_rid; uint8 smblmpwd[LM_HASH_LEN], smbntpwd[NT_HASH_LEN]; + BOOL use_samba_attrs = True; uint16 acct_ctrl = 0, logon_divs; uint16 bad_password_count = 0, @@ -708,27 +692,56 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state, hours_len = 21; memset(hours, 0xff, hours_len); - if (!smbldap_get_single_pstring (ldap_state->smbldap_state->ldap_struct, entry, - get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LMPW), temp)) { - /* leave as default */ - } else { - pdb_gethexpwd(temp, smblmpwd); - memset((char *)temp, '\0', strlen(temp)+1); - if (!pdb_set_lanman_passwd(sampass, smblmpwd, PDB_SET)) - return False; - ZERO_STRUCT(smblmpwd); - } + if (ldap_state->is_nds_ldap) { + char *user_dn; + size_t pwd_len; + uchar clear_text_pw[512]; + + /* Make call to Novell eDirectory ldap extension to get clear text password. + NOTE: This will only work if we have an SSL connection to eDirectory. */ + user_dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry); + if (user_dn != NULL) { + DEBUG(3, ("init_sam_from_ldap: smbldap_get_dn(%s) returned '%s'\n", username, user_dn)); + + pwd_len = sizeof(clear_text_pw); + if (pdb_nds_get_password(ldap_state->smbldap_state, user_dn, &pwd_len, clear_text_pw) == LDAP_SUCCESS) { + nt_lm_owf_gen(clear_text_pw, smbntpwd, smblmpwd); + if (!pdb_set_lanman_passwd(sampass, smblmpwd, PDB_SET)) + return False; + ZERO_STRUCT(smblmpwd); + if (!pdb_set_nt_passwd(sampass, smbntpwd, PDB_SET)) + return False; + ZERO_STRUCT(smbntpwd); + use_samba_attrs = False; + } + } else { + DEBUG(0, ("init_sam_from_ldap: failed to get user_dn for '%s'\n", username)); + } + } - if (!smbldap_get_single_pstring (ldap_state->smbldap_state->ldap_struct, entry, - get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_NTPW), temp)) { - /* leave as default */ - } else { - pdb_gethexpwd(temp, smbntpwd); - memset((char *)temp, '\0', strlen(temp)+1); - if (!pdb_set_nt_passwd(sampass, smbntpwd, PDB_SET)) - return False; - ZERO_STRUCT(smbntpwd); - } + if (use_samba_attrs) { + if (!smbldap_get_single_pstring (ldap_state->smbldap_state->ldap_struct, entry, + get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LMPW), temp)) { + /* leave as default */ + } else { + pdb_gethexpwd(temp, smblmpwd); + memset((char *)temp, '\0', strlen(temp)+1); + if (!pdb_set_lanman_passwd(sampass, smblmpwd, PDB_SET)) + return False; + ZERO_STRUCT(smblmpwd); + } + + if (!smbldap_get_single_pstring (ldap_state->smbldap_state->ldap_struct, entry, + get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_NTPW), temp)) { + /* leave as default */ + } else { + pdb_gethexpwd(temp, smbntpwd); + memset((char *)temp, '\0', strlen(temp)+1); + if (!pdb_set_nt_passwd(sampass, smbntpwd, PDB_SET)) + return False; + ZERO_STRUCT(smbntpwd); + } + } account_policy_get(AP_PASSWORD_HISTORY, &pwHistLen); if (pwHistLen > 0){ @@ -1491,15 +1504,17 @@ static NTSTATUS ldapsam_modify_entry(struct pdb_methods *my_methods, (pdb_get_plaintext_passwd(newpwd)!=NULL)) { BerElement *ber; struct berval *bv; - char *retoid; - struct berval *retdata; + char *retoid = NULL; + struct berval *retdata = NULL; char *utf8_password; char *utf8_dn; - if (!ldapsam_can_pwchange_exop(ldap_state->smbldap_state)) { - DEBUG(2, ("ldap password change requested, but LDAP " - "server does not support it -- ignoring\n")); - return NT_STATUS_OK; + if (!ldap_state->is_nds_ldap) { + if (!ldapsam_can_pwchange_exop(ldap_state->smbldap_state)) { + DEBUG(2, ("ldap password change requested, but LDAP " + "server does not support it -- ignoring\n")); + return NT_STATUS_OK; + } } if (push_utf8_allocate(&utf8_password, pdb_get_plaintext_passwd(newpwd)) == (size_t)-1) { @@ -1533,10 +1548,16 @@ static NTSTATUS ldapsam_modify_entry(struct pdb_methods *my_methods, SAFE_FREE(utf8_password); ber_free(ber, 1); - if ((rc = smbldap_extended_operation(ldap_state->smbldap_state, - LDAP_EXOP_MODIFY_PASSWD, - bv, NULL, NULL, &retoid, - &retdata)) != LDAP_SUCCESS) { + if (!ldap_state->is_nds_ldap) { + rc = smbldap_extended_operation(ldap_state->smbldap_state, + LDAP_EXOP_MODIFY_PASSWD, + bv, NULL, NULL, &retoid, + &retdata); + } else { + rc = pdb_nds_set_password(ldap_state->smbldap_state, dn, + pdb_get_plaintext_passwd(newpwd)); + } + if (rc != LDAP_SUCCESS) { char *ld_error = NULL; if (rc == LDAP_OBJECT_CLASS_VIOLATION) { @@ -1559,8 +1580,10 @@ static NTSTATUS ldapsam_modify_entry(struct pdb_methods *my_methods, #ifdef DEBUG_PASSWORD DEBUG(100,("ldapsam_modify_entry: LDAP Password changed to %s\n",pdb_get_plaintext_passwd(newpwd))); #endif - ber_bvfree(retdata); - ber_memfree(retoid); + if (retdata) + ber_bvfree(retdata); + if (retoid) + ber_memfree(retoid); } ber_bvfree(bv); } @@ -3174,7 +3197,7 @@ static NTSTATUS pdb_init_ldapsam_common(PDB_CONTEXT *pdb_context, PDB_METHODS ** Initialise the 'compat' mode for pdb_ldap *********************************************************************/ -static NTSTATUS pdb_init_ldapsam_compat(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location) +NTSTATUS pdb_init_ldapsam_compat(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location) { NTSTATUS nt_status; struct ldapsam_privates *ldap_state; @@ -3213,7 +3236,7 @@ static NTSTATUS pdb_init_ldapsam_compat(PDB_CONTEXT *pdb_context, PDB_METHODS ** Initialise the normal mode for pdb_ldap *********************************************************************/ -static NTSTATUS pdb_init_ldapsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location) +NTSTATUS pdb_init_ldapsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location) { NTSTATUS nt_status; struct ldapsam_privates *ldap_state; @@ -3318,5 +3341,8 @@ NTSTATUS pdb_ldap_init(void) if (!NT_STATUS_IS_OK(nt_status = smb_register_passdb(PASSDB_INTERFACE_VERSION, "ldapsam_compat", pdb_init_ldapsam_compat))) return nt_status; + /* Let pdb_nds register backends */ + pdb_nds_init(); + return NT_STATUS_OK; } diff --git a/source3/passdb/pdb_nds.c b/source3/passdb/pdb_nds.c new file mode 100644 index 0000000000..6ad7deec38 --- /dev/null +++ b/source3/passdb/pdb_nds.c @@ -0,0 +1,937 @@ +/* + Unix SMB/CIFS mplementation. + NDS LDAP helper functions for SAMBA + Copyright (C) Vince Brimhall 2004-2005 + + 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; either version 2 of the License, or + (at your option) any later version. + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +#include +#include +#include +#include + +#include "smbldap.h" + +#define NMASLDAP_GET_LOGIN_CONFIG_REQUEST "2.16.840.1.113719.1.39.42.100.3" +#define NMASLDAP_GET_LOGIN_CONFIG_RESPONSE "2.16.840.1.113719.1.39.42.100.4" +#define NMASLDAP_SET_PASSWORD_REQUEST "2.16.840.1.113719.1.39.42.100.11" +#define NMASLDAP_SET_PASSWORD_RESPONSE "2.16.840.1.113719.1.39.42.100.12" +#define NMASLDAP_GET_PASSWORD_REQUEST "2.16.840.1.113719.1.39.42.100.13" +#define NMASLDAP_GET_PASSWORD_RESPONSE "2.16.840.1.113719.1.39.42.100.14" + +#define NMAS_LDAP_EXT_VERSION 1 + +/********************************************************************** + Take the request BER value and input data items and BER encodes the + data into the BER value +**********************************************************************/ + +static int berEncodePasswordData( + struct berval **requestBV, + const char *objectDN, + const char *password, + const char *password2) +{ + int err = 0, rc=0; + BerElement *requestBer = NULL; + + const char * utf8ObjPtr = NULL; + int utf8ObjSize = 0; + const char * utf8PwdPtr = NULL; + int utf8PwdSize = 0; + const char * utf8Pwd2Ptr = NULL; + int utf8Pwd2Size = 0; + + + /* Convert objectDN and tag strings from Unicode to UTF-8 */ + utf8ObjSize = strlen(objectDN)+1; + utf8ObjPtr = objectDN; + + if (password != NULL) + { + utf8PwdSize = strlen(password)+1; + utf8PwdPtr = password; + } + + if (password2 != NULL) + { + utf8Pwd2Size = strlen(password2)+1; + utf8Pwd2Ptr = password2; + } + + /* Allocate a BerElement for the request parameters. */ + if((requestBer = ber_alloc()) == NULL) + { + err = LDAP_ENCODING_ERROR; + goto Cleanup; + } + + if (password != NULL && password2 != NULL) + { + /* BER encode the NMAS Version, the objectDN, and the password */ + rc = ber_printf(requestBer, "{iooo}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize, utf8PwdPtr, utf8PwdSize, utf8Pwd2Ptr, utf8Pwd2Size); + } + else if (password != NULL) + { + /* BER encode the NMAS Version, the objectDN, and the password */ + rc = ber_printf(requestBer, "{ioo}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize, utf8PwdPtr, utf8PwdSize); + } + else + { + /* BER encode the NMAS Version and the objectDN */ + rc = ber_printf(requestBer, "{io}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize); + } + + if (rc < 0) + { + err = LDAP_ENCODING_ERROR; + goto Cleanup; + } + else + { + err = 0; + } + + /* Convert the BER we just built to a berval that we'll send with the extended request. */ + if(ber_flatten(requestBer, requestBV) == LBER_ERROR) + { + err = LDAP_ENCODING_ERROR; + goto Cleanup; + } + +Cleanup: + + if(requestBer) + { + ber_free(requestBer, 1); + } + + return err; +} + +/********************************************************************** + Take the request BER value and input data items and BER encodes the + data into the BER value +**********************************************************************/ + +static int berEncodeLoginData( + struct berval **requestBV, + char *objectDN, + unsigned int methodIDLen, + unsigned int *methodID, + char *tag, + size_t putDataLen, + void *putData) +{ + int err = 0; + BerElement *requestBer = NULL; + + unsigned int i; + unsigned int elemCnt = methodIDLen / sizeof(unsigned int); + + char *utf8ObjPtr=NULL; + int utf8ObjSize = 0; + + char *utf8TagPtr = NULL; + int utf8TagSize = 0; + + utf8ObjPtr = objectDN; + utf8ObjSize = strlen(utf8ObjPtr)+1; + + utf8TagPtr = tag; + utf8TagSize = strlen(utf8TagPtr)+1; + + /* Allocate a BerElement for the request parameters. */ + if((requestBer = ber_alloc()) == NULL) + { + err = LDAP_ENCODING_ERROR; + goto Cleanup; + } + + /* BER encode the NMAS Version and the objectDN */ + err = (ber_printf(requestBer, "{io", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize) < 0) ? LDAP_ENCODING_ERROR : 0; + + /* BER encode the MethodID Length and value */ + if (!err) + { + err = (ber_printf(requestBer, "{i{", methodIDLen) < 0) ? LDAP_ENCODING_ERROR : 0; + } + + for (i = 0; !err && i < elemCnt; i++) + { + err = (ber_printf(requestBer, "i", methodID[i]) < 0) ? LDAP_ENCODING_ERROR : 0; + } + + if (!err) + { + err = (ber_printf(requestBer, "}}", 0) < 0) ? LDAP_ENCODING_ERROR : 0; + } + + if(putData) + { + /* BER Encode the the tag and data */ + err = (ber_printf(requestBer, "oio}", utf8TagPtr, utf8TagSize, putDataLen, putData, putDataLen) < 0) ? LDAP_ENCODING_ERROR : 0; + } + else + { + /* BER Encode the the tag */ + err = (ber_printf(requestBer, "o}", utf8TagPtr, utf8TagSize) < 0) ? LDAP_ENCODING_ERROR : 0; + } + + if (err) + { + goto Cleanup; + } + + /* Convert the BER we just built to a berval that we'll send with the extended request. */ + if(ber_flatten(requestBer, requestBV) == LBER_ERROR) + { + err = LDAP_ENCODING_ERROR; + goto Cleanup; + } + +Cleanup: + + if(requestBer) + { + ber_free(requestBer, 1); + } + + return err; +} + +/********************************************************************** + Takes the reply BER Value and decodes the NMAS server version and + return code and if a non null retData buffer was supplied, tries to + decode the the return data and length +**********************************************************************/ + +static int berDecodeLoginData( + struct berval *replyBV, + int *serverVersion, + size_t *retDataLen, + void *retData ) +{ + int rc=0, err = 0; + BerElement *replyBer = NULL; + char *retOctStr = NULL; + size_t retOctStrLen = 0; + + if((replyBer = ber_init(replyBV)) == NULL) + { + err = LDAP_OPERATIONS_ERROR; + goto Cleanup; + } + + if(retData) + { + retOctStrLen = *retDataLen + 1; + retOctStr = (char *)malloc(retOctStrLen); + if(!retOctStr) + { + err = LDAP_OPERATIONS_ERROR; + goto Cleanup; + } + + if( (rc = ber_scanf(replyBer, "{iis}", serverVersion, &err, retOctStr, &retOctStrLen)) != -1) + { + if (*retDataLen >= retOctStrLen) + { + memcpy(retData, retOctStr, retOctStrLen); + } + else if (!err) + { + err = LDAP_NO_MEMORY; + } + + *retDataLen = retOctStrLen; + } + else if (!err) + { + err = LDAP_DECODING_ERROR; + } + } + else + { + if( (rc = ber_scanf(replyBer, "{ii}", serverVersion, &err)) == -1) + { + if (!err) + { + err = LDAP_DECODING_ERROR; + } + } + } + +Cleanup: + + if(replyBer) + { + ber_free(replyBer, 1); + } + + if (retOctStr != NULL) + { + memset(retOctStr, 0, retOctStrLen); + free(retOctStr); + } + + return err; +} + +/********************************************************************** + Retrieves data in the login configuration of the specified object + that is tagged with the specified methodID and tag. +**********************************************************************/ + +static int getLoginConfig( + LDAP *ld, + char *objectDN, + unsigned int methodIDLen, + unsigned int *methodID, + char *tag, + size_t *dataLen, + void *data ) +{ + int err = 0; + struct berval *requestBV = NULL; + char *replyOID = NULL; + struct berval *replyBV = NULL; + int serverVersion = 0; + + /* Validate unicode parameters. */ + if((strlen(objectDN) == 0) || ld == NULL) + { + return LDAP_NO_SUCH_ATTRIBUTE; + } + + err = berEncodeLoginData(&requestBV, objectDN, methodIDLen, methodID, tag, 0, NULL); + if(err) + { + goto Cleanup; + } + + /* Call the ldap_extended_operation (synchronously) */ + if((err = ldap_extended_operation_s(ld, NMASLDAP_GET_LOGIN_CONFIG_REQUEST, + requestBV, NULL, NULL, &replyOID, &replyBV))) + { + goto Cleanup; + } + + /* Make sure there is a return OID */ + if(!replyOID) + { + err = LDAP_NOT_SUPPORTED; + goto Cleanup; + } + + /* Is this what we were expecting to get back. */ + if(strcmp(replyOID, NMASLDAP_GET_LOGIN_CONFIG_RESPONSE)) + { + err = LDAP_NOT_SUPPORTED; + goto Cleanup; + } + + /* Do we have a good returned berval? */ + if(!replyBV) + { + /* No; returned berval means we experienced a rather drastic error. */ + /* Return operations error. */ + err = LDAP_OPERATIONS_ERROR; + goto Cleanup; + } + + err = berDecodeLoginData(replyBV, &serverVersion, dataLen, data); + + if(serverVersion != NMAS_LDAP_EXT_VERSION) + { + err = LDAP_OPERATIONS_ERROR; + goto Cleanup; + } + +Cleanup: + + if(replyBV) + { + ber_bvfree(replyBV); + } + + /* Free the return OID string if one was returned. */ + if(replyOID) + { + ldap_memfree(replyOID); + } + + /* Free memory allocated while building the request ber and berval. */ + if(requestBV) + { + ber_bvfree(requestBV); + } + + /* Return the appropriate error/success code. */ + return err; +} + +/********************************************************************** + Attempts to get the Simple Password +**********************************************************************/ + +static int nmasldap_get_simple_pwd( + LDAP *ld, + char *objectDN, + size_t pwdLen, + char *pwd ) +{ + int err = 0; + unsigned int methodID = 0; + unsigned int methodIDLen = sizeof(methodID); + char tag[] = {'P','A','S','S','W','O','R','D',' ','H','A','S','H',0}; + char *pwdBuf=NULL; + size_t pwdBufLen, bufferLen; + + bufferLen = pwdBufLen = pwdLen+2; + pwdBuf = (char *)malloc(pwdBufLen); /* digest and null */ + if(pwdBuf == NULL) + { + return LDAP_NO_MEMORY; + } + + err = getLoginConfig(ld, objectDN, methodIDLen, &methodID, tag, &pwdBufLen, pwdBuf); + if (err == 0) + { + if (pwdBufLen !=0) + { + pwdBuf[pwdBufLen] = 0; /* null terminate */ + + switch (pwdBuf[0]) + { + case 1: /* cleartext password */ + break; + case 2: /* SHA1 HASH */ + case 3: /* MD5_ID */ + case 4: /* UNIXCrypt_ID */ + case 8: /* SSHA_ID */ + default: /* Unknown digest */ + err = LDAP_INAPPROPRIATE_AUTH; /* only return clear text */ + break; + } + + if (!err) + { + if (pwdLen >= pwdBufLen-1) + { + memcpy(pwd, &pwdBuf[1], pwdBufLen-1); /* skip digest tag and include null */ + } + else + { + err = LDAP_NO_MEMORY; + } + } + } + } + + if (pwdBuf != NULL) + { + memset(pwdBuf, 0, bufferLen); + free(pwdBuf); + } + + return err; +} + + +/********************************************************************** + Attempts to set the Universal Password +**********************************************************************/ + +static int nmasldap_set_password( + LDAP *ld, + const char *objectDN, + const char *pwd ) +{ + int err = 0; + + struct berval *requestBV = NULL; + char *replyOID = NULL; + struct berval *replyBV = NULL; + int serverVersion; + + /* Validate char parameters. */ + if(objectDN == NULL || (strlen(objectDN) == 0) || pwd == NULL || ld == NULL) + { + return LDAP_NO_SUCH_ATTRIBUTE; + } + + err = berEncodePasswordData(&requestBV, objectDN, pwd, NULL); + if(err) + { + goto Cleanup; + } + + /* Call the ldap_extended_operation (synchronously) */ + if((err = ldap_extended_operation_s(ld, NMASLDAP_SET_PASSWORD_REQUEST, requestBV, NULL, NULL, &replyOID, &replyBV))) + { + goto Cleanup; + } + + /* Make sure there is a return OID */ + if(!replyOID) + { + err = LDAP_NOT_SUPPORTED; + goto Cleanup; + } + + /* Is this what we were expecting to get back. */ + if(strcmp(replyOID, NMASLDAP_SET_PASSWORD_RESPONSE)) + { + err = LDAP_NOT_SUPPORTED; + goto Cleanup; + } + + /* Do we have a good returned berval? */ + if(!replyBV) + { + /* No; returned berval means we experienced a rather drastic error. */ + /* Return operations error. */ + err = LDAP_OPERATIONS_ERROR; + goto Cleanup; + } + + err = berDecodeLoginData(replyBV, &serverVersion, NULL, NULL); + + if(serverVersion != NMAS_LDAP_EXT_VERSION) + { + err = LDAP_OPERATIONS_ERROR; + goto Cleanup; + } + +Cleanup: + + if(replyBV) + { + ber_bvfree(replyBV); + } + + /* Free the return OID string if one was returned. */ + if(replyOID) + { + ldap_memfree(replyOID); + } + + /* Free memory allocated while building the request ber and berval. */ + if(requestBV) + { + ber_bvfree(requestBV); + } + + /* Return the appropriate error/success code. */ + return err; +} + +/********************************************************************** + Attempts to get the Universal Password +**********************************************************************/ + +static int nmasldap_get_password( + LDAP *ld, + char *objectDN, + size_t *pwdSize, /* in bytes */ + char *pwd ) +{ + int err = 0; + + struct berval *requestBV = NULL; + char *replyOID = NULL; + struct berval *replyBV = NULL; + int serverVersion; + char *pwdBuf; + size_t pwdBufLen, bufferLen; + + /* Validate char parameters. */ + if(objectDN == NULL || (strlen(objectDN) == 0) || pwdSize == NULL || ld == NULL) + { + return LDAP_NO_SUCH_ATTRIBUTE; + } + + bufferLen = pwdBufLen = *pwdSize; + pwdBuf = (char *)malloc(pwdBufLen+2); + if(pwdBuf == NULL) + { + return LDAP_NO_MEMORY; + } + + err = berEncodePasswordData(&requestBV, objectDN, NULL, NULL); + if(err) + { + goto Cleanup; + } + + /* Call the ldap_extended_operation (synchronously) */ + if((err = ldap_extended_operation_s(ld, NMASLDAP_GET_PASSWORD_REQUEST, requestBV, NULL, NULL, &replyOID, &replyBV))) + { + goto Cleanup; + } + + /* Make sure there is a return OID */ + if(!replyOID) + { + err = LDAP_NOT_SUPPORTED; + goto Cleanup; + } + + /* Is this what we were expecting to get back. */ + if(strcmp(replyOID, NMASLDAP_GET_PASSWORD_RESPONSE)) + { + err = LDAP_NOT_SUPPORTED; + goto Cleanup; + } + + /* Do we have a good returned berval? */ + if(!replyBV) + { + /* No; returned berval means we experienced a rather drastic error. */ + /* Return operations error. */ + err = LDAP_OPERATIONS_ERROR; + goto Cleanup; + } + + err = berDecodeLoginData(replyBV, &serverVersion, &pwdBufLen, pwdBuf); + + if(serverVersion != NMAS_LDAP_EXT_VERSION) + { + err = LDAP_OPERATIONS_ERROR; + goto Cleanup; + } + + if (!err && pwdBufLen != 0) + { + if (*pwdSize >= pwdBufLen+1 && pwd != NULL) + { + memcpy(pwd, pwdBuf, pwdBufLen); + pwd[pwdBufLen] = 0; /* add null termination */ + } + *pwdSize = pwdBufLen; /* does not include null termination */ + } + +Cleanup: + + if(replyBV) + { + ber_bvfree(replyBV); + } + + /* Free the return OID string if one was returned. */ + if(replyOID) + { + ldap_memfree(replyOID); + } + + /* Free memory allocated while building the request ber and berval. */ + if(requestBV) + { + ber_bvfree(requestBV); + } + + if (pwdBuf != NULL) + { + memset(pwdBuf, 0, bufferLen); + free(pwdBuf); + } + + /* Return the appropriate error/success code. */ + return err; +} + +/********************************************************************** + Get the user's password from NDS. + *********************************************************************/ + +int pdb_nds_get_password( + struct smbldap_state *ldap_state, + char *object_dn, + int *pwd_len, + char *pwd ) +{ + LDAP *ld = ldap_state->ldap_struct; + int rc = -1; + + rc = nmasldap_get_password(ld, object_dn, pwd_len, pwd); + if (rc == LDAP_SUCCESS) { +#ifdef DEBUG_PASSWORD + DEBUG(100,("nmasldap_get_password returned %s for %s\n", pwd, object_dn)); +#endif + DEBUG(5, ("NDS Universal Password retrieved for %s\n", object_dn)); + } else { + DEBUG(3, ("NDS Universal Password NOT retrieved for %s\n", object_dn)); + } + + if (rc != LDAP_SUCCESS) { + rc = nmasldap_get_simple_pwd(ld, object_dn, *pwd_len, pwd); + if (rc == LDAP_SUCCESS) { +#ifdef DEBUG_PASSWORD + DEBUG(100,("nmasldap_get_simple_pwd returned %s for %s\n", pwd, object_dn)); +#endif + DEBUG(5, ("NDS Simple Password retrieved for %s\n", object_dn)); + } else { + /* We couldn't get the password */ + DEBUG(3, ("NDS Simple Password NOT retrieved for %s\n", object_dn)); + return LDAP_INVALID_CREDENTIALS; + } + } + + /* We got the password */ + return LDAP_SUCCESS; +} + +/********************************************************************** + Set the users NDS, Universal and Simple passwords. + ********************************************************************/ + +int pdb_nds_set_password( + struct smbldap_state *ldap_state, + char *object_dn, + const char *pwd ) +{ + LDAP *ld = ldap_state->ldap_struct; + int rc = -1; + LDAPMod **tmpmods = NULL; + + rc = nmasldap_set_password(ld, object_dn, pwd); + if (rc == LDAP_SUCCESS) { + DEBUG(5,("NDS Universal Password changed for user %s\n", object_dn)); + } else { + /* This will fail if Universal Password is not enabled for the user's context */ + DEBUG(3,("NDS Universal Password could not be changed for user %s: %d\n", + object_dn, rc)); + } + + /* Set eDirectory Password */ + smbldap_set_mod(&tmpmods, LDAP_MOD_REPLACE, "userPassword", pwd); + rc = smbldap_modify(ldap_state, object_dn, tmpmods); + + return rc; +} + +/********************************************************************** + Allow ldap server to update internal login attempt counters by + performing a simple bind. If the samba authentication failed attempt + the bind with a bogus, randomly generated password to count the + failed attempt. If the bind fails even though samba authentication + succeeded, this would indicate that the user's account is disabled, + time restrictions are in place or some other password policy + violation. +*********************************************************************/ + +static NTSTATUS pdb_nds_update_login_attempts(struct pdb_methods *methods, + SAM_ACCOUNT *sam_acct, BOOL success) +{ + struct ldapsam_privates *ldap_state; + + if ((!methods) || (!sam_acct)) { + DEBUG(3,("pdb_nds_update_login_attempts: invalid parameter.\n")); + return NT_STATUS_MEMORY_NOT_ALLOCATED; + } + + ldap_state = (struct ldapsam_privates *)methods->private_data; + + if (ldap_state) { + /* Attempt simple bind with user credentials to update eDirectory + password policy */ + int rc = 0; + char *dn; + LDAPMessage *result = NULL; + LDAPMessage *entry = NULL; + const char **attr_list; + size_t pwd_len; + uchar clear_text_pw[512]; + const char *p = NULL; + LDAP *ld = NULL; + int ldap_port = 0; + char protocol[12]; + char ldap_server[256]; + const char *username = pdb_get_username(sam_acct); + + DEBUG(5,("pdb_nds_update_login_attempts: %s login for %s\n", + success ? "Successful" : "Failed", username)); + + result = pdb_get_backend_private_data(sam_acct, methods); + if (!result) { + attr_list = get_userattr_list(ldap_state->schema_ver); + rc = ldapsam_search_suffix_by_name(ldap_state, username, &result, attr_list ); + free_attr_list( attr_list ); + if (rc != LDAP_SUCCESS) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + pdb_set_backend_private_data(sam_acct, result, private_data_free_fn, methods, PDB_CHANGED); + } + + if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result) == 0) { + DEBUG(0, ("pdb_nds_update_login_attempts: No user to modify!\n")); + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result); + dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry); + if (!dn) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + DEBUG(3, ("pdb_nds_update_login_attempts: username %s found dn '%s'\n", username, dn)); + + pwd_len = sizeof(clear_text_pw); + if (success == True) { + if (pdb_nds_get_password(ldap_state->smbldap_state, dn, &pwd_len, clear_text_pw) == LDAP_SUCCESS) { + /* */ + } + } else { + generate_random_buffer(clear_text_pw, 24); + clear_text_pw[24] = '\0'; + DEBUG(5,("pdb_nds_update_login_attempts: using random password %s\n", clear_text_pw)); + } + + /* Parse the location string */ + p = ldap_state->location; + + /* skip leading "URL:" (if any) */ + if ( strnequal( p, "URL:", 4 ) ) { + p += 4; + } + + sscanf(p, "%10[^:]://%254[^:/]:%d", protocol, ldap_server, &ldap_port); + + if (ldap_port == 0) { + if (strequal(protocol, "ldap")) { + ldap_port = LDAP_PORT; + } else if (strequal(protocol, "ldaps")) { + ldap_port = LDAPS_PORT; + } else { + DEBUG(0, ("unrecognised protocol (%s)!\n", protocol)); + } + } + + ld = ldap_init(ldap_server, ldap_port); + + if(ld != NULL) { + int version; + + /* LDAP version 3 required for ldap_sasl */ + if (ldap_get_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS) { + if (version != LDAP_VERSION3) { + version = LDAP_VERSION3; + if (ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS) { + DEBUG(4, ("pdb_nds_update_login_attempts: Set protocol version to LDAP_VERSION3\n")); + } + } + } + + /* Turn on ssl if required */ + if(strequal(protocol, "ldaps")) { + int tls = LDAP_OPT_X_TLS_HARD; + if (ldap_set_option (ld, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS) { + DEBUG(1, ("pdb_nds_update_login_attempts: Failed to setup a TLS session\n")); + } else { + DEBUG(4, ("pdb_nds_update_login_attempts: Activated TLS on session\n")); + } + } + } + + /* Attempt simple bind with real or bogus password */ + rc = ldap_simple_bind_s(ld, dn, clear_text_pw); + if (rc == LDAP_SUCCESS) { + DEBUG(5,("pdb_nds_update_login_attempts: ldap_simple_bind_s Successful for %s\n", username)); + ldap_unbind_ext(ld, NULL, NULL); + } else { + NTSTATUS nt_status = NT_STATUS_ACCOUNT_RESTRICTION; + DEBUG(5,("pdb_nds_update_login_attempts: ldap_simple_bind_s Failed for %s\n", username)); + switch(rc) { + case LDAP_INVALID_CREDENTIALS: + nt_status = NT_STATUS_WRONG_PASSWORD; + break; + default: + break; + } + return nt_status; + } + } + + return NT_STATUS_OK; +} + +/********************************************************************** + Intitalise the parts of the pdb_context that are common to NDS_ldapsam modes + *********************************************************************/ + +static NTSTATUS pdb_init_NDS_ldapsam_common(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location) +{ + struct ldapsam_privates *ldap_state = (*pdb_method)->private_data; + + /* Mark this as eDirectory ldap */ + ldap_state->is_nds_ldap = True; + + /* Add pdb_nds specific method for updating login attempts. */ + (*pdb_method)->update_login_attempts = pdb_nds_update_login_attempts; + + /* Save location for use in pdb_nds_update_login_attempts */ + ldap_state->location = strdup(location); + + return NT_STATUS_OK; +} + + +/********************************************************************** + Initialise the 'nds compat' mode for pdb_ldap + *********************************************************************/ + +static NTSTATUS pdb_init_NDS_ldapsam_compat(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location) +{ + NTSTATUS nt_status = pdb_init_ldapsam_compat(pdb_context, pdb_method, location); + + (*pdb_method)->name = "NDS_ldapsam_compat"; + + pdb_init_NDS_ldapsam_common(pdb_context, pdb_method, location); + + return nt_status; +} + + +/********************************************************************** + Initialise the 'nds' normal mode for pdb_ldap + *********************************************************************/ + +static NTSTATUS pdb_init_NDS_ldapsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location) +{ + NTSTATUS nt_status = pdb_init_ldapsam(pdb_context, pdb_method, location); + + (*pdb_method)->name = "NDS_ldapsam"; + + pdb_init_NDS_ldapsam_common(pdb_context, pdb_method, location); + + return nt_status; +} + +NTSTATUS pdb_nds_init(void) +{ + NTSTATUS nt_status; + if (!NT_STATUS_IS_OK(nt_status = smb_register_passdb(PASSDB_INTERFACE_VERSION, "NDS_ldapsam", pdb_init_NDS_ldapsam))) + return nt_status; + + if (!NT_STATUS_IS_OK(nt_status = smb_register_passdb(PASSDB_INTERFACE_VERSION, "NDS_ldapsam_compat", pdb_init_NDS_ldapsam_compat))) + return nt_status; + + return NT_STATUS_OK; +} -- cgit