diff options
Diffstat (limited to 'source/passdb')
-rw-r--r-- | source/passdb/.cvsignore | 2 | ||||
-rw-r--r-- | source/passdb/ldap.c | 1017 | ||||
-rw-r--r-- | source/passdb/machine_sid.c | 255 | ||||
-rw-r--r-- | source/passdb/nispass.c | 1083 | ||||
-rw-r--r-- | source/passdb/pampass.c | 892 | ||||
-rw-r--r-- | source/passdb/pass_check.c | 776 | ||||
-rw-r--r-- | source/passdb/passdb.c | 1813 | ||||
-rw-r--r-- | source/passdb/passgrp.c | 217 | ||||
-rw-r--r-- | source/passdb/pdb_ldap.c | 1045 | ||||
-rw-r--r-- | source/passdb/pdb_nisplus.c | 1409 | ||||
-rw-r--r-- | source/passdb/pdb_smbpasswd.c | 1545 | ||||
-rw-r--r-- | source/passdb/pdb_tdb.c | 808 | ||||
-rw-r--r-- | source/passdb/secrets.c | 247 | ||||
-rw-r--r-- | source/passdb/smbpass.c | 304 | ||||
-rw-r--r-- | source/passdb/smbpasschange.c | 25 | ||||
-rw-r--r-- | source/passdb/smbpassgroup.c | 195 |
16 files changed, 11329 insertions, 304 deletions
diff --git a/source/passdb/.cvsignore b/source/passdb/.cvsignore new file mode 100644 index 00000000000..5f2a5c4cf75 --- /dev/null +++ b/source/passdb/.cvsignore @@ -0,0 +1,2 @@ +*.po +*.po32 diff --git a/source/passdb/ldap.c b/source/passdb/ldap.c new file mode 100644 index 00000000000..e0ac013fc8f --- /dev/null +++ b/source/passdb/ldap.c @@ -0,0 +1,1017 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + LDAP protocol helper functions for SAMBA + Copyright (C) Jean François Micouleau 1998 + + 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. + +*/ + +#ifdef WITH_LDAP + +#include "includes.h" + +#include <lber.h> +#include <ldap.h> + +#define ADD_USER 1 +#define MODIFY_USER 2 + +/******************************************************************* + open a connection to the ldap serve. +******************************************************************/ +static BOOL ldap_open_connection(LDAP **ldap_struct) +{ + if ( (*ldap_struct = ldap_open(lp_ldap_server(),lp_ldap_port()) ) == NULL) + { + DEBUG( 0, ( "The LDAP server is not responding !\n" ) ); + return( False ); + } + DEBUG(2,("ldap_open_connection: connection opened\n")); + return (True); +} + + +/******************************************************************* + connect anonymously to the ldap server. + FIXME: later (jfm) +******************************************************************/ +static BOOL ldap_connect_anonymous(LDAP *ldap_struct) +{ + if ( ldap_simple_bind_s(ldap_struct,lp_ldap_root(),lp_ldap_rootpasswd()) ! = LDAP_SUCCESS) + { + DEBUG( 0, ( "Couldn't bind to the LDAP server !\n" ) ); + return(False); + } + return (True); +} + + +/******************************************************************* + connect to the ldap server under system privileg. +******************************************************************/ +static BOOL ldap_connect_system(LDAP *ldap_struct) +{ + if ( ldap_simple_bind_s(ldap_struct,lp_ldap_root(),lp_ldap_rootpasswd()) ! = LDAP_SUCCESS) + { + DEBUG( 0, ( "Couldn't bind to the LDAP server!\n" ) ); + return(False); + } + DEBUG(2,("ldap_connect_system: succesful connection to the LDAP server\n")); + return (True); +} + +/******************************************************************* + connect to the ldap server under a particular user. +******************************************************************/ +static BOOL ldap_connect_user(LDAP *ldap_struct, const char *user, const char *password) +{ + if ( ldap_simple_bind_s(ldap_struct,lp_ldap_root(),lp_ldap_rootpasswd()) ! = LDAP_SUCCESS) + { + DEBUG( 0, ( "Couldn't bind to the LDAP server !\n" ) ); + return(False); + } + DEBUG(2,("ldap_connect_user: succesful connection to the LDAP server\n")); + return (True); +} + +/******************************************************************* + run the search by name. +******************************************************************/ +static BOOL ldap_search_one_user(LDAP *ldap_struct, char *filter, LDAPMessage **result) +{ + int scope = LDAP_SCOPE_ONELEVEL; + int rc; + + DEBUG(2,("ldap_search_one_user: searching for:[%s]\n", filter)); + + rc = ldap_search_s(ldap_struct, lp_ldap_suffix(), scope, filter, NULL, 0, result); + + if (rc ! = LDAP_SUCCESS ) + { + DEBUG( 0, ( "Problem during the LDAP search\n" ) ); + return(False); + } + return (True); +} + +/******************************************************************* + run the search by name. +******************************************************************/ +static BOOL ldap_search_one_user_by_name(LDAP *ldap_struct, const char *user, LDAPMessage **result) +{ + pstring filter; + /* + in the filter expression, replace %u with the real name + so in ldap filter, %u MUST exist :-) + */ + pstrcpy(filter,lp_ldap_filter()); + pstring_sub(filter,"%u",user); + + if ( !ldap_search_one_user(ldap_struct, filter, result) ) + { + return(False); + } + return (True); +} + +/******************************************************************* + run the search by uid. +******************************************************************/ +static BOOL ldap_search_one_user_by_uid(LDAP *ldap_struct, int uid, LDAPMessage **result) +{ + pstring filter; + + slprintf(filter, sizeof(pstring)-1, "uidAccount = %d", uid); + + if ( !ldap_search_one_user(ldap_struct, filter, result) ) + { + return(False); + } + return (True); +} + +/******************************************************************* + search an attribute and return the first value found. +******************************************************************/ +static void get_single_attribute(LDAP *ldap_struct, LDAPMessage *entry, char *attribute, char *value) +{ + char **valeurs; + + if ( (valeurs = ldap_get_values(ldap_struct, entry, attribute)) ! = NULL) + { + pstrcpy(value, valeurs[0]); + ldap_value_free(valeurs); + DEBUG(3,("get_single_attribute: [%s] = [%s]\n", attribute, value)); + } + else + { + value = NULL; + } +} + +/******************************************************************* + check if the returned entry is a sambaAccount objectclass. +******************************************************************/ +static BOOL ldap_check_user(LDAP *ldap_struct, LDAPMessage *entry) +{ + BOOL sambaAccount = False; + char **valeur; + int i; + + DEBUG(2,("ldap_check_user: ")); + valeur = ldap_get_values(ldap_struct, entry, "objectclass"); + if (valeur! = NULL) + { + for (i = 0;valeur[i]! = NULL;i++) + { + if (!strcmp(valeur[i],"sambaAccount")) sambaAccount = True; + } + } + DEBUG(2,("%s\n",sambaAccount?"yes":"no")); + ldap_value_free(valeur); + return (sambaAccount); +} + +/******************************************************************* + check if the returned entry is a sambaTrust objectclass. +******************************************************************/ +static BOOL ldap_check_trust(LDAP *ldap_struct, LDAPMessage *entry) +{ + BOOL sambaTrust = False; + char **valeur; + int i; + + DEBUG(2,("ldap_check_trust: ")); + valeur = ldap_get_values(ldap_struct, entry, "objectclass"); + if (valeur! = NULL) + { + for (i = 0;valeur[i]! = NULL;i++) + { + if (!strcmp(valeur[i],"sambaTrust")) sambaTrust = True; + } + } + DEBUG(2,("%s\n",sambaTrust?"yes":"no")); + ldap_value_free(valeur); + return (sambaTrust); +} + +/******************************************************************* + retrieve the user's info and contruct a smb_passwd structure. +******************************************************************/ +static void ldap_get_smb_passwd(LDAP *ldap_struct,LDAPMessage *entry, + struct smb_passwd *user) +{ + static pstring user_name; + static pstring user_pass; + static pstring temp; + static unsigned char smblmpwd[16]; + static unsigned char smbntpwd[16]; + + pdb_init_smb(user); + + memset((char *)smblmpwd, '\0', sizeof(smblmpwd)); + memset((char *)smbntpwd, '\0', sizeof(smbntpwd)); + + get_single_attribute(ldap_struct, entry, "cn", user_name); + DEBUG(2,("ldap_get_smb_passwd: user: %s\n",user_name)); + +#ifdef LDAP_PLAINTEXT_PASSWORD + get_single_attribute(ldap_struct, entry, "userPassword", temp); + nt_lm_owf_gen(temp, user->smb_nt_passwd, user->smb_passwd); + memset((char *)temp, '\0', sizeof(temp)); /* destroy local copy of the password */ +#else + get_single_attribute(ldap_struct, entry, "unicodePwd", temp); + pdb_gethexpwd(temp, smbntpwd); + memset((char *)temp, '\0', sizeof(temp)); /* destroy local copy of the password */ + + get_single_attribute(ldap_struct, entry, "dBCSPwd", temp); + pdb_gethexpwd(temp, smblmpwd); + memset((char *)temp, '\0', sizeof(temp)); /* destroy local copy of the password */ +#endif + + get_single_attribute(ldap_struct, entry, "userAccountControl", temp); + user->acct_ctrl = pdb_decode_acct_ctrl(temp); + + get_single_attribute(ldap_struct, entry, "pwdLastSet", temp); + user->pass_last_set_time = (time_t)strtol(temp, NULL, 16); + + get_single_attribute(ldap_struct, entry, "rid", temp); + + /* the smb (unix) ids are not stored: they are created */ + user->smb_userid = pdb_user_rid_to_uid (atoi(temp)); + + if (user->acct_ctrl & (ACB_DOMTRUST|ACB_WSTRUST|ACB_SVRTRUST) ) + { + DEBUG(0,("Inconsistency in the LDAP database\n")); + } + if (user->acct_ctrl & ACB_NORMAL) + { + user->smb_name = user_name; + user->smb_passwd = smblmpwd; + user->smb_nt_passwd = smbntpwd; + } +} + +/******************************************************************* + retrieve the user's info and contruct a sam_passwd structure. + + calls ldap_get_smb_passwd function first, though, to save code duplication. + +******************************************************************/ +static void ldap_get_sam_passwd(LDAP *ldap_struct, LDAPMessage *entry, + struct sam_passwd *user) +{ + static pstring user_name; + static pstring fullname; + static pstring home_dir; + static pstring dir_drive; + static pstring logon_script; + static pstring profile_path; + static pstring acct_desc; + static pstring workstations; + static pstring temp; + static struct smb_passwd pw_buf; + + pdb_init_sam(user); + + ldap_get_smb_passwd(ldap_struct, entry, &pw_buf); + + user->pass_last_set_time = pw_buf.pass_last_set_time; + + get_single_attribute(ldap_struct, entry, "logonTime", temp); + user->pass_last_set_time = (time_t)strtol(temp, NULL, 16); + + get_single_attribute(ldap_struct, entry, "logoffTime", temp); + user->pass_last_set_time = (time_t)strtol(temp, NULL, 16); + + get_single_attribute(ldap_struct, entry, "kickoffTime", temp); + user->pass_last_set_time = (time_t)strtol(temp, NULL, 16); + + get_single_attribute(ldap_struct, entry, "pwdLastSet", temp); + user->pass_last_set_time = (time_t)strtol(temp, NULL, 16); + + get_single_attribute(ldap_struct, entry, "pwdCanChange", temp); + user->pass_last_set_time = (time_t)strtol(temp, NULL, 16); + + get_single_attribute(ldap_struct, entry, "pwdMustChange", temp); + user->pass_last_set_time = (time_t)strtol(temp, NULL, 16); + + user->smb_name = pw_buf.smb_name; + + DEBUG(2,("ldap_get_sam_passwd: user: %s\n", user_name)); + + get_single_attribute(ldap_struct, entry, "userFullName", fullname); + user->full_name = fullname; + + get_single_attribute(ldap_struct, entry, "homeDirectory", home_dir); + user->home_dir = home_dir; + + get_single_attribute(ldap_struct, entry, "homeDrive", dir_drive); + user->dir_drive = dir_drive; + + get_single_attribute(ldap_struct, entry, "scriptPath", logon_script); + user->logon_script = logon_script; + + get_single_attribute(ldap_struct, entry, "profilePath", profile_path); + user->profile_path = profile_path; + + get_single_attribute(ldap_struct, entry, "comment", acct_desc); + user->acct_desc = acct_desc; + + get_single_attribute(ldap_struct, entry, "userWorkstations", workstations); + user->workstations = workstations; + + user->unknown_str = NULL; /* don't know, yet! */ + user->munged_dial = NULL; /* "munged" dial-back telephone number */ + + get_single_attribute(ldap_struct, entry, "rid", temp); + user->user_rid = atoi(temp); + + get_single_attribute(ldap_struct, entry, "primaryGroupID", temp); + user->group_rid = atoi(temp); + + /* the smb (unix) ids are not stored: they are created */ + user->smb_userid = pw_buf.smb_userid; + user->smb_grpid = group_rid_to_uid(user->group_rid); + + user->acct_ctrl = pw_buf.acct_ctrl; + + user->unknown_3 = 0xffffff; /* don't know */ + user->logon_divs = 168; /* hours per week */ + user->hours_len = 21; /* 21 times 8 bits = 168 */ + memset(user->hours, 0xff, user->hours_len); /* available at all hours */ + user->unknown_5 = 0x00000000; /* don't know */ + user->unknown_6 = 0x000004ec; /* don't know */ + + if (user->acct_ctrl & (ACB_DOMTRUST|ACB_WSTRUST|ACB_SVRTRUST) ) + { + DEBUG(0,("Inconsistency in the LDAP database\n")); + } + + if (!(user->acct_ctrl & ACB_NORMAL)) + { + DEBUG(0,("User's acct_ctrl bits not set to ACT_NORMAL in LDAP database\n")); + return; + } +} + +/************************************************************************ + Routine to manage the LDAPMod structure array + manage memory used by the array, by each struct, and values + +************************************************************************/ +static void make_a_mod(LDAPMod ***modlist,int modop, const char *attribute, const char *value) +{ + LDAPMod **mods, **tmods; + int i; + int j; + + mods = *modlist; + + if (mods == NULL) + { + tmods = (LDAPMod **)malloc( sizeof(LDAPMod *) ); + if (tmods == NULL) + { + DEBUG(0,("make_a_mod: out of memory!\n")); + return; + } + mods = tmods; + mods[0] = NULL; + } + + for ( i = 0; mods[ i ] ! = NULL; ++i ) + { + if ( mods[ i ]->mod_op == modop && + !strcasecmp( mods[ i ]->mod_type, attribute ) ) + { + break; + } + } + + if (mods[i] == NULL) + { + tmods = (LDAPMod **)Realloc( mods, (i+2) * sizeof( LDAPMod * ) ); + if (tmods == NULL) + { + DEBUG(0,("make_a_mod: out of memory!\n")); + return; + } + mods = tmods; + mods[i] = (LDAPMod *)malloc( sizeof( LDAPMod ) ); + if (mods[i] == NULL) + { + DEBUG(0,("make_a_mod: out of memory!\n")); + return; + } + mods[i]->mod_op = modop; + mods[i]->mod_values = NULL; + mods[i]->mod_type = strdup( attribute ); + mods[i+1] = NULL; + } + + if (value ! = NULL ) + { + char **tmval; + + j = 0; + if ( mods[ i ]->mod_values ! = NULL ) + { + for ( ; mods[ i ]->mod_values[ j ] ! = NULL; j++ ); + } + tmval = (char **)Realloc(mods[ i ]->mod_values, + (j+2) * sizeof( char * )); + if ( tmval == NULL) + { + DEBUG(0, "make_a_mod: Memory allocation failure!\n"); + return; + } + mods[ i ]->mod_values = tmval; + mods[ i ]->mod_values[ j ] = strdup(value); + mods[ i ]->mod_values[ j + 1 ] = NULL; + } + *modlist = mods; +} + +/************************************************************************ + Add or modify an entry. Only the smb struct values + +*************************************************************************/ +static BOOL modadd_ldappwd_entry(struct smb_passwd *newpwd, int flag) +{ + + /* assume the struct is correct and filled + that's the job of passdb.c to check */ + int scope = LDAP_SCOPE_ONELEVEL; + int rc; + char *smb_name; + int trust = False; + int ldap_state; + pstring filter; + pstring dn; + pstring lmhash; + pstring nthash; + pstring rid; + pstring lst; + pstring temp; + + LDAP *ldap_struct; + LDAPMessage *result; + LDAPMod **mods; + + smb_name = newpwd->smb_name; + + if (!ldap_open_connection(&ldap_struct)) /* open a connection to the server */ + { + return False; + } + + if (!ldap_connect_system(ldap_struct)) /* connect as system account */ + { + ldap_unbind(ldap_struct); + return False; + } + + if (smb_name[strlen(smb_name)-1] == '$' ) + { + smb_name[strlen(smb_name)-1] = '\0'; + trust = True; + } + + slprintf(filter, sizeof(filter)-1, + "(&(cn = %s)(|(objectclass = sambaTrust)(objectclass = sambaAccount)))", + smb_name); + + rc = ldap_search_s(ldap_struct, lp_ldap_suffix(), scope, filter, NULL, 0, &result); + + switch (flag) + { + case ADD_USER: + { + if (ldap_count_entries(ldap_struct, result) ! = 0) + { + DEBUG(0,("User already in the base, with samba properties\n")); + ldap_unbind(ldap_struct); + return False; + } + ldap_state = LDAP_MOD_ADD; + break; + } + case MODIFY_USER: + { + if (ldap_count_entries(ldap_struct, result) ! = 1) + { + DEBUG(0,("No user to modify !\n")); + ldap_unbind(ldap_struct); + return False; + } + ldap_state = LDAP_MOD_REPLACE; + break; + } + default: + { + DEBUG(0,("How did you come here? \n")); + ldap_unbind(ldap_struct); + return False; + break; + } + } + slprintf(dn, sizeof(dn)-1, "cn = %s, %s",smb_name, lp_ldap_suffix() ); + + if (newpwd->smb_passwd ! = NULL) + { + int i; + for( i = 0; i < 16; i++) + { + slprintf(&temp[2*i], sizeof(temp) - 1, "%02X", newpwd->smb_passwd[i]); + } + + } + else + { + if (newpwd->acct_ctrl & ACB_PWNOTREQ) + { + slprintf(temp, sizeof(temp) - 1, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX"); + } + else + { + slprintf(temp, sizeof(temp) - 1, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); + } + } + slprintf(lmhash, sizeof(lmhash)-1, "%s", temp); + + if (newpwd->smb_nt_passwd ! = NULL) + { + int i; + for( i = 0; i < 16; i++) + { + slprintf(&temp[2*i], sizeof(temp) - 1, "%02X", newpwd->smb_nt_passwd[i]); + } + + } + else + { + if (newpwd->acct_ctrl & ACB_PWNOTREQ) + { + slprintf(temp, sizeof(temp) - 1, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX"); + } + else + { + slprintf(temp, sizeof(temp) - 1, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); + } + } + slprintf(nthash, sizeof(nthash)-1, "%s", temp); + + slprintf(rid, sizeof(rid)-1, "%d", uid_to_user_rid(newpwd->smb_userid) ); + slprintf(lst, sizeof(lst)-1, "%08X", newpwd->pass_last_set_time); + + mods = NULL; + + if (trust) + { + make_a_mod(&mods, ldap_state, "objectclass", "sambaTrust"); + make_a_mod(&mods, ldap_state, "netbiosTrustName", smb_name); + make_a_mod(&mods, ldap_state, "trustPassword", nthash); + } + else + { + make_a_mod(&mods, ldap_state, "objectclass", "sambaAccount"); + make_a_mod(&mods, ldap_state, "dBCSPwd", lmhash); + make_a_mod(&mods, ldap_state, "uid", smb_name); + make_a_mod(&mods, ldap_state, "unicodePwd", nthash); + } + + make_a_mod(&mods, ldap_state, "cn", smb_name); + + make_a_mod(&mods, ldap_state, "rid", rid); + make_a_mod(&mods, ldap_state, "pwdLastSet", lst); + make_a_mod(&mods, ldap_state, "userAccountControl", + smbpasswd_encode_acb_info(newpwd->acct_ctrl)); + + switch(flag) + { + case ADD_USER: + { + ldap_add_s(ldap_struct, dn, mods); + DEBUG(2,("modadd_ldappwd_entry: added: cn = %s in the LDAP database\n",smb_name)); + break; + } + case MODIFY_USER: + { + ldap_modify_s(ldap_struct, dn, mods); + DEBUG(2,("modadd_ldappwd_entry: changed: cn = %s in the LDAP database_n",smb_name)); + break; + } + default: + { + DEBUG(2,("modadd_ldappwd_entry: How did you come here? \n")); + ldap_unbind(ldap_struct); + return False; + break; + } + } + + ldap_mods_free(mods, 1); + + ldap_unbind(ldap_struct); + + return True; +} + +/************************************************************************ + Add or modify an entry. everything except the smb struct + +*************************************************************************/ +static BOOL modadd_ldap21pwd_entry(struct sam_passwd *newpwd, int flag) +{ + + /* assume the struct is correct and filled + that's the job of passdb.c to check */ + int scope = LDAP_SCOPE_ONELEVEL; + int rc; + char *smb_name; + int trust = False; + int ldap_state; + pstring filter; + pstring dn; + pstring lmhash; + pstring nthash; + pstring rid; + pstring lst; + pstring temp; + + LDAP *ldap_struct; + LDAPMessage *result; + LDAPMod **mods; + + smb_name = newpwd->smb_name; + + if (!ldap_open_connection(&ldap_struct)) /* open a connection to the server */ + { + return False; + } + + if (!ldap_connect_system(ldap_struct)) /* connect as system account */ + { + ldap_unbind(ldap_struct); + return False; + } + + if (smb_name[strlen(smb_name)-1] == '$' ) + { + smb_name[strlen(smb_name)-1] = '\0'; + trust = True; + } + + slprintf(filter, sizeof(filter)-1, + "(&(cn = %s)(|(objectclass = sambaTrust)(objectclass = sambaAccount)))", + smb_name); + + rc = ldap_search_s(ldap_struct, lp_ldap_suffix(), scope, filter, NULL, 0, &result); + + switch (flag) + { + case ADD_USER: + { + if (ldap_count_entries(ldap_struct, result) ! = 1) + { + DEBUG(2,("User already in the base, with samba properties\n")); + ldap_unbind(ldap_struct); + return False; + } + ldap_state = LDAP_MOD_ADD; + break; + } + + case MODIFY_USER: + { + if (ldap_count_entries(ldap_struct, result) ! = 1) + { + DEBUG(2,("No user to modify !\n")); + ldap_unbind(ldap_struct); + return False; + } + ldap_state = LDAP_MOD_REPLACE; + break; + } + + default: + { + DEBUG(2,("How did you come here? \n")); + ldap_unbind(ldap_struct); + return False; + break; + } + } + slprintf(dn, sizeof(dn)-1, "cn = %s, %s",smb_name, lp_ldap_suffix() ); + + mods = NULL; + + if (trust) + { + } + else + { + } + + make_a_mod(&mods, ldap_state, "cn", smb_name); + + make_a_mod(&mods, ldap_state, "rid", rid); + make_a_mod(&mods, ldap_state, "pwdLastSet", lst); + make_a_mod(&mods, ldap_state, "userAccountControl", + smbpasswd_encode_acct_ctrl(newpwd->acct_ctrl)); + + ldap_modify_s(ldap_struct, dn, mods); + + ldap_mods_free(mods, 1); + + ldap_unbind(ldap_struct); + + return True; +} + +/************************************************************************ + Routine to add an entry to the ldap passwd file. + + do not call this function directly. use passdb.c instead. + +*************************************************************************/ +static BOOL add_ldappwd_entry(struct smb_passwd *newpwd) +{ + return (modadd_ldappwd_entry(newpwd, ADD_USER) ); +} + +/************************************************************************ + Routine to search the ldap passwd file for an entry matching the username. + and then modify its password entry. We can't use the startldappwent()/ + getldappwent()/endldappwent() interfaces here as we depend on looking + in the actual file to decide how much room we have to write data. + override = False, normal + override = True, override XXXXXXXX'd out password or NO PASS + + do not call this function directly. use passdb.c instead. + +************************************************************************/ +static BOOL mod_ldappwd_entry(struct smb_passwd *pwd, BOOL override) +{ + return (modadd_ldappwd_entry(pwd, MODIFY_USER) ); +} + +/************************************************************************ + Routine to add an entry to the ldap passwd file. + + do not call this function directly. use passdb.c instead. + +*************************************************************************/ +static BOOL add_ldap21pwd_entry(struct sam_passwd *newpwd) +{ + return( modadd_ldappwd_entry(newpwd, ADD_USER)? + modadd_ldap21pwd_entry(newpwd, ADD_USER):False); +} + +/************************************************************************ + Routine to search the ldap passwd file for an entry matching the username. + and then modify its password entry. We can't use the startldappwent()/ + getldappwent()/endldappwent() interfaces here as we depend on looking + in the actual file to decide how much room we have to write data. + override = False, normal + override = True, override XXXXXXXX'd out password or NO PASS + + do not call this function directly. use passdb.c instead. + +************************************************************************/ +static BOOL mod_ldap21pwd_entry(struct sam_passwd *pwd, BOOL override) +{ + return( modadd_ldappwd_entry(pwd, MODIFY_USER)? + modadd_ldap21pwd_entry(pwd, MODIFY_USER):False); +} + +struct ldap_enum_info +{ + LDAP *ldap_struct; + LDAPMessage *result; + LDAPMessage *entry; +}; + +static struct ldap_enum_info ldap_ent; + +/*************************************************************** + Start to enumerate the ldap passwd list. Returns a void pointer + to ensure no modification outside this module. + + do not call this function directly. use passdb.c instead. + + ****************************************************************/ +static void *startldappwent(BOOL update) +{ + int scope = LDAP_SCOPE_ONELEVEL; + int rc; + + pstring filter; + + if (!ldap_open_connection(&ldap_ent.ldap_struct)) /* open a connection to the server */ + { + return NULL; + } + + if (!ldap_connect_system(ldap_ent.ldap_struct)) /* connect as system account */ + { + return NULL; + } + + /* when the class is known the search is much faster */ + switch (0) + { + case 1: + { + pstrcpy(filter, "objectclass = sambaAccount"); + break; + } + case 2: + { + pstrcpy(filter, "objectclass = sambaTrust"); + break; + } + default: + { + pstrcpy(filter, "(|(objectclass = sambaTrust)(objectclass = sambaAccount))"); + break; + } + } + + rc = ldap_search_s(ldap_ent.ldap_struct, lp_ldap_suffix(), scope, filter, NULL, 0, &ldap_ent.result); + + DEBUG(2,("%d entries in the base!\n", ldap_count_entries(ldap_ent.ldap_struct, ldap_ent.result) )); + + ldap_ent.entry = ldap_first_entry(ldap_ent.ldap_struct, ldap_ent.result); + + return &ldap_ent; +} + +/************************************************************************* + Routine to return the next entry in the ldap passwd list. + + do not call this function directly. use passdb.c instead. + + *************************************************************************/ +static struct smb_passwd *getldappwent(void *vp) +{ + static struct smb_passwd user; + struct ldap_enum_info *ldap_vp = (struct ldap_enum_info *)vp; + + ldap_vp->entry = ldap_next_entry(ldap_vp->ldap_struct, ldap_vp->entry); + + if (ldap_vp->entry ! = NULL) + { + ldap_get_smb_passwd(ldap_vp->ldap_struct, ldap_vp->entry, &user); + return &user; + } + return NULL; +} + +/************************************************************************* + Routine to return the next entry in the ldap passwd list. + + do not call this function directly. use passdb.c instead. + + *************************************************************************/ +static struct sam_passwd *getldap21pwent(void *vp) +{ + static struct sam_passwd user; + struct ldap_enum_info *ldap_vp = (struct ldap_enum_info *)vp; + + ldap_vp->entry = ldap_next_entry(ldap_vp->ldap_struct, ldap_vp->entry); + + if (ldap_vp->entry ! = NULL) + { + ldap_get_sam_passwd(ldap_vp->ldap_struct, ldap_vp->entry, &user); + return &user; + } + return NULL; +} + +/*************************************************************** + End enumeration of the ldap passwd list. + + do not call this function directly. use passdb.c instead. + +****************************************************************/ +static void endldappwent(void *vp) +{ + struct ldap_enum_info *ldap_vp = (struct ldap_enum_info *)vp; + ldap_msgfree(ldap_vp->result); + ldap_unbind(ldap_vp->ldap_struct); +} + +/************************************************************************* + Return the current position in the ldap passwd list as an SMB_BIG_UINT. + This must be treated as an opaque token. + + do not call this function directly. use passdb.c instead. + +*************************************************************************/ +static SMB_BIG_UINT getldappwpos(void *vp) +{ + return (SMB_BIG_UINT)0; +} + +/************************************************************************* + Set the current position in the ldap passwd list from SMB_BIG_UINT. + This must be treated as an opaque token. + + do not call this function directly. use passdb.c instead. + +*************************************************************************/ +static BOOL setldappwpos(void *vp, SMB_BIG_UINT tok) +{ + return False; +} + +/* + * Ldap derived functions. + */ + +static struct smb_passwd *getldappwnam(char *name) +{ + return pdb_sam_to_smb(iterate_getsam21pwnam(name)); +} + +static struct smb_passwd *getldappwuid(uid_t smb_userid) +{ + return pdb_sam_to_smb(iterate_getsam21pwuid(smb_userid)); +} + +static struct smb_passwd *getldappwrid(uint32 user_rid) +{ + return pdb_sam_to_smb(iterate_getsam21pwuid(pdb_user_rid_to_uid(user_rid))); +} + +static struct smb_passwd *getldappwent(void *vp) +{ + return pdb_sam_to_smb(getldap21pwent(vp)); +} + +static BOOL add_ldappwd_entry(struct smb_passwd *newpwd) +{ + return add_ldap21pwd_entry(pdb_smb_to_sam(newpwd)); +} + +static BOOL mod_ldappwd_entry(struct smb_passwd* pwd, BOOL override) +{ + return mod_ldap21pwd_entry(pdb_smb_to_sam(pwd), override); +} + +static BOOL del_ldappwd_entry(const char *name) +{ + return False; /* Dummy... */ +} + +static struct sam_passwd *getldap21pwuid(uid_t uid) +{ + return pdb_smb_to_sam(iterate_getsam21pwuid(pdb_uid_to_user_rid(uid))); +} + +static struct passdb_ops ldap_ops = +{ + startldappwent, + endldappwent, + getldappwpos, + setldappwpos, + getldappwnam, + getldappwuid, + getldappwrid, + getldappwent, + add_ldappwd_entry, + mod_ldappwd_entry, + del_ldappwd_entry, + getldap21pwent, + iterate_getsam21pwnam, /* From passdb.c */ + iterate_getsam21pwuid, /* From passdb.c */ + iterate_getsam21pwrid, /* From passdb.c */ + add_ldap21pwd_entry, + mod_ldap21pwd_entry +}; + +struct passdb_ops *ldap_initialize_password_db(void) +{ + return &ldap_ops; +} + +#else + void dummy_function(void); + void dummy_function(void) { } /* stop some compilers complaining */ +#endif diff --git a/source/passdb/machine_sid.c b/source/passdb/machine_sid.c new file mode 100644 index 00000000000..859f00b4c6e --- /dev/null +++ b/source/passdb/machine_sid.c @@ -0,0 +1,255 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Password and authentication handling + Copyright (C) Jeremy Allison 1996-1998 + Copyright (C) Luke Kenneth Casson Leighton 1996-1998 + Copyright (C) Gerald (Jerry) Carter 2000 + + 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" + +/**************************************************************************** + Read the machine SID from a file. +****************************************************************************/ + +static BOOL read_sid_from_file(int fd, char *sid_file) +{ + fstring fline; + + memset(fline, '\0', sizeof(fline)); + + if(read(fd, fline, sizeof(fline) -1 ) < 0) { + DEBUG(0,("unable to read file %s. Error was %s\n", + sid_file, strerror(errno) )); + return False; + } + + /* + * Convert to the machine SID. + */ + + fline[sizeof(fline)-1] = '\0'; + if(!string_to_sid( &global_sam_sid, fline)) { + DEBUG(0,("unable to generate machine SID.\n")); + return False; + } + + return True; +} + +/**************************************************************************** + Generate the global machine sid. Look for the MACHINE.SID file first, if + not found then look in smb.conf and use it to create the MACHINE.SID file. + Note this function will be replaced soon. JRA. +****************************************************************************/ + +BOOL pdb_generate_sam_sid(void) +{ + int fd; + pstring sid_file; + fstring sid_string; + SMB_STRUCT_STAT st; + BOOL overwrite_bad_sid = False; + + generate_wellknown_sids(); + + pstrcpy(sid_file, lp_private_dir()); + + if (!directory_exist(sid_file, NULL)) { + if (mkdir(sid_file, 0700) != 0) { + DEBUG(0,("can't create private directory %s : %s\n", + sid_file, strerror(errno))); + return False; + } + } + + pstrcat(sid_file, "/MACHINE.SID"); + + if((fd = sys_open(sid_file, O_RDWR | O_CREAT, 0644)) == -1) { + DEBUG(0,("unable to open or create file %s. Error was %s\n", + sid_file, strerror(errno) )); + return False; + } + + /* + * Check if the file contains data. + */ + + if(sys_fstat( fd, &st) < 0) { + DEBUG(0,("unable to stat file %s. Error was %s\n", + sid_file, strerror(errno) )); + close(fd); + return False; + } + + if(st.st_size > 0) { + /* + * We have a valid SID - read it. + */ + if(!read_sid_from_file( fd, sid_file)) { + DEBUG(0,("unable to read file %s. Error was %s\n", + sid_file, strerror(errno) )); + close(fd); + return False; + } + + /* + * JRA. Reversed the sense of this test now that I have + * actually done this test *personally*. One more reason + * to never trust third party information you have not + * independently verified.... sigh. JRA. + */ + + if(global_sam_sid.num_auths > 0 && global_sam_sid.sub_auths[0] == 0x21) { + /* + * Fix and re-write... + */ + overwrite_bad_sid = True; + global_sam_sid.sub_auths[0] = 21; + DEBUG(5,("pdb_generate_sam_sid: Old (incorrect) sid id_auth of hex 21 \ +detected - re-writing to be decimal 21 instead.\n" )); + sid_to_string(sid_string, &global_sam_sid); + if(sys_lseek(fd, (SMB_OFF_T)0, SEEK_SET) != 0) { + DEBUG(0,("unable to seek file file %s. Error was %s\n", + sid_file, strerror(errno) )); + close(fd); + return False; + } + } else { + close(fd); + return True; + } + } else { + /* + * The file contains no data - we need to generate our + * own sid. + * Generate the new sid data & turn it into a string. + */ + int i; + uchar raw_sid_data[12]; + DOM_SID mysid; + + memset((char *)&mysid, '\0', sizeof(DOM_SID)); + mysid.sid_rev_num = 1; + mysid.id_auth[5] = 5; + mysid.num_auths = 0; + mysid.sub_auths[mysid.num_auths++] = 21; + + generate_random_buffer( raw_sid_data, 12, True); + for( i = 0; i < 3; i++) + mysid.sub_auths[mysid.num_auths++] = IVAL(raw_sid_data, i*4); + + sid_to_string(sid_string, &mysid); + } + + fstrcat(sid_string, "\n"); + + /* + * Ensure our new SID is valid. + */ + + if(!string_to_sid( &global_sam_sid, sid_string)) { + DEBUG(0,("unable to generate machine SID.\n")); + return False; + } + + /* + * Do an exclusive blocking lock on the file. + */ + + if(!do_file_lock( fd, 60, F_WRLCK)) { + DEBUG(0,("unable to lock file %s. Error was %s\n", + sid_file, strerror(errno) )); + close(fd); + return False; + } + + if(!overwrite_bad_sid) { + /* + * At this point we have a blocking lock on the SID + * file - check if in the meantime someone else wrote + * SID data into the file. If so - they were here first, + * use their data. + */ + + if(sys_fstat( fd, &st) < 0) { + DEBUG(0,("unable to stat file %s. Error was %s\n", + sid_file, strerror(errno) )); + close(fd); + return False; + } + + if(st.st_size > 0) { + /* + * Unlock as soon as possible to reduce + * contention on the exclusive lock. + */ + do_file_lock( fd, 60, F_UNLCK); + + /* + * We have a valid SID - read it. + */ + + if(!read_sid_from_file( fd, sid_file)) { + DEBUG(0,("unable to read file %s. Error was %s\n", + sid_file, strerror(errno) )); + close(fd); + return False; + } + close(fd); + return True; + } + } + + /* + * The file is still empty and we have an exlusive lock on it, + * or we're fixing an earlier mistake. + * Write out out SID data into the file. + */ + + /* + * Use chmod here as some (strange) UNIX's don't + * have fchmod. JRA. + */ + + if(chmod(sid_file, 0644) < 0) { + DEBUG(0,("unable to set correct permissions on file %s. \ +Error was %s\n", sid_file, strerror(errno) )); + do_file_lock( fd, 60, F_UNLCK); + close(fd); + return False; + } + + if(write( fd, sid_string, strlen(sid_string)) != strlen(sid_string)) { + DEBUG(0,("unable to write file %s. Error was %s\n", + sid_file, strerror(errno) )); + do_file_lock( fd, 60, F_UNLCK); + close(fd); + return False; + } + + /* + * Unlock & exit. + */ + + do_file_lock( fd, 60, F_UNLCK); + close(fd); + return True; +} + + diff --git a/source/passdb/nispass.c b/source/passdb/nispass.c new file mode 100644 index 00000000000..2b1f6b54927 --- /dev/null +++ b/source/passdb/nispass.c @@ -0,0 +1,1083 @@ +/* + * Unix SMB/Netbios implementation. Version 1.9. SMB parameters and setup + * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995. + * Copyright (C) Benny Holmgren 1998 <bigfoot@astrakan.hgs.se> + * Copyright (C) Luke Kenneth Casson Leighton 1996-1998. + * + * 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" + +#ifdef WITH_NISPLUS + +#ifdef BROKEN_NISPLUS_INCLUDE_FILES + +/* + * The following lines are needed due to buggy include files + * in Solaris 2.6 which define GROUP in both /usr/include/sys/acl.h and + * also in /usr/include/rpcsvc/nis.h. The definitions conflict. JRA. + * Also GROUP_OBJ is defined as 0x4 in /usr/include/sys/acl.h and as + * an enum in /usr/include/rpcsvc/nis.h. + */ + +#if defined(GROUP) +#undef GROUP +#endif + +#if defined(GROUP_OBJ) +#undef GROUP_OBJ +#endif + +#endif + +#include <rpcsvc/nis.h> + +extern int DEBUGLEVEL; + +static VOLATILE sig_atomic_t gotalarm; + +/*************************************************************** + + the fields for the NIS+ table, generated from mknissmbpwtbl.sh, are: + + name=S,nogw=r + uid=S,nogw=r + user_rid=S,nogw=r + smb_grpid=,nw+r + group_rid=,nw+r + acb=,nw+r + + lmpwd=C,nw=,g=r,o=rm + ntpwd=C,nw=,g=r,o=rm + + logon_t=,nw+r + logoff_t=,nw+r + kick_t=,nw+r + pwdlset_t=,nw+r + pwdlchg_t=,nw+r + pwdmchg_t=,nw+r + + full_name=,nw+r + home_dir=,nw+r + dir_drive=,nw+r + logon_script=,nw+r + profile_path=,nw+r + acct_desc=,nw+r + workstations=,nw+r + + hours=,nw+r + +****************************************************************/ + +#define NPF_NAME 0 +#define NPF_UID 1 +#define NPF_USER_RID 2 +#define NPF_SMB_GRPID 3 +#define NPF_GROUP_RID 4 +#define NPF_ACB 5 +#define NPF_LMPWD 6 +#define NPF_NTPWD 7 +#define NPF_LOGON_T 8 +#define NPF_LOGOFF_T 9 +#define NPF_KICK_T 10 +#define NPF_PWDLSET_T 11 +#define NPF_PWDLCHG_T 12 +#define NPF_PWDMCHG_T 13 +#define NPF_FULL_NAME 14 +#define NPF_HOME_DIR 15 +#define NPF_DIR_DRIVE 16 +#define NPF_LOGON_SCRIPT 17 +#define NPF_PROFILE_PATH 18 +#define NPF_ACCT_DESC 19 +#define NPF_WORKSTATIONS 20 +#define NPF_HOURS 21 + +/*************************************************************** + Signal function to tell us we timed out. +****************************************************************/ +static void gotalarm_sig(void) +{ + gotalarm = 1; +} + +/*************************************************************** + make_nisname_from_user_rid + ****************************************************************/ +static char *make_nisname_from_user_rid(uint32 rid, char *pfile) +{ + static pstring nisname; + + safe_strcpy(nisname, "[user_rid=", sizeof(nisname)-1); + slprintf(nisname, sizeof(nisname)-1, "%s%d", nisname, rid); + safe_strcat(nisname, "],", sizeof(nisname)-strlen(nisname)-1); + safe_strcat(nisname, pfile, sizeof(nisname)-strlen(nisname)-1); + + return nisname; +} + +/*************************************************************** + make_nisname_from_uid + ****************************************************************/ +static char *make_nisname_from_uid(int uid, char *pfile) +{ + static pstring nisname; + + safe_strcpy(nisname, "[uid=", sizeof(nisname)-1); + slprintf(nisname, sizeof(nisname)-1, "%s%d", nisname, uid); + safe_strcat(nisname, "],", sizeof(nisname)-strlen(nisname)-1); + safe_strcat(nisname, pfile, sizeof(nisname)-strlen(nisname)-1); + + return nisname; +} + +/*************************************************************** + make_nisname_from_name + ****************************************************************/ +static char *make_nisname_from_name(char *user_name, char *pfile) +{ + static pstring nisname; + + safe_strcpy(nisname, "[name=", sizeof(nisname)-1); + safe_strcat(nisname, user_name, sizeof(nisname) - strlen(nisname) - 1); + safe_strcat(nisname, "],", sizeof(nisname)-strlen(nisname)-1); + safe_strcat(nisname, pfile, sizeof(nisname)-strlen(nisname)-1); + + return nisname; +} + +/************************************************************************* + gets a NIS+ attribute + *************************************************************************/ +static void get_single_attribute(nis_object *new_obj, int col, + char *val, int len) +{ + int entry_len; + + if (new_obj == NULL || val == NULL) return; + + entry_len = ENTRY_LEN(new_obj, col); + if (len > entry_len) + { + DEBUG(10,("get_single_attribute: entry length truncated\n")); + len = entry_len; + } + + safe_strcpy(val, ENTRY_VAL(new_obj, col), len-1); +} + +/************************************************************************ + makes a struct sam_passwd from a NIS+ object. + ************************************************************************/ +static BOOL make_sam_from_nisp_object(struct sam_passwd *pw_buf, nis_object *obj) +{ + int uidval; + static pstring user_name; + static pstring full_name; + static pstring home_dir; + static pstring home_drive; + static pstring logon_script; + static pstring profile_path; + static pstring acct_desc; + static pstring workstations; + static pstring temp; + static unsigned char smbpwd[16]; + static unsigned char smbntpwd[16]; + + char *p; + + pdb_init_sam(pw_buf); + pw_buf->acct_ctrl = ACB_NORMAL; + + pstrcpy(user_name, ENTRY_VAL(obj, NPF_NAME)); + pw_buf->smb_name = user_name; + + uidval = atoi(ENTRY_VAL(obj, NPF_UID)); + pw_buf->smb_userid = uidval; + + /* Check the lanman password column. */ + p = (char *)ENTRY_VAL(obj, NPF_LMPWD); + if (*p == '*' || *p == 'X') { + /* Password deliberately invalid - end here. */ + DEBUG(10, ("make_sam_from_nisp_object: entry invalidated for user %s\n", user_name)); + pw_buf->smb_nt_passwd = NULL; + pw_buf->smb_passwd = NULL; + pw_buf->acct_ctrl |= ACB_DISABLED; + return True; + } + if (!strncasecmp(p, "NO PASSWORD", 11)) { + pw_buf->smb_passwd = NULL; + pw_buf->acct_ctrl |= ACB_PWNOTREQ; + } else { + if (strlen(p) != 32 || !pdb_gethexpwd(p, smbpwd)) + { + DEBUG(0, ("make_sam_from_nisp_object: malformed LM pwd entry.\n")); + return False; + } + } + + pw_buf->smb_passwd = smbpwd; + + /* Check the NT password column. */ + p = ENTRY_VAL(obj, NPF_NTPWD); + if (*p != '*' && *p != 'X') { + if (strlen(p) != 32 || !pdb_gethexpwd(p, smbntpwd)) + { + DEBUG(0, ("make_smb_from_nisp_object: malformed NT pwd entry\n")); + return False; + } + pw_buf->smb_nt_passwd = smbntpwd; + } + + p = (char *)ENTRY_VAL(obj, NPF_ACB); + if (*p == '[') + { + pw_buf->acct_ctrl = pdb_decode_acct_ctrl(p); + + /* Must have some account type set. */ + if(pw_buf->acct_ctrl == 0) + pw_buf->acct_ctrl = ACB_NORMAL; + + /* Now try and get the last change time. */ + if(*p == ']') + p++; + if(*p == ':') { + p++; + if(*p && (StrnCaseCmp(p, "LCT-", 4)==0)) { + int i; + p += 4; + for(i = 0; i < 8; i++) { + if(p[i] == '\0' || !isxdigit(p[i])) + break; + } + if(i == 8) { + /* + * p points at 8 characters of hex digits - + * read into a time_t as the seconds since + * 1970 that the password was last changed. + */ + pw_buf->pass_last_set_time = (time_t)strtol(p, NULL, 16); + } + } + } + } else { + /* 'Old' style file. Fake up based on user name. */ + /* + * Currently trust accounts are kept in the same + * password file as 'normal accounts'. If this changes + * we will have to fix this code. JRA. + */ + if(pw_buf->smb_name[strlen(pw_buf->smb_name) - 1] == '$') { + pw_buf->acct_ctrl &= ~ACB_NORMAL; + pw_buf->acct_ctrl |= ACB_WSTRUST; + } + } + + get_single_attribute(obj, NPF_SMB_GRPID, temp, sizeof(pstring)); + pw_buf->smb_grpid = atoi(temp); + + get_single_attribute(obj, NPF_USER_RID, temp, sizeof(pstring)); + pw_buf->user_rid = (strlen(temp) > 0) ? + strtol(temp, NULL, 16) : pdb_uid_to_user_rid (pw_buf->smb_userid); + + if (pw_buf->smb_name[strlen(pw_buf->smb_name)-1] != '$') { + + get_single_attribute(obj, NPF_GROUP_RID, temp, sizeof(pstring)); + + if (strlen(temp) > 0) + pw_buf->group_rid = strtol(temp, NULL, 16); + else { + GROUP_MAP map; + + if (get_group_map_from_gid(pw_buf->smb_grpid, &map, MAPPING_WITHOUT_PRIV)) { + pw_buf->group_rid = map.rid; + } + else + pw_buf->group_rid = pdb_gid_to_group_rid(pw_buf->smb_grpid); + } + + get_single_attribute(obj, NPF_FULL_NAME, full_name, sizeof(pstring)); +#if 1 + /* It seems correct to use the global values - but in that case why + * do we want these NIS+ entries anyway ?? + */ + pstrcpy(logon_script , lp_logon_script ()); + pstrcpy(profile_path , lp_logon_path ()); + pstrcpy(home_drive , lp_logon_drive ()); + pstrcpy(home_dir , lp_logon_home ()); +#else + get_single_attribute(obj, NPF_LOGON_SCRIPT, logon_script, sizeof(pstring)); + get_single_attribute(obj, NPF_PROFILE_PATH, profile_path, sizeof(pstring)); + get_single_attribute(obj, NPF_DIR_DRIVE, home_drive, sizeof(pstring)); + get_single_attribute(obj, NPF_HOME_DIR, home_dir, sizeof(pstring)); +#endif + get_single_attribute(obj, NPF_ACCT_DESC, acct_desc, sizeof(pstring)); + get_single_attribute(obj, NPF_WORKSTATIONS, workstations, sizeof(pstring)); + + } else { + + pw_buf->group_rid = DOMAIN_GROUP_RID_USERS; /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. */ + + pstrcpy(full_name , ""); + pstrcpy(logon_script , ""); + pstrcpy(profile_path , ""); + pstrcpy(home_drive , ""); + pstrcpy(home_dir , ""); + pstrcpy(acct_desc , ""); + pstrcpy(workstations , ""); + } + + pw_buf->full_name = full_name; + pw_buf->home_dir = home_dir; + pw_buf->dir_drive = home_drive; + pw_buf->logon_script = logon_script; + pw_buf->profile_path = profile_path; + pw_buf->acct_desc = acct_desc; + pw_buf->workstations = workstations; + + pw_buf->unknown_str = NULL; /* don't know, yet! */ + pw_buf->munged_dial = NULL; /* "munged" dial-back telephone number */ + + pw_buf->unknown_3 = 0xffffff; /* don't know */ + pw_buf->logon_divs = 168; /* hours per week */ + pw_buf->hours_len = 21; /* 21 times 8 bits = 168 */ + memset(pw_buf->hours, 0xff, pw_buf->hours_len); /* available at all hours */ + pw_buf->unknown_5 = 0x00000000; /* don't know */ + pw_buf->unknown_6 = 0x000004ec; /* don't know */ + + return True; +} + +/************************************************************************ + makes a struct sam_passwd from a NIS+ result. + ************************************************************************/ +static BOOL make_sam_from_nisresult(struct sam_passwd *pw_buf, nis_result *result) +{ + if (pw_buf == NULL || result == NULL) return False; + + if (result->status != NIS_SUCCESS && result->status != NIS_NOTFOUND) + { + DEBUG(0, ("make_sam_from_nisresult: NIS+ lookup failure: %s\n", + nis_sperrno(result->status))); + return False; + } + + /* User not found. */ + if (NIS_RES_NUMOBJ(result) <= 0) + { + DEBUG(10, ("make_sam_from_nisresult: user not found in NIS+\n")); + return False; + } + + if (NIS_RES_NUMOBJ(result) > 1) + { + DEBUG(10, ("make_sam_from_nisresult: WARNING: Multiple entries for user in NIS+ table!\n")); + } + + /* Grab the first hit. */ + return make_sam_from_nisp_object(pw_buf, &NIS_RES_OBJECT(result)[0]); + } + +/*************************************************************** + calls nis_list, returns results. + ****************************************************************/ +static nis_result *nisp_get_nis_list(char *nis_name) +{ + nis_result *result; + result = nis_list(nis_name, FOLLOW_PATH|EXPAND_NAME|HARD_LOOKUP,NULL,NULL); + + alarm(0); + CatchSignal(SIGALRM, SIGNAL_CAST SIG_DFL); + + if (gotalarm) + { + DEBUG(0,("nisp_get_nis_list: NIS+ lookup time out\n")); + nis_freeresult(result); + return NULL; + } + return result; +} + + + +struct nisp_enum_info +{ + nis_result *result; + int enum_entry; +}; + +/*************************************************************** + Start to enumerate the nisplus passwd list. Returns a void pointer + to ensure no modification outside this module. + + do not call this function directly. use passdb.c instead. + + ****************************************************************/ +static void *startnisppwent(BOOL update) +{ + static struct nisp_enum_info res; + res.result = nisp_get_nis_list(lp_smb_passwd_file()); + res.enum_entry = 0; + return res.result != NULL ? &res : NULL; +} + +/*************************************************************** + End enumeration of the nisplus passwd list. +****************************************************************/ +static void endnisppwent(void *vp) +{ + struct nisp_enum_info *res = (struct nisp_enum_info *)vp; + nis_freeresult(res->result); + DEBUG(7,("endnisppwent: freed enumeration list\n")); +} + +/************************************************************************* + Routine to return the next entry in the nisplus passwd list. + + do not call this function directly. use passdb.c instead. + + *************************************************************************/ +static struct sam_passwd *getnisp21pwent(void *vp) +{ + struct nisp_enum_info *res = (struct nisp_enum_info *)vp; + static struct sam_passwd pw_buf; + int which; + BOOL ret; + + if (res == NULL || (int)(res->enum_entry) < 0 || + (int)(res->enum_entry) > (NIS_RES_NUMOBJ(res->result) - 1)) { + ret = False; + } else { + which = (int)(res->enum_entry); + ret = make_sam_from_nisp_object(&pw_buf, + &NIS_RES_OBJECT(res->result)[which]); + if (ret && which < (NIS_RES_NUMOBJ(res->result) - 1)) + (int)(res->enum_entry)++; + } + + return ret ? &pw_buf : NULL; +} + +/************************************************************************* + Return the current position in the nisplus passwd list as an SMB_BIG_UINT. + This must be treated as an opaque token. + + do not call this function directly. use passdb.c instead. + +*************************************************************************/ +static SMB_BIG_UINT getnisppwpos(void *vp) +{ + struct nisp_enum_info *res = (struct nisp_enum_info *)vp; + return (SMB_BIG_UINT)(res->enum_entry); +} + +/************************************************************************* + Set the current position in the nisplus passwd list from SMB_BIG_UINT. + This must be treated as an opaque token. + + do not call this function directly. use passdb.c instead. + +*************************************************************************/ +static BOOL setnisppwpos(void *vp, SMB_BIG_UINT tok) +{ + struct nisp_enum_info *res = (struct nisp_enum_info *)vp; + if (tok < (NIS_RES_NUMOBJ(res->result) - 1)) { + res->enum_entry = tok; + return True; + } else { + return False; + } +} + +/************************************************************************* + sets a NIS+ attribute + *************************************************************************/ +static void set_single_attribute(nis_object *new_obj, int col, + const char *val, int len, int flags) +{ + if (new_obj == NULL) return; + + ENTRY_VAL(new_obj, col) = val; + ENTRY_LEN(new_obj, col) = len+1; + + if (flags != 0) + { + new_obj->EN_data.en_cols.en_cols_val[col].ec_flags = flags; + } +} + +/************************************************************************ + Routine to add an entry to the nisplus passwd file. + + do not call this function directly. use passdb.c instead. + +*************************************************************************/ +static BOOL add_nisp21pwd_entry(struct sam_passwd *newpwd) +{ + char *pfile; + char *nisname; + nis_result *nis_user; + nis_result *result = NULL, + *tblresult = NULL, + *addresult = NULL; + nis_object new_obj, *obj; + + fstring uid; + fstring user_rid; + fstring smb_grpid; + fstring group_rid; + fstring acb; + + fstring smb_passwd; + fstring smb_nt_passwd; + + fstring logon_t; + fstring logoff_t; + fstring kickoff_t; + fstring pwdlset_t; + fstring pwdlchg_t; + fstring pwdmchg_t; + + memset((char *)logon_t , '\0', sizeof(logon_t )); + memset((char *)logoff_t , '\0', sizeof(logoff_t )); + memset((char *)kickoff_t, '\0', sizeof(kickoff_t)); + memset((char *)pwdlset_t, '\0', sizeof(pwdlset_t)); + memset((char *)pwdlchg_t, '\0', sizeof(pwdlchg_t)); + memset((char *)pwdmchg_t, '\0', sizeof(pwdmchg_t)); + + pfile = lp_smb_passwd_file(); + + nisname = make_nisname_from_name(newpwd->smb_name, pfile); + result = nisp_get_nis_list(nisname); + if (result->status != NIS_SUCCESS && result->status != NIS_NOTFOUND) + { + DEBUG(3, ( "add_nis21ppwd_entry: nis_list failure: %s: %s\n", + nisname, nis_sperrno(result->status))); + nis_freeresult(result); + return False; + } + + if (result->status == NIS_SUCCESS && NIS_RES_NUMOBJ(result) > 0) + { + DEBUG(3, ("add_nisp21pwd_entry: User already exists in NIS+ password db: %s\n", + pfile)); + nis_freeresult(result); + return False; + } + +#if 0 + /* User not found. */ + if (!add_user) + { + DEBUG(3, ("add_nisp21pwd_entry: User not found in NIS+ password db: %s\n", + pfile)); + nis_freeresult(result); + return False; + } + +#endif + + tblresult = nis_lookup(pfile, FOLLOW_PATH | EXPAND_NAME | HARD_LOOKUP ); + if (tblresult->status != NIS_SUCCESS) + { + nis_freeresult(result); + nis_freeresult(tblresult); + DEBUG(3, ( "add_nisp21pwd_entry: nis_lookup failure: %s\n", + nis_sperrno(tblresult->status))); + return False; + } + + new_obj.zo_name = NIS_RES_OBJECT(tblresult)->zo_name; + new_obj.zo_domain = NIS_RES_OBJECT(tblresult)->zo_domain; + new_obj.zo_owner = NIS_RES_OBJECT(tblresult)->zo_owner; + new_obj.zo_group = NIS_RES_OBJECT(tblresult)->zo_group; + new_obj.zo_access = NIS_RES_OBJECT(tblresult)->zo_access; + new_obj.zo_ttl = NIS_RES_OBJECT(tblresult)->zo_ttl; + + new_obj.zo_data.zo_type = ENTRY_OBJ; + + new_obj.zo_data.objdata_u.en_data.en_type = NIS_RES_OBJECT(tblresult)->zo_data.objdata_u.ta_data.ta_type; + new_obj.zo_data.objdata_u.en_data.en_cols.en_cols_len = NIS_RES_OBJECT(tblresult)->zo_data.objdata_u.ta_data.ta_maxcol; + new_obj.zo_data.objdata_u.en_data.en_cols.en_cols_val = calloc(new_obj.zo_data.objdata_u.en_data.en_cols.en_cols_len, sizeof(entry_col)); + + if (new_obj.zo_data.objdata_u.en_data.en_cols.en_cols_val == NULL) + { + DEBUG(0, ("add_nisp21pwd_entry: memory allocation failure\n")); + nis_freeresult(result); + nis_freeresult(tblresult); + return False; + } + + pdb_sethexpwd(smb_passwd , newpwd->smb_passwd , newpwd->acct_ctrl); + pdb_sethexpwd(smb_nt_passwd, newpwd->smb_nt_passwd, newpwd->acct_ctrl); + + newpwd->pass_last_set_time = (time_t)time(NULL); + newpwd->logon_time = (time_t)-1; + newpwd->logoff_time = (time_t)-1; + newpwd->kickoff_time = (time_t)-1; + newpwd->pass_can_change_time = (time_t)-1; + newpwd->pass_must_change_time = (time_t)-1; + + slprintf(logon_t, 13, "LNT-%08X", (uint32)newpwd->logon_time); + slprintf(logoff_t, 13, "LOT-%08X", (uint32)newpwd->logoff_time); + slprintf(kickoff_t, 13, "KOT-%08X", (uint32)newpwd->kickoff_time); + slprintf(pwdlset_t, 13, "LCT-%08X", (uint32)newpwd->pass_last_set_time); + slprintf(pwdlchg_t, 13, "CCT-%08X", (uint32)newpwd->pass_can_change_time); + slprintf(pwdmchg_t, 13, "MCT-%08X", (uint32)newpwd->pass_must_change_time); + + slprintf(uid, sizeof(uid)-1, "%u", newpwd->smb_userid); + slprintf(user_rid, sizeof(user_rid)-1, "0x%x", newpwd->user_rid); + slprintf(smb_grpid, sizeof(smb_grpid)-1, "%u", newpwd->smb_grpid); + slprintf(group_rid, sizeof(group_rid)-1, "0x%x", newpwd->group_rid); + + safe_strcpy(acb, smbpasswd_encode_acct_ctrl(newpwd->acct_ctrl), + sizeof(acb)-1); + + set_single_attribute(&new_obj, NPF_NAME , newpwd->smb_name , strlen(newpwd->smb_name) , 0); + set_single_attribute(&new_obj, NPF_UID , uid , strlen(uid) , 0); + set_single_attribute(&new_obj, NPF_USER_RID , user_rid , strlen(user_rid) , 0); + set_single_attribute(&new_obj, NPF_SMB_GRPID , smb_grpid , strlen(smb_grpid) , 0); + set_single_attribute(&new_obj, NPF_GROUP_RID , group_rid , strlen(group_rid) , 0); + set_single_attribute(&new_obj, NPF_ACB , acb , strlen(acb) , 0); + set_single_attribute(&new_obj, NPF_LMPWD , smb_passwd , strlen(smb_passwd) , EN_CRYPT); + set_single_attribute(&new_obj, NPF_NTPWD , smb_nt_passwd , strlen(smb_nt_passwd) , EN_CRYPT); + set_single_attribute(&new_obj, NPF_LOGON_T , logon_t , strlen(logon_t) , 0); + set_single_attribute(&new_obj, NPF_LOGOFF_T , logoff_t , strlen(logoff_t) , 0); + set_single_attribute(&new_obj, NPF_KICK_T , kickoff_t , strlen(kickoff_t) , 0); + set_single_attribute(&new_obj, NPF_PWDLSET_T , pwdlset_t , strlen(pwdlset_t) , 0); + set_single_attribute(&new_obj, NPF_PWDLCHG_T , pwdlchg_t , strlen(pwdlchg_t) , 0); + set_single_attribute(&new_obj, NPF_PWDMCHG_T , pwdmchg_t , strlen(pwdmchg_t) , 0); +#if 0 + set_single_attribute(&new_obj, NPF_FULL_NAME , newpwd->full_name , strlen(newpwd->full_name) , 0); + set_single_attribute(&new_obj, NPF_HOME_DIR , newpwd->home_dir , strlen(newpwd->home_dir) , 0); + set_single_attribute(&new_obj, NPF_DIR_DRIVE , newpwd->dir_drive , strlen(newpwd->dir_drive) , 0); + set_single_attribute(&new_obj, NPF_LOGON_SCRIPT , newpwd->logon_script , strlen(newpwd->logon_script) , 0); + set_single_attribute(&new_obj, NPF_PROFILE_PATH , newpwd->profile_path , strlen(newpwd->profile_path) , 0); + set_single_attribute(&new_obj, NPF_ACCT_DESC , newpwd->acct_desc , strlen(newpwd->acct_desc) , 0); + set_single_attribute(&new_obj, NPF_WORKSTATIONS , newpwd->workstations , strlen(newpwd->workstations) , 0); + set_single_attribute(&new_obj, NPF_HOURS , newpwd->hours , newpwd->hours_len , 0); +#endif + + obj = &new_obj; + + addresult = nis_add_entry(pfile, obj, ADD_OVERWRITE | FOLLOW_PATH | EXPAND_NAME | HARD_LOOKUP); + + + if (addresult->status != NIS_SUCCESS) + { + DEBUG(3, ( "add_nisp21pwd_entry: NIS+ table update failed: %s\n", + nisname, nis_sperrno(addresult->status))); + nis_freeresult(tblresult); + nis_freeresult(addresult); + nis_freeresult(result); + return False; + } + + nis_freeresult(tblresult); + nis_freeresult(addresult); + nis_freeresult(result); + + return True; +} + +/************************************************************************ + Routine to search the nisplus passwd file for an entry matching the username. + and then modify its password entry. We can't use the startnisppwent()/ + getnisppwent()/endnisppwent() interfaces here as we depend on looking + in the actual file to decide how much room we have to write data. + override = False, normal + override = True, override XXXXXXXX'd out password or NO PASS + + do not call this function directly. use passdb.c instead. + +************************************************************************/ +static BOOL mod_nisp21pwd_entry(struct sam_passwd* pwd, BOOL override) +{ + char *oldnislmpwd, *oldnisntpwd, *oldnisacb, *oldnislct, *user_name; + char lmpwd[33], ntpwd[33], lct[13]; + nis_result *result, *addresult; + nis_object *obj; + fstring acb; + pstring nisname; + BOOL got_pass_last_set_time, ret; + int i; + + if (!*lp_smb_passwd_file()) + { + DEBUG(0, ("mod_getnisp21pwd_entry: no SMB password file set\n")); + return False; + } + + DEBUG(10, ("mod_getnisp21pwd_entry: search by name: %s\n", pwd->smb_name)); + DEBUG(10, ("mod_getnisp21pwd_entry: using NIS+ table %s\n", lp_smb_passwd_file())); + + slprintf(nisname, sizeof(nisname)-1, "[name=%s],%s", pwd->smb_name, lp_smb_passwd_file()); + + /* Search the table. */ + gotalarm = 0; + CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig); + alarm(5); + + result = nis_list(nisname, FOLLOW_PATH | EXPAND_NAME | HARD_LOOKUP, NULL, NULL); + + alarm(0); + CatchSignal(SIGALRM, SIGNAL_CAST SIG_DFL); + + if (gotalarm) + { + DEBUG(0,("mod_getnisp21pwd_entry: NIS+ lookup time out\n")); + nis_freeresult(result); + return False; + } + + if(result->status != NIS_SUCCESS || NIS_RES_NUMOBJ(result) <= 0) { + /* User not found. */ + DEBUG(0,("mod_getnisp21pwd_entry: user not found in NIS+\n")); + nis_freeresult(result); + return False; + } + + DEBUG(6,("mod_getnisp21pwd_entry: entry exists\n")); + + obj = NIS_RES_OBJECT(result); + + user_name = ENTRY_VAL(obj, NPF_NAME); + oldnislmpwd = ENTRY_VAL(obj, NPF_LMPWD); + oldnisntpwd = ENTRY_VAL(obj, NPF_NTPWD); + oldnisacb = ENTRY_VAL(obj, NPF_ACB); + oldnislct = ENTRY_VAL(obj, NPF_PWDLSET_T); + + + if (!override && (*oldnislmpwd == '*' || *oldnislmpwd == 'X' || + *oldnisntpwd == '*' || *oldnisntpwd == 'X')) { + /* Password deliberately invalid - end here. */ + DEBUG(10, ("mod_nisp21pwd_entry: entry invalidated for user %s\n", user_name)); + nis_freeresult(result); + return False; + } + + if (strlen(oldnislmpwd) != 32 || strlen(oldnisntpwd) != 32) { + DEBUG(0, ("mod_nisp21pwd_entry: malformed password entry (incorrect length)\n")); + nis_freeresult(result); + return False; + } + + + + /* + * Now check if the account info and the password last + * change time is available. + */ + + /* + * If both NT and lanman passwords are provided - reset password + * not required flag. + */ + + if(pwd->smb_passwd != NULL || pwd->smb_nt_passwd != NULL) { + /* Require password in the future (should ACB_DISABLED also be reset?) */ + pwd->acct_ctrl &= ~(ACB_PWNOTREQ); + } + + if (*oldnisacb == '[') { + + i = 0; + acb[i++] = oldnisacb[i]; + while(i < (sizeof(fstring) - 2) && (oldnisacb[i] != ']')) + acb[i] = oldnisacb[i++]; + + acb[i++] = ']'; + acb[i++] = '\0'; + + if (i == NEW_PW_FORMAT_SPACE_PADDED_LEN) { + /* + * We are using a new format, space padded + * acct ctrl field. Encode the given acct ctrl + * bits into it. + */ + fstrcpy(acb, smbpasswd_encode_acct_ctrl(pwd->acct_ctrl)); + } else { + /* + * If using the old format and the ACB_DISABLED or + * ACB_PWNOTREQ are set then set the lanman and NT passwords to NULL + * here as we have no space to encode the change. + */ + if(pwd->acct_ctrl & (ACB_DISABLED|ACB_PWNOTREQ)) { + pwd->smb_passwd = NULL; + pwd->smb_nt_passwd = NULL; + } + } + + /* Now do the LCT stuff. */ + if (StrnCaseCmp(oldnislct, "LCT-", 4) == 0) { + + for(i = 0; i < 8; i++) { + if(oldnislct[i+4] == '\0' || !isxdigit(oldnislct[i+4])) + break; + } + if (i == 8) { + /* + * p points at 8 characters of hex digits - + * read into a time_t as the seconds since + * 1970 that the password was last changed. + */ + got_pass_last_set_time = True; + } /* i == 8 */ + } /* StrnCaseCmp() */ + + } /* p == '[' */ + + /* Entry is correctly formed. */ + + + + /* Create the 32 byte representation of the new p16 */ + if(pwd->smb_passwd != NULL) { + for (i = 0; i < 16; i++) { + slprintf(&lmpwd[i*2], 32, "%02X", (uchar) pwd->smb_passwd[i]); + } + } else { + if(pwd->acct_ctrl & ACB_PWNOTREQ) + fstrcpy(lmpwd, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX"); + else + fstrcpy(lmpwd, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); + } + + /* Add on the NT md4 hash */ + if (pwd->smb_nt_passwd != NULL) { + for (i = 0; i < 16; i++) { + slprintf(&ntpwd[i*2], 32, "%02X", (uchar) pwd->smb_nt_passwd[i]); + } + } else { + if(pwd->acct_ctrl & ACB_PWNOTREQ) + fstrcpy(ntpwd, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX"); + else + fstrcpy(ntpwd, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); + } + + pwd->pass_last_set_time = time(NULL); + + if(got_pass_last_set_time) { + slprintf(lct, 13, "LCT-%08X", (uint32)pwd->pass_last_set_time); + } + + set_single_attribute(obj, NPF_LMPWD, lmpwd, strlen(lmpwd), EN_CRYPT); + set_single_attribute(obj, NPF_NTPWD, ntpwd, strlen(ntpwd), EN_CRYPT); + set_single_attribute(obj, NPF_ACB, acb, strlen(acb), 0); + set_single_attribute(obj, NPF_PWDLSET_T, lct, strlen(lct), 0); + + addresult = + nis_add_entry(lp_smb_passwd_file(), obj, + ADD_OVERWRITE | FOLLOW_PATH | EXPAND_NAME | HARD_LOOKUP); + + if(addresult->status != NIS_SUCCESS) { + DEBUG(0, ("mod_nisp21pwd_entry: NIS+ table update failed: %s %s\n", nisname, nis_sperrno(addresult->status))); + nis_freeresult(addresult); + nis_freeresult(result); + return False; + } + + DEBUG(6,("mod_nisp21pwd_entry: password changed\n")); + + nis_freeresult(addresult); + nis_freeresult(result); + + return True; +} + + +/************************************************************************* + Routine to search the nisplus passwd file for an entry matching the username + *************************************************************************/ +static struct sam_passwd *getnisp21pwnam(char *name) +{ + /* Static buffers we will return. */ + static struct sam_passwd pw_buf; + nis_result *result; + pstring nisname; + BOOL ret; + + if (!*lp_smb_passwd_file()) + { + DEBUG(0, ("No SMB password file set\n")); + return NULL; + } + + DEBUG(10, ("getnisp21pwnam: search by name: %s\n", name)); + DEBUG(10, ("getnisp21pwnam: using NIS+ table %s\n", lp_smb_passwd_file())); + + slprintf(nisname, sizeof(nisname)-1, "[name=%s],%s", name, lp_smb_passwd_file()); + + /* Search the table. */ + gotalarm = 0; + CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig); + alarm(5); + + result = nis_list(nisname, FOLLOW_PATH | EXPAND_NAME | HARD_LOOKUP, NULL, NULL); + + alarm(0); + CatchSignal(SIGALRM, SIGNAL_CAST SIG_DFL); + + if (gotalarm) + { + DEBUG(0,("getnisp21pwnam: NIS+ lookup time out\n")); + nis_freeresult(result); + return NULL; + } + + ret = make_sam_from_nisresult(&pw_buf, result); + nis_freeresult(result); + + return ret ? &pw_buf : NULL; +} + +/************************************************************************* + Routine to search the nisplus passwd file for an entry matching the username + *************************************************************************/ +static struct sam_passwd *getnisp21pwrid(uint32 rid) +{ + /* Static buffers we will return. */ + static struct sam_passwd pw_buf; + nis_result *result; + char *nisname; + BOOL ret; + + if (!*lp_smb_passwd_file()) + { + DEBUG(0, ("getnisp21pwrid: no SMB password file set\n")); + return NULL; + } + + DEBUG(10, ("getnisp21pwrid: search by rid: %x\n", rid)); + DEBUG(10, ("getnisp21pwrid: using NIS+ table %s\n", lp_smb_passwd_file())); + + nisname = make_nisname_from_user_rid(rid, lp_smb_passwd_file()); + + /* Search the table. */ + gotalarm = 0; + CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig); + alarm(5); + + result = nis_list(nisname, FOLLOW_PATH | EXPAND_NAME | HARD_LOOKUP, NULL, NULL); + + alarm(0); + CatchSignal(SIGALRM, SIGNAL_CAST SIG_DFL); + + if (gotalarm) + { + DEBUG(0,("getnisp21pwrid: NIS+ lookup time out\n")); + nis_freeresult(result); + return NULL; + } + + ret = make_sam_from_nisresult(&pw_buf, result); + nis_freeresult(result); + + return ret ? &pw_buf : NULL; +} + +/* + * Derived functions for NIS+. + */ + +static struct smb_passwd *getnisppwent(void *vp) +{ + return pdb_sam_to_smb(getnisp21pwent(vp)); +} + +static BOOL add_nisppwd_entry(struct smb_passwd *newpwd) +{ + return add_nisp21pwd_entry(pdb_smb_to_sam(newpwd)); +} + +static BOOL mod_nisppwd_entry(struct smb_passwd* pwd, BOOL override) +{ + return mod_nisp21pwd_entry(pdb_smb_to_sam(pwd), override); +} + +static BOOL del_nisppwd_entry(const char *name) +{ + return False; /* Dummy. */ +} + +static struct smb_passwd *getnisppwnam(char *name) +{ + return pdb_sam_to_smb(getnisp21pwnam(name)); +} + +static struct sam_passwd *getnisp21pwuid(uid_t smb_userid) +{ + return getnisp21pwrid(pdb_uid_to_user_rid(smb_userid)); +} + +static struct smb_passwd *getnisppwrid(uint32 user_rid) +{ + return pdb_sam_to_smb(getnisp21pwuid(pdb_user_rid_to_uid(user_rid))); +} + +static struct smb_passwd *getnisppwuid(uid_t smb_userid) +{ + return pdb_sam_to_smb(getnisp21pwuid(smb_userid)); +} + +static struct passdb_ops nispasswd_ops = { + startnisppwent, + endnisppwent, + getnisppwpos, + setnisppwpos, + getnisppwnam, + getnisppwuid, + getnisppwrid, + getnisppwent, + add_nisppwd_entry, + mod_nisppwd_entry, + del_nisppwd_entry, + getnisp21pwent, + getnisp21pwnam, + getnisp21pwuid, + getnisp21pwrid, + add_nisp21pwd_entry, + mod_nisp21pwd_entry +}; + +struct passdb_ops *nisplus_initialize_password_db(void) +{ + return &nispasswd_ops; +} + +#else + void nisplus_dummy_function(void); + void nisplus_dummy_function(void) { } /* stop some compilers complaining */ +#endif /* WITH_NISPLUS */ + +/* useful code i can't bring myself to delete */ +#if 0 +static void useful_code(void) { + /* checks user in unix password database. don't want to do that, here. */ + nisname = make_nisname_from_name(newpwd->smb_name, "passwd.org_dir"); + + nis_user = nis_list(nisname, FOLLOW_PATH | EXPAND_NAME | HARD_LOOKUP, NULL, NULL); + + if (nis_user->status != NIS_SUCCESS || NIS_RES_NUMOBJ(nis_user) <= 0) + { + DEBUG(3, ("useful_code: Unable to get NIS+ passwd entry for user: %s.\n", + nis_sperrno(nis_user->status))); + return False; + } + + user_obj = NIS_RES_OBJECT(nis_user); + make_nisname_from_name(ENTRY_VAL(user_obj,0), pfile); +} +#endif diff --git a/source/passdb/pampass.c b/source/passdb/pampass.c new file mode 100644 index 00000000000..018eae3a07e --- /dev/null +++ b/source/passdb/pampass.c @@ -0,0 +1,892 @@ +/* + Unix SMB/Netbios implementation. + Version 2.2. + PAM Password checking + Copyright (C) Andrew Tridgell 1992-2001 + Copyright (C) John H Terpsta 1999-2001 + Copyright (C) Andrew Bartlett 2001 + Copyright (C) Jeremy Allison 2001 + + 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. +*/ + +/* + * This module provides PAM based functions for validation of + * username/password pairs, account managment, session and access control. + * Note: SMB password checking is done in smbpass.c + */ + +#include "includes.h" + +#ifdef WITH_PAM + +/******************************************************************* + * Handle PAM authentication + * - Access, Authentication, Session, Password + * Note: See PAM Documentation and refer to local system PAM implementation + * which determines what actions/limitations/allowances become affected. + *********************************************************************/ + +#include <security/pam_appl.h> + +/* + * Structure used to communicate between the conversation function + * and the server_login/change password functions. + */ + +struct smb_pam_userdata { + const char *PAM_username; + const char *PAM_password; + const char *PAM_newpassword; +}; + +typedef int (*smb_pam_conv_fn)(int, const struct pam_message **, struct pam_response **, void *appdata_ptr); + +/* + * Macros to help make life easy + */ +#define COPY_STRING(s) (s) ? strdup(s) : NULL + +/******************************************************************* + PAM error handler. + *********************************************************************/ + +static BOOL smb_pam_error_handler(pam_handle_t *pamh, int pam_error, char *msg, int dbglvl) +{ + + if( pam_error != PAM_SUCCESS) { + DEBUG(dbglvl, ("smb_pam_error_handler: PAM: %s : %s\n", + msg, pam_strerror(pamh, pam_error))); + + return False; + } + return True; +} + +/******************************************************************* + This function is a sanity check, to make sure that we NEVER report + failure as sucess. +*********************************************************************/ + +static BOOL smb_pam_nt_status_error_handler(pam_handle_t *pamh, int pam_error, + char *msg, int dbglvl, + NTSTATUS *nt_status) +{ + if (smb_pam_error_handler(pamh, pam_error, msg, dbglvl)) + return True; + + if (NT_STATUS_IS_OK(*nt_status)) { + /* Complain LOUDLY */ + DEBUG(0, ("smb_pam_nt_status_error_handler: PAM: BUG: PAM and NT_STATUS \ +error MISMATCH, forcing to NT_STATUS_LOGON_FAILURE")); + *nt_status = NT_STATUS_LOGON_FAILURE; + } + return False; +} + +/* + * PAM conversation function + * Here we assume (for now, at least) that echo on means login name, and + * echo off means password. + */ + +static int smb_pam_conv(int num_msg, + const struct pam_message **msg, + struct pam_response **resp, + void *appdata_ptr) +{ + int replies = 0; + struct pam_response *reply = NULL; + struct smb_pam_userdata *udp = (struct smb_pam_userdata *)appdata_ptr; + + *resp = NULL; + + if (num_msg <= 0) + return PAM_CONV_ERR; + + /* + * Apparantly HPUX has a buggy PAM that doesn't support the + * appdata_ptr. Fail if this is the case. JRA. + */ + + if (udp == NULL) { + DEBUG(0,("smb_pam_conv: PAM on this system is broken - appdata_ptr == NULL !\n")); + return PAM_CONV_ERR; + } + + reply = malloc(sizeof(struct pam_response) * num_msg); + if (!reply) + return PAM_CONV_ERR; + + memset(reply, '\0', sizeof(struct pam_response) * num_msg); + + for (replies = 0; replies < num_msg; replies++) { + switch (msg[replies]->msg_style) { + case PAM_PROMPT_ECHO_ON: + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = COPY_STRING(udp->PAM_username); + /* PAM frees resp */ + break; + + case PAM_PROMPT_ECHO_OFF: + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = COPY_STRING(udp->PAM_password); + /* PAM frees resp */ + break; + + case PAM_TEXT_INFO: + /* fall through */ + + case PAM_ERROR_MSG: + /* ignore it... */ + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = NULL; + break; + + default: + /* Must be an error of some sort... */ + SAFE_FREE(reply); + return PAM_CONV_ERR; + } + } + if (reply) + *resp = reply; + return PAM_SUCCESS; +} + +/* + * PAM password change conversation function + * Here we assume (for now, at least) that echo on means login name, and + * echo off means password. + */ + +static void special_char_sub(char *buf) +{ + all_string_sub(buf, "\\n", "", 0); + all_string_sub(buf, "\\r", "", 0); + all_string_sub(buf, "\\s", " ", 0); + all_string_sub(buf, "\\t", "\t", 0); +} + +static void pwd_sub(char *buf, const char *username, const char *oldpass, const char *newpass) +{ + pstring_sub(buf, "%u", username); + all_string_sub(buf, "%o", oldpass, sizeof(fstring)); + all_string_sub(buf, "%n", newpass, sizeof(fstring)); +} + + +struct chat_struct { + struct chat_struct *next, *prev; + fstring prompt; + fstring reply; +}; + +/************************************************************** + Create a linked list containing chat data. +***************************************************************/ + +static struct chat_struct *make_pw_chat(char *p) +{ + fstring prompt; + fstring reply; + struct chat_struct *list = NULL; + struct chat_struct *t; + struct chat_struct *tmp; + + while (1) { + t = (struct chat_struct *)malloc(sizeof(*t)); + if (!t) { + DEBUG(0,("make_pw_chat: malloc failed!\n")); + return NULL; + } + + ZERO_STRUCTP(t); + + DLIST_ADD_END(list, t, tmp); + + if (!next_token(&p, prompt, NULL, sizeof(fstring))) + break; + + if (strequal(prompt,".")) + fstrcpy(prompt,"*"); + + special_char_sub(prompt); + fstrcpy(t->prompt, prompt); + strlower(t->prompt); + trim_string(t->prompt, " ", " "); + + if (!next_token(&p, reply, NULL, sizeof(fstring))) + break; + + if (strequal(reply,".")) + fstrcpy(reply,""); + + special_char_sub(reply); + fstrcpy(t->reply, reply); + strlower(t->reply); + trim_string(t->reply, " ", " "); + + } + return list; +} + +static void free_pw_chat(struct chat_struct *list) +{ + while (list) { + struct chat_struct *old_head = list; + DLIST_REMOVE(list, list); + SAFE_FREE(old_head); + } +} + +static int smb_pam_passchange_conv(int num_msg, + const struct pam_message **msg, + struct pam_response **resp, + void *appdata_ptr) +{ + int replies = 0; + struct pam_response *reply = NULL; + fstring current_prompt; + fstring current_reply; + struct smb_pam_userdata *udp = (struct smb_pam_userdata *)appdata_ptr; + struct chat_struct *pw_chat= make_pw_chat(lp_passwd_chat()); + struct chat_struct *t; + BOOL found; + *resp = NULL; + + DEBUG(10,("smb_pam_passchange_conv: starting converstation for %d messages\n", num_msg)); + + if (num_msg <= 0) + return PAM_CONV_ERR; + + if (pw_chat == NULL) + return PAM_CONV_ERR; + + /* + * Apparantly HPUX has a buggy PAM that doesn't support the + * appdata_ptr. Fail if this is the case. JRA. + */ + + if (udp == NULL) { + DEBUG(0,("smb_pam_passchange_conv: PAM on this system is broken - appdata_ptr == NULL !\n")); + free_pw_chat(pw_chat); + return PAM_CONV_ERR; + } + + reply = malloc(sizeof(struct pam_response) * num_msg); + if (!reply) { + DEBUG(0,("smb_pam_passchange_conv: malloc for reply failed!\n")); + free_pw_chat(pw_chat); + return PAM_CONV_ERR; + } + + for (replies = 0; replies < num_msg; replies++) { + found = False; + DEBUG(10,("smb_pam_passchange_conv: Processing message %d\n", replies)); + switch (msg[replies]->msg_style) { + case PAM_PROMPT_ECHO_ON: + DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: PAM said: %s\n", msg[replies]->msg)); + fstrcpy(current_prompt, msg[replies]->msg); + trim_string(current_prompt, " ", " "); + for (t=pw_chat; t; t=t->next) { + + DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: trying to match |%s| to |%s|\n", + t->prompt, current_prompt )); + + if (unix_wild_match(t->prompt, current_prompt) == 0) { + fstrcpy(current_reply, t->reply); + DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: We sent: %s\n", current_reply)); + pwd_sub(current_reply, udp->PAM_username, udp->PAM_password, udp->PAM_newpassword); +#ifdef DEBUG_PASSWORD + DEBUG(100,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: We actualy sent: %s\n", current_reply)); +#endif + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = COPY_STRING(current_reply); + found = True; + break; + } + } + /* PAM frees resp */ + if (!found) { + DEBUG(3,("smb_pam_passchange_conv: Could not find reply for PAM prompt: %s\n",msg[replies]->msg)); + free_pw_chat(pw_chat); + SAFE_FREE(reply); + return PAM_CONV_ERR; + } + break; + + case PAM_PROMPT_ECHO_OFF: + DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: PAM said: %s\n", msg[replies]->msg)); + fstrcpy(current_prompt, msg[replies]->msg); + trim_string(current_prompt, " ", " "); + for (t=pw_chat; t; t=t->next) { + + DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: trying to match |%s| to |%s|\n", + t->prompt, current_prompt )); + + if (unix_wild_match(t->prompt, current_prompt) == 0) { + fstrcpy(current_reply, t->reply); + DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: We sent: %s\n", current_reply)); + pwd_sub(current_reply, udp->PAM_username, udp->PAM_password, udp->PAM_newpassword); + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = COPY_STRING(current_reply); +#ifdef DEBUG_PASSWORD + DEBUG(100,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: We actualy sent: %s\n", current_reply)); +#endif + found = True; + break; + } + } + /* PAM frees resp */ + + if (!found) { + DEBUG(3,("smb_pam_passchange_conv: Could not find reply for PAM prompt: %s\n",msg[replies]->msg)); + free_pw_chat(pw_chat); + SAFE_FREE(reply); + return PAM_CONV_ERR; + } + break; + + case PAM_TEXT_INFO: + /* fall through */ + + case PAM_ERROR_MSG: + /* ignore it... */ + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = NULL; + break; + + default: + /* Must be an error of some sort... */ + free_pw_chat(pw_chat); + SAFE_FREE(reply); + return PAM_CONV_ERR; + } + } + + free_pw_chat(pw_chat); + if (reply) + *resp = reply; + return PAM_SUCCESS; +} + +/*************************************************************************** + Free up a malloced pam_conv struct. +****************************************************************************/ + +static void smb_free_pam_conv(struct pam_conv *pconv) +{ + if (pconv) + SAFE_FREE(pconv->appdata_ptr); + + SAFE_FREE(pconv); +} + +/*************************************************************************** + Allocate a pam_conv struct. +****************************************************************************/ + +static struct pam_conv *smb_setup_pam_conv(smb_pam_conv_fn smb_pam_conv_fnptr, const char *user, + const char *passwd, const char *newpass) +{ + struct pam_conv *pconv = (struct pam_conv *)malloc(sizeof(struct pam_conv)); + struct smb_pam_userdata *udp = (struct smb_pam_userdata *)malloc(sizeof(struct smb_pam_userdata)); + + if (pconv == NULL || udp == NULL) { + SAFE_FREE(pconv); + SAFE_FREE(udp); + return NULL; + } + + udp->PAM_username = user; + udp->PAM_password = passwd; + udp->PAM_newpassword = newpass; + + pconv->conv = smb_pam_conv_fnptr; + pconv->appdata_ptr = (void *)udp; + return pconv; +} + +/* + * PAM Closing out cleanup handler + */ + +static BOOL smb_pam_end(pam_handle_t *pamh, struct pam_conv *smb_pam_conv_ptr) +{ + int pam_error; + + smb_free_pam_conv(smb_pam_conv_ptr); + + if( pamh != NULL ) { + pam_error = pam_end(pamh, 0); + if(smb_pam_error_handler(pamh, pam_error, "End Cleanup Failed", 2) == True) { + DEBUG(4, ("smb_pam_end: PAM: PAM_END OK.\n")); + return True; + } + } + DEBUG(2,("smb_pam_end: PAM: not initialised")); + return False; +} + +/* + * Start PAM authentication for specified account + */ + +static BOOL smb_pam_start(pam_handle_t **pamh, const char *user, const char *rhost, struct pam_conv *pconv) +{ + int pam_error; + const char *our_rhost; + + *pamh = (pam_handle_t *)NULL; + + DEBUG(4,("smb_pam_start: PAM: Init user: %s\n", user)); + + pam_error = pam_start("samba", user, pconv, pamh); + if( !smb_pam_error_handler(*pamh, pam_error, "Init Failed", 0)) { + *pamh = (pam_handle_t *)NULL; + return False; + } + + if (rhost == NULL) { + our_rhost = client_name(); + if (strequal(rhost,"UNKNOWN")) + our_rhost = client_addr(); + } else { + our_rhost = rhost; + } + +#ifdef PAM_RHOST + DEBUG(4,("smb_pam_start: PAM: setting rhost to: %s\n", our_rhost)); + pam_error = pam_set_item(*pamh, PAM_RHOST, our_rhost); + if(!smb_pam_error_handler(*pamh, pam_error, "set rhost failed", 0)) { + smb_pam_end(*pamh, pconv); + *pamh = (pam_handle_t *)NULL; + return False; + } +#endif +#ifdef PAM_TTY + DEBUG(4,("smb_pam_start: PAM: setting tty\n")); + pam_error = pam_set_item(*pamh, PAM_TTY, "samba"); + if (!smb_pam_error_handler(*pamh, pam_error, "set tty failed", 0)) { + smb_pam_end(*pamh, pconv); + *pamh = (pam_handle_t *)NULL; + return False; + } +#endif + DEBUG(4,("smb_pam_start: PAM: Init passed for user: %s\n", user)); + return True; +} + +/* + * PAM Authentication Handler + */ +static NTSTATUS smb_pam_auth(pam_handle_t *pamh, char *user) +{ + int pam_error; + NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; + + /* + * To enable debugging set in /etc/pam.d/samba: + * auth required /lib/security/pam_pwdb.so nullok shadow audit + */ + + DEBUG(4,("smb_pam_auth: PAM: Authenticate User: %s\n", user)); + pam_error = pam_authenticate(pamh, PAM_SILENT | lp_null_passwords() ? 0 : PAM_DISALLOW_NULL_AUTHTOK); + switch( pam_error ){ + case PAM_AUTH_ERR: + DEBUG(2, ("smb_pam_auth: PAM: Athentication Error for user %s\n", user)); + nt_status = NT_STATUS_WRONG_PASSWORD; + break; + case PAM_CRED_INSUFFICIENT: + DEBUG(2, ("smb_pam_auth: PAM: Insufficient Credentials for user %s\n", user)); + nt_status = NT_STATUS_INSUFFICIENT_LOGON_INFO; + break; + case PAM_AUTHINFO_UNAVAIL: + DEBUG(2, ("smb_pam_auth: PAM: Authentication Information Unavailable for user %s\n", user)); + nt_status = NT_STATUS_LOGON_FAILURE; + break; + case PAM_USER_UNKNOWN: + DEBUG(2, ("smb_pam_auth: PAM: Username %s NOT known to Authentication system\n", user)); + nt_status = NT_STATUS_NO_SUCH_USER; + break; + case PAM_MAXTRIES: + DEBUG(2, ("smb_pam_auth: PAM: One or more authentication modules reports user limit for user %s exceeeded\n", user)); + nt_status = NT_STATUS_REMOTE_SESSION_LIMIT; + break; + case PAM_ABORT: + DEBUG(0, ("smb_pam_auth: PAM: One or more PAM modules failed to load for user %s\n", user)); + nt_status = NT_STATUS_LOGON_FAILURE; + break; + case PAM_SUCCESS: + DEBUG(4, ("smb_pam_auth: PAM: User %s Authenticated OK\n", user)); + nt_status = NT_STATUS_OK; + break; + default: + DEBUG(0, ("smb_pam_auth: PAM: UNKNOWN ERROR while authenticating user %s\n", user)); + nt_status = NT_STATUS_LOGON_FAILURE; + break; + } + + smb_pam_nt_status_error_handler(pamh, pam_error, "Authentication Failure", 2, &nt_status); + return nt_status; +} + +/* + * PAM Account Handler + */ +static NTSTATUS smb_pam_account(pam_handle_t *pamh, const char * user) +{ + int pam_error; + NTSTATUS nt_status = NT_STATUS_ACCOUNT_DISABLED; + + DEBUG(4,("smb_pam_account: PAM: Account Management for User: %s\n", user)); + pam_error = pam_acct_mgmt(pamh, PAM_SILENT); /* Is user account enabled? */ + switch( pam_error ) { + case PAM_AUTHTOK_EXPIRED: + DEBUG(2, ("smb_pam_account: PAM: User %s is valid but password is expired\n", user)); + nt_status = NT_STATUS_PASSWORD_EXPIRED; + break; + case PAM_ACCT_EXPIRED: + DEBUG(2, ("smb_pam_account: PAM: User %s no longer permitted to access system\n", user)); + nt_status = NT_STATUS_ACCOUNT_EXPIRED; + break; + case PAM_AUTH_ERR: + DEBUG(2, ("smb_pam_account: PAM: There was an authentication error for user %s\n", user)); + nt_status = NT_STATUS_LOGON_FAILURE; + break; + case PAM_PERM_DENIED: + DEBUG(0, ("smb_pam_account: PAM: User %s is NOT permitted to access system at this time\n", user)); + nt_status = NT_STATUS_ACCOUNT_RESTRICTION; + break; + case PAM_USER_UNKNOWN: + DEBUG(0, ("smb_pam_account: PAM: User \"%s\" is NOT known to account management\n", user)); + nt_status = NT_STATUS_NO_SUCH_USER; + break; + case PAM_SUCCESS: + DEBUG(4, ("smb_pam_account: PAM: Account OK for User: %s\n", user)); + nt_status = NT_STATUS_OK; + break; + default: + nt_status = NT_STATUS_ACCOUNT_DISABLED; + DEBUG(0, ("smb_pam_account: PAM: UNKNOWN PAM ERROR (%d) during Account Management for User: %s\n", pam_error, user)); + break; + } + + smb_pam_nt_status_error_handler(pamh, pam_error, "Account Check Failed", 2, &nt_status); + return nt_status; +} + +/* + * PAM Credential Setting + */ + +static NTSTATUS smb_pam_setcred(pam_handle_t *pamh, char * user) +{ + int pam_error; + NTSTATUS nt_status = NT_STATUS_NO_TOKEN; + + /* + * This will allow samba to aquire a kerberos token. And, when + * exporting an AFS cell, be able to /write/ to this cell. + */ + + DEBUG(4,("PAM: Account Management SetCredentials for User: %s\n", user)); + pam_error = pam_setcred(pamh, (PAM_ESTABLISH_CRED|PAM_SILENT)); + switch( pam_error ) { + case PAM_CRED_UNAVAIL: + DEBUG(0, ("smb_pam_setcred: PAM: Credentials not found for user:%s\n", user )); + nt_status = NT_STATUS_NO_TOKEN; + break; + case PAM_CRED_EXPIRED: + DEBUG(0, ("smb_pam_setcred: PAM: Credentials for user: \"%s\" EXPIRED!\n", user )); + nt_status = NT_STATUS_PASSWORD_EXPIRED; + break; + case PAM_USER_UNKNOWN: + DEBUG(0, ("smb_pam_setcred: PAM: User: \"%s\" is NOT known so can not set credentials!\n", user )); + nt_status = NT_STATUS_NO_SUCH_USER; + break; + case PAM_CRED_ERR: + DEBUG(0, ("smb_pam_setcred: PAM: Unknown setcredentials error - unable to set credentials for %s\n", user )); + nt_status = NT_STATUS_LOGON_FAILURE; + break; + case PAM_SUCCESS: + DEBUG(4, ("smb_pam_setcred: PAM: SetCredentials OK for User: %s\n", user)); + nt_status = NT_STATUS_OK; + break; + default: + DEBUG(0, ("smb_pam_setcred: PAM: UNKNOWN PAM ERROR (%d) during SetCredentials for User: %s\n", pam_error, user)); + nt_status = NT_STATUS_NO_TOKEN; + break; + } + + smb_pam_nt_status_error_handler(pamh, pam_error, "Set Credential Failure", 2, &nt_status); + return nt_status; +} + +/* + * PAM Internal Session Handler + */ +static BOOL smb_internal_pam_session(pam_handle_t *pamh, char *user, char *tty, BOOL flag) +{ + int pam_error; + +#ifdef PAM_TTY + DEBUG(4,("smb_internal_pam_session: PAM: tty set to: %s\n", tty)); + pam_error = pam_set_item(pamh, PAM_TTY, tty); + if (!smb_pam_error_handler(pamh, pam_error, "set tty failed", 0)) + return False; +#endif + + if (flag) { + pam_error = pam_open_session(pamh, PAM_SILENT); + if (!smb_pam_error_handler(pamh, pam_error, "session setup failed", 0)) + return False; + } else { + pam_setcred(pamh, (PAM_DELETE_CRED|PAM_SILENT)); /* We don't care if this fails */ + pam_error = pam_close_session(pamh, PAM_SILENT); /* This will probably pick up the error anyway */ + if (!smb_pam_error_handler(pamh, pam_error, "session close failed", 0)) + return False; + } + return (True); +} + +/* + * Internal PAM Password Changer. + */ + +static BOOL smb_pam_chauthtok(pam_handle_t *pamh, const char * user) +{ + int pam_error; + + DEBUG(4,("smb_pam_chauthtok: PAM: Password Change for User: %s\n", user)); + + pam_error = pam_chauthtok(pamh, PAM_SILENT); /* Change Password */ + + switch( pam_error ) { + case PAM_AUTHTOK_ERR: + DEBUG(2, ("PAM: unable to obtain the new authentication token - is password to weak?\n")); + break; + + /* This doesn't seem to be defined on Solaris. JRA */ +#ifdef PAM_AUTHTOK_RECOVER_ERR + case PAM_AUTHTOK_RECOVER_ERR: + DEBUG(2, ("PAM: unable to obtain the old authentication token - was the old password wrong?.\n")); + break; +#endif + + case PAM_AUTHTOK_LOCK_BUSY: + DEBUG(2, ("PAM: unable to change the authentication token since it is currently locked.\n")); + break; + case PAM_AUTHTOK_DISABLE_AGING: + DEBUG(2, ("PAM: Authentication token aging has been disabled.\n")); + break; + case PAM_PERM_DENIED: + DEBUG(0, ("PAM: Permission denied.\n")); + break; + case PAM_TRY_AGAIN: + DEBUG(0, ("PAM: Could not update all authentication token(s). No authentication tokens were updated.\n")); + break; + case PAM_USER_UNKNOWN: + DEBUG(0, ("PAM: User not known to PAM\n")); + break; + case PAM_SUCCESS: + DEBUG(4, ("PAM: Account OK for User: %s\n", user)); + break; + default: + DEBUG(0, ("PAM: UNKNOWN PAM ERROR (%d) for User: %s\n", pam_error, user)); + } + + if(!smb_pam_error_handler(pamh, pam_error, "Password Change Failed", 2)) { + return False; + } + + /* If this point is reached, the password has changed. */ + return True; +} + +/* + * PAM Externally accessible Session handler + */ + +BOOL smb_pam_claim_session(char *user, char *tty, char *rhost) +{ + pam_handle_t *pamh = NULL; + struct pam_conv *pconv = NULL; + + /* Ignore PAM if told to. */ + + if (!lp_obey_pam_restrictions()) + return True; + + if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL) + return False; + + if (!smb_pam_start(&pamh, user, rhost, pconv)) + return False; + + if (!smb_internal_pam_session(pamh, user, tty, True)) { + smb_pam_end(pamh, pconv); + return False; + } + + return smb_pam_end(pamh, pconv); +} + +/* + * PAM Externally accessible Session handler + */ + +BOOL smb_pam_close_session(char *user, char *tty, char *rhost) +{ + pam_handle_t *pamh = NULL; + struct pam_conv *pconv = NULL; + + /* Ignore PAM if told to. */ + + if (!lp_obey_pam_restrictions()) + return True; + + if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL) + return False; + + if (!smb_pam_start(&pamh, user, rhost, pconv)) + return False; + + if (!smb_internal_pam_session(pamh, user, tty, False)) { + smb_pam_end(pamh, pconv); + return False; + } + + return smb_pam_end(pamh, pconv); +} + +/* + * PAM Externally accessible Account handler + */ + +NTSTATUS smb_pam_accountcheck(const char * user) +{ + NTSTATUS nt_status = NT_STATUS_ACCOUNT_DISABLED; + pam_handle_t *pamh = NULL; + struct pam_conv *pconv = NULL; + + /* Ignore PAM if told to. */ + + if (!lp_obey_pam_restrictions()) + return NT_STATUS_OK; + + if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL) + return NT_STATUS_NO_MEMORY; + + if (!smb_pam_start(&pamh, user, NULL, pconv)) + return NT_STATUS_ACCOUNT_DISABLED; + + if (!NT_STATUS_IS_OK(nt_status = smb_pam_account(pamh, user))) + DEBUG(0, ("smb_pam_accountcheck: PAM: Account Validation Failed - Rejecting User %s!\n", user)); + + smb_pam_end(pamh, pconv); + return nt_status; +} + +/* + * PAM Password Validation Suite + */ + +NTSTATUS smb_pam_passcheck(char * user, char * password) +{ + pam_handle_t *pamh = NULL; + NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; + struct pam_conv *pconv = NULL; + + /* + * Note we can't ignore PAM here as this is the only + * way of doing auths on plaintext passwords when + * compiled --with-pam. + */ + + if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, password, NULL)) == NULL) + return NT_STATUS_LOGON_FAILURE; + + if (!smb_pam_start(&pamh, user, NULL, pconv)) + return NT_STATUS_LOGON_FAILURE; + + if (!NT_STATUS_IS_OK(nt_status = smb_pam_auth(pamh, user))) { + DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_auth failed - Rejecting User %s !\n", user)); + smb_pam_end(pamh, pconv); + return nt_status; + } + + if (!NT_STATUS_IS_OK(nt_status = smb_pam_account(pamh, user))) { + DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_account failed - Rejecting User %s !\n", user)); + smb_pam_end(pamh, pconv); + return nt_status; + } + + if (!NT_STATUS_IS_OK(nt_status = smb_pam_setcred(pamh, user))) { + DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_setcred failed - Rejecting User %s !\n", user)); + smb_pam_end(pamh, pconv); + return nt_status; + } + + smb_pam_end(pamh, pconv); + return nt_status; +} + +/* + * PAM Password Change Suite + */ + +BOOL smb_pam_passchange(const char * user, const char * oldpassword, const char * newpassword) +{ + /* Appropriate quantities of root should be obtained BEFORE calling this function */ + struct pam_conv *pconv = NULL; + pam_handle_t *pamh = NULL; + + if ((pconv = smb_setup_pam_conv(smb_pam_passchange_conv, user, oldpassword, newpassword)) == NULL) + return False; + + if(!smb_pam_start(&pamh, user, NULL, pconv)) + return False; + + if (!smb_pam_chauthtok(pamh, user)) { + DEBUG(0, ("smb_pam_passchange: PAM: Password Change Failed for user %s!\n", user)); + smb_pam_end(pamh, pconv); + return False; + } + + return smb_pam_end(pamh, pconv); +} + +#else + +/* If PAM not used, no PAM restrictions on accounts. */ +NTSTATUS smb_pam_accountcheck(const char * user) +{ + return NT_STATUS_OK; +} + +/* If PAM not used, also no PAM restrictions on sessions. */ +BOOL smb_pam_claim_session(char *user, char *tty, char *rhost) +{ + return True; +} + +/* If PAM not used, also no PAM restrictions on sessions. */ +BOOL smb_pam_close_session(char *in_user, char *tty, char *rhost) +{ + return True; +} +#endif /* WITH_PAM */ diff --git a/source/passdb/pass_check.c b/source/passdb/pass_check.c new file mode 100644 index 00000000000..77839e4bb0c --- /dev/null +++ b/source/passdb/pass_check.c @@ -0,0 +1,776 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Password checking + Copyright (C) Andrew Tridgell 1992-1998 + + 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. +*/ + +/* this module is for checking a username/password against a system + password database. The SMB encrypted password support is elsewhere */ + +#include "includes.h" + +/* these are kept here to keep the string_combinations function simple */ +static fstring this_user; +#if !defined(WITH_PAM) +static fstring this_salt; +static fstring this_crypted; +#endif + +#ifdef WITH_AFS + +#include <afs/stds.h> +#include <afs/kautils.h> + +/******************************************************************* +check on AFS authentication +********************************************************************/ +static BOOL afs_auth(char *user, char *password) +{ + long password_expires = 0; + char *reason; + + /* For versions of AFS prior to 3.3, this routine has few arguments, */ + /* but since I can't find the old documentation... :-) */ + setpag(); + if (ka_UserAuthenticateGeneral + (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0, /* instance */ + (char *)0, /* cell */ + password, 0, /* lifetime, default */ + &password_expires, /*days 'til it expires */ + 0, /* spare 2 */ + &reason) == 0) + { + return (True); + } + DEBUG(1, + ("AFS authentication for \"%s\" failed (%s)\n", user, reason)); + return (False); +} +#endif + + +#ifdef WITH_DFS + +#include <dce/dce_error.h> +#include <dce/sec_login.h> + +/***************************************************************** + This new version of the DFS_AUTH code was donated by Karsten Muuss + <muuss@or.uni-bonn.de>. It fixes the following problems with the + old code : + + - Server credentials may expire + - Client credential cache files have wrong owner + - purge_context() function is called with invalid argument + + This new code was modified to ensure that on exit the uid/gid is + still root, and the original directory is restored. JRA. +******************************************************************/ + +sec_login_handle_t my_dce_sec_context; +int dcelogin_atmost_once = 0; + +/******************************************************************* +check on a DCE/DFS authentication +********************************************************************/ +static BOOL dfs_auth(char *user, char *password) +{ + error_status_t err; + int err2; + int prterr; + signed32 expire_time, current_time; + boolean32 password_reset; + struct passwd *pw; + sec_passwd_rec_t passwd_rec; + sec_login_auth_src_t auth_src = sec_login_auth_src_network; + unsigned char dce_errstr[dce_c_error_string_len]; + gid_t egid; + + if (dcelogin_atmost_once) + return (False); + +#ifdef HAVE_CRYPT + /* + * We only go for a DCE login context if the given password + * matches that stored in the local password file.. + * Assumes local passwd file is kept in sync w/ DCE RGY! + */ + + if (strcmp((char *)crypt(password, this_salt), this_crypted)) + { + return (False); + } +#endif + + sec_login_get_current_context(&my_dce_sec_context, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr)); + + return (False); + } + + sec_login_certify_identity(my_dce_sec_context, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr)); + + return (False); + } + + sec_login_get_expiration(my_dce_sec_context, &expire_time, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr)); + + return (False); + } + + time(¤t_time); + + if (expire_time < (current_time + 60)) + { + struct passwd *pw; + sec_passwd_rec_t *key; + + sec_login_get_pwent(my_dce_sec_context, + (sec_login_passwd_t *) & pw, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr)); + + return (False); + } + + sec_login_refresh_identity(my_dce_sec_context, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't refresh identity. %s\n", + dce_errstr)); + + return (False); + } + + sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL, + (unsigned char *)pw->pw_name, + sec_c_key_version_none, + (void **)&key, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't get key for %s. %s\n", + pw->pw_name, dce_errstr)); + + return (False); + } + + sec_login_valid_and_cert_ident(my_dce_sec_context, key, + &password_reset, &auth_src, + &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, + ("DCE can't validate and certify identity for %s. %s\n", + pw->pw_name, dce_errstr)); + } + + sec_key_mgmt_free_key(key, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't free key.\n", dce_errstr)); + } + } + + if (sec_login_setup_identity((unsigned char *)user, + sec_login_no_flags, + &my_dce_sec_context, &err) == 0) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE Setup Identity for %s failed: %s\n", + user, dce_errstr)); + return (False); + } + + sec_login_get_pwent(my_dce_sec_context, + (sec_login_passwd_t *) & pw, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr)); + + return (False); + } + + sec_login_purge_context(&my_dce_sec_context, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr)); + + return (False); + } + + /* + * NB. I'd like to change these to call something like change_to_user() + * instead but currently we don't have a connection + * context to become the correct user. This is already + * fairly platform specific code however, so I think + * this should be ok. I have added code to go + * back to being root on error though. JRA. + */ + + egid = getegid(); + + set_effective_gid(pw->pw_gid); + set_effective_uid(pw->pw_uid); + + if (sec_login_setup_identity((unsigned char *)user, + sec_login_no_flags, + &my_dce_sec_context, &err) == 0) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE Setup Identity for %s failed: %s\n", + user, dce_errstr)); + goto err; + } + + sec_login_get_pwent(my_dce_sec_context, + (sec_login_passwd_t *) & pw, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr)); + goto err; + } + + passwd_rec.version_number = sec_passwd_c_version_none; + passwd_rec.pepper = NULL; + passwd_rec.key.key_type = sec_passwd_plain; + passwd_rec.key.tagged_union.plain = (idl_char *) password; + + sec_login_validate_identity(my_dce_sec_context, + &passwd_rec, &password_reset, + &auth_src, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, + ("DCE Identity Validation failed for principal %s: %s\n", + user, dce_errstr)); + goto err; + } + + sec_login_certify_identity(my_dce_sec_context, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr)); + goto err; + } + + if (auth_src != sec_login_auth_src_network) + { + DEBUG(0, ("DCE context has no network credentials.\n")); + } + + sec_login_set_context(my_dce_sec_context, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, + ("DCE login failed for principal %s, cant set context: %s\n", + user, dce_errstr)); + + sec_login_purge_context(&my_dce_sec_context, &err); + goto err; + } + + sec_login_get_pwent(my_dce_sec_context, + (sec_login_passwd_t *) & pw, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr)); + goto err; + } + + DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n", + user, sys_getpid())); + + DEBUG(3, ("DCE principal: %s\n" + " uid: %d\n" + " gid: %d\n", + pw->pw_name, pw->pw_uid, pw->pw_gid)); + DEBUG(3, (" info: %s\n" + " dir: %s\n" + " shell: %s\n", + pw->pw_gecos, pw->pw_dir, pw->pw_shell)); + + sec_login_get_expiration(my_dce_sec_context, &expire_time, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr)); + goto err; + } + + set_effective_uid(0); + set_effective_gid(0); + + DEBUG(0, + ("DCE context expires: %s", asctime(localtime(&expire_time)))); + + dcelogin_atmost_once = 1; + return (True); + + err: + + /* Go back to root, JRA. */ + set_effective_uid(0); + set_effective_gid(egid); + return (False); +} + +void dfs_unlogin(void) +{ + error_status_t err; + int err2; + unsigned char dce_errstr[dce_c_error_string_len]; + + sec_login_purge_context(&my_dce_sec_context, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, + ("DCE purge login context failed for server instance %d: %s\n", + sys_getpid(), dce_errstr)); + } +} +#endif + +#ifdef LINUX_BIGCRYPT +/**************************************************************************** +an enhanced crypt for Linux to handle password longer than 8 characters +****************************************************************************/ +static int linux_bigcrypt(char *password, char *salt1, char *crypted) +{ +#define LINUX_PASSWORD_SEG_CHARS 8 + char salt[3]; + int i; + + StrnCpy(salt, salt1, 2); + crypted += 2; + + for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) { + char *p = crypt(password, salt) + 2; + if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0) + return (0); + password += LINUX_PASSWORD_SEG_CHARS; + crypted += strlen(p); + } + + return (1); +} +#endif + +#ifdef OSF1_ENH_SEC +/**************************************************************************** +an enhanced crypt for OSF1 +****************************************************************************/ +static char *osf1_bigcrypt(char *password, char *salt1) +{ + static char result[AUTH_MAX_PASSWD_LENGTH] = ""; + char *p1; + char *p2 = password; + char salt[3]; + int i; + int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS; + if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS) + parts++; + + StrnCpy(salt, salt1, 2); + StrnCpy(result, salt1, 2); + result[2] = '\0'; + + for (i = 0; i < parts; i++) { + p1 = crypt(p2, salt); + strncat(result, p1 + 2, + AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1); + StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2); + p2 += AUTH_CLEARTEXT_SEG_CHARS; + } + + return (result); +} +#endif + + +/**************************************************************************** +apply a function to upper/lower case combinations +of a string and return true if one of them returns true. +try all combinations with N uppercase letters. +offset is the first char to try and change (start with 0) +it assumes the string starts lowercased +****************************************************************************/ +static NTSTATUS string_combinations2(char *s, int offset, NTSTATUS (*fn) (char *), + int N) +{ + int len = strlen(s); + int i; + NTSTATUS nt_status; + +#ifdef PASSWORD_LENGTH + len = MIN(len, PASSWORD_LENGTH); +#endif + + if (N <= 0 || offset >= len) + return (fn(s)); + + for (i = offset; i < (len - (N - 1)); i++) { + char c = s[i]; + if (!islower(c)) + continue; + s[i] = toupper(c); + if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, i + 1, fn, N - 1),NT_STATUS_WRONG_PASSWORD)) { + return (nt_status); + } + s[i] = c; + } + return (NT_STATUS_WRONG_PASSWORD); +} + +/**************************************************************************** +apply a function to upper/lower case combinations +of a string and return true if one of them returns true. +try all combinations with up to N uppercase letters. +offset is the first char to try and change (start with 0) +it assumes the string starts lowercased +****************************************************************************/ +static NTSTATUS string_combinations(char *s, NTSTATUS (*fn) (char *), int N) +{ + int n; + NTSTATUS nt_status; + for (n = 1; n <= N; n++) + if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, 0, fn, n), NT_STATUS_WRONG_PASSWORD)) + return nt_status; + return NT_STATUS_WRONG_PASSWORD; +} + + +/**************************************************************************** +core of password checking routine +****************************************************************************/ +static NTSTATUS password_check(char *password) +{ +#ifdef WITH_PAM + return smb_pam_passcheck(this_user, password); +#else + + BOOL ret; + +#ifdef WITH_AFS + if (afs_auth(this_user, password)) + return NT_STATUS_OK; +#endif /* WITH_AFS */ + +#ifdef WITH_DFS + if (dfs_auth(this_user, password)) + return NT_STATUS_OK; +#endif /* WITH_DFS */ + +#ifdef OSF1_ENH_SEC + + ret = (strcmp(osf1_bigcrypt(password, this_salt), + this_crypted) == 0); + if (!ret) { + DEBUG(2, + ("OSF1_ENH_SEC failed. Trying normal crypt.\n")); + ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0); + } + if (ret) { + return NT_STATUS_OK; + } else { + return NT_STATUS_WRONG_PASSWORD; + } + +#endif /* OSF1_ENH_SEC */ + +#ifdef ULTRIX_AUTH + ret = (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0); + if (ret) { + return NT_STATUS_OK; + } else { + return NT_STATUS_WRONG_PASSWORD; + } + +#endif /* ULTRIX_AUTH */ + +#ifdef LINUX_BIGCRYPT + ret = (linux_bigcrypt(password, this_salt, this_crypted)); + if (ret) { + return NT_STATUS_OK; + } else { + return NT_STATUS_WRONG_PASSWORD; + } +#endif /* LINUX_BIGCRYPT */ + +#if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS) + + /* + * Some systems have bigcrypt in the C library but might not + * actually use it for the password hashes (HPUX 10.20) is + * a noteable example. So we try bigcrypt first, followed + * by crypt. + */ + + if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0) + return NT_STATUS_OK; + else + ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0); + if (ret) { + return NT_STATUS_OK; + } else { + return NT_STATUS_WRONG_PASSWORD; + } +#else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */ + +#ifdef HAVE_BIGCRYPT + ret = (strcmp(bigcrypt(password, this_salt), this_crypted) == 0); + if (ret) { + return NT_STATUS_OK; + } else { + return NT_STATUS_WRONG_PASSWORD; + } +#endif /* HAVE_BIGCRYPT */ + +#ifndef HAVE_CRYPT + DEBUG(1, ("Warning - no crypt available\n")); + return NT_STATUS_LOGON_FAILURE; +#else /* HAVE_CRYPT */ + ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0); + if (ret) { + return NT_STATUS_OK; + } else { + return NT_STATUS_WRONG_PASSWORD; + } +#endif /* HAVE_CRYPT */ +#endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */ +#endif /* WITH_PAM || KRB4_AUTH || KRB5_AUTH */ +} + + + +/**************************************************************************** +CHECK if a username/password is OK +the function pointer fn() points to a function to call when a successful +match is found and is used to update the encrypted password file +return NT_STATUS_OK on correct match, appropriate error otherwise +****************************************************************************/ + +NTSTATUS pass_check(struct passwd *pass, char *user, char *password, + int pwlen, BOOL (*fn) (char *, char *), BOOL run_cracker) +{ + pstring pass2; + int level = lp_passwordlevel(); + + NTSTATUS nt_status; + if (password) + password[pwlen] = 0; + +#if DEBUG_PASSWORD + DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password)); +#endif + + if (!password) + return NT_STATUS_LOGON_FAILURE; + + if (((!*password) || (!pwlen)) && !lp_null_passwords()) + return NT_STATUS_LOGON_FAILURE; + +#if defined(WITH_PAM) + + /* + * If we're using PAM we want to short-circuit all the + * checks below and dive straight into the PAM code. + */ + + fstrcpy(this_user, user); + + DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen)); + +#else /* Not using PAM or Kerebos */ + + DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen)); + + if (!pass) { + DEBUG(3, ("Couldn't find user %s\n", user)); + return NT_STATUS_NO_SUCH_USER; + } + +#ifdef HAVE_GETSPNAM + { + struct spwd *spass; + + /* many shadow systems require you to be root to get + the password, in most cases this should already be + the case when this function is called, except + perhaps for IPC password changing requests */ + + spass = getspnam(pass->pw_name); + if (spass && spass->sp_pwdp) + pstrcpy(pass->pw_passwd, spass->sp_pwdp); + } +#elif defined(IA_UINFO) + { + /* Need to get password with SVR4.2's ia_ functions + instead of get{sp,pw}ent functions. Required by + UnixWare 2.x, tested on version + 2.1. (tangent@cyberport.com) */ + uinfo_t uinfo; + if (ia_openinfo(pass->pw_name, &uinfo) != -1) + ia_get_logpwd(uinfo, &(pass->pw_passwd)); + } +#endif + +#ifdef HAVE_GETPRPWNAM + { + struct pr_passwd *pr_pw = getprpwnam(pass->pw_name); + if (pr_pw && pr_pw->ufld.fd_encrypt) + pstrcpy(pass->pw_passwd, pr_pw->ufld.fd_encrypt); + } +#endif + +#ifdef OSF1_ENH_SEC + { + struct pr_passwd *mypasswd; + DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n", + user)); + mypasswd = getprpwnam(user); + if (mypasswd) { + fstrcpy(pass->pw_name, mypasswd->ufld.fd_name); + fstrcpy(pass->pw_passwd, mypasswd->ufld.fd_encrypt); + } else { + DEBUG(5, + ("OSF1_ENH_SEC: No entry for user %s in protected database !\n", + user)); + } + } +#endif + +#ifdef ULTRIX_AUTH + { + AUTHORIZATION *ap = getauthuid(pass->pw_uid); + if (ap) { + fstrcpy(pass->pw_passwd, ap->a_password); + endauthent(); + } + } +#endif + + /* extract relevant info */ + fstrcpy(this_salt, pass->pw_passwd); + +#if defined(HAVE_TRUNCATED_SALT) + /* crypt on some platforms (HPUX in particular) + won't work with more than 2 salt characters. */ + this_salt[2] = 0; +#endif + + fstrcpy(this_crypted, pass->pw_passwd); + + if (!*this_crypted) { + if (!lp_null_passwords()) { + DEBUG(2, ("Disallowing %s with null password\n", + this_user)); + return NT_STATUS_LOGON_FAILURE; + } + if (!*password) { + DEBUG(3, + ("Allowing access to %s with null password\n", + this_user)); + return NT_STATUS_OK; + } + } + +#endif /* defined(WITH_PAM) */ + + /* try it as it came to us */ + nt_status = password_check(password); + if NT_STATUS_IS_OK(nt_status) { + if (fn) { + fn(user, password); + } + return (nt_status); + } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) { + /* No point continuing if its not the password thats to blame (ie PAM disabled). */ + return (nt_status); + } + + if (!run_cracker) { + return (nt_status); + } + + /* if the password was given to us with mixed case then we don't + * need to proceed as we know it hasn't been case modified by the + * client */ + if (strhasupper(password) && strhaslower(password)) { + return nt_status; + } + + /* make a copy of it */ + StrnCpy(pass2, password, sizeof(pstring) - 1); + + /* try all lowercase if it's currently all uppercase */ + if (strhasupper(password)) { + strlower(password); + if NT_STATUS_IS_OK(nt_status = password_check(password)) { + if (fn) + fn(user, password); + return (nt_status); + } + } + + /* give up? */ + if (level < 1) { + /* restore it */ + fstrcpy(password, pass2); + return NT_STATUS_WRONG_PASSWORD; + } + + /* last chance - all combinations of up to level chars upper! */ + strlower(password); + + + if NT_STATUS_IS_OK(nt_status = string_combinations(password, password_check, level)) { + if (fn) + fn(user, password); + return nt_status; + } + + /* restore it */ + fstrcpy(password, pass2); + + return NT_STATUS_WRONG_PASSWORD; +} diff --git a/source/passdb/passdb.c b/source/passdb/passdb.c new file mode 100644 index 00000000000..fa4946b0930 --- /dev/null +++ b/source/passdb/passdb.c @@ -0,0 +1,1813 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Password and authentication handling + Copyright (C) Jeremy Allison 1996-2001 + Copyright (C) Luke Kenneth Casson Leighton 1996-1998 + Copyright (C) Gerald (Jerry) Carter 2000-2001 + + 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" + +/* + * This is set on startup - it defines the SID for this + * machine, and therefore the SAM database for which it is + * responsible. + */ + +extern DOM_SID global_sam_sid; + +struct passdb_ops *pdb_ops; + +#if 0 /* JERRY */ +static void* pdb_handle = NULL; +#endif + +/*************************************************************** + Initialize the password db operations. +***************************************************************/ + +BOOL initialize_password_db(BOOL reload) +{ + /* + * This function is unfinished right now, so just + * ignore the details and always return True. It + * is here only as a placeholder --jerry + */ + return True; + +} + + +/************************************************************ + Fill the SAM_ACCOUNT with default values. + ***********************************************************/ + +static BOOL pdb_fill_default_sam(SAM_ACCOUNT *user) +{ + if (user == NULL) { + DEBUG(0,("pdb_fill_default_sam: SAM_ACCOUNT was NULL\n")); + return False; + } + + ZERO_STRUCTP(user); + + /* Don't change these timestamp settings without a good reason. + They are important for NT member server compatibility. */ + + user->logon_time = (time_t)0; + user->pass_last_set_time = (time_t)0; + user->pass_can_change_time = (time_t)0; + user->logoff_time = + user->kickoff_time = + user->pass_must_change_time = get_time_t_max(); + user->unknown_3 = 0x00ffffff; /* don't know */ + user->logon_divs = 168; /* hours per week */ + user->hours_len = 21; /* 21 times 8 bits = 168 */ + memset(user->hours, 0xff, user->hours_len); /* available at all hours */ + user->unknown_5 = 0x00000000; /* don't know */ + user->unknown_6 = 0x000004ec; /* don't know */ + return True; +} + + +/************************************************************* + Alloc memory and initialises a struct sam_passwd. + ************************************************************/ + +BOOL pdb_init_sam(SAM_ACCOUNT **user) +{ + if (*user != NULL) { + DEBUG(0,("pdb_init_sam: SAM_ACCOUNT was non NULL\n")); +#if 0 + smb_panic("NULL pointer passed to pdb_init_sam\n"); +#endif + return False; + } + + *user=(SAM_ACCOUNT *)malloc(sizeof(SAM_ACCOUNT)); + + if (*user==NULL) { + DEBUG(0,("pdb_init_sam: error while allocating memory\n")); + return False; + } + + pdb_fill_default_sam(*user); + + return True; +} + + +/************************************************************* + Initialises a struct sam_passwd with sane values. + ************************************************************/ + +BOOL pdb_init_sam_pw(SAM_ACCOUNT **new_sam_acct, const struct passwd *pwd) +{ + pstring str; + GROUP_MAP map; + uint32 rid; + + if (!pwd) { + new_sam_acct = NULL; + return False; + } + + if (!pdb_init_sam(new_sam_acct)) { + new_sam_acct = NULL; + return False; + } + + pdb_set_username(*new_sam_acct, pwd->pw_name); + pdb_set_fullname(*new_sam_acct, pwd->pw_gecos); + + pdb_set_uid(*new_sam_acct, &pwd->pw_uid); + pdb_set_gid(*new_sam_acct, &pwd->pw_gid); + + pdb_set_user_rid(*new_sam_acct, pdb_uid_to_user_rid(pwd->pw_uid)); + + /* call the mapping code here */ + if(get_group_map_from_gid(pwd->pw_gid, &map, MAPPING_WITHOUT_PRIV)) { + sid_peek_rid(&map.sid, &rid); + } else + rid=pdb_gid_to_group_rid(pwd->pw_gid); + pdb_set_group_rid(*new_sam_acct, rid); + + pstrcpy(str, lp_logon_path()); + standard_sub_advanced(-1, pwd->pw_name, "", pwd->pw_gid, pwd->pw_name, str); + pdb_set_profile_path(*new_sam_acct, str); + + pstrcpy(str, lp_logon_home()); + standard_sub_advanced(-1, pwd->pw_name, "", pwd->pw_gid, pwd->pw_name, str); + pdb_set_homedir(*new_sam_acct, str); + + pstrcpy(str, lp_logon_drive()); + standard_sub_advanced(-1, pwd->pw_name, "", pwd->pw_gid, pwd->pw_name, str); + pdb_set_dir_drive(*new_sam_acct, str); + + pstrcpy(str, lp_logon_script()); + standard_sub_advanced(-1, pwd->pw_name, "", pwd->pw_gid, pwd->pw_name, str); + pdb_set_logon_script(*new_sam_acct, str); + + return True; +} + + +/************************************************************ + Free the NT/LM hashes only. + ***********************************************************/ + +static BOOL pdb_free_sam_contents(SAM_ACCOUNT *user) +{ + if (user == NULL) { + DEBUG(0,("pdb_free_sam_contents: SAM_ACCOUNT was NULL\n")); +#if 0 + smb_panic("NULL pointer passed to pdb_free_sam_contents\n"); +#endif + return False; + } + + /* As we start mallocing more strings this is where + we should free them. */ + + SAFE_FREE(user->nt_pw); + SAFE_FREE(user->lm_pw); + + SAFE_FREE(user->uid); + SAFE_FREE(user->gid); + + return True; +} + + +/************************************************************ + Reset the SAM_ACCOUNT and free the NT/LM hashes. + - note: they are not zero'ed out however. + ***********************************************************/ + +BOOL pdb_reset_sam(SAM_ACCOUNT *user) +{ + if (user == NULL) { + DEBUG(0,("pdb_reset_sam: SAM_ACCOUNT was NULL\n")); +#if 0 + smb_panic("NULL pointer passed to pdb_free_sam\n"); +#endif + return False; + } + + if (!pdb_free_sam_contents(user)) { + return False; + } + + if (!pdb_fill_default_sam(user)) { + return False; + } + + return True; +} + + +/************************************************************ + Free the SAM_ACCOUNT and the NT/LM hashes. + ***********************************************************/ + +BOOL pdb_free_sam(SAM_ACCOUNT **user) +{ + if (*user == NULL) { + DEBUG(0,("pdb_free_sam: SAM_ACCOUNT was NULL\n")); +#if 0 + smb_panic("NULL pointer passed to pdb_free_sam\n"); +#endif + return False; + } + + if (!pdb_free_sam_contents(*user)) { + return False; + } + + SAFE_FREE(*user); + + return True; +} + + +/********************************************************** + Encode the account control bits into a string. + length = length of string to encode into (including terminating + null). length *MUST BE MORE THAN 2* ! + **********************************************************/ + +char *pdb_encode_acct_ctrl(uint16 acct_ctrl, size_t length) +{ + static fstring acct_str; + size_t i = 0; + + acct_str[i++] = '['; + + if (acct_ctrl & ACB_PWNOTREQ ) acct_str[i++] = 'N'; + if (acct_ctrl & ACB_DISABLED ) acct_str[i++] = 'D'; + if (acct_ctrl & ACB_HOMDIRREQ) acct_str[i++] = 'H'; + if (acct_ctrl & ACB_TEMPDUP ) acct_str[i++] = 'T'; + if (acct_ctrl & ACB_NORMAL ) acct_str[i++] = 'U'; + if (acct_ctrl & ACB_MNS ) acct_str[i++] = 'M'; + if (acct_ctrl & ACB_WSTRUST ) acct_str[i++] = 'W'; + if (acct_ctrl & ACB_SVRTRUST ) acct_str[i++] = 'S'; + if (acct_ctrl & ACB_AUTOLOCK ) acct_str[i++] = 'L'; + if (acct_ctrl & ACB_PWNOEXP ) acct_str[i++] = 'X'; + if (acct_ctrl & ACB_DOMTRUST ) acct_str[i++] = 'I'; + + for ( ; i < length - 2 ; i++ ) + acct_str[i] = ' '; + + i = length - 2; + acct_str[i++] = ']'; + acct_str[i++] = '\0'; + + return acct_str; +} + +/********************************************************** + Decode the account control bits from a string. + **********************************************************/ + +uint16 pdb_decode_acct_ctrl(const char *p) +{ + uint16 acct_ctrl = 0; + BOOL finished = False; + + /* + * Check if the account type bits have been encoded after the + * NT password (in the form [NDHTUWSLXI]). + */ + + if (*p != '[') + return 0; + + for (p++; *p && !finished; p++) { + switch (*p) { + case 'N': { acct_ctrl |= ACB_PWNOTREQ ; break; /* 'N'o password. */ } + case 'D': { acct_ctrl |= ACB_DISABLED ; break; /* 'D'isabled. */ } + case 'H': { acct_ctrl |= ACB_HOMDIRREQ; break; /* 'H'omedir required. */ } + case 'T': { acct_ctrl |= ACB_TEMPDUP ; break; /* 'T'emp account. */ } + case 'U': { acct_ctrl |= ACB_NORMAL ; break; /* 'U'ser account (normal). */ } + case 'M': { acct_ctrl |= ACB_MNS ; break; /* 'M'NS logon user account. What is this ? */ } + case 'W': { acct_ctrl |= ACB_WSTRUST ; break; /* 'W'orkstation account. */ } + case 'S': { acct_ctrl |= ACB_SVRTRUST ; break; /* 'S'erver account. */ } + case 'L': { acct_ctrl |= ACB_AUTOLOCK ; break; /* 'L'ocked account. */ } + case 'X': { acct_ctrl |= ACB_PWNOEXP ; break; /* No 'X'piry on password */ } + case 'I': { acct_ctrl |= ACB_DOMTRUST ; break; /* 'I'nterdomain trust account. */ } + case ' ': { break; } + case ':': + case '\n': + case '\0': + case ']': + default: { finished = True; } + } + } + + return acct_ctrl; +} + +/************************************************************* + Routine to set 32 hex password characters from a 16 byte array. +**************************************************************/ + +void pdb_sethexpwd(char *p, const unsigned char *pwd, uint16 acct_ctrl) +{ + if (pwd != NULL) { + int i; + for (i = 0; i < 16; i++) + slprintf(&p[i*2], 3, "%02X", pwd[i]); + } else { + if (acct_ctrl & ACB_PWNOTREQ) + safe_strcpy(p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", 33); + else + safe_strcpy(p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 33); + } +} + +/************************************************************* + Routine to get the 32 hex characters and turn them + into a 16 byte array. +**************************************************************/ + +BOOL pdb_gethexpwd(const char *p, unsigned char *pwd) +{ + int i; + unsigned char lonybble, hinybble; + char *hexchars = "0123456789ABCDEF"; + char *p1, *p2; + + if (!p) + return (False); + + for (i = 0; i < 32; i += 2) { + hinybble = toupper(p[i]); + lonybble = toupper(p[i + 1]); + + p1 = strchr(hexchars, hinybble); + p2 = strchr(hexchars, lonybble); + + if (!p1 || !p2) + return (False); + + hinybble = PTR_DIFF(p1, hexchars); + lonybble = PTR_DIFF(p2, hexchars); + + pwd[i / 2] = (hinybble << 4) | lonybble; + } + return (True); +} + +/******************************************************************* + Group and User RID username mapping function + ********************************************************************/ + +BOOL pdb_name_to_rid(const char *user_name, uint32 *u_rid, uint32 *g_rid) +{ + GROUP_MAP map; + struct passwd *pw = Get_Pwnam(user_name); + + if (u_rid == NULL || g_rid == NULL || user_name == NULL) + return False; + + if (!pw) { + DEBUG(1,("Username %s is invalid on this system\n", user_name)); + return False; + } + + /* turn the unix UID into a Domain RID. this is what the posix + sub-system does (adds 1000 to the uid) */ + *u_rid = pdb_uid_to_user_rid(pw->pw_uid); + + /* absolutely no idea what to do about the unix GID to Domain RID mapping */ + /* map it ! */ + if (get_group_map_from_gid(pw->pw_gid, &map, MAPPING_WITHOUT_PRIV)) { + sid_peek_rid(&map.sid, g_rid); + } else + *g_rid = pdb_gid_to_group_rid(pw->pw_gid); + + return True; +} + +/******************************************************************* + Converts NT user RID to a UNIX uid. + ********************************************************************/ + +uid_t pdb_user_rid_to_uid(uint32 user_rid) +{ + return (uid_t)(((user_rid & (~USER_RID_TYPE))- 1000)/RID_MULTIPLIER); +} + + +/******************************************************************* + Converts NT group RID to a UNIX gid. + ********************************************************************/ + +gid_t pdb_group_rid_to_gid(uint32 group_rid) +{ + return (gid_t)(((group_rid & (~GROUP_RID_TYPE))- 1000)/RID_MULTIPLIER); +} + +/******************************************************************* + converts UNIX uid to an NT User RID. + ********************************************************************/ + +uint32 pdb_uid_to_user_rid(uid_t uid) +{ + return (((((uint32)uid)*RID_MULTIPLIER) + 1000) | USER_RID_TYPE); +} + +/******************************************************************* + converts NT Group RID to a UNIX uid. + + warning: you must not call that function only + you must do a call to the group mapping first. + there is not anymore a direct link between the gid and the rid. + ********************************************************************/ + +uint32 pdb_gid_to_group_rid(gid_t gid) +{ + return (((((uint32)gid)*RID_MULTIPLIER) + 1000) | GROUP_RID_TYPE); +} + +/******************************************************************* + Decides if a RID is a well known RID. + ********************************************************************/ + +static BOOL pdb_rid_is_well_known(uint32 rid) +{ + return (rid < 1000); +} + +/******************************************************************* + Decides if a RID is a user or group RID. + ********************************************************************/ + +BOOL pdb_rid_is_user(uint32 rid) +{ + /* lkcl i understand that NT attaches an enumeration to a RID + * such that it can be identified as either a user, group etc + * type. there are 5 such categories, and they are documented. + */ + if(pdb_rid_is_well_known(rid)) { + /* + * The only well known user RIDs are DOMAIN_USER_RID_ADMIN + * and DOMAIN_USER_RID_GUEST. + */ + if(rid == DOMAIN_USER_RID_ADMIN || rid == DOMAIN_USER_RID_GUEST) + return True; + } else if((rid & RID_TYPE_MASK) == USER_RID_TYPE) { + return True; + } + return False; +} + +/******************************************************************* + Convert a rid into a name. Used in the lookup SID rpc. + ********************************************************************/ + +BOOL local_lookup_sid(DOM_SID *sid, char *name, enum SID_NAME_USE *psid_name_use) +{ + uint32 rid; + BOOL is_user; + + sid_peek_rid(sid, &rid); + is_user = pdb_rid_is_user(rid); + *psid_name_use = SID_NAME_UNKNOWN; + + DEBUG(5,("local_lookup_sid: looking up %s RID %u.\n", is_user ? "user" : + "group", (unsigned int)rid)); + + if(is_user) { + if(rid == DOMAIN_USER_RID_ADMIN) { + pstring admin_users; + char *p = admin_users; + *psid_name_use = SID_NAME_USER; + if(!next_token(&p, name, NULL, sizeof(fstring))) + fstrcpy(name, "Administrator"); + } else if (rid == DOMAIN_USER_RID_GUEST) { + pstring guest_users; + char *p = guest_users; + *psid_name_use = SID_NAME_USER; + if(!next_token(&p, name, NULL, sizeof(fstring))) + fstrcpy(name, "Guest"); + } else { + uid_t uid; + struct passwd *pass; + + /* + * Don't try to convert the rid to a name if + * running in appliance mode + */ + if (lp_hide_local_users()) + return False; + + uid = pdb_user_rid_to_uid(rid); + pass = sys_getpwuid(uid); + + *psid_name_use = SID_NAME_USER; + + DEBUG(5,("local_lookup_sid: looking up uid %u %s\n", (unsigned int)uid, + pass ? "succeeded" : "failed" )); + + if(!pass) { + slprintf(name, sizeof(fstring)-1, "unix_user.%u", (unsigned int)uid); + return True; + } + + fstrcpy(name, pass->pw_name); + + DEBUG(5,("local_lookup_sid: found user %s for rid %u\n", name, + (unsigned int)rid )); + } + + } else { + gid_t gid; + struct group *gr; + GROUP_MAP map; + + /* + * Don't try to convert the rid to a name if running + * in appliance mode + */ + + if (lp_hide_local_users()) + return False; + + /* check if it's a mapped group */ + if (get_group_map_from_sid(*sid, &map, MAPPING_WITHOUT_PRIV)) { + if (map.gid!=-1) { + DEBUG(5,("local_lookup_sid: mapped group %s to gid %u\n", map.nt_name, (unsigned int)map.gid)); + fstrcpy(name, map.nt_name); + *psid_name_use = map.sid_name_use; + return True; + } + } + + gid = pdb_group_rid_to_gid(rid); + gr = getgrgid(gid); + + *psid_name_use = SID_NAME_ALIAS; + + DEBUG(5,("local_lookup_sid: looking up gid %u %s\n", (unsigned int)gid, + gr ? "succeeded" : "failed" )); + + if(!gr) { + slprintf(name, sizeof(fstring)-1, "unix_group.%u", (unsigned int)gid); + return False; + } + + fstrcpy( name, gr->gr_name); + + DEBUG(5,("local_lookup_sid: found group %s for rid %u\n", name, + (unsigned int)rid )); + } + + return True; +} + +/******************************************************************* + Convert a name into a SID. Used in the lookup name rpc. + ********************************************************************/ + +BOOL local_lookup_name(const char *c_domain, const char *c_user, DOM_SID *psid, enum SID_NAME_USE *psid_name_use) +{ + extern DOM_SID global_sid_World_Domain; + struct passwd *pass = NULL; + DOM_SID local_sid; + fstring user; + fstring domain; + + *psid_name_use = SID_NAME_UNKNOWN; + + /* + * domain and user may be quoted const strings, and map_username and + * friends can modify them. Make a modifiable copy. JRA. + */ + + fstrcpy(domain, c_domain); + fstrcpy(user, c_user); + + sid_copy(&local_sid, &global_sam_sid); + + /* + * Special case for MACHINE\Everyone. Map to the world_sid. + */ + + if(strequal(user, "Everyone")) { + sid_copy( psid, &global_sid_World_Domain); + sid_append_rid(psid, 0); + *psid_name_use = SID_NAME_ALIAS; + return True; + } + + /* + * Don't lookup local unix users if running in appliance mode + */ + if (lp_hide_local_users()) + return False; + + (void)map_username(user); + + if((pass = Get_Pwnam(user))) { + sid_append_rid( &local_sid, pdb_uid_to_user_rid(pass->pw_uid)); + *psid_name_use = SID_NAME_USER; + } else { + /* + * Maybe it was a group ? + */ + struct group *grp; + GROUP_MAP map; + + /* check if it's a mapped group */ + if (get_group_map_from_ntname(user, &map, MAPPING_WITHOUT_PRIV)) { + if (map.gid!=-1) { + /* yes it's a mapped group to a valid unix group */ + sid_copy(&local_sid, &map.sid); + *psid_name_use = map.sid_name_use; + } + else + /* it's a correct name but not mapped so it points to nothing*/ + return False; + } else { + /* it's not a mapped group */ + grp = getgrnam(user); + if(!grp) + return False; + + /* + *check if it's mapped, if it is reply it doesn't exist + * + * that's to prevent this case: + * + * unix group ug is mapped to nt group ng + * someone does a lookup on ug + * we must not reply as it doesn't "exist" anymore + * for NT. For NT only ng exists. + * JFM, 30/11/2001 + */ + + if(get_group_map_from_gid(grp->gr_gid, &map, MAPPING_WITHOUT_PRIV)){ + return False; + } + + sid_append_rid( &local_sid, pdb_gid_to_group_rid(grp->gr_gid)); + *psid_name_use = SID_NAME_ALIAS; + } + } + + sid_copy( psid, &local_sid); + + return True; +} + +/**************************************************************************** + Convert a uid to SID - locally. +****************************************************************************/ + +DOM_SID *local_uid_to_sid(DOM_SID *psid, uid_t uid) +{ + extern DOM_SID global_sam_sid; + + sid_copy(psid, &global_sam_sid); + sid_append_rid(psid, pdb_uid_to_user_rid(uid)); + + return psid; +} + +/**************************************************************************** + Convert a SID to uid - locally. +****************************************************************************/ + +BOOL local_sid_to_uid(uid_t *puid, DOM_SID *psid, enum SID_NAME_USE *name_type) +{ + extern DOM_SID global_sam_sid; + + DOM_SID dom_sid; + uint32 rid; + fstring str; + struct passwd *pass; + + *name_type = SID_NAME_UNKNOWN; + + sid_copy(&dom_sid, psid); + sid_split_rid(&dom_sid, &rid); + + if (!pdb_rid_is_user(rid)) + return False; + + /* + * We can only convert to a uid if this is our local + * Domain SID (ie. we are the controling authority). + */ + if (!sid_equal(&global_sam_sid, &dom_sid)) + return False; + + *puid = pdb_user_rid_to_uid(rid); + + /* + * Ensure this uid really does exist. + */ + if(!(pass = sys_getpwuid(*puid))) + return False; + + DEBUG(10,("local_sid_to_uid: SID %s -> uid (%u) (%s).\n", sid_to_string( str, psid), + (unsigned int)*puid, pass->pw_name )); + + *name_type = SID_NAME_USER; + + return True; +} + +/**************************************************************************** + Convert a gid to SID - locally. +****************************************************************************/ + +DOM_SID *local_gid_to_sid(DOM_SID *psid, gid_t gid) +{ + extern DOM_SID global_sam_sid; + GROUP_MAP map; + + sid_copy(psid, &global_sam_sid); + + if (get_group_map_from_gid(gid, &map, MAPPING_WITHOUT_PRIV)) { + sid_copy(psid, &map.sid); + } + else { + sid_append_rid(psid, pdb_gid_to_group_rid(gid)); + } + + return psid; +} + +/**************************************************************************** + Convert a SID to gid - locally. +****************************************************************************/ + +BOOL local_sid_to_gid(gid_t *pgid, DOM_SID *psid, enum SID_NAME_USE *name_type) +{ + extern DOM_SID global_sam_sid; + DOM_SID dom_sid; + uint32 rid; + fstring str; + struct group *grp; + GROUP_MAP map; + + *name_type = SID_NAME_UNKNOWN; + + sid_copy(&dom_sid, psid); + sid_split_rid(&dom_sid, &rid); + + /* + * We can only convert to a gid if this is our local + * Domain SID (ie. we are the controling authority). + * + * Or in the Builtin SID too. JFM, 11/30/2001 + */ + + if (!sid_equal(&global_sam_sid, &dom_sid)) + return False; + + if (pdb_rid_is_user(rid)) + return False; + + if (get_group_map_from_sid(*psid, &map, MAPPING_WITHOUT_PRIV)) { + + /* the SID is in the mapping table but not mapped */ + if (map.gid==-1) + return False; + + sid_peek_rid(&map.sid, pgid); + *name_type = map.sid_name_use; + } else { + *pgid = pdb_group_rid_to_gid(rid); + *name_type = SID_NAME_ALIAS; + } + + /* + * Ensure this gid really does exist. + */ + + if(!(grp = getgrgid(*pgid))) + return False; + + DEBUG(10,("local_sid_to_gid: SID %s -> gid (%u) (%s).\n", sid_to_string( str, psid), + (unsigned int)*pgid, grp->gr_name )); + + return True; +} + +static void select_name(pstring string, const UNISTR2 *from) +{ + if (from->buffer != 0) + unistr2_to_ascii(string, from, sizeof(pstring)); +} + +/************************************************************* + Copies a SAM_USER_INFO_23 to a SAM_ACCOUNT + **************************************************************/ + +void copy_id23_to_sam_passwd(SAM_ACCOUNT *to, SAM_USER_INFO_23 *from) +{ + + if (from == NULL || to == NULL) + return; + + to->logon_time = nt_time_to_unix(&from->logon_time); + to->logoff_time = nt_time_to_unix(&from->logoff_time); + to->kickoff_time = nt_time_to_unix(&from->kickoff_time); + to->pass_last_set_time = nt_time_to_unix(&from->pass_last_set_time); + to->pass_can_change_time = nt_time_to_unix(&from->pass_can_change_time); + to->pass_must_change_time = nt_time_to_unix(&from->pass_must_change_time); + + select_name(to->username , &from->uni_user_name ); + select_name(to->full_name , &from->uni_full_name ); + select_name(to->home_dir , &from->uni_home_dir ); + select_name(to->dir_drive , &from->uni_dir_drive ); + select_name(to->logon_script, &from->uni_logon_script); + select_name(to->profile_path, &from->uni_profile_path); + select_name(to->acct_desc , &from->uni_acct_desc ); + select_name(to->workstations, &from->uni_workstations); + select_name(to->unknown_str , &from->uni_unknown_str ); + select_name(to->munged_dial , &from->uni_munged_dial ); + + if (from->user_rid) + to->user_rid = from->user_rid; + if (from->group_rid) + to->group_rid = from->group_rid; + + to->acct_ctrl = from->acb_info; + to->unknown_3 = from->unknown_3; + + to->logon_divs = from->logon_divs; + to->hours_len = from->logon_hrs.len; + memcpy(to->hours, from->logon_hrs.hours, MAX_HOURS_LEN); + + to->unknown_5 = from->unknown_5; + to->unknown_6 = from->unknown_6; +} + +/************************************************************* + Copies a sam passwd. + **************************************************************/ + +void copy_id21_to_sam_passwd(SAM_ACCOUNT *to, SAM_USER_INFO_21 *from) +{ + if (from == NULL || to == NULL) + return; + + to->logon_time = nt_time_to_unix(&from->logon_time); + to->logoff_time = nt_time_to_unix(&from->logoff_time); + to->kickoff_time = nt_time_to_unix(&from->kickoff_time); + to->pass_last_set_time = nt_time_to_unix(&from->pass_last_set_time); + to->pass_can_change_time = nt_time_to_unix(&from->pass_can_change_time); + to->pass_must_change_time = nt_time_to_unix(&from->pass_must_change_time); + + select_name(to->username , &from->uni_user_name ); + select_name(to->full_name , &from->uni_full_name ); + select_name(to->home_dir , &from->uni_home_dir ); + select_name(to->dir_drive , &from->uni_dir_drive ); + select_name(to->logon_script, &from->uni_logon_script); + select_name(to->profile_path, &from->uni_profile_path); + select_name(to->acct_desc , &from->uni_acct_desc ); + select_name(to->workstations, &from->uni_workstations); + select_name(to->unknown_str , &from->uni_unknown_str ); + select_name(to->munged_dial , &from->uni_munged_dial ); + + to->user_rid = from->user_rid; + to->group_rid = from->group_rid; + + /* FIXME!! Do we need to copy the passwords here as well? + I don't know. Need to figure this out --jerry */ + + to->acct_ctrl = from->acb_info; + to->unknown_3 = from->unknown_3; + + to->logon_divs = from->logon_divs; + to->hours_len = from->logon_hrs.len; + memcpy(to->hours, from->logon_hrs.hours, MAX_HOURS_LEN); + + to->unknown_5 = from->unknown_5; + to->unknown_6 = from->unknown_6; +} + +/************************************************************* + Change a password entry in the local smbpasswd file. + + FIXME!! The function needs to be abstracted into the + passdb interface or something. It is currently being called + by _api_samr_create_user() in rpc_server/srv_samr.c, + in SWAT and by smbpasswd/pdbedit. + + --jerry + *************************************************************/ + +BOOL local_password_change(const char *user_name, int local_flags, + const char *new_passwd, + char *err_str, size_t err_str_len, + char *msg_str, size_t msg_str_len) +{ + struct passwd *pwd = NULL; + SAM_ACCOUNT *sam_pass=NULL; + + *err_str = '\0'; + *msg_str = '\0'; + + /* Get the smb passwd entry for this user */ + pdb_init_sam(&sam_pass); + if(!pdb_getsampwnam(sam_pass, user_name)) { + pdb_free_sam(&sam_pass); + + if (local_flags & LOCAL_ADD_USER) { + /* + * Check for a local account - if we're adding only. + */ + + if(!(pwd = sys_getpwnam(user_name))) { + slprintf(err_str, err_str_len - 1, "User %s does not \ +exist in system password file (usually /etc/passwd). Cannot add \ +account without a valid local system user.\n", user_name); + return False; + } + } else { + slprintf(err_str, err_str_len-1,"Failed to find entry for user %s.\n", user_name); + return False; + } + + if (!pdb_init_sam_pw(&sam_pass, pwd)) { + slprintf(err_str, err_str_len-1, "Failed initialise SAM_ACCOUNT for user %s.\n", user_name); + return False; + } + + /* set account flags. Note that the default is non-expiring accounts */ + /*if (!pdb_set_acct_ctrl(sam_pass,((local_flags & LOCAL_TRUST_ACCOUNT) ? ACB_WSTRUST : ACB_NORMAL|ACB_PWNOEXP) )) {*/ + if (!pdb_set_acct_ctrl(sam_pass,((local_flags & LOCAL_TRUST_ACCOUNT) ? ACB_WSTRUST : ACB_NORMAL) )) { + slprintf(err_str, err_str_len-1, "Failed to set 'trust account' flags for user %s.\n", user_name); + pdb_free_sam(&sam_pass); + return False; + } + } else { + /* the entry already existed */ + local_flags &= ~LOCAL_ADD_USER; + } + + /* + * We are root - just write the new password + * and the valid last change time. + */ + + if (local_flags & LOCAL_DISABLE_USER) { + if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)|ACB_DISABLED)) { + slprintf(err_str, err_str_len-1, "Failed to set 'disabled' flag for user %s.\n", user_name); + pdb_free_sam(&sam_pass); + return False; + } + } else if (local_flags & LOCAL_ENABLE_USER) { + if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)&(~ACB_DISABLED))) { + slprintf(err_str, err_str_len-1, "Failed to unset 'disabled' flag for user %s.\n", user_name); + pdb_free_sam(&sam_pass); + return False; + } + } + + if (local_flags & LOCAL_SET_NO_PASSWORD) { + if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)|ACB_PWNOTREQ)) { + slprintf(err_str, err_str_len-1, "Failed to set 'no password required' flag for user %s.\n", user_name); + pdb_free_sam(&sam_pass); + return False; + } + } else if (local_flags & LOCAL_SET_PASSWORD) { + /* + * If we're dealing with setting a completely empty user account + * ie. One with a password of 'XXXX', but not set disabled (like + * an account created from scratch) then if the old password was + * 'XX's then getsmbpwent will have set the ACB_DISABLED flag. + * We remove that as we're giving this user their first password + * and the decision hasn't really been made to disable them (ie. + * don't create them disabled). JRA. + */ + if ((pdb_get_lanman_passwd(sam_pass)==NULL) && (pdb_get_acct_ctrl(sam_pass)&ACB_DISABLED)) { + if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)&(~ACB_DISABLED))) { + slprintf(err_str, err_str_len-1, "Failed to unset 'disabled' flag for user %s.\n", user_name); + pdb_free_sam(&sam_pass); + return False; + } + } + if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)&(~ACB_PWNOTREQ))) { + slprintf(err_str, err_str_len-1, "Failed to unset 'no password required' flag for user %s.\n", user_name); + pdb_free_sam(&sam_pass); + return False; + } + + if (!pdb_set_plaintext_passwd (sam_pass, new_passwd)) { + slprintf(err_str, err_str_len-1, "Failed to set password for user %s.\n", user_name); + pdb_free_sam(&sam_pass); + return False; + } + } + + if (local_flags & LOCAL_ADD_USER) { + if (pdb_add_sam_account(sam_pass)) { + slprintf(msg_str, msg_str_len-1, "Added user %s.\n", user_name); + pdb_free_sam(&sam_pass); + return True; + } else { + slprintf(err_str, err_str_len-1, "Failed to add entry for user %s.\n", user_name); + pdb_free_sam(&sam_pass); + return False; + } + } else if (local_flags & LOCAL_DELETE_USER) { + if (!pdb_delete_sam_account(user_name)) { + slprintf(err_str,err_str_len-1, "Failed to delete entry for user %s.\n", user_name); + pdb_free_sam(&sam_pass); + return False; + } + slprintf(msg_str, msg_str_len-1, "Deleted user %s.\n", user_name); + } else { + if(!pdb_update_sam_account(sam_pass, True)) { + slprintf(err_str, err_str_len-1, "Failed to modify entry for user %s.\n", user_name); + pdb_free_sam(&sam_pass); + return False; + } + if(local_flags & LOCAL_DISABLE_USER) + slprintf(msg_str, msg_str_len-1, "Disabled user %s.\n", user_name); + else if (local_flags & LOCAL_ENABLE_USER) + slprintf(msg_str, msg_str_len-1, "Enabled user %s.\n", user_name); + else if (local_flags & LOCAL_SET_NO_PASSWORD) + slprintf(msg_str, msg_str_len-1, "User %s password set to none.\n", user_name); + } + + pdb_free_sam(&sam_pass); + return True; +} + +/********************************************************************* + Collection of get...() functions for SAM_ACCOUNT_INFO. + ********************************************************************/ + +uint16 pdb_get_acct_ctrl (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->acct_ctrl); + else + return (ACB_DISABLED); +} + +time_t pdb_get_logon_time (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->logon_time); + else + return (0); +} + +time_t pdb_get_logoff_time (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->logoff_time); + else + return (-1); +} + +time_t pdb_get_kickoff_time (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->kickoff_time); + else + return (-1); +} + +time_t pdb_get_pass_last_set_time (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->pass_last_set_time); + else + return (-1); +} + +time_t pdb_get_pass_can_change_time (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->pass_can_change_time); + else + return (-1); +} + +time_t pdb_get_pass_must_change_time (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->pass_must_change_time); + else + return (-1); +} + +uint16 pdb_get_logon_divs (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->logon_divs); + else + return (-1); +} + +uint32 pdb_get_hours_len (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->hours_len); + else + return (-1); +} + +const uint8* pdb_get_hours (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->hours); + else + return (NULL); +} + +const uint8* pdb_get_nt_passwd (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->nt_pw); + else + return (NULL); +} + +const uint8* pdb_get_lanman_passwd (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->lm_pw); + else + return (NULL); +} + +uint32 pdb_get_user_rid (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->user_rid); + else + return (-1); +} + +uint32 pdb_get_group_rid (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->group_rid); + else + return (-1); +} + +uid_t *pdb_get_uid (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->uid); + else + return (NULL); +} + +gid_t *pdb_get_gid (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->gid); + else + return (NULL); +} + +const char* pdb_get_username (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->username); + else + return (NULL); +} + +const char* pdb_get_domain (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->domain); + else + return (NULL); +} + +const char* pdb_get_nt_username (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->nt_username); + else + return (NULL); +} + +const char* pdb_get_fullname (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->full_name); + else + return (NULL); +} + +const char* pdb_get_homedir (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->home_dir); + else + return (NULL); +} + +const char* pdb_get_dirdrive (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->dir_drive); + else + return (NULL); +} + +const char* pdb_get_logon_script (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->logon_script); + else + return (NULL); +} + +const char* pdb_get_profile_path (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->profile_path); + else + return (NULL); +} + +const char* pdb_get_acct_desc (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->acct_desc); + else + return (NULL); +} + +const char* pdb_get_workstations (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->workstations); + else + return (NULL); +} + +const char* pdb_get_munged_dial (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->munged_dial); + else + return (NULL); +} + +uint32 pdb_get_unknown3 (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->unknown_3); + else + return (-1); +} + +uint32 pdb_get_unknown5 (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->unknown_5); + else + return (-1); +} + +uint32 pdb_get_unknown6 (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->unknown_6); + else + return (-1); +} + +/********************************************************************* + Collection of set...() functions for SAM_ACCOUNT_INFO. + ********************************************************************/ + +BOOL pdb_set_acct_ctrl (SAM_ACCOUNT *sampass, uint16 flags) +{ + if (!sampass) + return False; + + if (sampass) { + sampass->acct_ctrl = flags; + return True; + } + + return False; +} + +BOOL pdb_set_logon_time (SAM_ACCOUNT *sampass, time_t mytime) +{ + if (!sampass) + return False; + + sampass->logon_time = mytime; + return True; +} + +BOOL pdb_set_logoff_time (SAM_ACCOUNT *sampass, time_t mytime) +{ + if (!sampass) + return False; + + sampass->logoff_time = mytime; + return True; +} + +BOOL pdb_set_kickoff_time (SAM_ACCOUNT *sampass, time_t mytime) +{ + if (!sampass) + return False; + + sampass->kickoff_time = mytime; + return True; +} + +BOOL pdb_set_pass_can_change_time (SAM_ACCOUNT *sampass, time_t mytime) +{ + if (!sampass) + return False; + + sampass->pass_can_change_time = mytime; + return True; +} + +BOOL pdb_set_pass_must_change_time (SAM_ACCOUNT *sampass, time_t mytime) +{ + if (!sampass) + return False; + + sampass->pass_must_change_time = mytime; + return True; +} + +BOOL pdb_set_pass_last_set_time (SAM_ACCOUNT *sampass, time_t mytime) +{ + if (!sampass) + return False; + + sampass->pass_last_set_time = mytime; + return True; +} + +BOOL pdb_set_hours_len (SAM_ACCOUNT *sampass, uint32 len) +{ + if (!sampass) + return False; + + sampass->hours_len = len; + return True; +} + +BOOL pdb_set_logons_divs (SAM_ACCOUNT *sampass, uint16 hours) +{ + if (!sampass) + return False; + + sampass->logon_divs = hours; + return True; +} + +/********************************************************************* + Set the user's UNIX uid, as a pointer to malloc'ed memory. + ********************************************************************/ + +BOOL pdb_set_uid (SAM_ACCOUNT *sampass, const uid_t *uid) +{ + if (!sampass) + return False; + + if (!uid) { + /* Allow setting to NULL */ + SAFE_FREE(sampass->uid); + return True; + } + + if (sampass->uid!=NULL) + DEBUG(4,("pdb_set_nt_passwd: uid non NULL overwritting ?\n")); + else + sampass->uid=(uid_t *)malloc(sizeof(uid_t)); + + if (sampass->uid==NULL) + return False; + + *sampass->uid = *uid; + + return True; + +} + +/********************************************************************* + Set the user's UNIX gid, as a pointer to malloc'ed memory. + ********************************************************************/ + +BOOL pdb_set_gid (SAM_ACCOUNT *sampass, const gid_t *gid) +{ + if (!sampass) + return False; + + if (!gid) { + /* Allow setting to NULL */ + SAFE_FREE(sampass->gid); + return True; + } + + if (sampass->gid!=NULL) + DEBUG(4,("pdb_set_nt_passwd: gid non NULL overwritting ?\n")); + else + sampass->gid=(gid_t *)malloc(sizeof(gid_t)); + + if (sampass->gid==NULL) + return False; + + *sampass->gid = *gid; + + return True; + +} + +BOOL pdb_set_user_rid (SAM_ACCOUNT *sampass, uint32 rid) +{ + if (!sampass) + return False; + + sampass->user_rid = rid; + return True; +} + +BOOL pdb_set_group_rid (SAM_ACCOUNT *sampass, uint32 grid) +{ + if (!sampass) + return False; + + sampass->group_rid = grid; + return True; +} + +/********************************************************************* + Set the user's UNIX name. + ********************************************************************/ + +BOOL pdb_set_username(SAM_ACCOUNT *sampass, const char *username) +{ + if (!sampass) + return False; + *sampass->username = '\0'; + if (!username) + return False; + + StrnCpy (sampass->username, username, strlen(username)); + + return True; +} + +/********************************************************************* + Set the domain name. + ********************************************************************/ + +BOOL pdb_set_domain(SAM_ACCOUNT *sampass, const char *domain) +{ + if (!sampass) + return False; + *sampass->domain = '\0'; + if (!domain) + return False; + + StrnCpy (sampass->domain, domain, strlen(domain)); + + return True; +} + +/********************************************************************* + Set the user's NT name. + ********************************************************************/ + +BOOL pdb_set_nt_username(SAM_ACCOUNT *sampass, const char *nt_username) +{ + if (!sampass) + return False; + *sampass->nt_username = '\0'; + if (!nt_username) + return False; + + StrnCpy (sampass->nt_username, nt_username, strlen(nt_username)); + + return True; +} + +/********************************************************************* + Set the user's full name. + ********************************************************************/ + +BOOL pdb_set_fullname(SAM_ACCOUNT *sampass, const char *fullname) +{ + if (!sampass) + return False; + *sampass->full_name = '\0'; + if (!fullname) + return False; + + StrnCpy (sampass->full_name, fullname, strlen(fullname)); + + return True; +} + +/********************************************************************* + Set the user's logon script. + ********************************************************************/ + +BOOL pdb_set_logon_script(SAM_ACCOUNT *sampass, const char *logon_script) +{ + if (!sampass) + return False; + *sampass->logon_script = '\0'; + if (!logon_script) + return False; + + StrnCpy (sampass->logon_script, logon_script, strlen(logon_script)); + + return True; +} + +/********************************************************************* + Set the user's profile path. + ********************************************************************/ + +BOOL pdb_set_profile_path (SAM_ACCOUNT *sampass, const char *profile_path) +{ + if (!sampass) + return False; + *sampass->profile_path = '\0'; + if (!profile_path) + return False; + + StrnCpy (sampass->profile_path, profile_path, strlen(profile_path)); + + return True; +} + +/********************************************************************* + Set the user's directory drive. + ********************************************************************/ + +BOOL pdb_set_dir_drive (SAM_ACCOUNT *sampass, const char *dir_drive) +{ + if (!sampass) + return False; + *sampass->dir_drive = '\0'; + if (!dir_drive) + return False; + + StrnCpy (sampass->dir_drive, dir_drive, strlen(dir_drive)); + + return True; +} + +/********************************************************************* + Set the user's home directory. + ********************************************************************/ + +BOOL pdb_set_homedir (SAM_ACCOUNT *sampass, const char *homedir) +{ + if (!sampass) + return False; + *sampass->home_dir = '\0'; + if (!homedir) + return False; + + StrnCpy (sampass->home_dir, homedir, strlen(homedir)); + + return True; +} + +/********************************************************************* + Set the user's account description. + ********************************************************************/ + +BOOL pdb_set_acct_desc (SAM_ACCOUNT *sampass, const char *acct_desc) +{ + if (!sampass) + return False; + *sampass->acct_desc = '\0'; + if (!acct_desc) + return False; + + StrnCpy (sampass->acct_desc, acct_desc, strlen(acct_desc)); + + return True; +} + +/********************************************************************* + Set the user's workstation allowed list. + ********************************************************************/ + +BOOL pdb_set_workstations (SAM_ACCOUNT *sampass, const char *workstations) +{ + if (!sampass) + return False; + *sampass->workstations = '\0'; + if (!workstations) + return False; + + StrnCpy (sampass->workstations, workstations, strlen(workstations)); + + return True; +} + +/********************************************************************* + Set the user's dial string. + ********************************************************************/ + +BOOL pdb_set_munged_dial (SAM_ACCOUNT *sampass, const char *munged_dial) +{ + if (!sampass) + return False; + *sampass->munged_dial = '\0'; + if (!munged_dial) + return False; + + StrnCpy (sampass->munged_dial, munged_dial, strlen(munged_dial)); + + return True; +} + +/********************************************************************* + Set the user's NT hash. + ********************************************************************/ + +BOOL pdb_set_nt_passwd (SAM_ACCOUNT *sampass, const uint8 *pwd) +{ + if (!sampass) + return False; + + if (!pwd) { + /* Allow setting to NULL */ + SAFE_FREE(sampass->nt_pw); + return True; + } + + if (sampass->nt_pw!=NULL) + DEBUG(4,("pdb_set_nt_passwd: NT hash non NULL overwritting ?\n")); + else + sampass->nt_pw=(unsigned char *)malloc(sizeof(unsigned char)*16); + + if (sampass->nt_pw==NULL) + return False; + + memcpy (sampass->nt_pw, pwd, 16); + + return True; +} + +/********************************************************************* + Set the user's LM hash. + ********************************************************************/ + +BOOL pdb_set_lanman_passwd (SAM_ACCOUNT *sampass, const uint8 *pwd) +{ + if (!sampass) + return False; + + if (!pwd) { + /* Allow setting to NULL */ + SAFE_FREE(sampass->lm_pw); + return True; + } + + if (sampass->lm_pw!=NULL) + DEBUG(4,("pdb_set_lanman_passwd: LM hash non NULL overwritting ?\n")); + else + sampass->lm_pw=(unsigned char *)malloc(sizeof(unsigned char)*16); + + if (sampass->lm_pw==NULL) + return False; + + memcpy (sampass->lm_pw, pwd, 16); + + return True; +} + +BOOL pdb_set_unknown_3 (SAM_ACCOUNT *sampass, uint32 unkn) +{ + if (!sampass) + return False; + + sampass->unknown_3 = unkn; + return True; +} + +BOOL pdb_set_unknown_5 (SAM_ACCOUNT *sampass, uint32 unkn) +{ + if (!sampass) + return False; + + sampass->unknown_5 = unkn; + return True; +} + +BOOL pdb_set_unknown_6 (SAM_ACCOUNT *sampass, uint32 unkn) +{ + if (!sampass) + return False; + + sampass->unknown_6 = unkn; + return True; +} + +BOOL pdb_set_hours (SAM_ACCOUNT *sampass, const uint8 *hours) +{ + if (!sampass) + return False; + + if (!hours) { + memset ((char *)sampass->hours, 0, MAX_HOURS_LEN); + return True; + } + + memcpy (sampass->hours, hours, MAX_HOURS_LEN); + + return True; +} + + +/* Helpful interfaces to the above */ + +/********************************************************************* + Sets the last changed times and must change times for a normal + password change. + ********************************************************************/ + +BOOL pdb_set_pass_changed_now (SAM_ACCOUNT *sampass) +{ + time_t expire; + + if (!sampass) + return False; + + if (!pdb_set_pass_last_set_time (sampass, time(NULL))) + return False; + + account_policy_get(AP_MAX_PASSWORD_AGE, (int *)&expire); + + if (expire==-1) { + if (!pdb_set_pass_must_change_time (sampass, 0)) + return False; + } else { + if (!pdb_set_pass_must_change_time (sampass, + pdb_get_pass_last_set_time(sampass) + + expire)) + return False; + } + + return True; +} + +/********************************************************************* + Set the user's PLAINTEXT password. Used as an interface to the above. + Also sets the last change time to NOW. + ********************************************************************/ + +BOOL pdb_set_plaintext_passwd (SAM_ACCOUNT *sampass, const char *plaintext) +{ + uchar new_lanman_p16[16]; + uchar new_nt_p16[16]; + + if (!sampass || !plaintext) + return False; + + nt_lm_owf_gen (plaintext, new_nt_p16, new_lanman_p16); + + if (!pdb_set_nt_passwd (sampass, new_nt_p16)) + return False; + + if (!pdb_set_lanman_passwd (sampass, new_lanman_p16)) + return False; + + if (!pdb_set_pass_changed_now (sampass)) + return False; + + return True; +} + + diff --git a/source/passdb/passgrp.c b/source/passdb/passgrp.c new file mode 100644 index 00000000000..55a71e94a27 --- /dev/null +++ b/source/passdb/passgrp.c @@ -0,0 +1,217 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Password and authentication handling + Copyright (C) Jeremy Allison 1996-1998 + Copyright (C) Luke Kenneth Casson Leighton 1996-1998 + + 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" + +/* + * NOTE. All these functions are abstracted into a structure + * that points to the correct function for the selected database. JRA. + * + * the API does NOT fill in the gaps if you set an API function + * to NULL: it will deliberately attempt to call the NULL function. + * + */ + +static struct passgrp_ops *pwgrp_ops; + +/*************************************************************** + Initialise the passgrp operations. +***************************************************************/ + +BOOL initialise_passgrp_db(void) +{ + if (pwgrp_ops) + { + return True; + } + +#ifdef WITH_NISPLUS + pwgrp_ops = nisplus_initialise_password_grp(); +#elif defined(WITH_LDAP) + pwgrp_ops = ldap_initialize_password_grp(); +#else + pwgrp_ops = file_initialise_password_grp(); +#endif + + return (pwgrp_ops != NULL); +} + +/* + * Functions that return/manipulate a struct smb_passwd. + */ + +/************************************************************************ + Utility function to search smb passwd by rid. +*************************************************************************/ + +struct smb_passwd *iterate_getsmbgrprid(uint32 user_rid, + uint32 **grps, int *num_grps, + uint32 **alss, int *num_alss) +{ + return iterate_getsmbgrpuid(pwdb_user_rid_to_uid(user_rid), + grps, num_grps, alss, num_alss); +} + +/************************************************************************ + Utility function to search smb passwd by uid. use this if your database + does not have search facilities. +*************************************************************************/ + +struct smb_passwd *iterate_getsmbgrpuid(uid_t smb_userid, + uint32 **grps, int *num_grps, + uint32 **alss, int *num_alss) +{ + struct smb_passwd *pwd = NULL; + void *fp = NULL; + + DEBUG(10, ("search by smb_userid: %x\n", (int)smb_userid)); + + /* Open the smb password database - not for update. */ + fp = startsmbgrpent(False); + + if (fp == NULL) + { + DEBUG(0, ("unable to open smb passgrp database.\n")); + return NULL; + } + + while ((pwd = getsmbgrpent(fp, grps, num_grps, alss, num_alss)) != NULL && pwd->smb_userid != smb_userid) + ; + + if (pwd != NULL) + { + DEBUG(10, ("found by smb_userid: %x\n", (int)smb_userid)); + } + + endsmbgrpent(fp); + return pwd; +} + +/************************************************************************ + Utility function to search smb passwd by name. use this if your database + does not have search facilities. +*************************************************************************/ + +struct smb_passwd *iterate_getsmbgrpnam(char *name, + uint32 **grps, int *num_grps, + uint32 **alss, int *num_alss) +{ + struct smb_passwd *pwd = NULL; + void *fp = NULL; + + DEBUG(10, ("search by name: %s\n", name)); + + /* Open the passgrp file - not for update. */ + fp = startsmbgrpent(False); + + if (fp == NULL) + { + DEBUG(0, ("unable to open smb passgrp database.\n")); + return NULL; + } + + while ((pwd = getsmbgrpent(fp, grps, num_grps, alss, num_alss)) != NULL && !strequal(pwd->smb_name, name)) + ; + + if (pwd != NULL) + { + DEBUG(10, ("found by name: %s\n", name)); + } + + endsmbgrpent(fp); + return pwd; +} + +/*************************************************************** + Start to enumerate the smb or sam passwd list. Returns a void pointer + to ensure no modification outside this module. + + Note that currently it is being assumed that a pointer returned + from this function may be used to enumerate struct sam_passwd + entries as well as struct smb_passwd entries. This may need + to change. JRA. + +****************************************************************/ + +void *startsmbgrpent(BOOL update) +{ + return pwgrp_ops->startsmbgrpent(update); +} + +/*************************************************************** + End enumeration of the smb or sam passwd list. + + Note that currently it is being assumed that a pointer returned + from this function may be used to enumerate struct sam_passwd + entries as well as struct smb_passwd entries. This may need + to change. JRA. + +****************************************************************/ + +void endsmbgrpent(void *vp) +{ + pwgrp_ops->endsmbgrpent(vp); +} + +/************************************************************************* + Routine to return the next entry in the smb passwd list. + *************************************************************************/ + +struct smb_passwd *getsmbgrpent(void *vp, + uint32 **grps, int *num_grps, + uint32 **alss, int *num_alss) +{ + return pwgrp_ops->getsmbgrpent(vp, grps, num_grps, alss, num_alss); +} + +/************************************************************************ + Routine to search smb passwd by name. +*************************************************************************/ + +struct smb_passwd *getsmbgrpnam(char *name, + uint32 **grps, int *num_grps, + uint32 **alss, int *num_alss) +{ + return pwgrp_ops->getsmbgrpnam(name, grps, num_grps, alss, num_alss); +} + +/************************************************************************ + Routine to search smb passwd by user rid. +*************************************************************************/ + +struct smb_passwd *getsmbgrprid(uint32 user_rid, + uint32 **grps, int *num_grps, + uint32 **alss, int *num_alss) +{ + return pwgrp_ops->getsmbgrprid(user_rid, grps, num_grps, alss, num_alss); +} + +/************************************************************************ + Routine to search smb passwd by uid. +*************************************************************************/ + +struct smb_passwd *getsmbgrpuid(uid_t smb_userid, + uint32 **grps, int *num_grps, + uint32 **alss, int *num_alss) +{ + return pwgrp_ops->getsmbgrpuid(smb_userid, grps, num_grps, alss, num_alss); +} diff --git a/source/passdb/pdb_ldap.c b/source/passdb/pdb_ldap.c new file mode 100644 index 00000000000..f426f926b19 --- /dev/null +++ b/source/passdb/pdb_ldap.c @@ -0,0 +1,1045 @@ +/* + Unix SMB/Netbios implementation. + Version 2.9. + LDAP protocol helper functions for SAMBA + Copyright (C) Shahms King 2001 + Copyright (C) Jean François Micouleau 1998 + + 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" + +#ifdef WITH_LDAP_SAM +/* TODO: +* persistent connections: if using NSS LDAP, many connections are made +* however, using only one within Samba would be nice +* +* Clean up SSL stuff, compile on OpenLDAP 1.x, 2.x, and Netscape SDK +* +* Other LDAP based login attributes: accountExpires, etc. +* (should be the domain of Samba proper, but the sam_password/SAM_ACCOUNT +* structures don't have fields for some of these attributes) +* +* SSL is done, but can't get the certificate based authentication to work +* against on my test platform (Linux 2.4, OpenLDAP 2.x) +*/ + +/* NOTE: this will NOT work against an Active Directory server +* due to the fact that the two password fields cannot be retrieved +* from a server; recommend using security = domain in this situation +* and/or winbind +*/ + +#include <lber.h> +#include <ldap.h> + +#ifndef SAM_ACCOUNT +#define SAM_ACCOUNT struct sam_passwd +#endif + +struct ldap_enum_info +{ + LDAP *ldap_struct; + LDAPMessage *result; + LDAPMessage *entry; +}; + +static struct ldap_enum_info global_ldap_ent; + + +/******************************************************************* + open a connection to the ldap server. +******************************************************************/ +static BOOL +ldap_open_connection (LDAP ** ldap_struct) +{ + int port; + int version, rc; + int tls = LDAP_OPT_X_TLS_HARD; + + if (lp_ldap_ssl() == LDAP_SSL_ON && lp_ldap_port() == 389) { + port = 636; + } + else { + port = lp_ldap_port(); + } + + if ((*ldap_struct = ldap_init(lp_ldap_server(), port)) == NULL) { + DEBUG(0, ("The LDAP server is not responding !\n")); + return (False); + } + + /* Connect to older servers using SSL and V2 rather than Start TLS */ + if (ldap_get_option(*ldap_struct, LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS) + { + if (version != LDAP_VERSION2) + { + version = LDAP_VERSION2; + ldap_set_option (*ldap_struct, LDAP_OPT_PROTOCOL_VERSION, &version); + } + } + + switch (lp_ldap_ssl()) + { + case LDAP_SSL_START_TLS: + if (ldap_get_option (*ldap_struct, LDAP_OPT_PROTOCOL_VERSION, + &version) == LDAP_OPT_SUCCESS) + { + if (version < LDAP_VERSION3) + { + version = LDAP_VERSION3; + ldap_set_option (*ldap_struct, LDAP_OPT_PROTOCOL_VERSION, + &version); + } + } + if ((rc = ldap_start_tls_s (*ldap_struct, NULL, NULL)) != LDAP_SUCCESS) + { + DEBUG(0, + ("Failed to issue the StartTLS instruction: %s\n", + ldap_err2string(rc))); + return False; + } + DEBUG (2, ("StartTLS issued: using a TLS connection\n")); + break; + case LDAP_SSL_ON: + if (ldap_set_option (*ldap_struct, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS) + { + DEBUG(0, ("Failed to setup a TLS session\n")); + } + break; + case LDAP_SSL_OFF: + default: + } + + DEBUG(2, ("ldap_open_connection: connection opened\n")); + return (True); +} + +/******************************************************************* + connect to the ldap server under system privilege. +******************************************************************/ +static BOOL ldap_connect_system(LDAP * ldap_struct) +{ + int rc; + static BOOL got_pw = False; + static pstring ldap_secret; + + /* get the password if we don't have it already */ + if (!got_pw && !(got_pw=fetch_ldap_pw(lp_ldap_admin_dn(), ldap_secret, sizeof(pstring)))) + { + DEBUG(0, ("ldap_connect_system: Failed to retrieve password for %s from secrets.tdb\n", + lp_ldap_admin_dn())); + return False; + } + + /* removed the sasl_bind_s "EXTERNAL" stuff, as my testsuite + (OpenLDAP) doesnt' seem to support it */ + if ((rc = ldap_simple_bind_s(ldap_struct, lp_ldap_admin_dn(), + ldap_secret)) != LDAP_SUCCESS) + { + DEBUG(0, ("Bind failed: %s\n", ldap_err2string(rc))); + return (False); + } + + DEBUG(2, ("ldap_connect_system: succesful connection to the LDAP server\n")); + return (True); +} + +/******************************************************************* + run the search by name. +******************************************************************/ +static int ldap_search_one_user (LDAP * ldap_struct, const char *filter, LDAPMessage ** result) +{ + int scope = LDAP_SCOPE_SUBTREE; + int rc; + + DEBUG(2, ("ldap_search_one_user: searching for:[%s]\n", filter)); + + rc = ldap_search_s (ldap_struct, lp_ldap_suffix (), scope, + filter, NULL, 0, result); + + if (rc != LDAP_SUCCESS) { + DEBUG(0,("ldap_search_one_user: Problem during the LDAP search: %s\n", + ldap_err2string (rc))); + DEBUG(3,("ldap_search_one_user: Query was: %s, %s\n", lp_ldap_suffix(), + filter)); + } + return (rc); +} + +/******************************************************************* + run the search by name. +******************************************************************/ +static int ldap_search_one_user_by_name (LDAP * ldap_struct, const char *user, + LDAPMessage ** result) +{ + pstring filter; + + /* + in the filter expression, replace %u with the real name + so in ldap filter, %u MUST exist :-) + */ + pstrcpy(filter, lp_ldap_filter()); + + /* have to use this here because $ is filtered out + * in pstring_sub + */ + all_string_sub(filter, "%u", user, sizeof(pstring)); + + return ldap_search_one_user(ldap_struct, filter, result); +} + +/******************************************************************* + run the search by uid. +******************************************************************/ +static int ldap_search_one_user_by_uid(LDAP * ldap_struct, int uid, + LDAPMessage ** result) +{ + struct passwd *user; + pstring filter; + + /* Get the username from the system and look that up in the LDAP */ + user = sys_getpwuid(uid); + pstrcpy(filter, lp_ldap_filter()); + all_string_sub(filter, "%u", user->pw_name, sizeof(pstring)); + + return ldap_search_one_user(ldap_struct, filter, result); +} + +/******************************************************************* + run the search by rid. +******************************************************************/ +static int ldap_search_one_user_by_rid (LDAP * ldap_struct, uint32 rid, + LDAPMessage ** result) +{ + pstring filter; + int rc; + + /* check if the user rid exsists, if not, try searching on the uid */ + snprintf(filter, sizeof(filter) - 1, "rid=%i", rid); + rc = ldap_search_one_user(ldap_struct, filter, result); + + if (rc != LDAP_SUCCESS) + rc = ldap_search_one_user_by_uid(ldap_struct, + pdb_user_rid_to_uid(rid), result); + + return rc; +} + +/******************************************************************* +search an attribute and return the first value found. +******************************************************************/ +static void get_single_attribute (LDAP * ldap_struct, LDAPMessage * entry, + char *attribute, char *value) +{ + char **valeurs; + + if ((valeurs = ldap_get_values (ldap_struct, entry, attribute)) != NULL) { + pstrcpy(value, valeurs[0]); + ldap_value_free(valeurs); + DEBUG (2, ("get_single_attribute: [%s] = [%s]\n", attribute, value)); + } + else { + value = NULL; + DEBUG (2, ("get_single_attribute: [%s] = [NULL]\n", attribute)); + } +} + +/************************************************************************ +Routine to manage the LDAPMod structure array +manage memory used by the array, by each struct, and values + +************************************************************************/ +static void make_a_mod (LDAPMod *** modlist, int modop, const char *attribute, const char *value) +{ + LDAPMod **mods; + int i; + int j; + + mods = *modlist; + + if (attribute == NULL || *attribute == '\0') + return; + + if (value == NULL || *value == '\0') + return; + + if (mods == NULL) + { + mods = (LDAPMod **) malloc(sizeof(LDAPMod *)); + if (mods == NULL) + { + DEBUG(0, ("make_a_mod: out of memory!\n")); + return; + } + mods[0] = NULL; + } + + for (i = 0; mods[i] != NULL; ++i) { + if (mods[i]->mod_op == modop && !strcasecmp(mods[i]->mod_type, attribute)) + break; + } + + if (mods[i] == NULL) + { + mods = (LDAPMod **) realloc (mods, (i + 2) * sizeof (LDAPMod *)); + if (mods == NULL) + { + DEBUG(0, ("make_a_mod: out of memory!\n")); + return; + } + mods[i] = (LDAPMod *) malloc(sizeof(LDAPMod)); + if (mods[i] == NULL) + { + DEBUG(0, ("make_a_mod: out of memory!\n")); + return; + } + mods[i]->mod_op = modop; + mods[i]->mod_values = NULL; + mods[i]->mod_type = strdup(attribute); + mods[i + 1] = NULL; + } + + if (value != NULL) + { + j = 0; + if (mods[i]->mod_values != NULL) { + for (; mods[i]->mod_values[j] != NULL; j++); + } + mods[i]->mod_values = (char **)realloc(mods[i]->mod_values, + (j + 2) * sizeof (char *)); + + if (mods[i]->mod_values == NULL) { + DEBUG (0, ("make_a_mod: Memory allocation failure!\n")); + return; + } + mods[i]->mod_values[j] = strdup(value); + mods[i]->mod_values[j + 1] = NULL; + } + *modlist = mods; +} + +/* New Interface is being implemented here */ + +/********************************************************************** +Initialize SAM_ACCOUNT from an LDAP query +(Based on init_sam_from_buffer in pdb_tdb.c) +*********************************************************************/ +static BOOL init_sam_from_ldap (SAM_ACCOUNT * sampass, + LDAP * ldap_struct, LDAPMessage * entry) +{ + time_t logon_time, + logoff_time, + kickoff_time, + pass_last_set_time, + pass_can_change_time, + pass_must_change_time; + static pstring username; + static pstring domain; + static pstring nt_username; + static pstring fullname; + static pstring homedir; + static pstring dir_drive; + static pstring logon_script; + static pstring profile_path; + static pstring acct_desc; + static pstring munged_dial; + static pstring workstations; + struct passwd *sys_user; + uint32 user_rid, group_rid; + static uint8 smblmpwd[16]; + static uint8 smbntpwd[16]; + uint16 acct_ctrl, logon_divs; + uint32 hours_len; + uint8 *hours; + pstring temp; + + get_single_attribute(ldap_struct, entry, "uid", username); + DEBUG(2, ("Entry found for user: %s\n", username)); + + pstrcpy(nt_username, username); + + get_single_attribute(ldap_struct, entry, "sambaDomain", domain); + if (!domain) + pstrcpy(domain, lp_workgroup()); + + get_single_attribute(ldap_struct, entry, "pwdLastSet", temp); + pass_last_set_time = (time_t) strtol(temp, NULL, 16); + + get_single_attribute(ldap_struct, entry, "logonTime", temp); + logon_time = (time_t) strtol(temp, NULL, 16); + + get_single_attribute(ldap_struct, entry, "logoffTime", temp); + logoff_time = (time_t) strtol(temp, NULL, 16); + + get_single_attribute(ldap_struct, entry, "kickoffTime", temp); + kickoff_time = (time_t) strtol(temp, NULL, 16); + + get_single_attribute(ldap_struct, entry, "pwdCanChange", temp); + pass_can_change_time = (time_t) strtol(temp, NULL, 16); + + get_single_attribute(ldap_struct, entry, "pwdMustChange", temp); + pass_must_change_time = (time_t) strtol(temp, NULL, 16); + + /* recommend that 'gecos' and 'displayName' should refer to the same + * attribute OID. userFullName depreciated, only used by Samba + * primary rules of LDAP: don't make a new attribute when one is already defined + * that fits your needs; using gecos then displayName then cn rather than 'userFullName' + */ + + get_single_attribute(ldap_struct, entry, "gecos", fullname); + + if (!fullname) { + get_single_attribute(ldap_struct, entry, "displayName", fullname); + get_single_attribute(ldap_struct, entry, "cn", fullname); + } + + get_single_attribute(ldap_struct, entry, "homeDrive", dir_drive); + DEBUG(5,("homeDrive is set to %s\n",dir_drive)); + if (!*dir_drive) { + pstrcpy(dir_drive, lp_logon_drive()); + DEBUG(5,("homeDrive fell back to %s\n",dir_drive)); + } + + get_single_attribute(ldap_struct, entry, "smbHome", homedir); + DEBUG(5,("smbHome is set to %s\n",homedir)); + if (!*homedir) { + pstrcpy(homedir, lp_logon_home()); + DEBUG(5,("smbHome fell back to %s\n",homedir)); + } + + get_single_attribute(ldap_struct, entry, "scriptPath", logon_script); + DEBUG(5,("scriptPath is set to %s\n",logon_script)); + if (!*logon_script) { + pstrcpy(logon_script, lp_logon_script()); + DEBUG(5,("scriptPath fell back to %s\n",logon_script)); + } + + get_single_attribute(ldap_struct, entry, "profilePath", profile_path); + DEBUG(5,("profilePath is set to %s\n",profile_path)); + if (!*profile_path) { + pstrcpy(profile_path, lp_logon_path()); + DEBUG(5,("profilePath fell back to %s\n",profile_path)); + } + + get_single_attribute(ldap_struct, entry, "description", acct_desc); + get_single_attribute(ldap_struct, entry, "userWorkstations", workstations); + get_single_attribute(ldap_struct, entry, "rid", temp); + user_rid = (uint32)strtol(temp, NULL, 10); + get_single_attribute(ldap_struct, entry, "primaryGroupID", temp); + group_rid = (uint32)strtol(temp, NULL, 10); + + + /* These values MAY be in LDAP, but they can also be retrieved through + * sys_getpw*() which is how we're doing it (if you use nss_ldap, then + * these values will be stored in LDAP as well, but if not, we want the + * local values to override the LDAP for this anyway + * homeDirectory attribute + */ + sys_user = sys_getpwnam(username); + if (sys_user == NULL) + return False; + + + /* FIXME: hours stuff should be cleaner */ + logon_divs = 168; + hours_len = 21; + hours = malloc(sizeof(hours) * hours_len); + memset(hours, 0xff, hours_len); + + get_single_attribute (ldap_struct, entry, "lmPassword", temp); + pdb_gethexpwd(temp, smblmpwd); + memset((char *)temp, '\0', sizeof(temp)); + get_single_attribute (ldap_struct, entry, "ntPassword", temp); + pdb_gethexpwd(temp, smbntpwd); + memset((char *)temp, '\0', sizeof(temp)); + get_single_attribute (ldap_struct, entry, "acctFlags", temp); + acct_ctrl = pdb_decode_acct_ctrl(temp); + + if (acct_ctrl == 0) + acct_ctrl |= ACB_NORMAL; + + + pdb_set_acct_ctrl(sampass, acct_ctrl); + pdb_set_logon_time(sampass, logon_time); + pdb_set_logoff_time(sampass, logoff_time); + pdb_set_kickoff_time(sampass, kickoff_time); + pdb_set_pass_can_change_time(sampass, pass_can_change_time); + pdb_set_pass_must_change_time(sampass, pass_must_change_time); + pdb_set_pass_last_set_time(sampass, pass_last_set_time); + + pdb_set_hours_len(sampass, hours_len); + pdb_set_logons_divs(sampass, logon_divs); + + pdb_set_uid(sampass, &sys_user->pw_uid); + pdb_set_gid(sampass, &sys_user->pw_gid); + pdb_set_user_rid(sampass, user_rid); + pdb_set_group_rid(sampass, group_rid); + + pdb_set_username(sampass, username); + + pdb_set_domain(sampass, domain); + pdb_set_nt_username(sampass, nt_username); + + pdb_set_fullname(sampass, fullname); + + pdb_set_logon_script(sampass, logon_script); + pdb_set_profile_path(sampass, profile_path); + pdb_set_dir_drive(sampass, dir_drive); + pdb_set_homedir(sampass, homedir); + pdb_set_acct_desc(sampass, acct_desc); + pdb_set_workstations(sampass, workstations); + pdb_set_munged_dial(sampass, munged_dial); + if (!pdb_set_nt_passwd(sampass, smbntpwd)) + return False; + if (!pdb_set_lanman_passwd(sampass, smblmpwd)) + return False; + + /* pdb_set_unknown_3(sampass, unknown3); */ + /* pdb_set_unknown_5(sampass, unknown5); */ + /* pdb_set_unknown_6(sampass, unknown6); */ + + pdb_set_hours(sampass, hours); + + return True; +} + +/********************************************************************** +Initialize SAM_ACCOUNT from an LDAP query +(Based on init_buffer_from_sam in pdb_tdb.c) +*********************************************************************/ +static BOOL init_ldap_from_sam (LDAPMod *** mods, int ldap_state, const SAM_ACCOUNT * sampass) +{ + pstring temp; + + *mods = NULL; + + /* + * took out adding "objectclass: sambaAccount" + * do this on a per-mod basis + */ + + + make_a_mod(mods, ldap_state, "uid", pdb_get_username(sampass)); + DEBUG(2, ("Setting entry for user: %s\n", pdb_get_username(sampass))); + + /* not sure about using this for the nt_username */ + make_a_mod(mods, ldap_state, "sambaDomain", pdb_get_domain(sampass)); + + slprintf(temp, sizeof(temp) - 1, "%i", pdb_get_uid(sampass)); + make_a_mod(mods, ldap_state, "uidNumber", temp); + + slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_last_set_time(sampass)); + make_a_mod(mods, ldap_state, "pwdLastSet", temp); + + slprintf(temp, sizeof(temp) - 1, "%li", pdb_get_logon_time(sampass)); + make_a_mod(mods, ldap_state, "logonTime", temp); + + slprintf(temp, sizeof(temp) - 1, "%li", pdb_get_logoff_time(sampass)); + make_a_mod(mods, ldap_state, "logoffTime", temp); + + slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_kickoff_time(sampass)); + make_a_mod(mods, ldap_state, "kickoffTime", temp); + + slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_can_change_time(sampass)); + make_a_mod(mods, ldap_state, "pwdCanChange", temp); + + slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_must_change_time(sampass)); + make_a_mod(mods, ldap_state, "pwdMustChange", temp); + + /* displayName, cn, and gecos should all be the same + * most easily accomplished by giving them the same OID + * gecos isn't set here b/c it should be handled by the + * add-user script + */ + + make_a_mod(mods, ldap_state, "displayName", pdb_get_fullname(sampass)); + make_a_mod(mods, ldap_state, "cn", pdb_get_fullname(sampass)); + + make_a_mod(mods, ldap_state, "smbHome", pdb_get_homedir(sampass)); + make_a_mod(mods, ldap_state, "homeDrive", pdb_get_dirdrive(sampass)); + make_a_mod(mods, ldap_state, "scriptPath", pdb_get_logon_script(sampass)); + make_a_mod(mods, ldap_state, "profilePath", pdb_get_profile_path(sampass)); + make_a_mod(mods, ldap_state, "description", pdb_get_acct_desc(sampass)); + make_a_mod(mods, ldap_state, "userWorkstations", pdb_get_workstations(sampass)); + + if ( !sampass->user_rid) + sampass->user_rid = pdb_uid_to_user_rid(pdb_get_uid(sampass)); + slprintf(temp, sizeof(temp) - 1, "%i", sampass->user_rid); + make_a_mod(mods, ldap_state, "rid", temp); + + if ( !sampass->group_rid) { + GROUP_MAP map; + + if (get_group_map_from_gid(pdb_get_gid(sampass), &map, MAPPING_WITHOUT_PRIV)) { + sid_peek_rid(&map.sid, &sampass->group_rid); + } + else + sampass->group_rid = pdb_gid_to_group_rid(pdb_get_gid(sampass)); + } + + slprintf(temp, sizeof(temp) - 1, "%i", sampass->group_rid); + make_a_mod(mods, ldap_state, "primaryGroupID", temp); + + /* FIXME: Hours stuff goes in LDAP */ + pdb_sethexpwd (temp, pdb_get_lanman_passwd(sampass), pdb_get_acct_ctrl(sampass)); + make_a_mod (mods, ldap_state, "lmPassword", temp); + pdb_sethexpwd (temp, pdb_get_nt_passwd(sampass), pdb_get_acct_ctrl(sampass)); + make_a_mod (mods, ldap_state, "ntPassword", temp); + make_a_mod (mods, ldap_state, "acctFlags", pdb_encode_acct_ctrl (pdb_get_acct_ctrl(sampass), + NEW_PW_FORMAT_SPACE_PADDED_LEN)); + + return True; +} + +/********************************************************************** +Connect to LDAP server for password enumeration +*********************************************************************/ +BOOL pdb_setsampwent(BOOL update) +{ + int rc; + pstring filter; + + if (!ldap_open_connection(&global_ldap_ent.ldap_struct)) + { + return False; + } + if (!ldap_connect_system(global_ldap_ent.ldap_struct)) + { + ldap_unbind(global_ldap_ent.ldap_struct); + return False; + } + + pstrcpy(filter, lp_ldap_filter()); + all_string_sub(filter, "%u", "*", sizeof(pstring)); + + rc = ldap_search_s(global_ldap_ent.ldap_struct, lp_ldap_suffix(), + LDAP_SCOPE_SUBTREE, filter, NULL, 0, + &global_ldap_ent.result); + + if (rc != LDAP_SUCCESS) + { + DEBUG(0, ("LDAP search failed: %s\n", ldap_err2string(rc))); + DEBUG(3, ("Query was: %s, %s\n", lp_ldap_suffix(), filter)); + ldap_msgfree(global_ldap_ent.result); + ldap_unbind(global_ldap_ent.ldap_struct); + global_ldap_ent.ldap_struct = NULL; + global_ldap_ent.result = NULL; + return False; + } + + DEBUG(2, ("pdb_setsampwent: %d entries in the base!\n", + ldap_count_entries(global_ldap_ent.ldap_struct, + global_ldap_ent.result))); + + global_ldap_ent.entry = ldap_first_entry(global_ldap_ent.ldap_struct, + global_ldap_ent.result); + + return True; +} + +/********************************************************************** +End enumeration of the LDAP password list +*********************************************************************/ +void pdb_endsampwent(void) +{ + if (global_ldap_ent.ldap_struct && global_ldap_ent.result) + { + ldap_msgfree(global_ldap_ent.result); + ldap_unbind(global_ldap_ent.ldap_struct); + global_ldap_ent.ldap_struct = NULL; + global_ldap_ent.result = NULL; + } +} + +/********************************************************************** +Get the next entry in the LDAP password database +*********************************************************************/ +BOOL pdb_getsampwent(SAM_ACCOUNT * user) +{ + if (!global_ldap_ent.entry) + return False; + + global_ldap_ent.entry = ldap_next_entry(global_ldap_ent.ldap_struct, + global_ldap_ent.entry); + + if (global_ldap_ent.entry != NULL) + { + return init_sam_from_ldap(user, global_ldap_ent.ldap_struct, + global_ldap_ent.entry); + } + return False; +} + +/********************************************************************** +Get SAM_ACCOUNT entry from LDAP by username +*********************************************************************/ +BOOL pdb_getsampwnam(SAM_ACCOUNT * user, const char *sname) +{ + LDAP *ldap_struct; + LDAPMessage *result; + LDAPMessage *entry; + + if (!ldap_open_connection(&ldap_struct)) + return False; + if (!ldap_connect_system(ldap_struct)) + { + ldap_unbind(ldap_struct); + return False; + } + if (ldap_search_one_user_by_name(ldap_struct, sname, &result) != + LDAP_SUCCESS) + { + ldap_unbind(ldap_struct); + return False; + } + if (ldap_count_entries(ldap_struct, result) < 1) + { + DEBUG(0, + ("We don't find this user [%s] count=%d\n", sname, + ldap_count_entries(ldap_struct, result))); + ldap_unbind(ldap_struct); + return False; + } + entry = ldap_first_entry(ldap_struct, result); + if (entry) + { + init_sam_from_ldap(user, ldap_struct, entry); + ldap_msgfree(result); + ldap_unbind(ldap_struct); + return True; + } + else + { + ldap_msgfree(result); + ldap_unbind(ldap_struct); + return False; + } +} + +/********************************************************************** +Get SAM_ACCOUNT entry from LDAP by rid +*********************************************************************/ +BOOL pdb_getsampwrid(SAM_ACCOUNT * user, uint32 rid) +{ + LDAP *ldap_struct; + LDAPMessage *result; + LDAPMessage *entry; + + if (!ldap_open_connection(&ldap_struct)) + return False; + + if (!ldap_connect_system(ldap_struct)) + { + ldap_unbind(ldap_struct); + return False; + } + if (ldap_search_one_user_by_rid(ldap_struct, rid, &result) != + LDAP_SUCCESS) + { + ldap_unbind(ldap_struct); + return False; + } + + if (ldap_count_entries(ldap_struct, result) < 1) + { + DEBUG(0, + ("We don't find this rid [%i] count=%d\n", rid, + ldap_count_entries(ldap_struct, result))); + ldap_unbind(ldap_struct); + return False; + } + + entry = ldap_first_entry(ldap_struct, result); + if (entry) + { + init_sam_from_ldap(user, ldap_struct, entry); + ldap_msgfree(result); + ldap_unbind(ldap_struct); + return True; + } + else + { + ldap_msgfree(result); + ldap_unbind(ldap_struct); + return False; + } +} + +/********************************************************************** + Get SAM_ACCOUNT entry from LDAP by uid +*********************************************************************/ +BOOL pdb_getsampwuid(SAM_ACCOUNT * user, uid_t uid) +{ + LDAP *ldap_struct; + LDAPMessage *result; + LDAPMessage *entry; + + if (!ldap_open_connection(&ldap_struct)) + return False; + + if (!ldap_connect_system(ldap_struct)) + { + ldap_unbind(ldap_struct); + return False; + } + if (ldap_search_one_user_by_uid(ldap_struct, uid, &result) != + LDAP_SUCCESS) + { + ldap_unbind(ldap_struct); + return False; + } + + if (ldap_count_entries(ldap_struct, result) < 1) + { + DEBUG(0, + ("We don't find this uid [%i] count=%d\n", uid, + ldap_count_entries(ldap_struct, result))); + ldap_unbind(ldap_struct); + return False; + } + entry = ldap_first_entry(ldap_struct, result); + if (entry) + { + init_sam_from_ldap(user, ldap_struct, entry); + ldap_msgfree(result); + ldap_unbind(ldap_struct); + return True; + } + else + { + ldap_msgfree(result); + ldap_unbind(ldap_struct); + return False; + } +} + + +/********************************************************************** +Delete entry from LDAP for username +*********************************************************************/ +BOOL pdb_delete_sam_account(const char *sname) +{ + int rc; + char *dn; + LDAP *ldap_struct; + LDAPMessage *entry; + LDAPMessage *result; + + if (!ldap_open_connection (&ldap_struct)) + return False; + + DEBUG (3, ("Deleting user %s from LDAP.\n", sname)); + + if (!ldap_connect_system (ldap_struct)) { + ldap_unbind (ldap_struct); + DEBUG(0, ("Failed to delete user %s from LDAP.\n", sname)); + return False; + } + + rc = ldap_search_one_user_by_name (ldap_struct, sname, &result); + if (ldap_count_entries (ldap_struct, result) == 0) { + DEBUG (0, ("User doesn't exit!\n")); + ldap_msgfree (result); + ldap_unbind (ldap_struct); + return False; + } + + entry = ldap_first_entry (ldap_struct, result); + dn = ldap_get_dn (ldap_struct, entry); + + rc = ldap_delete_s (ldap_struct, dn); + + ldap_memfree (dn); + if (rc != LDAP_SUCCESS) { + char *ld_error; + ldap_get_option (ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error); + DEBUG (0,("failed to delete user with uid = %s with: %s\n\t%s\n", + sname, ldap_err2string (rc), ld_error)); + free (ld_error); + ldap_unbind (ldap_struct); + return False; + } + + DEBUG (2,("successfully deleted uid = %s from the LDAP database\n", sname)); + ldap_unbind (ldap_struct); + return True; +} + +/********************************************************************** +Update SAM_ACCOUNT +*********************************************************************/ +BOOL pdb_update_sam_account(const SAM_ACCOUNT * newpwd, BOOL override) +{ + int rc; + char *dn; + LDAP *ldap_struct; + LDAPMessage *result; + LDAPMessage *entry; + LDAPMod **mods; + + if (!ldap_open_connection(&ldap_struct)) /* open a connection to the server */ + return False; + + if (!ldap_connect_system(ldap_struct)) /* connect as system account */ + { + ldap_unbind(ldap_struct); + return False; + } + + rc = ldap_search_one_user_by_name(ldap_struct, + pdb_get_username(newpwd), &result); + + if (ldap_count_entries(ldap_struct, result) == 0) + { + DEBUG(0, ("No user to modify!\n")); + ldap_msgfree(result); + ldap_unbind(ldap_struct); + return False; + } + + init_ldap_from_sam(&mods, LDAP_MOD_REPLACE, newpwd); + + entry = ldap_first_entry(ldap_struct, result); + dn = ldap_get_dn(ldap_struct, entry); + + rc = ldap_modify_s(ldap_struct, dn, mods); + + if (rc != LDAP_SUCCESS) + { + char *ld_error; + ldap_get_option(ldap_struct, LDAP_OPT_ERROR_STRING, + &ld_error); + DEBUG(0, + ("failed to modify user with uid = %s with: %s\n\t%s\n", + pdb_get_username(newpwd), ldap_err2string(rc), + ld_error)); + free(ld_error); + ldap_unbind(ldap_struct); + return False; + } + + DEBUG(2, + ("successfully modified uid = %s in the LDAP database\n", + pdb_get_username(newpwd))); + ldap_mods_free(mods, 1); + ldap_unbind(ldap_struct); + return True; +} + +/********************************************************************** +Add SAM_ACCOUNT to LDAP +*********************************************************************/ +BOOL pdb_add_sam_account(const SAM_ACCOUNT * newpwd) +{ + int rc; + pstring filter; + LDAP *ldap_struct; + LDAPMessage *result; + pstring dn; + LDAPMod **mods; + int ldap_op = LDAP_MOD_ADD; + + if (!ldap_open_connection(&ldap_struct)) /* open a connection to the server */ + { + return False; + } + + if (!ldap_connect_system(ldap_struct)) /* connect as system account */ + { + ldap_unbind(ldap_struct); + return False; + } + + if (pdb_get_username(newpwd) != NULL) { + slprintf (dn, sizeof (dn) - 1, "uid=%s,%s", + pdb_get_username(newpwd), lp_ldap_suffix ()); + } + else + { + return False; + } + + + rc = ldap_search_one_user_by_name (ldap_struct, pdb_get_username(newpwd), &result); + + if (ldap_count_entries(ldap_struct, result) != 0) + { + DEBUG(0,("User already in the base, with samba properties\n")); + ldap_msgfree(result); + ldap_unbind(ldap_struct); + return False; + } + ldap_msgfree(result); + + slprintf (filter, sizeof (filter) - 1, "uid=%s", pdb_get_username(newpwd)); + rc = ldap_search_one_user(ldap_struct, filter, &result); + if (ldap_count_entries(ldap_struct, result) == 1) + { + char *tmp; + LDAPMessage *entry; + DEBUG(3,("User exists without samba properties: adding them\n")); + ldap_op = LDAP_MOD_REPLACE; + entry = ldap_first_entry (ldap_struct, result); + tmp = ldap_get_dn (ldap_struct, entry); + slprintf (dn, sizeof (dn) - 1, "%s", tmp); + ldap_memfree (tmp); + } + else + { + DEBUG (3, ("More than one user with that uid exists: bailing out!\n")); + return False; + } + + ldap_msgfree(result); + + init_ldap_from_sam(&mods, ldap_op, newpwd); + make_a_mod(&mods, LDAP_MOD_ADD, "objectclass", "sambaAccount"); + + if (ldap_op == LDAP_MOD_REPLACE) { + rc = ldap_modify_s(ldap_struct, dn, mods); + } + else { + rc = ldap_add_s(ldap_struct, dn, mods); + } + + if (rc != LDAP_SUCCESS) + { + char *ld_error; + + ldap_get_option (ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error); + DEBUG(0,("failed to modify user with uid = %s with: %s\n\t%s\n", + pdb_get_username(newpwd), ldap_err2string (rc), ld_error)); + free(ld_error); + ldap_mods_free(mods, 1); + ldap_unbind(ldap_struct); + return False; + } + + DEBUG(2,("added: uid = %s in the LDAP database\n", pdb_get_username(newpwd))); + ldap_mods_free(mods, 1); + ldap_unbind(ldap_struct); + return True; +} + +#else +void dummy_function(void); +void +dummy_function (void) +{ +} /* stop some compilers complaining */ +#endif diff --git a/source/passdb/pdb_nisplus.c b/source/passdb/pdb_nisplus.c new file mode 100644 index 00000000000..2820fa14142 --- /dev/null +++ b/source/passdb/pdb_nisplus.c @@ -0,0 +1,1409 @@ +/* + * Unix SMB/Netbios implementation. Version 1.9. SMB parameters and setup + * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995. + * Copyright (C) Benny Holmgren 1998 <bigfoot@astrakan.hgs.se> + * Copyright (C) Luke Kenneth Casson Leighton 1996-1998. + * Copyright (C) Toomas Soome <tsoome@ut.ee> 2001 + * + * 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" + +#ifdef WITH_NISPLUS_SAM + +#ifdef BROKEN_NISPLUS_INCLUDE_FILES + +/* + * The following lines are needed due to buggy include files + * in Solaris 2.6 which define GROUP in both /usr/include/sys/acl.h and + * also in /usr/include/rpcsvc/nis.h. The definitions conflict. JRA. + * Also GROUP_OBJ is defined as 0x4 in /usr/include/sys/acl.h and as + * an enum in /usr/include/rpcsvc/nis.h. + */ + +#if defined(GROUP) +#undef GROUP +#endif + +#if defined(GROUP_OBJ) +#undef GROUP_OBJ +#endif + +#endif + +#include <rpcsvc/nis.h> + +extern int DEBUGLEVEL; + +struct nisp_enum_info +{ + nis_result *result; + int enum_entry; +}; + +static struct nisp_enum_info global_nisp_ent; +static VOLATILE sig_atomic_t gotalarm; + +/*************************************************************** + + the fields for the NIS+ table, generated from mknissmbpwtbl.sh, are: + + name=S,nogw=r + uid=S,nogw=r + user_rid=S,nogw=r + smb_grpid=,nw+r + group_rid=,nw+r + acb=,nw+r + + lmpwd=C,nw=,g=r,o=rm + ntpwd=C,nw=,g=r,o=rm + + logon_t=,nw+r + logoff_t=,nw+r + kick_t=,nw+r + pwdlset_t=,nw+r + pwdlchg_t=,nw+r + pwdmchg_t=,nw+r + + full_name=,nw+r + home_dir=,nw+r + dir_drive=,nw+r + logon_script=,nw+r + profile_path=,nw+r + acct_desc=,nw+r + workstations=,nw+r + + hours=,nw+r + +****************************************************************/ + +#define NPF_NAME 0 +#define NPF_UID 1 +#define NPF_USER_RID 2 +#define NPF_SMB_GRPID 3 +#define NPF_GROUP_RID 4 +#define NPF_ACB 5 +#define NPF_LMPWD 6 +#define NPF_NTPWD 7 +#define NPF_LOGON_T 8 +#define NPF_LOGOFF_T 9 +#define NPF_KICK_T 10 +#define NPF_PWDLSET_T 11 +#define NPF_PWDCCHG_T 12 +#define NPF_PWDMCHG_T 13 +#define NPF_FULL_NAME 14 +#define NPF_HOME_DIR 15 +#define NPF_DIR_DRIVE 16 +#define NPF_LOGON_SCRIPT 17 +#define NPF_PROFILE_PATH 18 +#define NPF_ACCT_DESC 19 +#define NPF_WORKSTATIONS 20 +#define NPF_HOURS 21 + +/*************************************************************** + Signal function to tell us we timed out. +****************************************************************/ +static void gotalarm_sig(void) +{ + gotalarm = 1; +} + +/*************************************************************** + make_nisname_from_user_rid + ****************************************************************/ +static char *make_nisname_from_user_rid(uint32 rid, char *pfile) +{ + static pstring nisname; + + safe_strcpy(nisname, "[user_rid=", sizeof(nisname)-1); + slprintf(nisname, sizeof(nisname)-1, "%s%d", nisname, rid); + safe_strcat(nisname, "],", sizeof(nisname)-strlen(nisname)-1); + safe_strcat(nisname, pfile, sizeof(nisname)-strlen(nisname)-1); + + return nisname; +} + +/*************************************************************** + make_nisname_from_uid + ****************************************************************/ +static char *make_nisname_from_uid(int uid, char *pfile) +{ + static pstring nisname; + + safe_strcpy(nisname, "[uid=", sizeof(nisname)-1); + slprintf(nisname, sizeof(nisname)-1, "%s%d", nisname, uid); + safe_strcat(nisname, "],", sizeof(nisname)-strlen(nisname)-1); + safe_strcat(nisname, pfile, sizeof(nisname)-strlen(nisname)-1); + + return nisname; +} + +/*************************************************************** + make_nisname_from_name + ****************************************************************/ +static char *make_nisname_from_name(const char *user_name, char *pfile) +{ + static pstring nisname; + + safe_strcpy(nisname, "[name=", sizeof(nisname)-1); + safe_strcat(nisname, user_name, sizeof(nisname) - strlen(nisname) - 1); + safe_strcat(nisname, "],", sizeof(nisname)-strlen(nisname)-1); + safe_strcat(nisname, pfile, sizeof(nisname)-strlen(nisname)-1); + + return nisname; +} + +/************************************************************************* + gets a NIS+ attribute + *************************************************************************/ +static void get_single_attribute(const nis_object *new_obj, int col, + char *val, int len) +{ + int entry_len; + + if (new_obj == NULL || val == NULL) return; + + entry_len = ENTRY_LEN(new_obj, col); + if (len > entry_len) + { + len = entry_len; + } + + safe_strcpy(val, ENTRY_VAL(new_obj, col), len-1); +} + +/************************************************************************ + makes a struct sam_passwd from a NIS+ object. + ************************************************************************/ +static BOOL make_sam_from_nisp_object(SAM_ACCOUNT *pw_buf, const nis_object *obj) +{ + char *ptr; + pstring full_name; /* this must be translated to dos code page */ + pstring acct_desc; /* this must be translated to dos code page */ + pstring home_dir; /* set default value from smb.conf for user */ + pstring home_drive; /* set default value from smb.conf for user */ + pstring logon_script; /* set default value from smb.conf for user */ + pstring profile_path; /* set default value from smb.conf for user */ + pstring hours; + int hours_len; + unsigned char smbpwd[16]; + unsigned char smbntpwd[16]; + + + /* + * time values. note: this code assumes 32bit time_t! + */ + + /* Don't change these timestamp settings without a good reason. They are + important for NT member server compatibility. */ + + pdb_set_logon_time(pw_buf, (time_t)0); + ptr = (uchar *)ENTRY_VAL(obj, NPF_LOGON_T); + if(ptr && *ptr && (StrnCaseCmp(ptr, "LNT-", 4)==0)) { + int i; + ptr += 4; + for(i = 0; i < 8; i++) { + if(ptr[i] == '\0' || !isxdigit(ptr[i])) + break; + } + if(i == 8) { + pdb_set_logon_time(pw_buf, (time_t)strtol(ptr, NULL, 16)); + } + } + + pdb_set_logoff_time(pw_buf, get_time_t_max()); + ptr = (uchar *)ENTRY_VAL(obj, NPF_LOGOFF_T); + if(ptr && *ptr && (StrnCaseCmp(ptr, "LOT-", 4)==0)) { + int i; + ptr += 4; + for(i = 0; i < 8; i++) { + if(ptr[i] == '\0' || !isxdigit(ptr[i])) + break; + } + if(i == 8) { + pdb_set_logoff_time(pw_buf, (time_t)strtol(ptr, NULL, 16)); + } + } + + pdb_set_kickoff_time(pw_buf, get_time_t_max()); + ptr = (uchar *)ENTRY_VAL(obj, NPF_KICK_T); + if(ptr && *ptr && (StrnCaseCmp(ptr, "KOT-", 4)==0)) { + int i; + ptr += 4; + for(i = 0; i < 8; i++) { + if(ptr[i] == '\0' || !isxdigit(ptr[i])) + break; + } + if(i == 8) { + pdb_set_kickoff_time(pw_buf, (time_t)strtol(ptr, NULL, 16)); + } + } + + pdb_set_pass_last_set_time(pw_buf, (time_t)0); + ptr = (uchar *)ENTRY_VAL(obj, NPF_PWDLSET_T); + if(ptr && *ptr && (StrnCaseCmp(ptr, "LCT-", 4)==0)) { + int i; + ptr += 4; + for(i = 0; i < 8; i++) { + if(ptr[i] == '\0' || !isxdigit(ptr[i])) + break; + } + if(i == 8) { + pdb_set_pass_last_set_time(pw_buf, (time_t)strtol(ptr, NULL, 16)); + } + } + + pdb_set_pass_can_change_time(pw_buf, (time_t)0); + ptr = (uchar *)ENTRY_VAL(obj, NPF_PWDCCHG_T); + if(ptr && *ptr && (StrnCaseCmp(ptr, "CCT-", 4)==0)) { + int i; + ptr += 4; + for(i = 0; i < 8; i++) { + if(ptr[i] == '\0' || !isxdigit(ptr[i])) + break; + } + if(i == 8) { + pdb_set_pass_can_change_time(pw_buf, (time_t)strtol(ptr, NULL, 16)); + } + } + + pdb_set_pass_must_change_time(pw_buf, get_time_t_max()); /* Password never expires. */ + ptr = (uchar *)ENTRY_VAL(obj, NPF_PWDMCHG_T); + if(ptr && *ptr && (StrnCaseCmp(ptr, "MCT-", 4)==0)) { + int i; + ptr += 4; + for(i = 0; i < 8; i++) { + if(ptr[i] == '\0' || !isxdigit(ptr[i])) + break; + } + if(i == 8) { + pdb_set_pass_must_change_time(pw_buf, (time_t)strtol(ptr, NULL, 16)); + } + } + + /* string values */ + pdb_set_username(pw_buf, ENTRY_VAL(obj, NPF_NAME)); + pdb_set_domain(pw_buf, lp_workgroup()); + /* pdb_set_nt_username() -- cant set it here... */ + + get_single_attribute(obj, NPF_FULL_NAME, full_name, sizeof(pstring)); + unix_to_dos(full_name, True); + pdb_set_fullname(pw_buf, full_name); + + pdb_set_acct_ctrl(pw_buf, pdb_decode_acct_ctrl(ENTRY_VAL(obj, + NPF_ACB))); + + get_single_attribute(obj, NPF_ACCT_DESC, acct_desc, sizeof(pstring)); + unix_to_dos(acct_desc, True); + pdb_set_acct_desc(pw_buf, acct_desc); + + pdb_set_workstations(pw_buf, ENTRY_VAL(obj, NPF_WORKSTATIONS)); + pdb_set_munged_dial(pw_buf, NULL); + + pdb_set_uid(pw_buf, &atoi(ENTRY_VAL(obj, NPF_UID))); + pdb_set_gid(pw_buf, &atoi(ENTRY_VAL(obj, NPF_SMB_GRPID))); + pdb_set_user_rid(pw_buf, atoi(ENTRY_VAL(obj, NPF_USER_RID))); + pdb_set_group_rid(pw_buf, atoi(ENTRY_VAL(obj, NPF_GROUP_RID))); + + /* values, must exist for user */ + if( !(pdb_get_acct_ctrl(pw_buf) & ACB_WSTRUST) ) { + + get_single_attribute(obj, NPF_HOME_DIR, home_dir, sizeof(pstring)); + if( !(home_dir && *home_dir) ) + pstrcpy(home_dir, lp_logon_home()); + pdb_set_homedir(pw_buf, home_dir); + + get_single_attribute(obj, NPF_DIR_DRIVE, home_drive, sizeof(pstring)); + if( !(home_drive && *home_drive) ) + pstrcpy(home_drive, lp_logon_drive()); + pdb_set_dir_drive(pw_buf, home_drive); + + get_single_attribute(obj, NPF_LOGON_SCRIPT, logon_script, + sizeof(pstring)); + if( !(logon_script && *logon_script) ) + pstrcpy(logon_script, lp_logon_script()); + pdb_set_logon_script(pw_buf, logon_script); + + get_single_attribute(obj, NPF_PROFILE_PATH, profile_path, + sizeof(pstring)); + if( !(profile_path && *profile_path) ) + pstrcpy(profile_path, lp_logon_path()); + pdb_set_profile_path(pw_buf, profile_path); + } else { + /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. */ + pdb_set_group_rid (pw_buf, DOMAIN_GROUP_RID_USERS); + } + + /* Check the lanman password column. */ + ptr = (char *)ENTRY_VAL(obj, NPF_LMPWD); + pdb_set_lanman_passwd(pw_buf, NULL); + + if (!strncasecmp(ptr, "NO PASSWORD", 11)) { + pdb_set_acct_ctrl(pw_buf, pdb_get_acct_ctrl(pw_buf) | ACB_PWNOTREQ); + } else { + if (strlen(ptr) != 32 || !pdb_gethexpwd(ptr, smbpwd)) { + DEBUG(0, ("malformed LM pwd entry: %s.\n", + pdb_get_username(pw_buf))); + return False; + } + pdb_set_lanman_passwd(pw_buf, smbpwd); + } + + /* Check the NT password column. */ + ptr = ENTRY_VAL(obj, NPF_NTPWD); + pdb_set_nt_passwd(pw_buf, NULL); + + if (!(pdb_get_acct_ctrl(pw_buf) & ACB_PWNOTREQ) && + strncasecmp(ptr, "NO PASSWORD", 11)) { + if (strlen(ptr) != 32 || !pdb_gethexpwd(ptr, smbntpwd)) { + DEBUG(0, ("malformed NT pwd entry: uid = %d.\n", + pdb_get_uid(pw_buf))); + return False; + } + pdb_set_nt_passwd(pw_buf, smbntpwd); + } + + pdb_set_unknown_3(pw_buf, 0xffffff); /* don't know */ + pdb_set_logons_divs(pw_buf, 168); /* hours per week */ + + if( (hours_len = ENTRY_LEN(obj, NPF_HOURS)) == 21 ) { + memcpy(hours, ENTRY_VAL(obj, NPF_HOURS), hours_len); + } else { + hours_len = 21; /* 21 times 8 bits = 168 */ + /* available at all hours */ + memset(hours, 0xff, hours_len); + } + pdb_set_hours_len(pw_buf, hours_len); + pdb_set_hours(pw_buf, hours); + + pdb_set_unknown_5(pw_buf, 0x00020000); /* don't know */ + pdb_set_unknown_6(pw_buf, 0x000004ec); /* don't know */ + + return True; +} + +/************************************************************************ + makes a struct sam_passwd from a NIS+ result. + ************************************************************************/ +static BOOL make_sam_from_nisresult(SAM_ACCOUNT *pw_buf, const nis_result *result) +{ + if (pw_buf == NULL || result == NULL) return False; + + if (result->status != NIS_SUCCESS && result->status != NIS_NOTFOUND) + { + DEBUG(0, ("NIS+ lookup failure: %s\n", + nis_sperrno(result->status))); + return False; + } + + /* User not found. */ + if (NIS_RES_NUMOBJ(result) <= 0) + { + DEBUG(10, ("user not found in NIS+\n")); + return False; + } + + if (NIS_RES_NUMOBJ(result) > 1) + { + DEBUG(10, ("WARNING: Multiple entries for user in NIS+ table!\n")); + } + + /* Grab the first hit. */ + return make_sam_from_nisp_object(pw_buf, &NIS_RES_OBJECT(result)[0]); +} + +/************************************************************************* + sets a NIS+ attribute + *************************************************************************/ +static void set_single_attribute(nis_object *new_obj, int col, + const char *val, int len, int flags) +{ + if (new_obj == NULL) return; + + ENTRY_VAL(new_obj, col) = val; + ENTRY_LEN(new_obj, col) = len+1; + + if (flags != 0) + { + new_obj->EN_data.en_cols.en_cols_val[col].ec_flags = flags; + } +} + +/*************************************************************** + copy or modify nis object. this object is used to add or update + nisplus table entry. + ****************************************************************/ +static BOOL init_nisp_from_sam(nis_object *obj, const SAM_ACCOUNT *sampass, + nis_object *old) +{ + /* + * Fill nis_object for entry add or update. + * if we are updateing, we have to find out differences and set + * EN_MODIFIED flag. also set need_to_modify to trigger + * nis_modify_entry() call in pdb_update_sam_account(). + * + * TODO: + * get data from SAM + * if (modify) get data from nis_object, compare and store if + * different + set EN_MODIFIED and need_to_modify + * else + * store + */ + BOOL need_to_modify = False; + const char *name = pdb_get_username(sampass); /* from SAM */ + /* these must be static or allocate and free entry columns! */ + static fstring uid; /* from SAM */ + static fstring user_rid; /* from SAM */ + static fstring gid; /* from SAM */ + static fstring group_rid; /* from SAM */ + char *acb; /* from SAM */ + static fstring smb_passwd; /* from SAM */ + static fstring smb_nt_passwd; /* from SAM */ + static fstring logon_t; /* from SAM */ + static fstring logoff_t; /* from SAM */ + static fstring kickoff_t; /* from SAM */ + static fstring pwdlset_t; /* from SAM */ + static fstring pwdlchg_t; /* from SAM */ + static fstring pwdmchg_t; /* from SAM */ + static fstring full_name; /* from SAM */ + static fstring acct_desc; /* from SAM */ + static char empty[1]; /* just an empty string */ + + slprintf(uid, sizeof(uid)-1, "%u", pdb_get_uid(sampass)); + slprintf(user_rid, sizeof(user_rid)-1, "%u", + pdb_get_user_rid(sampass)? pdb_get_user_rid(sampass): + pdb_uid_to_user_rid(pdb_get_uid(sampass))); + slprintf(gid, sizeof(gid)-1, "%u", pdb_get_gid(sampass)); + + { + uint32 rid; + GROUP_MAP map; + + rid=pdb_get_group_rid(sampass); + + if (rid==0) { + if (get_group_map_from_gid(pdb_get_gid(sampass), &map, MAPPING_WITHOUT_PRIV)) { + sid_peek_rid(&map.sid, &rid); + } else + rid=pdb_gid_to_group_rid(pdb_get_gid(sampass)); + } + + slprintf(group_rid, sizeof(group_rid)-1, "%u", rid); + } + + acb = pdb_encode_acct_ctrl(pdb_get_acct_ctrl(sampass), + NEW_PW_FORMAT_SPACE_PADDED_LEN); + pdb_sethexpwd (smb_passwd, pdb_get_lanman_passwd(sampass), + pdb_get_acct_ctrl(sampass)); + pdb_sethexpwd (smb_nt_passwd, pdb_get_nt_passwd(sampass), + pdb_get_acct_ctrl(sampass)); + slprintf(logon_t, 13, "LNT-%08X", + (uint32)pdb_get_logon_time(sampass)); + slprintf(logoff_t, 13, "LOT-%08X", + (uint32)pdb_get_logoff_time(sampass)); + slprintf(kickoff_t, 13, "KOT-%08X", + (uint32)pdb_get_kickoff_time(sampass)); + slprintf(pwdlset_t, 13, "LCT-%08X", + (uint32)pdb_get_pass_last_set_time(sampass)); + slprintf(pwdlchg_t, 13, "CCT-%08X", + (uint32)pdb_get_pass_can_change_time(sampass)); + slprintf(pwdmchg_t, 13, "MCT-%08X", + (uint32)pdb_get_pass_must_change_time(sampass)); + safe_strcpy(full_name, pdb_get_fullname(sampass), sizeof(full_name)-1); + dos_to_unix(full_name, True); + safe_strcpy(acct_desc, pdb_get_acct_desc(sampass), sizeof(acct_desc)-1); + dos_to_unix(acct_desc, True); + + if( old ) { + /* name */ + if(strcmp(ENTRY_VAL(old, NPF_NAME), name)) + { + need_to_modify = True; + set_single_attribute(obj, NPF_NAME, name, strlen(name), + EN_MODIFIED); + } + + + /* uid */ + if(pdb_get_uid(sampass) != -1) { + if(!ENTRY_VAL(old, NPF_UID) || strcmp(ENTRY_VAL(old, NPF_UID), uid)) + { + need_to_modify = True; + set_single_attribute(obj, NPF_UID, uid, + strlen(uid), EN_MODIFIED); + } + } + + /* user_rid */ + if (pdb_get_user_rid(sampass)) { + if(!ENTRY_VAL(old, NPF_USER_RID) || + strcmp(ENTRY_VAL(old, NPF_USER_RID), user_rid) ) { + need_to_modify = True; + set_single_attribute(obj, NPF_USER_RID, user_rid, + strlen(user_rid), EN_MODIFIED); + } + } + + /* smb_grpid */ + if (pdb_get_gid(sampass) != -1) { + if(!ENTRY_VAL(old, NPF_SMB_GRPID) || + strcmp(ENTRY_VAL(old, NPF_SMB_GRPID), gid) ) { + need_to_modify = True; + set_single_attribute(obj, NPF_SMB_GRPID, gid, + strlen(gid), EN_MODIFIED); + } + } + + /* group_rid */ + if (pdb_get_group_rid(sampass)) { + if(!ENTRY_VAL(old, NPF_GROUP_RID) || + strcmp(ENTRY_VAL(old, NPF_GROUP_RID), group_rid) ) { + need_to_modify = True; + set_single_attribute(obj, NPF_GROUP_RID, group_rid, + strlen(group_rid), EN_MODIFIED); + } + } + + /* acb */ + if (!ENTRY_VAL(old, NPF_ACB) || + strcmp(ENTRY_VAL(old, NPF_ACB), acb)) { + need_to_modify = True; + set_single_attribute(obj, NPF_ACB, acb, strlen(acb), EN_MODIFIED); + } + + /* lmpwd */ + if(!ENTRY_VAL(old, NPF_LMPWD) || + strcmp(ENTRY_VAL(old, NPF_LMPWD), smb_passwd) ) { + need_to_modify = True; + set_single_attribute(obj, NPF_LMPWD, smb_passwd, + strlen(smb_passwd), EN_CRYPT|EN_MODIFIED); + } + + /* ntpwd */ + if(!ENTRY_VAL(old, NPF_NTPWD) || + strcmp(ENTRY_VAL(old, NPF_NTPWD), smb_nt_passwd) ) { + need_to_modify = True; + set_single_attribute(obj, NPF_NTPWD, smb_nt_passwd, + strlen(smb_nt_passwd), EN_CRYPT|EN_MODIFIED); + } + + /* logon_t */ + if( pdb_get_logon_time(sampass) && + (!ENTRY_VAL(old, NPF_LOGON_T) || + strcmp(ENTRY_VAL(old, NPF_LOGON_T), logon_t ))) { + need_to_modify = True; + set_single_attribute(obj, NPF_LOGON_T, logon_t, + strlen(logon_t), EN_MODIFIED); + } + + /* logoff_t */ + if( pdb_get_logoff_time(sampass) && + (!ENTRY_VAL(old, NPF_LOGOFF_T) || + strcmp(ENTRY_VAL(old, NPF_LOGOFF_T), logoff_t))) { + need_to_modify = True; + set_single_attribute(obj, NPF_LOGOFF_T, logoff_t, + strlen(logoff_t), EN_MODIFIED); + } + + /* kick_t */ + if( pdb_get_kickoff_time(sampass) && + (!ENTRY_VAL(old, NPF_KICK_T) || + strcmp(ENTRY_VAL(old, NPF_KICK_T), kickoff_t))) { + need_to_modify = True; + set_single_attribute(obj, NPF_KICK_T, kickoff_t, + strlen(kickoff_t), EN_MODIFIED); + } + + /* pwdlset_t */ + if( pdb_get_pass_last_set_time(sampass) && + (!ENTRY_VAL(old, NPF_PWDLSET_T) || + strcmp(ENTRY_VAL(old, NPF_PWDLSET_T), pwdlset_t))) { + need_to_modify = True; + set_single_attribute(obj, NPF_PWDLSET_T, pwdlset_t, + strlen(pwdlset_t), EN_MODIFIED); + } + + /* pwdlchg_t */ + if( pdb_get_pass_can_change_time(sampass) && + (!ENTRY_VAL(old, NPF_PWDCCHG_T) || + strcmp(ENTRY_VAL(old, NPF_PWDCCHG_T), pwdlchg_t))) { + need_to_modify = True; + set_single_attribute(obj, NPF_PWDCCHG_T, pwdlchg_t, + strlen(pwdlchg_t), EN_MODIFIED); + } + + /* pwdmchg_t */ + if( pdb_get_pass_must_change_time(sampass) && + (!ENTRY_VAL(old, NPF_PWDMCHG_T) || + strcmp(ENTRY_VAL(old, NPF_PWDMCHG_T), pwdmchg_t))) { + need_to_modify = True; + set_single_attribute(obj, NPF_PWDMCHG_T, pwdmchg_t, + strlen(pwdmchg_t), EN_MODIFIED); + } + + /* full_name */ + /* must support set, unset and change */ + if ( (pdb_get_fullname(sampass) && + !ENTRY_VAL(old, NPF_FULL_NAME)) || + (ENTRY_VAL(old, NPF_FULL_NAME) && + !pdb_get_fullname(sampass)) || + (ENTRY_VAL(old, NPF_FULL_NAME) && + pdb_get_fullname(sampass) && + strcmp( ENTRY_VAL(old, NPF_FULL_NAME), full_name ))) { + need_to_modify = True; + set_single_attribute(obj, NPF_FULL_NAME, full_name, + strlen(full_name), EN_MODIFIED); + } + + /* home_dir */ + /* must support set, unset and change */ + if( (pdb_get_homedir(sampass) && + !ENTRY_VAL(old, NPF_HOME_DIR)) || + (ENTRY_VAL(old, NPF_HOME_DIR) && + !pdb_get_homedir(sampass)) || + (ENTRY_VAL(old, NPF_HOME_DIR) && + pdb_get_homedir(sampass) && + strcmp( ENTRY_VAL(old, NPF_HOME_DIR), + pdb_get_homedir(sampass)))) { + need_to_modify = True; + set_single_attribute(obj, NPF_HOME_DIR, pdb_get_homedir(sampass), + strlen(pdb_get_homedir(sampass)), EN_MODIFIED); + } + + /* dir_drive */ + /* must support set, unset and change */ + if( (pdb_get_dirdrive(sampass) && + !ENTRY_VAL(old, NPF_DIR_DRIVE)) || + (ENTRY_VAL(old, NPF_DIR_DRIVE) && + !pdb_get_dirdrive(sampass)) || + (ENTRY_VAL(old, NPF_DIR_DRIVE) && + pdb_get_dirdrive(sampass) && + strcmp( ENTRY_VAL(old, NPF_DIR_DRIVE), + pdb_get_dirdrive(sampass)))) { + need_to_modify = True; + set_single_attribute(obj, NPF_DIR_DRIVE, pdb_get_dirdrive(sampass), + strlen(pdb_get_dirdrive(sampass)), EN_MODIFIED); + } + + /* logon_script */ + /* must support set, unset and change */ + if( (pdb_get_logon_script(sampass) && + !ENTRY_VAL(old, NPF_LOGON_SCRIPT) || + (ENTRY_VAL(old, NPF_LOGON_SCRIPT) && + !pdb_get_logon_script(sampass)) || + ( ENTRY_VAL(old, NPF_LOGON_SCRIPT) && + pdb_get_logon_script(sampass) && + strcmp( ENTRY_VAL(old, NPF_LOGON_SCRIPT), + pdb_get_logon_script(sampass))))) { + need_to_modify = True; + set_single_attribute(obj, NPF_LOGON_SCRIPT, + pdb_get_logon_script(sampass), + strlen(pdb_get_logon_script(sampass)), + EN_MODIFIED); + } + + /* profile_path */ + /* must support set, unset and change */ + if( (pdb_get_profile_path(sampass) && + !ENTRY_VAL(old, NPF_PROFILE_PATH)) || + (ENTRY_VAL(old, NPF_PROFILE_PATH) && + !pdb_get_profile_path(sampass)) || + (ENTRY_VAL(old, NPF_PROFILE_PATH) && + pdb_get_profile_path(sampass) && + strcmp( ENTRY_VAL(old, NPF_PROFILE_PATH), + pdb_get_profile_path(sampass) ) )) { + need_to_modify = True; + set_single_attribute(obj, NPF_PROFILE_PATH, + pdb_get_profile_path(sampass), + strlen(pdb_get_profile_path(sampass)), + EN_MODIFIED); + } + + /* acct_desc */ + /* must support set, unset and change */ + if( (pdb_get_acct_desc(sampass) && + !ENTRY_VAL(old, NPF_ACCT_DESC)) || + (ENTRY_VAL(old, NPF_ACCT_DESC) && + !pdb_get_acct_desc(sampass)) || + (ENTRY_VAL(old, NPF_ACCT_DESC) && + pdb_get_acct_desc(sampass) && + strcmp( ENTRY_VAL(old, NPF_ACCT_DESC), acct_desc ) )) { + need_to_modify = True; + set_single_attribute(obj, NPF_ACCT_DESC, acct_desc, + strlen(acct_desc), EN_MODIFIED); + } + + /* workstations */ + /* must support set, unset and change */ + if ( (pdb_get_workstations(sampass) && + !ENTRY_VAL(old, NPF_WORKSTATIONS) ) || + (ENTRY_VAL(old, NPF_WORKSTATIONS) && + !pdb_get_workstations(sampass)) || + (ENTRY_VAL(old, NPF_WORKSTATIONS) && + pdb_get_workstations(sampass)) && + strcmp( ENTRY_VAL(old, NPF_WORKSTATIONS), + pdb_get_workstations(sampass))) { + need_to_modify = True; + set_single_attribute(obj, NPF_WORKSTATIONS, + pdb_get_workstations(sampass), + strlen(pdb_get_workstations(sampass)), + EN_MODIFIED); + } + + /* hours */ + if ((pdb_get_hours_len(sampass) != ENTRY_LEN(old, NPF_HOURS)) || + memcmp(pdb_get_hours(sampass), ENTRY_VAL(old, NPF_HOURS), + ENTRY_LEN(old, NPF_HOURS))) { + need_to_modify = True; + /* set_single_attribute will add 1 for len ... */ + set_single_attribute(obj, NPF_HOURS, pdb_get_hours(sampass), + pdb_get_hours_len(sampass)-1, EN_MODIFIED); + } + } else { + const char *homedir, *dirdrive, *logon_script, *profile_path, *workstations; + + *empty = '\0'; /* empty string */ + + set_single_attribute(obj, NPF_NAME, name, strlen(name), 0); + set_single_attribute(obj, NPF_UID, uid, strlen(uid), 0); + set_single_attribute(obj, NPF_USER_RID, user_rid, + strlen(user_rid), 0); + set_single_attribute(obj, NPF_SMB_GRPID, gid, strlen(gid), 0); + set_single_attribute(obj, NPF_GROUP_RID, group_rid, + strlen(group_rid), 0); + set_single_attribute(obj, NPF_ACB, acb, strlen(acb), 0); + set_single_attribute(obj, NPF_LMPWD, smb_passwd, + strlen(smb_passwd), EN_CRYPT); + set_single_attribute(obj, NPF_NTPWD, smb_nt_passwd, + strlen(smb_nt_passwd), EN_CRYPT); + set_single_attribute(obj, NPF_LOGON_T, logon_t, + strlen(logon_t), 0); + set_single_attribute(obj, NPF_LOGOFF_T, logoff_t, + strlen(logoff_t), 0); + set_single_attribute(obj, NPF_KICK_T, kickoff_t, + strlen(kickoff_t),0); + set_single_attribute(obj, NPF_PWDLSET_T, pwdlset_t, + strlen(pwdlset_t), 0); + set_single_attribute(obj, NPF_PWDCCHG_T, pwdlchg_t, + strlen(pwdlchg_t), 0); + set_single_attribute(obj, NPF_PWDMCHG_T, pwdmchg_t, + strlen(pwdmchg_t), 0); + set_single_attribute(obj, NPF_FULL_NAME , + full_name, strlen(full_name), 0); + + if(!(homedir = pdb_get_homedir(sampass))) + homedir = empty; + + set_single_attribute(obj, NPF_HOME_DIR, + homedir, strlen(homedir), 0); + + if(!(dirdrive = pdb_get_dirdrive(sampass))) + dirdrive = empty; + + set_single_attribute(obj, NPF_DIR_DRIVE, + dirdrive, strlen(dirdrive), 0); + + if(!(logon_script = pdb_get_logon_script(sampass))) + logon_script = empty; + + set_single_attribute(obj, NPF_LOGON_SCRIPT, + logon_script, strlen(logon_script), 0); + + if(!(profile_path = pdb_get_profile_path(sampass))) + profile_path = empty; + + set_single_attribute(obj, NPF_PROFILE_PATH, + profile_path, strlen(profile_path), 0); + + set_single_attribute(obj, NPF_ACCT_DESC, + acct_desc, strlen(acct_desc), 0); + + if(!(workstations = pdb_get_workstations(sampass))) + workstations = empty; + + set_single_attribute(obj, NPF_WORKSTATIONS, + workstations, strlen(workstations), 0); + + /* set_single_attribute will add 1 for len ... */ + set_single_attribute(obj, NPF_HOURS, + pdb_get_hours(sampass), + pdb_get_hours_len(sampass)-1, 0); + } + + return need_to_modify; +} + +/*************************************************************** + calls nis_list, returns results. + ****************************************************************/ +static nis_result *nisp_get_nis_list(const char *nis_name, uint_t flags) +{ + nis_result *result; + int i; + + if( ! flags) + flags = FOLLOW_LINKS|FOLLOW_PATH|EXPAND_NAME|HARD_LOOKUP; + + for(i = 0; i<2;i++ ) { + alarm(60); /* hopefully ok for long searches */ + result = nis_list(nis_name, flags,NULL,NULL); + + alarm(0); + CatchSignal(SIGALRM, SIGNAL_CAST SIG_DFL); + + if (gotalarm) + { + DEBUG(0,("NIS+ lookup time out\n")); + nis_freeresult(result); + return NULL; + } + if( !(flags & MASTER_ONLY) && NIS_RES_NUMOBJ(result) <= 0 ) { + /* nis replicas are not in sync perhaps? + * this can happen, if account was just added. + */ + DEBUG(10,("will try master only\n")); + nis_freeresult(result); + flags |= MASTER_ONLY; + } else + break; + } + return result; +} + +/*************************************************************** + Start to enumerate the nisplus passwd list. + ****************************************************************/ +BOOL pdb_setsampwent(BOOL update) +{ + char *sp, * p = lp_smb_passwd_file(); + pstring pfiletmp; + + if( (sp = strrchr( p, '/' )) ) + safe_strcpy(pfiletmp, sp+1, sizeof(pfiletmp)-1); + else + safe_strcpy(pfiletmp, p, sizeof(pfiletmp)-1); + safe_strcat(pfiletmp, ".org_dir", sizeof(pfiletmp)-strlen(pfiletmp)-1); + + pdb_endsampwent(); /* just in case */ + global_nisp_ent.result = nisp_get_nis_list( pfiletmp, 0 ); + global_nisp_ent.enum_entry = 0; + return global_nisp_ent.result != NULL ? True : False; +} + +/*************************************************************** + End enumeration of the nisplus passwd list. +****************************************************************/ +void pdb_endsampwent(void) +{ + if( global_nisp_ent.result ) + nis_freeresult(global_nisp_ent.result); + global_nisp_ent.result = NULL; + global_nisp_ent.enum_entry = 0; +} + +/************************************************************************* + Routine to return the next entry in the nisplus passwd list. + *************************************************************************/ +BOOL pdb_getsampwent(SAM_ACCOUNT *user) +{ + int enum_entry = (int)(global_nisp_ent.enum_entry); + nis_result *result = global_nisp_ent.result; + + if (user==NULL) { + DEBUG(0,("SAM_ACCOUNT is NULL.\n")); + return False; + } + + if (result == NULL || + enum_entry < 0 || enum_entry >= (NIS_RES_NUMOBJ(result) - 1)) + { + return False; + } + + if(!make_sam_from_nisp_object(user, &NIS_RES_OBJECT(result)[enum_entry]) ) + { + DEBUG(0,("Bad SAM_ACCOUNT entry returned from NIS+!\n")); + return False; + } + (int)(global_nisp_ent.enum_entry)++; + return True; +} + +/************************************************************************* + Routine to search the nisplus passwd file for an entry matching the username + *************************************************************************/ +BOOL pdb_getsampwnam(SAM_ACCOUNT * user, const char *sname) +{ + /* Static buffers we will return. */ + nis_result *result = NULL; + pstring nisname; + BOOL ret; + char *pfile = lp_smb_passwd_file(); + int i; + + if (!*pfile) + { + DEBUG(0, ("No SMB password file set\n")); + return False; + } + if( strrchr( pfile, '/') ) + pfile = strrchr( pfile, '/') + 1; + + slprintf(nisname, sizeof(nisname)-1, "[name=%s],%s.org_dir", sname, pfile); + DEBUG(10, ("search by nisname: %s\n", nisname)); + + /* Search the table. */ + + if(!(result = nisp_get_nis_list(nisname, 0))) + { + return False; + } + + ret = make_sam_from_nisresult(user, result); + nis_freeresult(result); + + return ret; +} + +/************************************************************************* + Routine to search the nisplus passwd file for an entry matching the username + *************************************************************************/ +BOOL pdb_getsampwrid(SAM_ACCOUNT * user, uint32 rid) +{ + nis_result *result; + char *nisname; + BOOL ret; + char *sp, *p = lp_smb_passwd_file(); + pstring pfiletmp; + + if (!*p) + { + DEBUG(0, ("no SMB password file set\n")); + return False; + } + + if( (sp = strrchr( p, '/' )) ) + safe_strcpy(pfiletmp, sp+1, sizeof(pfiletmp)-1); + else + safe_strcpy(pfiletmp, p, sizeof(pfiletmp)-1); + safe_strcat(pfiletmp, ".org_dir", sizeof(pfiletmp)-strlen(pfiletmp)-1); + + nisname = make_nisname_from_user_rid(rid, pfiletmp); + + DEBUG(10, ("search by rid: %s\n", nisname)); + + /* Search the table. */ + + if(!(result = nisp_get_nis_list(nisname, 0))) + { + return False; + } + + ret = make_sam_from_nisresult(user, result); + nis_freeresult(result); + + return ret; +} + +/************************************************************************* + Routine to search the nisplus passwd file for an entry matching the username + *************************************************************************/ +BOOL pdb_getsampwuid(SAM_ACCOUNT * user, uid_t uid) +{ + nis_result *result; + char *nisname; + BOOL ret; + char *sp, *p = lp_smb_passwd_file(); + pstring pfiletmp; + + if (!*p) + { + DEBUG(0, ("no SMB password file set\n")); + return False; + } + + if( (sp = strrchr( p, '/' )) ) + safe_strcpy(pfiletmp, sp+1, sizeof(pfiletmp)-1); + else + safe_strcpy(pfiletmp, p, sizeof(pfiletmp)-1); + safe_strcat(pfiletmp, ".org_dir", sizeof(pfiletmp)-strlen(pfiletmp)-1); + + nisname = make_nisname_from_uid(uid, pfiletmp); + + DEBUG(10, ("search by uid: %s\n", nisname)); + + /* Search the table. */ + + if(!(result = nisp_get_nis_list(nisname, 0))) + { + return False; + } + + ret = make_sam_from_nisresult(user, result); + nis_freeresult(result); + + return ret; +} + +/************************************************************************* + Routine to remove entry from the nisplus smbpasswd table + *************************************************************************/ +BOOL pdb_delete_sam_account(const char *sname) +{ + char *pfile = lp_smb_passwd_file(); + pstring nisname; + nis_result *result, *delresult; + nis_object *obj; + int i; + + if (!*pfile) + { + DEBUG(0, ("no SMB password file set\n")); + return False; + } + if( strrchr( pfile, '/') ) + pfile = strrchr( pfile, '/') + 1; + + slprintf(nisname, sizeof(nisname)-1, "[name=%s],%s.org_dir", sname, pfile); + + /* Search the table. */ + + if( !(result = nisp_get_nis_list(nisname, + MASTER_ONLY|FOLLOW_LINKS|FOLLOW_PATH|\ + EXPAND_NAME|HARD_LOOKUP))) { + return False; + } + + if(result->status != NIS_SUCCESS || NIS_RES_NUMOBJ(result) <= 0) { + /* User not found. */ + DEBUG(0,("user not found in NIS+\n")); + nis_freeresult(result); + return False; + } + + obj = NIS_RES_OBJECT(result); + slprintf(nisname, sizeof(nisname)-1, "[name=%s],%s.%s", sname, obj->zo_name, + obj->zo_domain); + + DEBUG(10, ("removing name: %s\n", nisname)); + delresult = nis_remove_entry(nisname, obj, + MASTER_ONLY|REM_MULTIPLE|ALL_RESULTS|FOLLOW_PATH|EXPAND_NAME|HARD_LOOKUP); + + nis_freeresult(result); + + if(delresult->status != NIS_SUCCESS) { + DEBUG(0, ("NIS+ table update failed: %s %s\n", + nisname, nis_sperrno(delresult->status))); + nis_freeresult(delresult); + return False; + } + nis_freeresult(delresult); + return True; +} + +/************************************************************************ + Routine to add an entry to the nisplus passwd file. +*************************************************************************/ +BOOL pdb_add_sam_account(const SAM_ACCOUNT * newpwd) +{ + int local_user = 0; + char *pfile; + pstring pfiletmp; + char *nisname; + nis_result *result = NULL, + *tblresult = NULL; + nis_object new_obj; + entry_col *ecol; + int ta_maxcol; + + /* + * 1. find user domain. + * a. try nis search in passwd.org_dir - if found use domain from result. + * b. try getpwnam. this may be needed if user is defined + * in /etc/passwd file (or elsewere) and not in passwd.org_dir. + * if found, use host default domain. + * c. exit with False - no such user. + * + * 2. add user + * a. find smbpasswd table + * search pfile in user domain if not found, try host default + * domain. + * b. smbpasswd domain is found, fill data and add entry. + * + * pfile should contain ONLY table name, org_dir will be concated. + * so, at first we will clear path prefix from pfile, and + * then we will use pfiletmp as playground to put together full + * nisname string. + * such approach will make it possible to specify samba private dir + * AND still use NIS+ table. as all domain related data is normally + * stored in org_dir.DOMAIN, this should be ok do do. + */ + + pfile = lp_smb_passwd_file(); + if( strrchr( pfile, '/') ) + pfile = strrchr( pfile, '/') + 1; + + /* + * Check if user is already there. + */ + safe_strcpy(pfiletmp, pfile, sizeof(pfiletmp)-1); + safe_strcat(pfiletmp, ".org_dir", + sizeof(pfiletmp)-strlen(pfiletmp)-1); + + if(pdb_get_username(newpwd) != NULL) { + nisname = make_nisname_from_name(pdb_get_username(newpwd), + pfiletmp); + } else { + return False; + } + + if(!(result = nisp_get_nis_list(nisname, MASTER_ONLY|FOLLOW_LINKS|\ + FOLLOW_PATH|EXPAND_NAME|HARD_LOOKUP))) { + return False; + } + if (result->status != NIS_SUCCESS && + result->status != NIS_NOTFOUND) { + DEBUG(3, ( "nis_list failure: %s: %s\n", + nisname, nis_sperrno(result->status))); + nis_freeresult(result); + return False; + } + + if (result->status == NIS_SUCCESS && NIS_RES_NUMOBJ(result) > 0) + { + DEBUG(3, ("User already exists in NIS+ password db: %s\n", + pfile)); + nis_freeresult(result); + return False; + } + + nis_freeresult(result); /* no such user, free results */ + + /* + * check for user in unix password database. we need this to get + * domain, where smbpasswd entry should be stored. + */ + + nisname = make_nisname_from_name(pdb_get_username(newpwd), + "passwd.org_dir"); + + result = nisp_get_nis_list(nisname, + MASTER_ONLY|FOLLOW_LINKS|FOLLOW_PATH|\ + EXPAND_NAME|HARD_LOOKUP); + + if (result->status != NIS_SUCCESS || NIS_RES_NUMOBJ(result) <= 0) + { + DEBUG(3, ("nis_list failure: %s: %s\n", + nisname, nis_sperrno(result->status))); + nis_freeresult(result); + + if (!sys_getpwnam(pdb_get_username(newpwd))) { + /* no such user in system! */ + return False; + } + /* + * user is defined, but not in passwd.org_dir. + */ + local_user = 1; + } else { + safe_strcpy(pfiletmp, pfile, sizeof(pfiletmp)-1); + safe_strcat(pfiletmp, ".", sizeof(pfiletmp)-strlen(pfiletmp)-1); + safe_strcat(pfiletmp, NIS_RES_OBJECT(result)->zo_domain, + sizeof(pfiletmp)-strlen(pfiletmp)-1); + nis_freeresult(result); /* not needed any more */ + + tblresult = nisp_get_nis_list(pfiletmp, + MASTER_ONLY|FOLLOW_LINKS|\ + FOLLOW_PATH|EXPAND_NAME|HARD_LOOKUP); + } + + if (local_user || tblresult->status != NIS_SUCCESS) + { + /* + * no user domain or + * smbpasswd table not found in user domain, fallback to + * default domain. + */ + if (!local_user) /* free previous failed search result */ + nis_freeresult(tblresult); + + safe_strcpy(pfiletmp, pfile, sizeof(pfiletmp)-1); + safe_strcat(pfiletmp, ".org_dir", + sizeof(pfiletmp)-strlen(pfiletmp)-1); + tblresult = nis_lookup(pfiletmp, MASTER_ONLY|FOLLOW_LINKS|\ + FOLLOW_PATH|EXPAND_NAME|HARD_LOOKUP); + if (tblresult->status != NIS_SUCCESS) + { + /* still nothing. bail out */ + nis_freeresult(tblresult); + DEBUG(3, ( "nis_lookup failure: %s\n", + nis_sperrno(tblresult->status))); + return False; + } + /* we need full name for nis_add_entry() */ + safe_strcpy(pfiletmp, pfile, sizeof(pfiletmp)-1); + safe_strcat(pfiletmp, ".", sizeof(pfiletmp)-strlen(pfiletmp)-1); + safe_strcat(pfiletmp, NIS_RES_OBJECT(tblresult)->zo_domain, + sizeof(pfiletmp)-strlen(pfiletmp)-1); + } + + memset((char *)&new_obj, 0, sizeof (new_obj)); + /* fill entry headers */ + /* we do not free these. */ + new_obj.zo_name = NIS_RES_OBJECT(tblresult)->zo_name; + new_obj.zo_owner = NIS_RES_OBJECT(tblresult)->zo_owner; + new_obj.zo_group = NIS_RES_OBJECT(tblresult)->zo_group; + new_obj.zo_domain = NIS_RES_OBJECT(tblresult)->zo_domain; + /* uints */ + new_obj.zo_access = NIS_RES_OBJECT(tblresult)->zo_access; + new_obj.zo_ttl = NIS_RES_OBJECT(tblresult)->zo_ttl; + + new_obj.zo_data.zo_type = ENTRY_OBJ; + new_obj.EN_data.en_type = + NIS_RES_OBJECT(tblresult)->TA_data.ta_type; + + ta_maxcol = NIS_RES_OBJECT(tblresult)->TA_data.ta_maxcol; + + if(!(ecol = (entry_col*)malloc(ta_maxcol*sizeof(entry_col)))) { + DEBUG(0, ("memory allocation failure\n")); + nis_freeresult(tblresult); + return False; + } + + memset((char *)ecol, 0, ta_maxcol*sizeof (entry_col)); + new_obj.EN_data.en_cols.en_cols_val = ecol; + new_obj.EN_data.en_cols.en_cols_len = ta_maxcol; + + init_nisp_from_sam(&new_obj, newpwd, NULL); + + DEBUG(10, ( "add NIS+ entry: %s\n", nisname)); + result = nis_add_entry(pfiletmp, &new_obj, 0); + + free(ecol); /* free allocated entry space */ + + if (result->status != NIS_SUCCESS) + { + DEBUG(3, ( "NIS+ table update failed: %s\n", + nisname, nis_sperrno(result->status))); + nis_freeresult(tblresult); + nis_freeresult(result); + return False; + } + + nis_freeresult(tblresult); + nis_freeresult(result); + + return True; +} + +/************************************************************************ + Routine to modify the nisplus passwd entry. +************************************************************************/ +BOOL pdb_update_sam_account(const SAM_ACCOUNT * newpwd, BOOL override) +{ + nis_result *result, *addresult; + nis_object *obj; + nis_object new_obj; + entry_col *ecol; + int ta_maxcol; + char *pfile = lp_smb_passwd_file(); + pstring nisname; + int i; + + if (!*pfile) + { + DEBUG(0, ("no SMB password file set\n")); + return False; + } + if( strrchr( pfile, '/') ) + pfile = strrchr( pfile, '/') + 1; + + slprintf(nisname, sizeof(nisname)-1, "[name=%s],%s.org_dir", + pdb_get_username(newpwd), pfile); + + DEBUG(10, ("search by name: %s\n", nisname)); + + /* Search the table. */ + + if( !(result = nisp_get_nis_list(nisname, MASTER_ONLY|FOLLOW_LINKS|\ + FOLLOW_PATH|EXPAND_NAME|HARD_LOOKUP))) { + return False; + } + + if(result->status != NIS_SUCCESS || NIS_RES_NUMOBJ(result) <= 0) { + /* User not found. */ + DEBUG(0,("user not found in NIS+\n")); + nis_freeresult(result); + return False; + } + + obj = NIS_RES_OBJECT(result); + DEBUG(6,("entry found in %s\n", obj->zo_domain)); + + /* we must create new stub object with EN_MODIFIED flag. + this is because obj from result is going to be freed and + we do not want to break it or cause memory leaks or corruption. + */ + + memmove((char *)&new_obj, obj, sizeof (new_obj)); + ta_maxcol = obj->TA_data.ta_maxcol; + + if(!(ecol = (entry_col*)malloc(ta_maxcol*sizeof(entry_col)))) { + DEBUG(0, ("memory allocation failure\n")); + nis_freeresult(result); + return False; + } + + memmove((char *)ecol, obj->EN_data.en_cols.en_cols_val, + ta_maxcol*sizeof (entry_col)); + new_obj.EN_data.en_cols.en_cols_val = ecol; + new_obj.EN_data.en_cols.en_cols_len = ta_maxcol; + + if ( init_nisp_from_sam(&new_obj, newpwd, obj) == True ) { + slprintf(nisname, sizeof(nisname)-1, "[name=%s],%s.%s", + pdb_get_username(newpwd), pfile, obj->zo_domain); + + DEBUG(10, ("NIS+ table update: %s\n", nisname)); + addresult = + nis_modify_entry(nisname, &new_obj, + MOD_SAMEOBJ | FOLLOW_PATH | EXPAND_NAME | HARD_LOOKUP); + + if(addresult->status != NIS_SUCCESS) { + DEBUG(0, ("NIS+ table update failed: %s %s\n", + nisname, nis_sperrno(addresult->status))); + nis_freeresult(addresult); + nis_freeresult(result); + free(ecol); + return False; + } + + DEBUG(6,("password changed\n")); + nis_freeresult(addresult); + } else { + DEBUG(6,("nothing to change!\n")); + } + + free(ecol); + nis_freeresult(result); + + return True; +} + +#else + void nisplus_dummy_function(void); + void nisplus_dummy_function(void) { } /* stop some compilers complaining */ +#endif /* WITH_NISPLUSSAM */ + diff --git a/source/passdb/pdb_smbpasswd.c b/source/passdb/pdb_smbpasswd.c new file mode 100644 index 00000000000..9cfad2540c4 --- /dev/null +++ b/source/passdb/pdb_smbpasswd.c @@ -0,0 +1,1545 @@ +/* + * Unix SMB/Netbios implementation. + * Version 1.9. SMB parameters and setup + * Copyright (C) Andrew Tridgell 1992-1998 + * Modified by Jeremy Allison 1995. + * Modified by Gerald (Jerry) Carter 2000-2001 + * + * 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" + +#ifdef WITH_SMBPASSWD_SAM + + +/* + smb_passwd is analogous to sam_passwd used everywhere + else. However, smb_passwd is limited to the information + stored by an smbpasswd entry + */ + +struct smb_passwd +{ + uid_t smb_userid; /* this is actually the unix uid_t */ + const char *smb_name; /* username string */ + + const unsigned char *smb_passwd; /* Null if no password */ + const unsigned char *smb_nt_passwd; /* Null if no password */ + + uint16 acct_ctrl; /* account info (ACB_xxxx bit-mask) */ + time_t pass_last_set_time; /* password last set time */ +}; + + +extern struct passdb_ops pdb_ops; + +/* used for maintain locks on the smbpasswd file */ +static int pw_file_lock_depth; +static void *global_vp; + + +enum pwf_access_type { PWF_READ, PWF_UPDATE, PWF_CREATE }; + +/*************************************************************** + Lock an fd. Abandon after waitsecs seconds. +****************************************************************/ + +static BOOL pw_file_lock(int fd, int type, int secs, int *plock_depth) +{ + if (fd < 0) + return False; + + if(*plock_depth == 0) { + if (!do_file_lock(fd, secs, type)) { + DEBUG(10,("pw_file_lock: locking file failed, error = %s.\n", + strerror(errno))); + return False; + } + } + + (*plock_depth)++; + + return True; +} + +/*************************************************************** + Unlock an fd. Abandon after waitsecs seconds. +****************************************************************/ + +static BOOL pw_file_unlock(int fd, int *plock_depth) +{ + BOOL ret=True; + + if(*plock_depth == 1) + ret = do_file_lock(fd, 5, F_UNLCK); + + if (*plock_depth > 0) + (*plock_depth)--; + + if(!ret) + DEBUG(10,("pw_file_unlock: unlocking file failed, error = %s.\n", + strerror(errno))); + return ret; +} + + +/************************************************************** + Intialize a smb_passwd struct + *************************************************************/ + +static void pdb_init_smb(struct smb_passwd *user) +{ + if (user == NULL) + return; + ZERO_STRUCTP (user); + + user->pass_last_set_time = (time_t)0; +} + +/*************************************************************** + Internal fn to enumerate the smbpasswd list. Returns a void pointer + to ensure no modification outside this module. Checks for atomic + rename of smbpasswd file on update or create once the lock has + been granted to prevent race conditions. JRA. +****************************************************************/ + +static void *startsmbfilepwent(const char *pfile, enum pwf_access_type type, int *lock_depth) +{ + FILE *fp = NULL; + const char *open_mode = NULL; + int race_loop = 0; + int lock_type = F_RDLCK; + + if (!*pfile) { + DEBUG(0, ("startsmbfilepwent: No SMB password file set\n")); + return (NULL); + } + + switch(type) { + case PWF_READ: + open_mode = "rb"; + lock_type = F_RDLCK; + break; + case PWF_UPDATE: + open_mode = "r+b"; + lock_type = F_WRLCK; + break; + case PWF_CREATE: + /* + * Ensure atomic file creation. + */ + { + int i, fd = -1; + + for(i = 0; i < 5; i++) { + if((fd = sys_open(pfile, O_CREAT|O_TRUNC|O_EXCL|O_RDWR, 0600))!=-1) + break; + sys_usleep(200); /* Spin, spin... */ + } + if(fd == -1) { + DEBUG(0,("startsmbfilepwent_internal: too many race conditions creating file %s\n", pfile)); + return NULL; + } + close(fd); + open_mode = "r+b"; + lock_type = F_WRLCK; + break; + } + } + + for(race_loop = 0; race_loop < 5; race_loop++) { + DEBUG(10, ("startsmbfilepwent_internal: opening file %s\n", pfile)); + + if((fp = sys_fopen(pfile, open_mode)) == NULL) { + DEBUG(2, ("startsmbfilepwent_internal: unable to open file %s. Error was %s\n", pfile, strerror(errno) )); + return NULL; + } + + if (!pw_file_lock(fileno(fp), lock_type, 5, lock_depth)) { + DEBUG(0, ("startsmbfilepwent_internal: unable to lock file %s. Error was %s\n", pfile, strerror(errno) )); + fclose(fp); + return NULL; + } + + /* + * Only check for replacement races on update or create. + * For read we don't mind if the data is one record out of date. + */ + + if(type == PWF_READ) { + break; + } else { + SMB_STRUCT_STAT sbuf1, sbuf2; + + /* + * Avoid the potential race condition between the open and the lock + * by doing a stat on the filename and an fstat on the fd. If the + * two inodes differ then someone did a rename between the open and + * the lock. Back off and try the open again. Only do this 5 times to + * prevent infinate loops. JRA. + */ + + if (sys_stat(pfile,&sbuf1) != 0) { + DEBUG(0, ("startsmbfilepwent_internal: unable to stat file %s. Error was %s\n", pfile, strerror(errno))); + pw_file_unlock(fileno(fp), lock_depth); + fclose(fp); + return NULL; + } + + if (sys_fstat(fileno(fp),&sbuf2) != 0) { + DEBUG(0, ("startsmbfilepwent_internal: unable to fstat file %s. Error was %s\n", pfile, strerror(errno))); + pw_file_unlock(fileno(fp), lock_depth); + fclose(fp); + return NULL; + } + + if( sbuf1.st_ino == sbuf2.st_ino) { + /* No race. */ + break; + } + + /* + * Race occurred - back off and try again... + */ + + pw_file_unlock(fileno(fp), lock_depth); + fclose(fp); + } + } + + if(race_loop == 5) { + DEBUG(0, ("startsmbfilepwent_internal: too many race conditions opening file %s\n", pfile)); + return NULL; + } + + /* Set a buffer to do more efficient reads */ + setvbuf(fp, (char *)NULL, _IOFBF, 1024); + + /* Make sure it is only rw by the owner */ + if(fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) { + DEBUG(0, ("startsmbfilepwent_internal: failed to set 0600 permissions on password file %s. \ +Error was %s\n.", pfile, strerror(errno) )); + pw_file_unlock(fileno(fp), lock_depth); + fclose(fp); + return NULL; + } + + /* We have a lock on the file. */ + return (void *)fp; +} + +/*************************************************************** + End enumeration of the smbpasswd list. +****************************************************************/ +static void endsmbfilepwent(void *vp, int *lock_depth) +{ + FILE *fp = (FILE *)vp; + + pw_file_unlock(fileno(fp), lock_depth); + fclose(fp); + DEBUG(7, ("endsmbfilepwent_internal: closed password file.\n")); +} + +/************************************************************************* + Routine to return the next entry in the smbpasswd list. + *************************************************************************/ + +static struct smb_passwd *getsmbfilepwent(void *vp) +{ + /* Static buffers we will return. */ + static struct smb_passwd pw_buf; + static pstring user_name; + static unsigned char smbpwd[16]; + static unsigned char smbntpwd[16]; + FILE *fp = (FILE *)vp; + char linebuf[256]; + unsigned char c; + unsigned char *p; + long uidval; + size_t linebuf_len; + + if(fp == NULL) { + DEBUG(0,("getsmbfilepwent: Bad password file pointer.\n")); + return NULL; + } + + pdb_init_smb(&pw_buf); + + pw_buf.acct_ctrl = ACB_NORMAL; + + /* + * Scan the file, a line at a time and check if the name matches. + */ + while (!feof(fp)) { + linebuf[0] = '\0'; + + fgets(linebuf, 256, fp); + if (ferror(fp)) { + return NULL; + } + + /* + * Check if the string is terminated with a newline - if not + * then we must keep reading and discard until we get one. + */ + if ((linebuf_len = strlen(linebuf)) == 0) + continue; + + if (linebuf[linebuf_len - 1] != '\n') { + c = '\0'; + while (!ferror(fp) && !feof(fp)) { + c = fgetc(fp); + if (c == '\n') + break; + } + } else + linebuf[linebuf_len - 1] = '\0'; + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("getsmbfilepwent: got line |%s|\n", linebuf)); +#endif + if ((linebuf[0] == 0) && feof(fp)) { + DEBUG(4, ("getsmbfilepwent: end of file reached\n")); + break; + } + /* + * The line we have should be of the form :- + * + * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently + * ignored.... + * + * or, + * + * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored.... + * + * if Windows NT compatible passwords are also present. + * [Account type] is an ascii encoding of the type of account. + * LCT-(8 hex digits) is the time_t value of the last change time. + */ + + if (linebuf[0] == '#' || linebuf[0] == '\0') { + DEBUG(6, ("getsmbfilepwent: skipping comment or blank line\n")); + continue; + } + p = (unsigned char *) strchr_m(linebuf, ':'); + if (p == NULL) { + DEBUG(0, ("getsmbfilepwent: malformed password entry (no :)\n")); + continue; + } + /* + * As 256 is shorter than a pstring we don't need to check + * length here - if this ever changes.... + */ + strncpy(user_name, linebuf, PTR_DIFF(p, linebuf)); + user_name[PTR_DIFF(p, linebuf)] = '\0'; + + /* Get smb uid. */ + + p++; /* Go past ':' */ + + if(*p == '-') { + DEBUG(0, ("getsmbfilepwent: uids in the smbpasswd file must not be negative.\n")); + continue; + } + + if (!isdigit(*p)) { + DEBUG(0, ("getsmbfilepwent: malformed password entry (uid not number)\n")); + continue; + } + + uidval = atoi((char *) p); + + while (*p && isdigit(*p)) + p++; + + if (*p != ':') { + DEBUG(0, ("getsmbfilepwent: malformed password entry (no : after uid)\n")); + continue; + } + + pw_buf.smb_name = user_name; + pw_buf.smb_userid = uidval; + + /* + * Now get the password value - this should be 32 hex digits + * which are the ascii representations of a 16 byte string. + * Get two at a time and put them into the password. + */ + + /* Skip the ':' */ + p++; + + if (*p == '*' || *p == 'X') { + /* Password deliberately invalid - end here. */ + DEBUG(10, ("getsmbfilepwent: entry invalidated for user %s\n", user_name)); + pw_buf.smb_nt_passwd = NULL; + pw_buf.smb_passwd = NULL; + pw_buf.acct_ctrl |= ACB_DISABLED; + return &pw_buf; + } + + if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) { + DEBUG(0, ("getsmbfilepwent: malformed password entry (passwd too short)\n")); + continue; + } + + if (p[32] != ':') { + DEBUG(0, ("getsmbfilepwent: malformed password entry (no terminating :)\n")); + continue; + } + + if (!strncasecmp((char *) p, "NO PASSWORD", 11)) { + pw_buf.smb_passwd = NULL; + pw_buf.acct_ctrl |= ACB_PWNOTREQ; + } else { + if (!pdb_gethexpwd((char *)p, smbpwd)) { + DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry (non hex chars)\n")); + continue; + } + pw_buf.smb_passwd = smbpwd; + } + + /* + * Now check if the NT compatible password is + * available. + */ + pw_buf.smb_nt_passwd = NULL; + + p += 33; /* Move to the first character of the line after + the lanman password. */ + if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) { + if (*p != '*' && *p != 'X') { + if(pdb_gethexpwd((char *)p,smbntpwd)) + pw_buf.smb_nt_passwd = smbntpwd; + } + p += 33; /* Move to the first character of the line after + the NT password. */ + } + + DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n", + user_name, uidval)); + + if (*p == '[') + { + unsigned char *end_p = (unsigned char *)strchr_m((char *)p, ']'); + pw_buf.acct_ctrl = pdb_decode_acct_ctrl((char*)p); + + /* Must have some account type set. */ + if(pw_buf.acct_ctrl == 0) + pw_buf.acct_ctrl = ACB_NORMAL; + + /* Now try and get the last change time. */ + if(end_p) + p = end_p + 1; + if(*p == ':') { + p++; + if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) { + int i; + p += 4; + for(i = 0; i < 8; i++) { + if(p[i] == '\0' || !isxdigit(p[i])) + break; + } + if(i == 8) { + /* + * p points at 8 characters of hex digits - + * read into a time_t as the seconds since + * 1970 that the password was last changed. + */ + pw_buf.pass_last_set_time = (time_t)strtol((char *)p, NULL, 16); + } + } + } + } else { + /* 'Old' style file. Fake up based on user name. */ + /* + * Currently trust accounts are kept in the same + * password file as 'normal accounts'. If this changes + * we will have to fix this code. JRA. + */ + if(pw_buf.smb_name[strlen(pw_buf.smb_name) - 1] == '$') { + pw_buf.acct_ctrl &= ~ACB_NORMAL; + pw_buf.acct_ctrl |= ACB_WSTRUST; + } + } + + return &pw_buf; + } + + DEBUG(5,("getsmbfilepwent: end of file reached.\n")); + return NULL; +} + +/************************************************************************ + Create a new smbpasswd entry - malloced space returned. +*************************************************************************/ + +static char *format_new_smbpasswd_entry(const struct smb_passwd *newpwd) +{ + int new_entry_length; + char *new_entry; + char *p; + int i; + + new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2; + + if((new_entry = (char *)malloc( new_entry_length )) == NULL) { + DEBUG(0, ("format_new_smbpasswd_entry: Malloc failed adding entry for user %s.\n", newpwd->smb_name )); + return NULL; + } + + slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid); + p = &new_entry[strlen(new_entry)]; + + if(newpwd->smb_passwd != NULL) { + for( i = 0; i < 16; i++) { + slprintf((char *)&p[i*2], new_entry_length - (p - new_entry) - 1, "%02X", newpwd->smb_passwd[i]); + } + } else { + i=0; + if(newpwd->acct_ctrl & ACB_PWNOTREQ) + safe_strcpy((char *)p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry)); + else + safe_strcpy((char *)p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry)); + } + + p += 32; + + *p++ = ':'; + + if(newpwd->smb_nt_passwd != NULL) { + for( i = 0; i < 16; i++) { + slprintf((char *)&p[i*2], new_entry_length - 1 - (p - new_entry), "%02X", newpwd->smb_nt_passwd[i]); + } + } else { + if(newpwd->acct_ctrl & ACB_PWNOTREQ) + safe_strcpy((char *)p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry)); + else + safe_strcpy((char *)p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry)); + } + + p += 32; + + *p++ = ':'; + + /* Add the account encoding and the last change time. */ + slprintf((char *)p, new_entry_length - 1 - (p - new_entry), "%s:LCT-%08X:\n", + pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN), + (uint32)newpwd->pass_last_set_time); + + return new_entry; +} + +/************************************************************************ + Routine to add an entry to the smbpasswd file. +*************************************************************************/ + +static BOOL add_smbfilepwd_entry(const struct smb_passwd *newpwd) +{ + char *pfile = lp_smb_passwd_file(); + struct smb_passwd *pwd = NULL; + FILE *fp = NULL; + int wr_len; + int fd; + size_t new_entry_length; + char *new_entry; + SMB_OFF_T offpos; + + /* Open the smbpassword file - for update. */ + fp = startsmbfilepwent(pfile, PWF_UPDATE, &pw_file_lock_depth); + + if (fp == NULL && errno == ENOENT) { + /* Try again - create. */ + fp = startsmbfilepwent(pfile, PWF_CREATE, &pw_file_lock_depth); + } + + if (fp == NULL) { + DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n")); + return False; + } + + /* + * Scan the file, a line at a time and check if the name matches. + */ + + while ((pwd = getsmbfilepwent(fp)) != NULL) + { + if (strequal(newpwd->smb_name, pwd->smb_name)) + { + DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name)); + endsmbfilepwent(fp, &pw_file_lock_depth); + return False; + } + } + + /* Ok - entry doesn't exist. We can add it */ + + /* Create a new smb passwd entry and set it to the given password. */ + /* + * The add user write needs to be atomic - so get the fd from + * the fp and do a raw write() call. + */ + fd = fileno(fp); + + if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1) + { + DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \ +Error was %s\n", newpwd->smb_name, pfile, strerror(errno))); + endsmbfilepwent(fp, &pw_file_lock_depth); + return False; + } + + if((new_entry = format_new_smbpasswd_entry(newpwd)) == NULL) + { + DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \ +Error was %s\n", newpwd->smb_name, pfile, strerror(errno))); + endsmbfilepwent(fp, &pw_file_lock_depth); + return False; + } + + new_entry_length = strlen(new_entry); + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d made line |%s|", + fd, new_entry_length, new_entry)); +#endif + + if ((wr_len = write(fd, new_entry, new_entry_length)) != new_entry_length) + { + DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \ +Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno))); + + /* Remove the entry we just wrote. */ + if(sys_ftruncate(fd, offpos) == -1) + { + DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \ +Error was %s. Password file may be corrupt ! Please examine by hand !\n", + newpwd->smb_name, strerror(errno))); + } + + endsmbfilepwent(fp, &pw_file_lock_depth); + free(new_entry); + return False; + } + + free(new_entry); + endsmbfilepwent(fp, &pw_file_lock_depth); + return True; +} + +/************************************************************************ + Routine to search the smbpasswd file for an entry matching the username. + and then modify its password entry. We can't use the startsmbpwent()/ + getsmbpwent()/endsmbpwent() interfaces here as we depend on looking + in the actual file to decide how much room we have to write data. + override = False, normal + override = True, override XXXXXXXX'd out password or NO PASS +************************************************************************/ + +static BOOL mod_smbfilepwd_entry(const struct smb_passwd* pwd, BOOL override) +{ + /* Static buffers we will return. */ + static pstring user_name; + + char linebuf[256]; + char readbuf[1024]; + unsigned char c; + fstring ascii_p16; + fstring encode_bits; + unsigned char *p = NULL; + size_t linebuf_len = 0; + FILE *fp; + int lockfd; + char *pfile = lp_smb_passwd_file(); + BOOL found_entry = False; + BOOL got_pass_last_set_time = False; + + SMB_OFF_T pwd_seekpos = 0; + + int i; + int wr_len; + int fd; + + if (!*pfile) { + DEBUG(0, ("No SMB password file set\n")); + return False; + } + DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile)); + + fp = sys_fopen(pfile, "r+"); + + if (fp == NULL) { + DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile)); + return False; + } + /* Set a buffer to do more efficient reads */ + setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf)); + + lockfd = fileno(fp); + + if (!pw_file_lock(lockfd, F_WRLCK, 5, &pw_file_lock_depth)) { + DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile)); + fclose(fp); + return False; + } + + /* Make sure it is only rw by the owner */ + chmod(pfile, 0600); + + /* We have a write lock on the file. */ + /* + * Scan the file, a line at a time and check if the name matches. + */ + while (!feof(fp)) { + pwd_seekpos = sys_ftell(fp); + + linebuf[0] = '\0'; + + fgets(linebuf, sizeof(linebuf), fp); + if (ferror(fp)) { + pw_file_unlock(lockfd, &pw_file_lock_depth); + fclose(fp); + return False; + } + + /* + * Check if the string is terminated with a newline - if not + * then we must keep reading and discard until we get one. + */ + linebuf_len = strlen(linebuf); + if (linebuf[linebuf_len - 1] != '\n') { + c = '\0'; + while (!ferror(fp) && !feof(fp)) { + c = fgetc(fp); + if (c == '\n') { + break; + } + } + } else { + linebuf[linebuf_len - 1] = '\0'; + } + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf)); +#endif + + if ((linebuf[0] == 0) && feof(fp)) { + DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n")); + break; + } + + /* + * The line we have should be of the form :- + * + * username:uid:[32hex bytes]:....other flags presently + * ignored.... + * + * or, + * + * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored. + * + * if Windows NT compatible passwords are also present. + */ + + if (linebuf[0] == '#' || linebuf[0] == '\0') { + DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n")); + continue; + } + + p = (unsigned char *) strchr_m(linebuf, ':'); + + if (p == NULL) { + DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n")); + continue; + } + + /* + * As 256 is shorter than a pstring we don't need to check + * length here - if this ever changes.... + */ + strncpy(user_name, linebuf, PTR_DIFF(p, linebuf)); + user_name[PTR_DIFF(p, linebuf)] = '\0'; + if (strequal(user_name, pwd->smb_name)) { + found_entry = True; + break; + } + } + + if (!found_entry) { + pw_file_unlock(lockfd, &pw_file_lock_depth); + fclose(fp); + return False; + } + + DEBUG(6, ("mod_smbfilepwd_entry: entry exists\n")); + + /* User name matches - get uid and password */ + p++; /* Go past ':' */ + + if (!isdigit(*p)) { + DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (uid not number)\n")); + pw_file_unlock(lockfd, &pw_file_lock_depth); + fclose(fp); + return False; + } + + while (*p && isdigit(*p)) + p++; + if (*p != ':') { + DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no : after uid)\n")); + pw_file_unlock(lockfd, &pw_file_lock_depth); + fclose(fp); + return False; + } + + /* + * Now get the password value - this should be 32 hex digits + * which are the ascii representations of a 16 byte string. + * Get two at a time and put them into the password. + */ + p++; + + /* Record exact password position */ + pwd_seekpos += PTR_DIFF(p, linebuf); + + if (!override && (*p == '*' || *p == 'X')) { + /* Password deliberately invalid - end here. */ + DEBUG(10, ("mod_smbfilepwd_entry: entry invalidated for user %s\n", user_name)); + pw_file_unlock(lockfd, &pw_file_lock_depth); + fclose(fp); + return False; + } + + if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) { + DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n")); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return (False); + } + + if (p[32] != ':') { + DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n")); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return False; + } + + if (!override && (*p == '*' || *p == 'X')) { + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return False; + } + + /* Now check if the NT compatible password is + available. */ + p += 33; /* Move to the first character of the line after + the lanman password. */ + if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) { + DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n")); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return (False); + } + + if (p[32] != ':') { + DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n")); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return False; + } + + /* + * Now check if the account info and the password last + * change time is available. + */ + p += 33; /* Move to the first character of the line after + the NT password. */ + + if (*p == '[') { + + i = 0; + encode_bits[i++] = *p++; + while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']')) + encode_bits[i++] = *p++; + + encode_bits[i++] = ']'; + encode_bits[i++] = '\0'; + + if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) { + /* + * We are using a new format, space padded + * acct ctrl field. Encode the given acct ctrl + * bits into it. + */ + fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN)); + } else { + DEBUG(0,("mod_smbfilepwd_entry: Using old smbpasswd format. This is no longer supported.!\n")); + DEBUG(0,("mod_smbfilepwd_entry: No changes made, failing.!\n")); + return False; + } + + /* Go past the ']' */ + if(linebuf_len > PTR_DIFF(p, linebuf)) + p++; + + if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) { + p++; + + /* We should be pointing at the LCT entry. */ + if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) { + + p += 4; + for(i = 0; i < 8; i++) { + if(p[i] == '\0' || !isxdigit(p[i])) + break; + } + if(i == 8) { + /* + * p points at 8 characters of hex digits - + * read into a time_t as the seconds since + * 1970 that the password was last changed. + */ + got_pass_last_set_time = True; + } /* i == 8 */ + } /* *p && StrnCaseCmp() */ + } /* p == ':' */ + } /* p == '[' */ + + /* Entry is correctly formed. */ + + /* Create the 32 byte representation of the new p16 */ + if(pwd->smb_passwd != NULL) { + for (i = 0; i < 16; i++) { + slprintf(&ascii_p16[i*2], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_passwd[i]); + } + } else { + if(pwd->acct_ctrl & ACB_PWNOTREQ) + fstrcpy(ascii_p16, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX"); + else + fstrcpy(ascii_p16, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); + } + + /* Add on the NT md4 hash */ + ascii_p16[32] = ':'; + wr_len = 66; + if (pwd->smb_nt_passwd != NULL) { + for (i = 0; i < 16; i++) { + slprintf(&ascii_p16[(i*2)+33], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_nt_passwd[i]); + } + } else { + if(pwd->acct_ctrl & ACB_PWNOTREQ) + fstrcpy(&ascii_p16[33], "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX"); + else + fstrcpy(&ascii_p16[33], "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); + } + ascii_p16[65] = ':'; + ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */ + + /* Add on the account info bits and the time of last + password change. */ + + if(got_pass_last_set_time) { + slprintf(&ascii_p16[strlen(ascii_p16)], + sizeof(ascii_p16)-(strlen(ascii_p16)+1), + "%s:LCT-%08X:", + encode_bits, (uint32)pwd->pass_last_set_time ); + wr_len = strlen(ascii_p16); + } + +#ifdef DEBUG_PASSWORD + DEBUG(100,("mod_smbfilepwd_entry: ")); + dump_data(100, ascii_p16, wr_len); +#endif + + if(wr_len > sizeof(linebuf)) { + DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1)); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return (False); + } + + /* + * Do an atomic write into the file at the position defined by + * seekpos. + */ + + /* The mod user write needs to be atomic - so get the fd from + the fp and do a raw write() call. + */ + + fd = fileno(fp); + + if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) { + DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile)); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return False; + } + + /* Sanity check - ensure the areas we are writing are framed by ':' */ + if (read(fd, linebuf, wr_len+1) != wr_len+1) { + DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile)); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return False; + } + + if ((linebuf[0] != ':') || (linebuf[wr_len] != ':')) { + DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile)); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return False; + } + + if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) { + DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile)); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return False; + } + + if (write(fd, ascii_p16, wr_len) != wr_len) { + DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile)); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return False; + } + + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return True; +} + +/************************************************************************ + Routine to delete an entry in the smbpasswd file by name. +*************************************************************************/ + +static BOOL del_smbfilepwd_entry(const char *name) +{ + char *pfile = lp_smb_passwd_file(); + pstring pfile2; + struct smb_passwd *pwd = NULL; + FILE *fp = NULL; + FILE *fp_write = NULL; + int pfile2_lockdepth = 0; + + slprintf(pfile2, sizeof(pfile2)-1, "%s.%u", pfile, (unsigned)sys_getpid() ); + + /* + * Open the smbpassword file - for update. It needs to be update + * as we need any other processes to wait until we have replaced + * it. + */ + + if((fp = startsmbfilepwent(pfile, PWF_UPDATE, &pw_file_lock_depth)) == NULL) { + DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile)); + return False; + } + + /* + * Create the replacement password file. + */ + if((fp_write = startsmbfilepwent(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) { + DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile)); + endsmbfilepwent(fp, &pw_file_lock_depth); + return False; + } + + /* + * Scan the file, a line at a time and check if the name matches. + */ + + while ((pwd = getsmbfilepwent(fp)) != NULL) { + char *new_entry; + size_t new_entry_length; + + if (strequal(name, pwd->smb_name)) { + DEBUG(10, ("add_smbfilepwd_entry: found entry with name %s - deleting it.\n", name)); + continue; + } + + /* + * We need to copy the entry out into the second file. + */ + + if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL) + { + DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \ +Error was %s\n", pwd->smb_name, pfile2, strerror(errno))); + unlink(pfile2); + endsmbfilepwent(fp, &pw_file_lock_depth); + endsmbfilepwent(fp_write, &pfile2_lockdepth); + return False; + } + + new_entry_length = strlen(new_entry); + + if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length) + { + DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \ +Error was %s\n", pwd->smb_name, pfile2, strerror(errno))); + unlink(pfile2); + endsmbfilepwent(fp, &pw_file_lock_depth); + endsmbfilepwent(fp_write, &pfile2_lockdepth); + free(new_entry); + return False; + } + + free(new_entry); + } + + /* + * Ensure pfile2 is flushed before rename. + */ + + if(fflush(fp_write) != 0) + { + DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno))); + endsmbfilepwent(fp, &pw_file_lock_depth); + endsmbfilepwent(fp_write,&pfile2_lockdepth); + return False; + } + + /* + * Do an atomic rename - then release the locks. + */ + + if(rename(pfile2,pfile) != 0) { + unlink(pfile2); + } + + endsmbfilepwent(fp, &pw_file_lock_depth); + endsmbfilepwent(fp_write,&pfile2_lockdepth); + return True; +} + +/********************************************************************* + Create a smb_passwd struct from a SAM_ACCOUNT. + We will not allocate any new memory. The smb_passwd struct + should only stay around as long as the SAM_ACCOUNT does. + ********************************************************************/ +static BOOL build_smb_pass (struct smb_passwd *smb_pw, const SAM_ACCOUNT *sampass) +{ + uid_t *uid; + gid_t *gid; + + if (sampass == NULL) + return False; + uid = pdb_get_uid(sampass); + gid = pdb_get_gid(sampass); + + if (!uid || !gid) { + DEBUG(0,("build_sam_pass: Failing attempt to store user without a UNIX uid or gid. \n")); + return False; + } + + ZERO_STRUCTP(smb_pw); + + smb_pw->smb_userid=*uid; + smb_pw->smb_name=pdb_get_username(sampass); + + smb_pw->smb_passwd=pdb_get_lanman_passwd(sampass); + smb_pw->smb_nt_passwd=pdb_get_nt_passwd(sampass); + + smb_pw->acct_ctrl=pdb_get_acct_ctrl(sampass); + smb_pw->pass_last_set_time=pdb_get_pass_last_set_time(sampass); + + if (*uid != pdb_user_rid_to_uid(pdb_get_user_rid(sampass))) { + DEBUG(0,("build_sam_pass: Failing attempt to store user with non-uid based user RID. \n")); + return False; + } + +#if 0 + /* + * ifdef'out by JFM on 11/29/2001. + * this assertion is no longer valid + * and I don't understand the goal + * and doing the same thing with the group mapping code + * is hairy ! + * + * We just have the RID, in which SID is it valid ? + * our domain SID ? well known SID ? local SID ? + */ + + if (*gid != pdb_group_rid_to_gid(pdb_get_group_rid(sampass))) { + DEBUG(0,("build_sam_pass: Failing attempt to store user with non-gid based primary group RID. \n")); + DEBUG(0,("build_sam_pass: %d %d %d. \n", *gid, pdb_group_rid_to_gid(pdb_get_group_rid(sampass)), pdb_get_group_rid(sampass))); + return False; + } +#endif + + return True; +} + +/********************************************************************* + Create a SAM_ACCOUNT from a smb_passwd struct + ********************************************************************/ +static BOOL build_sam_account(SAM_ACCOUNT *sam_pass, const struct smb_passwd *pw_buf) +{ + struct passwd *pwfile; + + if (sam_pass==NULL) { + DEBUG(5,("build_sam_account: SAM_ACCOUNT is NULL\n")); + return False; + } + + /* Verify in system password file... + FIXME!!! This is where we should look up an internal + mapping of allocated uid for machine accounts as well + --jerry */ + pwfile = sys_getpwnam(pw_buf->smb_name); + if (pwfile == NULL) { + DEBUG(0,("build_sam_account: smbpasswd database is corrupt! username %s not in unix passwd database!\n", pw_buf->smb_name)); + return False; + } + + pdb_set_uid (sam_pass, &pwfile->pw_uid); + pdb_set_gid (sam_pass, &pwfile->pw_gid); + + pdb_set_fullname(sam_pass, pwfile->pw_gecos); + + pdb_set_user_rid(sam_pass, pdb_uid_to_user_rid (pwfile->pw_uid)); + + { + uint32 rid; + GROUP_MAP map; + + if (get_group_map_from_gid(pwfile->pw_gid, &map, MAPPING_WITHOUT_PRIV)) { + sid_peek_rid(&map.sid, &rid); + } + else + rid=pdb_gid_to_group_rid(pwfile->pw_gid); + + pdb_set_group_rid(sam_pass, rid); + } + + pdb_set_username (sam_pass, pw_buf->smb_name); + pdb_set_nt_passwd (sam_pass, pw_buf->smb_nt_passwd); + pdb_set_lanman_passwd (sam_pass, pw_buf->smb_passwd); + pdb_set_acct_ctrl (sam_pass, pw_buf->acct_ctrl); + pdb_set_pass_last_set_time (sam_pass, pw_buf->pass_last_set_time); + pdb_set_pass_can_change_time (sam_pass, pw_buf->pass_last_set_time); + pdb_set_domain (sam_pass, lp_workgroup()); + + pdb_set_dir_drive (sam_pass, lp_logon_drive()); + + /* the smbpasswd format doesn't have a must change time field, so + we can't get this right. The best we can do is to set this to + some time in the future. 21 days seems as reasonable as any other value :) + */ + pdb_set_pass_must_change_time (sam_pass, pw_buf->pass_last_set_time + MAX_PASSWORD_AGE); + + /* check if this is a user account or a machine account */ + if (pw_buf->smb_name[strlen(pw_buf->smb_name)-1] != '$') + { + pstring str; + + pstrcpy(str, lp_logon_path()); + standard_sub_advanced(-1, pwfile->pw_name, "", pwfile->pw_gid, pw_buf->smb_name, str); + pdb_set_profile_path(sam_pass, str); + + pstrcpy(str, lp_logon_home()); + standard_sub_advanced(-1, pwfile->pw_name, "", pwfile->pw_gid, pw_buf->smb_name, str); + pdb_set_homedir(sam_pass, str); + + pstrcpy(str, lp_logon_drive()); + standard_sub_advanced(-1, pwfile->pw_name, "", pwfile->pw_gid, pw_buf->smb_name, str); + pdb_set_dir_drive(sam_pass, str); + + pstrcpy(str, lp_logon_script()); + standard_sub_advanced(-1, pwfile->pw_name, "", pwfile->pw_gid, pw_buf->smb_name, str); + pdb_set_logon_script(sam_pass, str); + + } else { + /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. */ + /*pdb_set_group_rid (sam_pass, DOMAIN_GROUP_RID_USERS); */ + } + + return True; +} +/***************************************************************** + Functions to be implemented by the new passdb API + ****************************************************************/ +BOOL pdb_setsampwent (BOOL update) +{ + global_vp = startsmbfilepwent(lp_smb_passwd_file(), + update ? PWF_UPDATE : PWF_READ, + &pw_file_lock_depth); + + /* did we fail? Should we try to create it? */ + if (!global_vp && update && errno == ENOENT) + { + FILE *fp; + /* slprintf(msg_str,msg_str_len-1, + "smbpasswd file did not exist - attempting to create it.\n"); */ + DEBUG(0,("smbpasswd file did not exist - attempting to create it.\n")); + fp = sys_fopen(lp_smb_passwd_file(), "w"); + if (fp) + { + fprintf(fp, "# Samba SMB password file\n"); + fclose(fp); + } + + global_vp = startsmbfilepwent(lp_smb_passwd_file(), + update ? PWF_UPDATE : PWF_READ, + &pw_file_lock_depth); + } + + return (global_vp != NULL); +} + +void pdb_endsampwent (void) +{ + endsmbfilepwent(global_vp, &pw_file_lock_depth); +} + +/***************************************************************** + ****************************************************************/ +BOOL pdb_getsampwent(SAM_ACCOUNT *user) +{ + struct smb_passwd *pw_buf=NULL; + BOOL done = False; + DEBUG(5,("pdb_getsampwent\n")); + + if (user==NULL) { + DEBUG(5,("pdb_getsampwent: user is NULL\n")); +#if 0 + smb_panic("NULL pointer passed to pdb_getsampwent\n"); +#endif + return False; + } + + while (!done) + { + /* do we have an entry? */ + pw_buf = getsmbfilepwent(global_vp); + if (pw_buf == NULL) + return False; + + /* build the SAM_ACCOUNT entry from the smb_passwd struct. + We loop in case the user in the pdb does not exist in + the local system password file */ + if (build_sam_account(user, pw_buf)) + done = True; + } + + DEBUG(5,("pdb_getsampwent:done\n")); + + /* success */ + return True; +} + + +/**************************************************************** + Search smbpasswd file by iterating over the entries. Do not + call getpwnam() for unix account information until we have found + the correct entry + ***************************************************************/ +BOOL pdb_getsampwnam(SAM_ACCOUNT *sam_acct, const char *username) +{ + struct smb_passwd *smb_pw; + void *fp = NULL; + char *domain = NULL; + char *user = NULL; + fstring name; + + DEBUG(10, ("pdb_getsampwnam: search by name: %s\n", username)); + + + /* break the username from the domain if we have + been given a string in the form 'DOMAIN\user' */ + fstrcpy (name, username); + if ((user=strchr_m(name, '\\')) != NULL) { + domain = name; + *user = '\0'; + user++; + } + + /* if a domain was specified and it wasn't ours + then there is no chance of matching */ + if ( domain && !StrCaseCmp(domain, lp_workgroup()) ) + return False; + + /* startsmbfilepwent() is used here as we don't want to lookup + the UNIX account in the local system password file until + we have a match. */ + fp = startsmbfilepwent(lp_smb_passwd_file(), PWF_READ, &pw_file_lock_depth); + + if (fp == NULL) { + DEBUG(0, ("unable to open passdb database.\n")); + return False; + } + + /* if we have a domain name, then we should map it to a UNIX + username first */ + if ( domain ) + map_username(user); + + while ( ((smb_pw=getsmbfilepwent(fp)) != NULL)&& (!strequal(smb_pw->smb_name, username)) ) + /* do nothing....another loop */ ; + + endsmbfilepwent(fp, &pw_file_lock_depth); + + + /* did we locate the username in smbpasswd */ + if (smb_pw == NULL) + return False; + + DEBUG(10, ("pdb_getsampwnam: found by name: %s\n", smb_pw->smb_name)); + + if (!sam_acct) { + DEBUG(10,("pdb_getsampwnam:SAM_ACCOUNT is NULL\n")); +#if 0 + smb_panic("NULL pointer passed to pdb_getsampwnam\n"); +#endif + return False; + } + + /* now build the SAM_ACCOUNT */ + if (!build_sam_account(sam_acct, smb_pw)) + return False; + + /* success */ + return True; +} + + +BOOL pdb_getsampwuid (SAM_ACCOUNT *sam_acct, uid_t uid) +{ + struct smb_passwd *smb_pw; + void *fp = NULL; + + DEBUG(10, ("pdb_getsampwuid: search by uid: %d\n", (int)uid)); + + /* Open the sam password file - not for update. */ + fp = startsmbfilepwent(lp_smb_passwd_file(), PWF_READ, &pw_file_lock_depth); + + if (fp == NULL) { + DEBUG(0, ("unable to open passdb database.\n")); + return False; + } + + while ( ((smb_pw=getsmbfilepwent(fp)) != NULL) && (smb_pw->smb_userid != uid) ) + /* do nothing */ ; + + endsmbfilepwent(fp, &pw_file_lock_depth); + + /* did we locate the username in smbpasswd */ + if (smb_pw == NULL) + return False; + + DEBUG(10, ("pdb_getsampwuid: found by name: %s\n", smb_pw->smb_name)); + + if (!sam_acct) { + DEBUG(10,("pdb_getsampwuid:SAM_ACCOUNT is NULL\n")); +#if 0 + smb_panic("NULL pointer passed to pdb_getsampwuid\n"); +#endif + return False; + } + + /* now build the SAM_ACCOUNT */ + if (!build_sam_account(sam_acct, smb_pw)) + return False; + + /* success */ + return True; +} + +BOOL pdb_getsampwrid(SAM_ACCOUNT *sam_acct,uint32 rid) +{ + struct smb_passwd *smb_pw; + void *fp = NULL; + + DEBUG(10, ("pdb_getsampwrid: search by rid: %d\n", rid)); + + /* Open the sam password file - not for update. */ + fp = startsmbfilepwent(lp_smb_passwd_file(), PWF_READ, &pw_file_lock_depth); + + if (fp == NULL) { + DEBUG(0, ("unable to open passdb database.\n")); + return False; + } + + while ( ((smb_pw=getsmbfilepwent(fp)) != NULL) && (pdb_uid_to_user_rid(smb_pw->smb_userid) != rid) ) + /* do nothing */ ; + + endsmbfilepwent(fp, &pw_file_lock_depth); + + + /* did we locate the username in smbpasswd */ + if (smb_pw == NULL) + return False; + + DEBUG(10, ("pdb_getsampwrid: found by name: %s\n", smb_pw->smb_name)); + + if (!sam_acct) { + DEBUG(10,("pdb_getsampwrid:SAM_ACCOUNT is NULL\n")); +#if 0 + smb_panic("NULL pointer passed to pdb_getsampwrid\n"); +#endif + return False; + } + + /* now build the SAM_ACCOUNT */ + if (!build_sam_account (sam_acct, smb_pw)) + return False; + + /* success */ + return True; +} + +BOOL pdb_add_sam_account(const SAM_ACCOUNT *sampass) +{ + struct smb_passwd smb_pw; + + /* convert the SAM_ACCOUNT */ + if (!build_smb_pass(&smb_pw, sampass)) { + return False; + } + + /* add the entry */ + if(!add_smbfilepwd_entry(&smb_pw)) { + return False; + } + + return True; +} + +BOOL pdb_update_sam_account(const SAM_ACCOUNT *sampass, BOOL override) +{ + struct smb_passwd smb_pw; + + /* convert the SAM_ACCOUNT */ + build_smb_pass(&smb_pw, sampass); + + /* update the entry */ + if(!mod_smbfilepwd_entry(&smb_pw, override)) + return False; + + return True; +} + +BOOL pdb_delete_sam_account (const char* username) +{ + return del_smbfilepwd_entry(username); +} + +#else + /* Do *NOT* make this function static. It breaks the compile on gcc. JRA */ + void smbpass_dummy_function(void) { } /* stop some compilers complaining */ +#endif /* WTH_SMBPASSWD_SAM*/ + diff --git a/source/passdb/pdb_tdb.c b/source/passdb/pdb_tdb.c new file mode 100644 index 00000000000..1f1d1ab455b --- /dev/null +++ b/source/passdb/pdb_tdb.c @@ -0,0 +1,808 @@ +/* + * Unix SMB/Netbios implementation. Version 1.9. SMB parameters and setup + * Copyright (C) Andrew Tridgell 1992-1998 + * Copyright (C) Simo Sorce 2000 + * Copyright (C) Gerald Carter 2000 + * Copyright (C) Jeremy Allison 2001 + * + * 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" + +#ifdef WITH_TDB_SAM + +#define PDB_VERSION "20010830" +#define PASSDB_FILE_NAME "/passdb.tdb" +#define TDB_FORMAT_STRING "ddddddBBBBBBBBBBBBddBBwdwdBdd" +#define USERPREFIX "USER_" +#define RIDPREFIX "RID_" + +extern int DEBUGLEVEL; + +struct tdb_enum_info { + TDB_CONTEXT *passwd_tdb; + TDB_DATA key; +}; + +static struct tdb_enum_info global_tdb_ent; +/*static SAM_ACCOUNT global_sam_pass;*/ + +/********************************************************************** + Intialize a SAM_ACCOUNT struct from a BYTE buffer of size len + *********************************************************************/ + +static BOOL init_sam_from_buffer (SAM_ACCOUNT *sampass, uint8 *buf, uint32 buflen) +{ + + /* times are stored as 32bit integer + take care on system with 64bit wide time_t + --SSS */ + uint32 logon_time, + logoff_time, + kickoff_time, + pass_last_set_time, + pass_can_change_time, + pass_must_change_time; + char *username; + char *domain; + char *nt_username; + char *dir_drive; + char *unknown_str; + char *munged_dial; + char *fullname; + char *homedir; + char *logon_script; + char *profile_path; + char *acct_desc; + char *workstations; + uint32 username_len, domain_len, nt_username_len, + dir_drive_len, unknown_str_len, munged_dial_len, + fullname_len, homedir_len, logon_script_len, + profile_path_len, acct_desc_len, workstations_len; + + uint32 /* uid, gid,*/ user_rid, group_rid, unknown_3, hours_len, unknown_5, unknown_6; + uint16 acct_ctrl, logon_divs; + uint8 *hours; + static uint8 *lm_pw_ptr, *nt_pw_ptr; + uint32 len = 0; + uint32 lmpwlen, ntpwlen, hourslen; + BOOL ret = True; + + /* unpack the buffer into variables */ + len = tdb_unpack (buf, buflen, TDB_FORMAT_STRING, + &logon_time, + &logoff_time, + &kickoff_time, + &pass_last_set_time, + &pass_can_change_time, + &pass_must_change_time, + &username_len, &username, + &domain_len, &domain, + &nt_username_len, &nt_username, + &fullname_len, &fullname, + &homedir_len, &homedir, + &dir_drive_len, &dir_drive, + &logon_script_len, &logon_script, + &profile_path_len, &profile_path, + &acct_desc_len, &acct_desc, + &workstations_len, &workstations, + &unknown_str_len, &unknown_str, + &munged_dial_len, &munged_dial, + &user_rid, + &group_rid, + &lmpwlen, &lm_pw_ptr, + &ntpwlen, &nt_pw_ptr, + &acct_ctrl, + &unknown_3, + &logon_divs, + &hours_len, + &hourslen, &hours, + &unknown_5, + &unknown_6); + + if (len == -1) { + ret = False; + goto done; + } + + pdb_set_logon_time(sampass, logon_time); + pdb_set_logoff_time(sampass, logoff_time); + pdb_set_kickoff_time(sampass, kickoff_time); + pdb_set_pass_can_change_time(sampass, pass_can_change_time); + pdb_set_pass_must_change_time(sampass, pass_must_change_time); + pdb_set_pass_last_set_time(sampass, pass_last_set_time); + + pdb_set_username (sampass, username_len?username:NULL); + pdb_set_domain (sampass, domain_len?domain:NULL); + pdb_set_nt_username (sampass, nt_username_len?nt_username:NULL); + pdb_set_fullname (sampass, fullname_len?fullname:NULL); + pdb_set_homedir (sampass, homedir_len?homedir:NULL); + pdb_set_dir_drive (sampass, dir_drive_len?dir_drive:NULL); + pdb_set_logon_script (sampass, logon_script_len?logon_script:NULL); + pdb_set_profile_path (sampass, profile_path_len?profile_path:NULL); + pdb_set_acct_desc (sampass, acct_desc_len?acct_desc:NULL); + pdb_set_workstations (sampass, workstations_len?workstations:NULL); + pdb_set_munged_dial (sampass, munged_dial_len?munged_dial:NULL); + pdb_set_lanman_passwd(sampass, lmpwlen?lm_pw_ptr:NULL); + pdb_set_nt_passwd (sampass, ntpwlen?nt_pw_ptr:NULL); + + /*pdb_set_uid(sampass, uid); + pdb_set_gid(sampass, gid);*/ + pdb_set_user_rid(sampass, user_rid); + pdb_set_group_rid(sampass, group_rid); + pdb_set_unknown_3(sampass, unknown_3); + pdb_set_hours_len(sampass, hours_len); + pdb_set_unknown_5(sampass, unknown_5); + pdb_set_unknown_6(sampass, unknown_6); + pdb_set_acct_ctrl(sampass, acct_ctrl); + pdb_set_logons_divs(sampass, logon_divs); + pdb_set_hours(sampass, hours); + +done: + + SAFE_FREE(username); + SAFE_FREE(domain); + SAFE_FREE(nt_username); + SAFE_FREE(fullname); + SAFE_FREE(homedir); + SAFE_FREE(dir_drive); + SAFE_FREE(logon_script); + SAFE_FREE(profile_path); + SAFE_FREE(acct_desc); + SAFE_FREE(workstations); + SAFE_FREE(munged_dial); + + return ret; +} + +/********************************************************************** + Intialize a BYTE buffer from a SAM_ACCOUNT struct + *********************************************************************/ +static uint32 init_buffer_from_sam (uint8 **buf, const SAM_ACCOUNT *sampass) +{ + size_t len, buflen; + + /* times are stored as 32bit integer + take care on system with 64bit wide time_t + --SSS */ + uint32 logon_time, + logoff_time, + kickoff_time, + pass_last_set_time, + pass_can_change_time, + pass_must_change_time; + const char *username; + const char *domain; + const char *nt_username; + const char *dir_drive; + const char *unknown_str; + const char *munged_dial; + const char *fullname; + const char *homedir; + const char *logon_script; + const char *profile_path; + const char *acct_desc; + const char *workstations; + uint32 username_len, domain_len, nt_username_len, + dir_drive_len, unknown_str_len, munged_dial_len, + fullname_len, homedir_len, logon_script_len, + profile_path_len, acct_desc_len, workstations_len; + + const uint8 *lm_pw; + const uint8 *nt_pw; + uint32 lm_pw_len = 16; + uint32 nt_pw_len = 16; + + /* do we have a valid SAM_ACCOUNT pointer? */ + if (sampass == NULL) + return -1; + + *buf = NULL; + buflen = 0; + + logon_time = (uint32)pdb_get_logon_time(sampass); + logoff_time = (uint32)pdb_get_logoff_time(sampass); + kickoff_time = (uint32)pdb_get_kickoff_time(sampass); + pass_can_change_time = (uint32)pdb_get_pass_can_change_time(sampass); + pass_must_change_time = (uint32)pdb_get_pass_must_change_time(sampass); + pass_last_set_time = (uint32)pdb_get_pass_last_set_time(sampass); + + + username = pdb_get_username(sampass); + if (username) + username_len = strlen(username) +1; + else + username_len = 0; + domain = pdb_get_domain(sampass); + if (domain) + domain_len = strlen(domain) +1; + else + domain_len = 0; + nt_username = pdb_get_nt_username(sampass); + if (nt_username) + nt_username_len = strlen(nt_username) +1; + else + nt_username_len = 0; + dir_drive = pdb_get_dirdrive(sampass); + if (dir_drive) + dir_drive_len = strlen(dir_drive) +1; + else + dir_drive_len = 0; + unknown_str = NULL; + unknown_str_len = 0; + munged_dial = pdb_get_munged_dial(sampass); + if (munged_dial) + munged_dial_len = strlen(munged_dial) +1; + else + munged_dial_len = 0; + + fullname = pdb_get_fullname(sampass); + if (fullname) + fullname_len = strlen(fullname) +1; + else + fullname_len = 0; + homedir = pdb_get_homedir(sampass); + if (homedir) + homedir_len = strlen(homedir) +1; + else + homedir_len = 0; + logon_script = pdb_get_logon_script(sampass); + if (logon_script) + logon_script_len = strlen(logon_script) +1; + else + logon_script_len = 0; + profile_path = pdb_get_profile_path(sampass); + if (profile_path) + profile_path_len = strlen(profile_path) +1; + else + profile_path_len = 0; + acct_desc = pdb_get_acct_desc(sampass); + if (acct_desc) + acct_desc_len = strlen(acct_desc) +1; + else + acct_desc_len = 0; + workstations = pdb_get_workstations(sampass); + if (workstations) + workstations_len = strlen(workstations) +1; + else + workstations_len = 0; + + lm_pw = pdb_get_lanman_passwd(sampass); + if (!lm_pw) + lm_pw_len = 0; + + nt_pw = pdb_get_nt_passwd(sampass); + if (!nt_pw) + nt_pw_len = 0; + + /* one time to get the size needed */ + len = tdb_pack(NULL, 0, TDB_FORMAT_STRING, + logon_time, + logoff_time, + kickoff_time, + pass_last_set_time, + pass_can_change_time, + pass_must_change_time, + username_len, username, + domain_len, domain, + nt_username_len, nt_username, + fullname_len, fullname, + homedir_len, homedir, + dir_drive_len, dir_drive, + logon_script_len, logon_script, + profile_path_len, profile_path, + acct_desc_len, acct_desc, + workstations_len, workstations, + unknown_str_len, unknown_str, + munged_dial_len, munged_dial, + pdb_get_user_rid(sampass), + pdb_get_group_rid(sampass), + lm_pw_len, lm_pw, + nt_pw_len, nt_pw, + pdb_get_acct_ctrl(sampass), + pdb_get_unknown3(sampass), + pdb_get_logon_divs(sampass), + pdb_get_hours_len(sampass), + MAX_HOURS_LEN, pdb_get_hours(sampass), + pdb_get_unknown5(sampass), + pdb_get_unknown6(sampass)); + + + /* malloc the space needed */ + if ( (*buf=(uint8*)malloc(len)) == NULL) { + DEBUG(0,("init_buffer_from_sam: Unable to malloc() memory for buffer!\n")); + return (-1); + } + + /* now for the real call to tdb_pack() */ + buflen = tdb_pack(*buf, len, TDB_FORMAT_STRING, + logon_time, + logoff_time, + kickoff_time, + pass_last_set_time, + pass_can_change_time, + pass_must_change_time, + username_len, username, + domain_len, domain, + nt_username_len, nt_username, + fullname_len, fullname, + homedir_len, homedir, + dir_drive_len, dir_drive, + logon_script_len, logon_script, + profile_path_len, profile_path, + acct_desc_len, acct_desc, + workstations_len, workstations, + unknown_str_len, unknown_str, + munged_dial_len, munged_dial, + pdb_get_user_rid(sampass), + pdb_get_group_rid(sampass), + lm_pw_len, lm_pw, + nt_pw_len, nt_pw, + pdb_get_acct_ctrl(sampass), + pdb_get_unknown3(sampass), + pdb_get_logon_divs(sampass), + pdb_get_hours_len(sampass), + MAX_HOURS_LEN, pdb_get_hours(sampass), + pdb_get_unknown5(sampass), + pdb_get_unknown6(sampass)); + + + /* check to make sure we got it correct */ + if (buflen != len) { + /* error */ + SAFE_FREE (*buf); + return (-1); + } + + return (buflen); +} + +/*************************************************************** + Open the TDB passwd database for SAM account enumeration. +****************************************************************/ + +BOOL pdb_setsampwent(BOOL update) +{ + pstring tdbfile; + + get_private_directory(tdbfile); + pstrcat (tdbfile, PASSDB_FILE_NAME); + + /* Open tdb passwd */ + if (!(global_tdb_ent.passwd_tdb = tdb_open_log(tdbfile, 0, TDB_DEFAULT, update?(O_RDWR|O_CREAT):O_RDONLY, 0600))) + { + DEBUG(0, ("Unable to open/create TDB passwd\n")); + return False; + } + + global_tdb_ent.key = tdb_firstkey(global_tdb_ent.passwd_tdb); + + return True; +} + +/*************************************************************** + End enumeration of the TDB passwd list. +****************************************************************/ + +void pdb_endsampwent(void) +{ + if (global_tdb_ent.passwd_tdb) { + tdb_close(global_tdb_ent.passwd_tdb); + global_tdb_ent.passwd_tdb = NULL; + } + + DEBUG(7, ("endtdbpwent: closed password file.\n")); +} + +/***************************************************************** + Get one SAM_ACCOUNT from the TDB (next in line) +*****************************************************************/ + +BOOL pdb_getsampwent(SAM_ACCOUNT *user) +{ + TDB_DATA data; + struct passwd *pw; + uid_t uid; + gid_t gid; + char *prefix = USERPREFIX; + int prefixlen = strlen (prefix); + + if (user==NULL) { + DEBUG(0,("pdb_get_sampwent: SAM_ACCOUNT is NULL.\n")); + return False; + } + + /* skip all RID entries */ + while ((global_tdb_ent.key.dsize != 0) && (strncmp (global_tdb_ent.key.dptr, prefix, prefixlen))) + /* increment to next in line */ + global_tdb_ent.key = tdb_nextkey (global_tdb_ent.passwd_tdb, global_tdb_ent.key); + + /* do we have an valid interation pointer? */ + if(global_tdb_ent.passwd_tdb == NULL) { + DEBUG(0,("pdb_get_sampwent: Bad TDB Context pointer.\n")); + return False; + } + + data = tdb_fetch (global_tdb_ent.passwd_tdb, global_tdb_ent.key); + if (!data.dptr) { + DEBUG(5,("pdb_getsampwent: database entry not found.\n")); + return False; + } + + /* unpack the buffer */ + if (!init_sam_from_buffer (user, data.dptr, data.dsize)) { + DEBUG(0,("pdb_getsampwent: Bad SAM_ACCOUNT entry returned from TDB!\n")); + SAFE_FREE(data.dptr); + return False; + } + SAFE_FREE(data.dptr); + + /* validate the account and fill in UNIX uid and gid. sys_getpwnam() + is used instaed of Get_Pwnam() as we do not need to try case + permutations */ + if ((pw=sys_getpwnam(pdb_get_username(user))) == NULL) { + DEBUG(0,("pdb_getsampwent: getpwnam(%s) return NULL. User does not exist!\n", + pdb_get_username(user))); + return False; + } + + uid = pw->pw_uid; + gid = pw->pw_gid; + pdb_set_uid (user, &uid); + pdb_set_gid (user, &gid); + + /* increment to next in line */ + global_tdb_ent.key = tdb_nextkey (global_tdb_ent.passwd_tdb, global_tdb_ent.key); + + return True; +} + +/****************************************************************** + Lookup a name in the SAM TDB +******************************************************************/ + +BOOL pdb_getsampwnam (SAM_ACCOUNT *user, const char *sname) +{ + TDB_CONTEXT *pwd_tdb; + TDB_DATA data, key; + fstring keystr; + struct passwd *pw; + pstring tdbfile; + fstring name; + uid_t uid; + gid_t gid; + + + if (user==NULL) { + DEBUG(0,("pdb_getsampwnam: SAM_ACCOUNT is NULL.\n")); + return False; + } + + /* Data is stored in all lower-case */ + unix_strlower(sname, -1, name, sizeof(name)); + + get_private_directory(tdbfile); + pstrcat (tdbfile, PASSDB_FILE_NAME); + + /* set search key */ + slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name); + key.dptr = keystr; + key.dsize = strlen (keystr) + 1; + + /* open the accounts TDB */ + if (!(pwd_tdb = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDONLY, 0600))) { + DEBUG(0, ("pdb_getsampwnam: Unable to open TDB passwd!\n")); + return False; + } + + /* get the record */ + data = tdb_fetch (pwd_tdb, key); + if (!data.dptr) { + DEBUG(5,("pdb_getsampwnam (TDB): error fetching database.\n")); + DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb))); + tdb_close (pwd_tdb); + return False; + } + + /* unpack the buffer */ + if (!init_sam_from_buffer (user, data.dptr, data.dsize)) { + DEBUG(0,("pdb_getsampwent: Bad SAM_ACCOUNT entry returned from TDB!\n")); + SAFE_FREE(data.dptr); + return False; + } + SAFE_FREE(data.dptr); + + /* validate the account and fill in UNIX uid and gid. sys_getpwnam() + is used instaed of Get_Pwnam() as we do not need to try case + permutations */ + if ((pw=sys_getpwnam(pdb_get_username(user)))) { + uid = pw->pw_uid; + gid = pw->pw_gid; + pdb_set_uid (user, &uid); + pdb_set_gid (user, &gid); + } + + /* cleanup */ + tdb_close (pwd_tdb); + + return True; +} + +/*************************************************************************** + Search by uid + **************************************************************************/ + +BOOL pdb_getsampwuid (SAM_ACCOUNT* user, uid_t uid) +{ + struct passwd *pw; + fstring name; + + if (user==NULL) { + DEBUG(0,("pdb_getsampwuid: SAM_ACCOUNT is NULL.\n")); + return False; + } + + pw = sys_getpwuid(uid); + if (pw == NULL) { + DEBUG(0,("pdb_getsampwuid: getpwuid(%d) return NULL. User does not exist!\n", uid)); + return False; + } + fstrcpy (name, pw->pw_name); + + return pdb_getsampwnam (user, name); + +} + +/*************************************************************************** + Search by rid + **************************************************************************/ + +BOOL pdb_getsampwrid (SAM_ACCOUNT *user, uint32 rid) +{ + TDB_CONTEXT *pwd_tdb; + TDB_DATA data, key; + fstring keystr; + pstring tdbfile; + fstring name; + + if (user==NULL) { + DEBUG(0,("pdb_getsampwrid: SAM_ACCOUNT is NULL.\n")); + return False; + } + + get_private_directory(tdbfile); + pstrcat (tdbfile, PASSDB_FILE_NAME); + + /* set search key */ + slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid); + key.dptr = keystr; + key.dsize = strlen (keystr) + 1; + + /* open the accounts TDB */ + if (!(pwd_tdb = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDONLY, 0600))) { + DEBUG(0, ("pdb_getsampwrid: Unable to open TDB rid database!\n")); + return False; + } + + /* get the record */ + data = tdb_fetch (pwd_tdb, key); + if (!data.dptr) { + DEBUG(5,("pdb_getsampwrid (TDB): error fetching database.\n")); + DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb))); + tdb_close (pwd_tdb); + return False; + } + + fstrcpy (name, data.dptr); + SAFE_FREE(data.dptr); + + tdb_close (pwd_tdb); + + return pdb_getsampwnam (user, name); +} + +/*************************************************************************** + Delete a SAM_ACCOUNT +****************************************************************************/ + +BOOL pdb_delete_sam_account(const char *sname) +{ + SAM_ACCOUNT *sam_pass = NULL; + TDB_CONTEXT *pwd_tdb; + TDB_DATA key, data; + fstring keystr; + pstring tdbfile; + uint32 rid; + fstring name; + + unix_strlower(sname, -1, name, sizeof(name)); + + get_private_directory(tdbfile); + pstrcat (tdbfile, PASSDB_FILE_NAME); + + /* open the TDB */ + if (!(pwd_tdb = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDWR, 0600))) { + DEBUG(0, ("Unable to open TDB passwd!")); + return False; + } + + /* set the search key */ + slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name); + key.dptr = keystr; + key.dsize = strlen (keystr) + 1; + + /* get the record */ + data = tdb_fetch (pwd_tdb, key); + if (!data.dptr) { + DEBUG(5,("pdb_delete_sam_account (TDB): error fetching database.\n")); + DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb))); + tdb_close (pwd_tdb); + return False; + } + + /* unpack the buffer */ + if (!pdb_init_sam (&sam_pass)) { + tdb_close (pwd_tdb); + return False; + } + + if (!init_sam_from_buffer (sam_pass, data.dptr, data.dsize)) { + DEBUG(0,("pdb_getsampwent: Bad SAM_ACCOUNT entry returned from TDB!\n")); + tdb_close (pwd_tdb); + SAFE_FREE(data.dptr); + return False; + } + SAFE_FREE(data.dptr); + + rid = pdb_get_user_rid(sam_pass); + + pdb_free_sam (&sam_pass); + + /* it's outaa here! 8^) */ + if (tdb_delete(pwd_tdb, key) != TDB_SUCCESS) { + DEBUG(5, ("Error deleting entry from tdb passwd database!\n")); + DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb))); + tdb_close(pwd_tdb); + return False; + } + + /* delete also the RID key */ + + /* set the search key */ + slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid); + key.dptr = keystr; + key.dsize = strlen (keystr) + 1; + + /* it's outaa here! 8^) */ + if (tdb_delete(pwd_tdb, key) != TDB_SUCCESS) { + DEBUG(5, ("Error deleting entry from tdb rid database!\n")); + DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb))); + tdb_close(pwd_tdb); + return False; + } + + tdb_close(pwd_tdb); + + return True; +} + +/*************************************************************************** + Update the TDB SAM +****************************************************************************/ + +static BOOL tdb_update_sam(const SAM_ACCOUNT* newpwd, BOOL override, int flag) +{ + TDB_CONTEXT *pwd_tdb = NULL; + TDB_DATA key, data; + uint8 *buf = NULL; + fstring keystr; + pstring tdbfile; + fstring name; + BOOL ret = True; + + get_private_directory(tdbfile); + pstrcat (tdbfile, PASSDB_FILE_NAME); + + /* if we don't have a RID, then FAIL */ + if (!pdb_get_user_rid(newpwd)) + DEBUG (0,("tdb_update_sam: Failing to store a SAM_ACCOUNT for [%s] without a RID\n",pdb_get_username(newpwd))); + if (!pdb_get_group_rid(newpwd)) + DEBUG (0,("tdb_update_sam: Failing to store a SAM_ACCOUNT for [%s] without a primary group RID\n",pdb_get_username(newpwd))); + + /* copy the SAM_ACCOUNT struct into a BYTE buffer for storage */ + if ((data.dsize=init_buffer_from_sam (&buf, newpwd)) == -1) { + DEBUG(0,("tdb_update_sam: ERROR - Unable to copy SAM_ACCOUNT info BYTE buffer!\n")); + ret = False; + goto done; + } + data.dptr = buf; + + unix_strlower(pdb_get_username(newpwd), -1, name, sizeof(name)); + + /* setup the USER index key */ + slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name); + key.dptr = keystr; + key.dsize = strlen (keystr) + 1; + + /* invalidate the existing TDB iterator if it is open */ + if (global_tdb_ent.passwd_tdb) { + tdb_close(global_tdb_ent.passwd_tdb); + global_tdb_ent.passwd_tdb = NULL; + } + + /* open the account TDB passwd*/ + pwd_tdb = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0600); + if (!pwd_tdb) + { + DEBUG(0, ("tdb_update_sam: Unable to open TDB passwd!\n")); + return False; + } + + /* add the account */ + if (tdb_store(pwd_tdb, key, data, flag) != TDB_SUCCESS) { + DEBUG(0, ("Unable to modify passwd TDB!")); + DEBUGADD(0, (" Error: %s\n", tdb_errorstr(pwd_tdb))); + ret = False; + goto done; + } + + /* setup RID data */ + data.dsize = sizeof(fstring); + data.dptr = name; + + /* setup the RID index key */ + slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, pdb_get_user_rid(newpwd)); + key.dptr = keystr; + key.dsize = strlen (keystr) + 1; + + /* add the reference */ + if (tdb_store(pwd_tdb, key, data, flag) != TDB_SUCCESS) { + DEBUG(0, ("Unable to modify TDB passwd !")); + DEBUGADD(0, (" Error: %s\n", tdb_errorstr(pwd_tdb))); + ret = False; + goto done; + } + +done: + /* cleanup */ + tdb_close (pwd_tdb); + SAFE_FREE(buf); + + return (ret); +} + +/*************************************************************************** + Modifies an existing SAM_ACCOUNT +****************************************************************************/ + +BOOL pdb_update_sam_account (const SAM_ACCOUNT *newpwd, BOOL override) +{ + return (tdb_update_sam(newpwd, override, TDB_MODIFY)); +} + +/*************************************************************************** + Adds an existing SAM_ACCOUNT +****************************************************************************/ + +BOOL pdb_add_sam_account (const SAM_ACCOUNT *newpwd) +{ + return (tdb_update_sam(newpwd, True, TDB_INSERT)); +} + +#else + /* Do *NOT* make this function static. It breaks the compile on gcc. JRA */ + void samtdb_dummy_function(void) { } /* stop some compilers complaining */ +#endif /* WITH_TDB_SAM */ diff --git a/source/passdb/secrets.c b/source/passdb/secrets.c new file mode 100644 index 00000000000..198f557bd62 --- /dev/null +++ b/source/passdb/secrets.c @@ -0,0 +1,247 @@ +/* + Unix SMB/Netbios implementation. + Version 3.0. + Copyright (C) Andrew Tridgell 1992-2001 + + 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. +*/ + +/* the Samba secrets database stores any generated, private information + such as the local SID and machine trust password */ + +#include "includes.h" + +static TDB_CONTEXT *tdb; + +/* open up the secrets database */ +BOOL secrets_init(void) +{ + pstring fname; + + if (tdb) + return True; + + pstrcpy(fname, lp_private_dir()); + pstrcat(fname,"/secrets.tdb"); + + tdb = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600); + + if (!tdb) { + DEBUG(0,("Failed to open %s\n", fname)); + return False; + } + return True; +} + +/* read a entry from the secrets database - the caller must free the result + if size is non-null then the size of the entry is put in there + */ +void *secrets_fetch(char *key, size_t *size) +{ + TDB_DATA kbuf, dbuf; + secrets_init(); + if (!tdb) + return NULL; + kbuf.dptr = key; + kbuf.dsize = strlen(key); + dbuf = tdb_fetch(tdb, kbuf); + if (size) + *size = dbuf.dsize; + return dbuf.dptr; +} + +/* store a secrets entry + */ +BOOL secrets_store(char *key, void *data, size_t size) +{ + TDB_DATA kbuf, dbuf; + secrets_init(); + if (!tdb) + return False; + kbuf.dptr = key; + kbuf.dsize = strlen(key); + dbuf.dptr = data; + dbuf.dsize = size; + return tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) == 0; +} + + +/* delete a secets database entry + */ +BOOL secrets_delete(char *key) +{ + TDB_DATA kbuf; + secrets_init(); + if (!tdb) + return False; + kbuf.dptr = key; + kbuf.dsize = strlen(key); + return tdb_delete(tdb, kbuf) == 0; +} + +BOOL secrets_store_domain_sid(char *domain, DOM_SID *sid) +{ + fstring key; + + slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_DOMAIN_SID, domain); + return secrets_store(key, sid, sizeof(DOM_SID)); +} + +BOOL secrets_fetch_domain_sid(char *domain, DOM_SID *sid) +{ + DOM_SID *dyn_sid; + fstring key; + size_t size; + + slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_DOMAIN_SID, domain); + dyn_sid = (DOM_SID *)secrets_fetch(key, &size); + + if (dyn_sid == NULL) + return False; + + if (size != sizeof(DOM_SID)) + { + SAFE_FREE(dyn_sid); + return False; + } + + *sid = *dyn_sid; + SAFE_FREE(dyn_sid); + return True; +} + + +/************************************************************************ +form a key for fetching a domain trust password +************************************************************************/ +char *trust_keystr(char *domain) +{ + static fstring keystr; + + slprintf(keystr,sizeof(keystr)-1,"%s/%s", + SECRETS_MACHINE_ACCT_PASS, domain); + + return keystr; +} + +/************************************************************************ + Routine to get the trust account password for a domain. +************************************************************************/ +BOOL secrets_fetch_trust_account_password(char *domain, uint8 ret_pwd[16], + time_t *pass_last_set_time) +{ + struct machine_acct_pass *pass; + char *plaintext; + size_t size; + + plaintext = secrets_fetch_machine_password(); + if (plaintext) { + /* we have an ADS password - use that */ + DEBUG(4,("Using ADS machine password\n")); + E_md4hash((uchar *)plaintext, ret_pwd); + SAFE_FREE(plaintext); + return True; + } + + if (!(pass = secrets_fetch(trust_keystr(domain), &size))) { + DEBUG(5, ("secrets_fetch failed!\n")); + return False; + } + + if (size != sizeof(*pass)) { + DEBUG(0, ("secrets were of incorrect size!\n")); + return False; + } + + if (pass_last_set_time) *pass_last_set_time = pass->mod_time; + memcpy(ret_pwd, pass->hash, 16); + SAFE_FREE(pass); + return True; +} + + +/************************************************************************ + Routine to set the trust account password for a domain. +************************************************************************/ +BOOL secrets_store_trust_account_password(char *domain, uint8 new_pwd[16]) +{ + struct machine_acct_pass pass; + + pass.mod_time = time(NULL); + memcpy(pass.hash, new_pwd, 16); + + return secrets_store(trust_keystr(domain), (void *)&pass, sizeof(pass)); +} + +/************************************************************************ + Routine to set the plaintext machine account password for a realm +the password is assumed to be a null terminated ascii string +************************************************************************/ +BOOL secrets_store_machine_password(char *pass) +{ + return secrets_store(SECRETS_MACHINE_PASSWORD, pass, strlen(pass)+1); +} + + +/************************************************************************ + Routine to fetch the plaintext machine account password for a realm +the password is assumed to be a null terminated ascii string +************************************************************************/ +char *secrets_fetch_machine_password(void) +{ + return (char *)secrets_fetch(SECRETS_MACHINE_PASSWORD, NULL); +} + + + +/************************************************************************ + Routine to delete the trust account password file for a domain. +************************************************************************/ + +BOOL trust_password_delete(char *domain) +{ + return secrets_delete(trust_keystr(domain)); +} + +/******************************************************************* + Reset the 'done' variables so after a client process is created + from a fork call these calls will be re-done. This should be + expanded if more variables need reseting. + ******************************************************************/ + +void reset_globals_after_fork(void) +{ + unsigned char dummy; + + secrets_init(); + + /* + * Increment the global seed value to ensure every smbd starts + * with a new random seed. + */ + + if (tdb) { + uint32 initial_val = sys_getpid(); + tdb_change_int_atomic(tdb, "INFO/random_seed", (int *)&initial_val, 1); + set_rand_reseed_data((unsigned char *)&initial_val, sizeof(initial_val)); + } + + /* + * Re-seed the random crypto generator, so all smbd's + * started from the same parent won't generate the same + * sequence. + */ + generate_random_buffer( &dummy, 1, True); +} diff --git a/source/passdb/smbpass.c b/source/passdb/smbpass.c deleted file mode 100644 index 2dec15ffb43..00000000000 --- a/source/passdb/smbpass.c +++ /dev/null @@ -1,304 +0,0 @@ -#ifdef SMB_PASSWD -/* - * Unix SMB/Netbios implementation. Version 1.9. SMB parameters and setup - * Copyright (C) Andrew Tridgell 1992-1995 Modified by Jeremy Allison 1995. - * - * 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 "loadparm.h" - -extern int DEBUGLEVEL; - -int gotalarm; - -void -gotalarm_sig() -{ - gotalarm = 1; -} - -int -do_pw_lock(int fd, int waitsecs, int type) -{ - struct flock lock; - int ret; - - gotalarm = 0; - signal(SIGALRM, SIGNAL_CAST gotalarm_sig); - - lock.l_type = type; - lock.l_whence = SEEK_SET; - lock.l_start = 0; - lock.l_len = 1; - lock.l_pid = 0; - - alarm(5); - ret = fcntl(fd, F_SETLKW, &lock); - alarm(0); - signal(SIGALRM, SIGNAL_CAST SIG_DFL); - - if (gotalarm) { - DEBUG(0, ("do_pw_lock: failed to %s SMB passwd file.\n", - type == F_UNLCK ? "unlock" : "lock")); - return -1; - } - return ret; -} - -int -pw_file_lock(char *name, int type, int secs) -{ - int fd = open(name, O_RDWR | O_CREAT, 0666); - if (fd < 0) - return (-1); - if (do_pw_lock(fd, secs, type)) { - close(fd); - return -1; - } - return fd; -} - -int -pw_file_unlock(int fd) -{ - do_pw_lock(fd, 5, F_UNLCK); - return close(fd); -} - -/* - * Routine to get the next 32 hex characters and turn them - * into a 16 byte array. - */ - -static int gethexpwd(char *p, char *pwd) -{ - int i; - unsigned char lonybble, hinybble; - char *hexchars = "0123456789ABCDEF"; - char *p1, *p2; - - for (i = 0; i < 32; i += 2) { - hinybble = toupper(p[i]); - lonybble = toupper(p[i + 1]); - - p1 = strchr(hexchars, hinybble); - p2 = strchr(hexchars, lonybble); - if (!p1 || !p2) - return (False); - hinybble = PTR_DIFF(p1, hexchars); - lonybble = PTR_DIFF(p2, hexchars); - - pwd[i / 2] = (hinybble << 4) | lonybble; - } - return (True); -} - -/* - * Routine to search the smbpasswd file for an entry matching the username. - */ -struct smb_passwd * -get_smbpwnam(char *name) -{ - /* Static buffers we will return. */ - static struct smb_passwd pw_buf; - static pstring user_name; - static unsigned char smbpwd[16]; - static unsigned char smbntpwd[16]; - char linebuf[256]; - char readbuf[16 * 1024]; - unsigned char c; - unsigned char *p; - long uidval; - long linebuf_len; - FILE *fp; - int lockfd; - char *pfile = lp_smb_passwd_file(); - - if (!*pfile) { - DEBUG(0, ("No SMB password file set\n")); - return (NULL); - } - DEBUG(10, ("get_smbpwnam: opening file %s\n", pfile)); - - fp = fopen(pfile, "r"); - - if (fp == NULL) { - DEBUG(0, ("get_smbpwnam: unable to open file %s\n", pfile)); - return NULL; - } - /* Set a 16k buffer to do more efficient reads */ - setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf)); - - if ((lockfd = pw_file_lock(pfile, F_RDLCK, 5)) < 0) { - DEBUG(0, ("get_smbpwnam: unable to lock file %s\n", pfile)); - fclose(fp); - return NULL; - } - /* make sure it is only rw by the owner */ - chmod(pfile, 0600); - - /* We have a read lock on the file. */ - /* - * Scan the file, a line at a time and check if the name matches. - */ - while (!feof(fp)) { - linebuf[0] = '\0'; - - fgets(linebuf, 256, fp); - if (ferror(fp)) { - fclose(fp); - pw_file_unlock(lockfd); - return NULL; - } - /* - * Check if the string is terminated with a newline - if not - * then we must keep reading and discard until we get one. - */ - linebuf_len = strlen(linebuf); - if (linebuf[linebuf_len - 1] != '\n') { - c = '\0'; - while (!ferror(fp) && !feof(fp)) { - c = fgetc(fp); - if (c == '\n') - break; - } - } else - linebuf[linebuf_len - 1] = '\0'; - -#ifdef DEBUG_PASSWORD - DEBUG(100, ("get_smbpwnam: got line |%s|\n", linebuf)); -#endif - if ((linebuf[0] == 0) && feof(fp)) { - DEBUG(4, ("get_smbpwnam: end of file reached\n")); - break; - } - /* - * The line we have should be of the form :- - * - * username:uid:[32hex bytes]:....other flags presently - * ignored.... - * - * or, - * - * username:uid:[32hex bytes]:[32hex bytes]:....ignored.... - * - * if Windows NT compatible passwords are also present. - */ - - if (linebuf[0] == '#' || linebuf[0] == '\0') { - DEBUG(6, ("get_smbpwnam: skipping comment or blank line\n")); - continue; - } - p = (unsigned char *) strchr(linebuf, ':'); - if (p == NULL) { - DEBUG(0, ("get_smbpwnam: malformed password entry (no :)\n")); - continue; - } - /* - * As 256 is shorter than a pstring we don't need to check - * length here - if this ever changes.... - */ - strncpy(user_name, linebuf, PTR_DIFF(p, linebuf)); - user_name[PTR_DIFF(p, linebuf)] = '\0'; - if (!strequal(user_name, name)) - continue; - - /* User name matches - get uid and password */ - p++; /* Go past ':' */ - if (!isdigit(*p)) { - DEBUG(0, ("get_smbpwnam: malformed password entry (uid not number)\n")); - fclose(fp); - pw_file_unlock(lockfd); - return NULL; - } - uidval = atoi((char *) p); - while (*p && isdigit(*p)) - p++; - if (*p != ':') { - DEBUG(0, ("get_smbpwnam: malformed password entry (no : after uid)\n")); - fclose(fp); - pw_file_unlock(lockfd); - return NULL; - } - /* - * Now get the password value - this should be 32 hex digits - * which are the ascii representations of a 16 byte string. - * Get two at a time and put them into the password. - */ - p++; - if (*p == '*' || *p == 'X') { - /* Password deliberately invalid - end here. */ - DEBUG(10, ("get_smbpwnam: entry invalidated for user %s\n", user_name)); - fclose(fp); - pw_file_unlock(lockfd); - return NULL; - } - if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) { - DEBUG(0, ("get_smbpwnam: malformed password entry (passwd too short)\n")); - fclose(fp); - pw_file_unlock(lockfd); - return (False); - } - if (p[32] != ':') { - DEBUG(0, ("get_smbpwnam: malformed password entry (no terminating :)\n")); - fclose(fp); - pw_file_unlock(lockfd); - return NULL; - } - if (!strncasecmp((char *) p, "NO PASSWORD", 11)) { - pw_buf.smb_passwd = NULL; - } else { - if(!gethexpwd(p,smbpwd)) { - DEBUG(0, ("Malformed Lanman password entry (non hex chars)\n")); - fclose(fp); - pw_file_unlock(lockfd); - return NULL; - } - pw_buf.smb_passwd = smbpwd; - } - pw_buf.smb_name = user_name; - pw_buf.smb_userid = uidval; - pw_buf.smb_nt_passwd = NULL; - - /* Now check if the NT compatible password is - available. */ - p += 33; /* Move to the first character of the line after - the lanman password. */ - if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) { - if (*p != '*' && *p != 'X') { - if(gethexpwd(p,smbntpwd)) - pw_buf.smb_nt_passwd = smbntpwd; - } - } - - fclose(fp); - pw_file_unlock(lockfd); - DEBUG(5, ("get_smbpwname: returning passwd entry for user %s, uid %d\n", - user_name, uidval)); - return &pw_buf; - } - - fclose(fp); - pw_file_unlock(lockfd); - return NULL; -} -#else -void -smbpass_dummy(void) -{ -} /* To avoid compiler complaints */ -#endif diff --git a/source/passdb/smbpasschange.c b/source/passdb/smbpasschange.c new file mode 100644 index 00000000000..f4af6e8745f --- /dev/null +++ b/source/passdb/smbpasschange.c @@ -0,0 +1,25 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + change a password in a local smbpasswd file + Copyright (C) Andrew Tridgell 1998 + + 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" + + + diff --git a/source/passdb/smbpassgroup.c b/source/passdb/smbpassgroup.c new file mode 100644 index 00000000000..808abde6617 --- /dev/null +++ b/source/passdb/smbpassgroup.c @@ -0,0 +1,195 @@ +/* + * Unix SMB/Netbios implementation. Version 1.9. SMB parameters and setup + * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995. + * + * 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" + +#ifdef USE_SMBPASS_DB + +static int grp_file_lock_depth = 0; + +/*************************************************************** + Start to enumerate the smbpasswd list. Returns a void pointer + to ensure no modification outside this module. +****************************************************************/ + +static void *startsmbfilegrpent(BOOL update) +{ + static char s_readbuf[1024]; + return startfilepwent(lp_smb_passgrp_file(), s_readbuf, sizeof(s_readbuf), + &grp_file_lock_depth, update); +} + +/*************************************************************** + End enumeration of the smbpasswd list. +****************************************************************/ + +static void endsmbfilegrpent(void *vp) +{ + endfilepwent(vp, &grp_file_lock_depth); +} + +/************************************************************************* + Return the current position in the smbpasswd list as an SMB_BIG_UINT. + This must be treated as an opaque token. +*************************************************************************/ + +static SMB_BIG_UINT getsmbfilegrppos(void *vp) +{ + return getfilepwpos(vp); +} + +/************************************************************************* + Set the current position in the smbpasswd list from an SMB_BIG_UINT. + This must be treated as an opaque token. +*************************************************************************/ + +static BOOL setsmbfilegrppos(void *vp, SMB_BIG_UINT tok) +{ + return setfilepwpos(vp, tok); +} + +/************************************************************************* + Routine to return the next entry in the smbpasswd list. + *************************************************************************/ +static struct smb_passwd *getsmbfilegrpent(void *vp, + uint32 **grp_rids, int *num_grps, + uint32 **als_rids, int *num_alss) +{ + /* Static buffers we will return. */ + static struct smb_passwd pw_buf; + static pstring user_name; + struct passwd *pwfile; + pstring linebuf; + unsigned char *p; + int uidval; + size_t linebuf_len; + + if (vp == NULL) + { + DEBUG(0,("getsmbfilegrpent: Bad password file pointer.\n")); + return NULL; + } + + pwdb_init_smb(&pw_buf); + + /* + * Scan the file, a line at a time. + */ + while ((linebuf_len = getfileline(vp, linebuf, sizeof(linebuf))) > 0) + { + /* + * The line we have should be of the form :- + * + * username:uid:domainrid1,domainrid2..:aliasrid1,aliasrid2..: + */ + + /* + * As 256 is shorter than a pstring we don't need to check + * length here - if this ever changes.... + */ + p = strncpyn(user_name, linebuf, sizeof(user_name), ':'); + + /* Go past ':' */ + p++; + + /* Get smb uid. */ + + p = Atoic((char *) p, &uidval, ":"); + + pw_buf.smb_name = user_name; + pw_buf.smb_userid = uidval; + + /* + * Now get the password value - this should be 32 hex digits + * which are the ascii representations of a 16 byte string. + * Get two at a time and put them into the password. + */ + + /* Skip the ':' */ + p++; + + if (grp_rids != NULL && num_grps != NULL) + { + int i; + p = get_numlist(p, grp_rids, num_grps); + if (p == NULL) + { + DEBUG(0,("getsmbfilegrpent: invalid line\n")); + return NULL; + } + for (i = 0; i < (*num_grps); i++) + { + (*grp_rids)[i] = pwdb_gid_to_group_rid((*grp_rids)[i]); + } + } + + /* Skip the ':' */ + p++; + + if (als_rids != NULL && num_alss != NULL) + { + int i; + p = get_numlist(p, als_rids, num_alss); + if (p == NULL) + { + DEBUG(0,("getsmbfilegrpent: invalid line\n")); + return NULL; + } + for (i = 0; i < (*num_alss); i++) + { + (*als_rids)[i] = pwdb_gid_to_alias_rid((*als_rids)[i]); + } + } + + pwfile = Get_Pwnam(pw_buf.smb_name); + if (pwfile == NULL) + { + DEBUG(0,("getsmbfilegrpent: smbpasswd database is corrupt!\n")); + DEBUG(0,("getsmbfilegrpent: username %s not in unix passwd database!\n", pw_buf.smb_name)); + return NULL; + } + + return &pw_buf; + } + + DEBUG(5,("getsmbfilegrpent: end of file reached.\n")); + return NULL; +} + +static struct passgrp_ops file_ops = +{ + startsmbfilegrpent, + endsmbfilegrpent, + getsmbfilegrppos, + setsmbfilegrppos, + iterate_getsmbgrpnam, /* In passgrp.c */ + iterate_getsmbgrpuid, /* In passgrp.c */ + iterate_getsmbgrprid, /* In passgrp.c */ + getsmbfilegrpent, +}; + +struct passgrp_ops *file_initialise_password_grp(void) +{ + return &file_ops; +} + +#else + /* Do *NOT* make this function static. It breaks the compile on gcc. JRA */ + void smbpass_dummy_function(void) { } /* stop some compilers complaining */ +#endif /* USE_SMBPASS_DB */ |