diff options
Diffstat (limited to 'source/smbd/password.c')
-rw-r--r-- | source/smbd/password.c | 1657 |
1 files changed, 817 insertions, 840 deletions
diff --git a/source/smbd/password.c b/source/smbd/password.c index 87c1fef94c5..95560df66b4 100644 --- a/source/smbd/password.c +++ b/source/smbd/password.c @@ -2,7 +2,7 @@ Unix SMB/Netbios implementation. Version 1.9. Password and authentication handling - Copyright (C) Andrew Tridgell 1992-1995 + 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 @@ -20,7 +20,6 @@ */ #include "includes.h" -#include "loadparm.h" extern int DEBUGLEVEL; extern int Protocol; @@ -28,12 +27,10 @@ extern int Protocol; /* users from session setup */ static pstring session_users=""; -/* these are kept here to keep the string_combinations function simple */ -static char this_user[100]=""; -static char this_salt[100]=""; -static char this_crypted[100]=""; +extern pstring scope; +extern pstring global_myname; +extern fstring global_myworkgroup; -#ifdef SMB_PASSWD /* Data to do lanman1/2 password challenge. */ static unsigned char saved_challenge[8]; static BOOL challenge_sent=False; @@ -43,24 +40,40 @@ Get the next challenge value - no repeats. ********************************************************************/ void generate_next_challenge(char *challenge) { - extern void E1(char *,char *,char *); - static int counter = 0; - struct timeval tval; - int v1,v2; - GetTimeOfDay(&tval); - v1 = (counter++) + getpid() + tval.tv_sec; - v2 = (counter++) * getpid() + tval.tv_usec; - SIVAL(challenge,0,v1); - SIVAL(challenge,4,v2); - E1(challenge,"SAMBA",saved_challenge); - memcpy(challenge,saved_challenge,8); - challenge_sent = True; +#if 0 + /* + * Leave this ifdef'd out while we test + * the new crypto random number generator. + * JRA. + */ + unsigned char buf[16]; + static int counter = 0; + struct timeval tval; + int v1,v2; + + /* get a sort-of random number */ + GetTimeOfDay(&tval); + v1 = (counter++) + getpid() + tval.tv_sec; + v2 = (counter++) * getpid() + tval.tv_usec; + SIVAL(challenge,0,v1); + SIVAL(challenge,4,v2); + + /* mash it up with md4 */ + mdfour(buf, (unsigned char *)challenge, 8); +#else + unsigned char buf[8]; + + generate_random_buffer(buf,8,False); +#endif + memcpy(saved_challenge, buf, 8); + memcpy(challenge,buf,8); + challenge_sent = True; } /******************************************************************* set the last challenge sent, usually from a password server ********************************************************************/ -BOOL set_challenge(char *challenge) +BOOL set_challenge(unsigned char *challenge) { memcpy(saved_challenge,challenge,8); challenge_sent = True; @@ -70,44 +83,29 @@ BOOL set_challenge(char *challenge) /******************************************************************* get the last challenge sent ********************************************************************/ -BOOL last_challenge(char *challenge) +static BOOL last_challenge(unsigned char *challenge) { if (!challenge_sent) return(False); memcpy(challenge,saved_challenge,8); return(True); } -#endif /* this holds info on user ids that are already validated for this VC */ static user_struct *validated_users = NULL; static int num_validated_users = 0; /**************************************************************************** -check if a uid has been validated, and return an index if it has. -1 if not -****************************************************************************/ -int valid_uid(int uid) -{ - int i; - if (uid == -1) return(-1); - - for (i=0;i<num_validated_users;i++) - if (validated_users[i].uid == uid) - { - DEBUG(3,("valid uid %d mapped to vuid %d (user=%s)\n", - uid,i,validated_users[i].name)); - return(i); - } - return(-1); -} - -/**************************************************************************** check if a uid has been validated, and return an pointer to the user_struct -if it has. NULL if not +if it has. NULL if not. vuid is biased by an offset. This allows us to +tell random client vuid's (normally zero) from valid vuids. ****************************************************************************/ -user_struct *get_valid_user_struct(int uid) +user_struct *get_valid_user_struct(uint16 vuid) { - int vuid = valid_uid(uid); - if(vuid == -1 || validated_users[vuid].guest) + if (vuid == UID_FIELD_INVALID) + return NULL; + vuid -= VUID_OFFSET; + if ((vuid >= (uint16)num_validated_users) || + (validated_users[vuid].uid == (uid_t)-1) || (validated_users[vuid].gid == (gid_t)-1)) return NULL; return &validated_users[vuid]; } @@ -115,446 +113,230 @@ user_struct *get_valid_user_struct(int uid) /**************************************************************************** invalidate a uid ****************************************************************************/ -void invalidate_uid(int uid) +void invalidate_vuid(uint16 vuid) { - int i; - for (i=0;i<num_validated_users;i++) - if (validated_users[i].uid == uid) - { - user_struct *vuser = &validated_users[i]; - vuser->uid = -1; - vuser->gid = -1; - vuser->user_ngroups = 0; - if(vuser->user_groups && - (vuser->user_groups != (gid_t *)vuser->user_igroups)) - free(vuser->user_groups); - vuser->user_groups = NULL; - if(vuser->user_igroups) - free(vuser->user_igroups); - vuser->user_igroups = NULL; - } -} - - -/**************************************************************************** -return a validated username -****************************************************************************/ -char *validated_username(int vuid) -{ - return(validated_users[vuid].name); -} + user_struct *vuser = get_valid_user_struct(vuid); -/**************************************************************************** -register a uid/name pair as being valid and that a valid password -has been given. -****************************************************************************/ -void register_uid(int uid,int gid, char *name,BOOL guest) -{ - user_struct *vuser; + if (vuser == NULL) return; - if (valid_uid(uid) >= 0) - return; - validated_users = (user_struct *)Realloc(validated_users, - sizeof(user_struct)* - (num_validated_users+1)); + vuser->uid = (uid_t)-1; + vuser->gid = (gid_t)-1; - if (!validated_users) - { - DEBUG(0,("Failed to realloc users struct!\n")); - return; - } + vuser->n_sids = 0; - vuser = &validated_users[num_validated_users]; - vuser->uid = uid; - vuser->gid = gid; - vuser->guest = guest; - strcpy(vuser->name,name); + /* same number of igroups as groups */ + vuser->n_groups = 0; - vuser->user_ngroups = 0; - vuser->user_groups = NULL; - vuser->user_igroups = NULL; + if (vuser->groups) + free((char *)vuser->groups); - /* Find all the groups this uid is in and store them. - Used by become_user() */ - setup_groups(name,uid,gid, - &vuser->user_ngroups, - &vuser->user_igroups, - &vuser->user_groups); + if (vuser->sids) + free((char *)vuser->sids); - DEBUG(3,("uid %d registered to name %s\n",uid,name)); - - num_validated_users++; + vuser->sids = NULL; + vuser->groups = NULL; } /**************************************************************************** -add a name to the session users list +return a validated username ****************************************************************************/ -void add_session_user(char *user) +char *validated_username(uint16 vuid) { - fstring suser; - StrnCpy(suser,user,sizeof(suser)-1); - - if (!Get_Pwnam(suser,True)) return; - - if (suser && *suser && !in_list(suser,session_users,False)) - { - if (strlen(suser) + strlen(session_users) + 2 >= sizeof(pstring)) - DEBUG(1,("Too many session users??\n")); - else - { - strcat(session_users," "); - strcat(session_users,suser); - } - } + user_struct *vuser = get_valid_user_struct(vuid); + if (vuser == NULL) + return 0; + return(vuser->name); } -#ifdef NO_GETSPNAM -/* a fake shadow password routine which just fills a fake spwd struct - * with the sp_pwdp field. (sreiz@aie.nl) - */ -static struct spwd *getspnam(char *username) /* fake shadow password routine */ -{ - FILE *f; - char line[1024]; - static char pw[20]; - static struct spwd static_spwd; - - static_spwd.sp_pwdp=0; - if (!(f=fopen("/etc/master.passwd", "r"))) - return 0; - while (fgets(line, 1024, f)) { - if (!strncmp(line, username, strlen(username)) && - line[strlen(username)]==':') { /* found entry */ - char *p, *q; - - p=line+strlen(username)+1; - if ((q=strchr(p, ':'))) { - *q=0; - if (q-p+1>20) - break; - strcpy(pw, p); - static_spwd.sp_pwdp=pw; - } - break; - } - } - fclose(f); - if (static_spwd.sp_pwdp) - return &static_spwd; - return 0; -} -#endif - - -#ifdef OSF1_ENH_SEC /**************************************************************************** -an enhanced crypt for OSF1 +Setup the groups a user belongs to. ****************************************************************************/ -static char *osf1_bigcrypt(char *password,char *salt1) +int setup_groups(char *user, uid_t uid, gid_t gid, int *p_ngroups, gid_t **p_groups) { - 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++; + int i,ngroups; + gid_t grp = 0; + gid_t *groups = NULL; - StrnCpy(salt,salt1,2); - StrnCpy(result,salt1,2); + if (-1 == initgroups(user,gid)) + { + if (getuid() == 0) + { + DEBUG(0,("Unable to initgroups!\n")); + if (gid < 0 || gid > 16000 || uid < 0 || uid > 16000) + { + DEBUG(0,("This is probably a problem with the account %s\n", user)); + } + } + return -1; + } - for (i=0; i<parts;i++) - { - p1 = crypt(p2,salt); - strcat(result,p1+2); - StrnCpy(salt,&result[2+i*AUTH_CIPHERTEXT_SEG_CHARS],2); - p2 += AUTH_CLEARTEXT_SEG_CHARS; - } + ngroups = sys_getgroups(0,&grp); + if (ngroups <= 0) + { + ngroups = 32; + } - return(result); -} -#endif + if((groups = (gid_t *)malloc(sizeof(gid_t)*ngroups)) == NULL) + { + DEBUG(0,("setup_groups malloc fail !\n")); + return -1; + } + ngroups = sys_getgroups(ngroups,groups); -/**************************************************************************** -update the enhanced security database. Only relevant for OSF1 at the moment. -****************************************************************************/ -static void update_protected_database( char *user, BOOL result) -{ -#ifdef OSF1_ENH_SEC - struct pr_passwd *mypasswd; - time_t starttime; - long tz; - - mypasswd = getprpwnam (user); - starttime = time (NULL); - tz = mktime ( localtime ( &starttime ) ); + (*p_ngroups) = ngroups; + (*p_groups) = groups; - if (result) - { - mypasswd->ufld.fd_slogin = tz; - mypasswd->ufld.fd_nlogins = 0; - - putprpwnam(user,mypasswd); - - DEBUG(3,("Update protected database for Account %s after succesful connection\n",user)); - } - else - { - mypasswd->ufld.fd_ulogin = tz; - mypasswd->ufld.fd_nlogins = mypasswd->ufld.fd_nlogins + 1; - if ( mypasswd->ufld.fd_max_tries != 0 && mypasswd->ufld.fd_nlogins > mypasswd->ufld.fd_max_tries ) + DEBUG( 3, ( "%s is in %d groups: ", user, ngroups ) ); + for (i = 0; i < ngroups; i++ ) { - mypasswd->uflg.fg_lock = 0; - DEBUG(3,("Account is disabled -- see Account Administrator.\n")); + DEBUG( 3, ( "%s%d", (i ? ", " : ""), (int)groups[i] ) ); } - putprpwnam ( user , mypasswd ); - DEBUG(3,("Update protected database for Account %s after refusing connection\n",user)); - } -#else - DEBUG(6,("Updated database with %s %s\n",user,BOOLSTR(result))); -#endif -} - + DEBUG( 3, ( "\n" ) ); -#ifdef AFS_AUTH -/******************************************************************* -check on AFS authentication -********************************************************************/ -static BOOL afs_auth(char *this_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, - this_user, - (char *) 0, /* instance */ - (char *) 0, /* cell */ - password, - 0, /* lifetime, default */ - &password_expires, /*days 'til it expires */ - 0, /* spare 2 */ - &reason) == 0) - return(True); - return(False); + return 0; } -#endif - - -#ifdef DFS_AUTH -sec_login_handle_t my_dce_sec_context; -int dcelogin_atmost_once = 0; -/******************************************************************* -check on a DCE/DFS authentication -********************************************************************/ -static BOOL dfs_auth(char *this_user,char *password) +/**************************************************************************** +register a uid/name pair as being valid and that a valid password +has been given. vuid is biased by an offset. This allows us to +tell random client vuid's (normally zero) from valid vuids. +****************************************************************************/ +uint16 register_vuid(uid_t uid,gid_t gid, char *unix_name, char *requested_name, BOOL guest) { - error_status_t err; - int err2; - int prterr; - boolean32 password_reset; - sec_passwd_rec_t my_dce_password; - sec_login_auth_src_t auth_src = sec_login_auth_src_network; - unsigned char dce_errstr[dce_c_error_string_len]; + user_struct *vuser; + struct passwd *pwfile; /* for getting real name from passwd file */ + /* Ensure no vuid gets registered in share level security. */ + if(lp_security() == SEC_SHARE) + return UID_FIELD_INVALID; + +#if 0 /* - * 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! + * After observing MS-Exchange services writing to a Samba share + * I belive this code is incorrect. Each service does its own + * sessionsetup_and_X for the same user, and as each service shuts + * down, it does a user_logoff_and_X. As we are consolidating multiple + * sessionsetup_and_X's onto the same vuid here, when the first service + * shuts down, it invalidates all the open files for the other services. + * Hence I am removing this code and forcing each sessionsetup_and_X + * to get a new vuid. + * Jeremy Allison. (jallison@whistle.com). */ - if (!strcmp((char *)crypt(password,this_salt),this_crypted) || - dcelogin_atmost_once) - return(False); + int i; + for(i = 0; i < num_validated_users; i++) { + vuser = &validated_users[i]; + if ( vuser->uid == uid ) + return (uint16)(i + VUID_OFFSET); /* User already validated */ + } +#endif - if (sec_login_setup_identity( - (unsigned char *)this_user, - sec_login_no_flags, - &my_dce_sec_context, - &err) == 0) + validated_users = (user_struct *)Realloc(validated_users, + sizeof(user_struct)* + (num_validated_users+1)); + + if (!validated_users) { - dce_error_inq_text(err, dce_errstr, &err2); - DEBUG(0,("DCE Setup Identity for %s failed: %s\n", - this_user,dce_errstr)); - return(False); + DEBUG(0,("Failed to realloc users struct!\n")); + num_validated_users = 0; + return UID_FIELD_INVALID; } - my_dce_password.version_number = sec_passwd_c_version_none; - my_dce_password.pepper = NULL; - my_dce_password.key.key_type = sec_passwd_plain; - my_dce_password.key.tagged_union.plain = (idl_char *)password; - - if (sec_login_valid_and_cert_ident(my_dce_sec_context, - &my_dce_password, - &password_reset, - &auth_src, - &err) == 0 ) - { - dce_error_inq_text(err, dce_errstr, &err2); - DEBUG(0,("DCE Identity Validation failed for principal %s: %s\n", - this_user,dce_errstr)); - - return(False); - } + vuser = &validated_users[num_validated_users]; + num_validated_users++; - 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", - this_user,dce_errstr)); - sec_login_purge_context(my_dce_sec_context, &err); - return(False); - } - else - { - DEBUG(0,("DCE login succeeded for principal %s on pid %d\n", - this_user, getpid())); - } + vuser->uid = uid; + vuser->gid = gid; + vuser->guest = guest; + fstrcpy(vuser->name,unix_name); + fstrcpy(vuser->requested_name,requested_name); - dcelogin_atmost_once = 1; - return (True); -} + vuser->n_sids = 0; + vuser->sids = NULL; -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", - getpid(), dce_errstr)); - } -} + vuser->n_groups = 0; + vuser->groups = NULL; -#endif + /* Find all the groups this uid is in and store them. + Used by become_user() */ + setup_groups(unix_name,uid,gid, + &vuser->n_groups, + &vuser->groups); + DEBUG(3,("uid %d registered to name %s\n",(int)uid,unix_name)); -#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); + DEBUG(3, ("Clearing default real name\n")); + fstrcpy(vuser->real_name, "<Full Name>\0"); + if (lp_unix_realname()) { + if ((pwfile=getpwnam(vuser->name))!= NULL) + { + DEBUG(3, ("User name: %s\tReal name: %s\n",vuser->name,pwfile->pw_gecos)); + fstrcpy(vuser->real_name, pwfile->pw_gecos); + } } - - return(1); + + return (uint16)((num_validated_users - 1) + VUID_OFFSET); } -#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 +add a name to the session users list ****************************************************************************/ -static BOOL string_combinations2(char *s,int offset,BOOL (*fn)(),int N) +void add_session_user(char *user) { - int len = strlen(s); - int i; - -#ifdef PASSWORD_LENGTH - len = MIN(len,PASSWORD_LENGTH); -#endif + fstring suser; + StrnCpy(suser,user,sizeof(suser)-1); - if (N <= 0 || offset >= len) - return(fn(s)); + if (!Get_Pwnam(suser,True)) return; - for (i=offset;i<(len-(N-1));i++) - { - char c = s[i]; - if (!islower(c)) continue; - s[i] = toupper(c); - if (string_combinations2(s,i+1,fn,N-1)) - return(True); - s[i] = c; + if (suser && *suser && !in_list(suser,session_users,False)) + { + if (strlen(suser) + strlen(session_users) + 2 >= sizeof(pstring)) + DEBUG(1,("Too many session users??\n")); + else + { + pstrcat(session_users," "); + pstrcat(session_users,suser); + } } - return(False); -} - -/**************************************************************************** -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 BOOL string_combinations(char *s,BOOL (*fn)(),int N) -{ - int n; - for (n=1;n<=N;n++) - if (string_combinations2(s,0,fn,n)) return(True); - return(False); } - /**************************************************************************** -core of password checking routine -****************************************************************************/ -BOOL password_check(char *password) +update the encrypted smbpasswd file from the plaintext username and password +*****************************************************************************/ +static BOOL update_smbpassword_file(char *user, char *password) { -#ifdef AFS_AUTH - if (afs_auth(this_user,password)) return(True); -#endif - -#ifdef DFS_AUTH - if (dfs_auth(this_user,password)) return(True); -#endif + struct smb_passwd *smbpw; + BOOL ret; + + become_root(0); + smbpw = getsmbpwnam(user); + unbecome_root(0); + + if(smbpw == NULL) { + DEBUG(0,("getsmbpwnam returned NULL\n")); + return False; + } + + /* Here, the flag is one, because we want to ignore the + XXXXXXX'd out password */ + ret = change_oem_password( smbpw, password, True); + if (ret == False) { + DEBUG(3,("change_oem_password returned False\n")); + } -#ifdef PWDAUTH - if (pwdauth(this_user,password) == 0) - return(True); -#endif + return ret; +} -#ifdef OSF1_ENH_SEC - return(strcmp(osf1_bigcrypt(password,this_salt),this_crypted) == 0); -#endif -#ifdef ULTRIX_AUTH - return (strcmp((char *)crypt16(password, this_salt ),this_crypted) == 0); -#endif -#ifdef LINUX_BIGCRYPT - return(linux_bigcrypt(password,this_salt,this_crypted)); -#endif -#ifdef NO_CRYPT - DEBUG(1,("Warning - no crypt available\n")); - return(False); -#else - return(strcmp((char *)crypt(password,this_salt),this_crypted) == 0); -#endif -} -#ifdef SMB_PASSWD /**************************************************************************** core of smb password checking routine. ****************************************************************************/ @@ -564,10 +346,10 @@ BOOL smb_password_check(char *password, unsigned char *part_passwd, unsigned cha unsigned char p21[21]; unsigned char p24[24]; - if(part_passwd == NULL) + if (part_passwd == NULL) DEBUG(10,("No password set - allowing access\n")); /* No password set - always true ! */ - if(part_passwd == NULL) + if (part_passwd == NULL) return 1; memset(p21,'\0',21); @@ -596,282 +378,205 @@ BOOL smb_password_check(char *password, unsigned char *part_passwd, unsigned cha #endif return (memcmp(p24, password, 24) == 0); } -#endif /**************************************************************************** -check if a username/password is OK + Do a specific test for an smb password being correct, given a smb_password and + the lanman and NT responses. ****************************************************************************/ -BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd, BOOL is_nt_password) +BOOL smb_password_ok(struct smb_passwd *smb_pass, uchar chal[8], + uchar lm_pass[24], uchar nt_pass[24]) { - pstring pass2; - int level = lp_passwordlevel(); - struct passwd *pass; -#ifdef SMB_PASSWD - char challenge[8]; - struct smb_passwd *smb_pass; - BOOL challenge_done = False; -#endif + uchar challenge[8]; - if (password) password[pwlen] = 0; + if (!lm_pass || !smb_pass) return(False); -#ifdef SMB_PASSWD - if (pwlen == 24) - challenge_done = last_challenge(challenge); -#endif + DEBUG(4,("Checking SMB password for user %s\n", + smb_pass->smb_name)); -#if DEBUG_PASSWORD -#ifdef SMB_PASSWD - if (challenge_done) - { - int i; - DEBUG(100,("checking user=[%s] pass=[",user)); - for( i = 0; i < 24; i++) - DEBUG(100,("%0x ", (unsigned char)password[i])); - DEBUG(100,("]\n")); - } - else -#endif - DEBUG(100,("checking user=[%s] pass=[%s]\n",user,password)); -#endif - - if (!password) - return(False); - - if (((!*password) || (!pwlen)) && !lp_null_passwords()) - return(False); - - if (pwd && !user) - { - pass = (struct passwd *) pwd; - user = pass->pw_name; - } - else - pass = Get_Pwnam(user,True); - -#ifdef SMB_PASSWD - - DEBUG(4,("SMB Password - pwlen = %d, challenge_done = %d\n", pwlen, challenge_done)); - - if((pwlen == 24) && challenge_done) - { - DEBUG(4,("Checking SMB password for user %s (l=24)\n",user)); - - if (!pass) - { - DEBUG(3,("Couldn't find user %s\n",user)); - return(False); + if(smb_pass->acct_ctrl & ACB_DISABLED) { + DEBUG(3,("account for user %s was disabled.\n", + smb_pass->smb_name)); + return(False); } - smb_pass = get_smbpwnam(user); - if(!smb_pass) + if (chal == NULL) { - DEBUG(3,("Couldn't find user %s in smb_passwd file.\n", user)); - return(False); + DEBUG(5,("use last SMBnegprot challenge\n")); + if (!last_challenge(challenge)) + { + DEBUG(1,("no challenge done - password failed\n")); + return False; + } } - - /* Ensure the uid's match */ - if(smb_pass->smb_userid != pass->pw_uid) + else { - DEBUG(3,("Error : UNIX and SMB uids in password files do not match !\n")); - return(False); + DEBUG(5,("challenge received\n")); + memcpy(challenge, chal, 8); } - if(Protocol >= PROTOCOL_NT1 && is_nt_password) - { + if ((Protocol >= PROTOCOL_NT1) && (smb_pass->smb_nt_passwd != NULL)) { /* We have the NT MD4 hash challenge available - see if we can use it (ie. does it exist in the smbpasswd file). */ - if(smb_pass->smb_nt_passwd != NULL) - { - DEBUG(4,("Checking NT MD4 password\n")); - if(smb_password_check(password, smb_pass->smb_nt_passwd, challenge)) - { - update_protected_database(user,True); - return(True); - } - DEBUG(4,("NT MD4 password check failed\n")); - return (False); + DEBUG(4,("smb_password_ok: Checking NT MD4 password\n")); + if (smb_password_check((char *)nt_pass, + (uchar *)smb_pass->smb_nt_passwd, + challenge)) { + DEBUG(4,("NT MD4 password check succeeded\n")); + return(True); } + DEBUG(4,("NT MD4 password check failed\n")); } - /* Try against the lanman password */ + /* Try against the lanman password. smb_pass->smb_passwd == NULL means + no password, allow access. */ - if(smb_password_check(password, smb_pass->smb_passwd, challenge)) - { - update_protected_database(user,True); - return(True); - } + DEBUG(4,("Checking LM MD4 password\n")); - DEBUG(3,("Error smb_password_check failed\n")); - } -#endif + if((smb_pass->smb_passwd == NULL) && + (smb_pass->acct_ctrl & ACB_PWNOTREQ)) { + DEBUG(4,("no password required for user %s\n", + smb_pass->smb_name)); + return True; + } - DEBUG(4,("Checking password for user %s (l=%d)\n",user,pwlen)); + if((smb_pass->smb_passwd != NULL) && + smb_password_check((char *)lm_pass, + (uchar *)smb_pass->smb_passwd, challenge)) { + DEBUG(4,("LM MD4 password check succeeded\n")); + return(True); + } - if (!pass) - { - DEBUG(3,("Couldn't find user %s\n",user)); - return(False); - } + DEBUG(4,("LM MD4 password check failed\n")); -#ifdef SHADOW_PWD - { - struct spwd *spass; + return False; +} - /* 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) - pass->pw_passwd = spass->sp_pwdp; - } -#endif +/**************************************************************************** +check if a username/password is OK assuming the password is a 24 byte +SMB hash +return True if the password is correct, False otherwise +****************************************************************************/ -#ifdef SecureWare - { - struct pr_passwd *pr_pw = getprpwnam(pass->pw_name); - if (pr_pw && pr_pw->ufld.fd_encrypt) - pass->pw_passwd = pr_pw->ufld.fd_encrypt; - } -#endif +BOOL pass_check_smb(char *user, char *domain, + uchar *chal, uchar *lm_pwd, uchar *nt_pwd, + struct passwd *pwd) +{ + struct passwd *pass; + struct smb_passwd *smb_pass; -#ifdef HPUX_10_TRUSTED - { - struct pr_passwd *pr_pw = getprpwnam(pass->pw_name); - if (pr_pw && pr_pw->ufld.fd_encrypt) - pass->pw_passwd = pr_pw->ufld.fd_encrypt; - } -#endif + if (!lm_pwd || !nt_pwd) + { + return(False); + } -#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 ) - { - strcpy(pass->pw_name,mypasswd->ufld.fd_name); - strcpy(pass->pw_passwd,mypasswd->ufld.fd_encrypt); - } - else - { - DEBUG(5,("No entry for user %s in protected database !\n",user)); - return(False); - } - } -#endif + if (pwd != NULL && user == NULL) + { + pass = (struct passwd *) pwd; + user = pass->pw_name; + } + else + { + pass = Get_Pwnam(user,True); + } -#ifdef ULTRIX_AUTH - { - AUTHORIZATION *ap = getauthuid( pass->pw_uid ); - if (ap) - { - strcpy( pass->pw_passwd, ap->a_password ); - endauthent(); - } - } -#endif + if (pass == NULL) + { + DEBUG(3,("Couldn't find user %s\n",user)); + return(False); + } - /* extract relevant info */ - strcpy(this_user,pass->pw_name); - strcpy(this_salt,pass->pw_passwd); - strcpy(this_crypted,pass->pw_passwd); - - if (!*this_crypted) { - if (!lp_null_passwords()) { - DEBUG(2,("Disallowing access to %s due to null password\n",this_user)); - return(False); - } -#ifndef PWDAUTH - if (!*password) { - DEBUG(3,("Allowing access to %s with null password\n",this_user)); - return(True); - } -#endif - } + smb_pass = getsmbpwnam(user); - /* try it as it came to us */ - if (password_check(password)) - { - update_protected_database(user,True); - return(True); - } + if (smb_pass == NULL) + { + DEBUG(3,("Couldn't find user %s in smb_passwd file.\n", user)); + return(False); + } - /* 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(False); + /* Quit if the account was disabled. */ + if(smb_pass->acct_ctrl & ACB_DISABLED) { + DEBUG(3,("account for user %s was disabled.\n", user)); + return(False); + } - /* make a copy of it */ - StrnCpy(pass2,password,sizeof(pstring)-1); - - /* try all lowercase */ - strlower(password); - if (password_check(password)) - { - update_protected_database(user,True); - return(True); - } + /* Ensure the uid's match */ + if (smb_pass->smb_userid != pass->pw_uid) + { + DEBUG(3,("Error : UNIX and SMB uids in password files do not match !\n")); + return(False); + } - /* give up? */ - if(level < 1) - { - update_protected_database(user,False); + if (lm_pwd[0] == '\0' && IS_BITS_SET_ALL(smb_pass->acct_ctrl, ACB_PWNOTREQ) && lp_null_passwords()) + { + DEBUG(3,("account for user %s has no password and null passwords are allowed.\n", smb_pass->smb_name)); + return(True); + } - /* restore it */ - strcpy(password,pass2); + if (smb_password_ok(smb_pass, chal, lm_pwd, nt_pwd)) + { + return(True); + } + + DEBUG(3,("Error smb_password_check failed\n")); + return False; +} - return(False); - } +/**************************************************************************** +check if a username/password pair is OK either via the system password +database or the encrypted SMB password database +return True if the password is correct, False otherwise +****************************************************************************/ +BOOL password_ok(char *user, char *password, int pwlen, struct passwd *pwd) +{ + if (pwlen == 24 || (lp_encrypted_passwords() && (pwlen == 0) && lp_null_passwords())) + { + /* if 24 bytes long assume it is an encrypted password */ + uchar challenge[8]; - /* last chance - all combinations of up to level chars upper! */ - strlower(password); + if (!last_challenge(challenge)) + { + DEBUG(0,("Error: challenge not done for user=%s\n", user)); + return False; + } - if (string_combinations(password,password_check,level)) - { - update_protected_database(user,True); - return(True); - } + return pass_check_smb(user, global_myworkgroup, + challenge, (uchar *)password, (uchar *)password, pwd); + } - update_protected_database(user,False); - - /* restore it */ - strcpy(password,pass2); - - return(False); + return pass_check(user, password, pwlen, pwd, + lp_update_encrypted() ? + update_smbpassword_file : NULL); } - - /**************************************************************************** check if a username is valid ****************************************************************************/ BOOL user_ok(char *user,int snum) { - pstring valid, invalid; - BOOL ret; - - StrnCpy(valid, lp_valid_users(snum), sizeof(pstring)); - StrnCpy(invalid, lp_invalid_users(snum), sizeof(pstring)); - - string_sub(valid,"%S",lp_servicename(snum)); - string_sub(invalid,"%S",lp_servicename(snum)); - - ret = !user_in_list(user,invalid); - - if (ret && valid && *valid) - ret = user_in_list(user,valid); + pstring valid, invalid; + BOOL ret; + + StrnCpy(valid, lp_valid_users(snum), sizeof(pstring)); + StrnCpy(invalid, lp_invalid_users(snum), sizeof(pstring)); + + string_sub(valid,"%S",lp_servicename(snum)); + string_sub(invalid,"%S",lp_servicename(snum)); + + ret = !user_in_list(user,invalid); + + if (ret && valid && *valid) { + ret = user_in_list(user,valid); + } - if (ret && lp_onlyuser(snum)) { - char *user_list = lp_username(snum); - string_sub(user_list,"%S",lp_servicename(snum)); - ret = user_in_list(user,user_list); - } + if (ret && lp_onlyuser(snum)) { + char *user_list = lp_username(snum); + string_sub(user_list,"%S",lp_servicename(snum)); + ret = user_in_list(user,user_list); + } - return(ret); + return(ret); } @@ -882,14 +587,14 @@ validate a group username entry. Return the username or NULL ****************************************************************************/ static char *validate_group(char *group,char *password,int pwlen,int snum) { -#ifdef NETGROUP +#ifdef HAVE_NETGROUP { char *host, *user, *domain; setnetgrent(group); while (getnetgrent(&host, &user, &domain)) { if (user) { if (user_ok(user, snum) && - password_ok(user,password,pwlen,NULL,False)) { + password_ok(user,password,pwlen,NULL)) { endnetgrent(); return(user); } @@ -899,7 +604,7 @@ static char *validate_group(char *group,char *password,int pwlen,int snum) } #endif -#if HAVE_GETGRNAM +#ifdef HAVE_GETGRNAM { struct group *gptr = (struct group *)getgrnam(group); char **member; @@ -909,9 +614,9 @@ static char *validate_group(char *group,char *password,int pwlen,int snum) while (member && *member) { static fstring name; - strcpy(name,*member); + fstrcpy(name,*member); if (user_ok(name,snum) && - password_ok(name,password,pwlen,NULL,False)) + password_ok(name,password,pwlen,NULL)) return(&name[0]); member++; } @@ -924,8 +629,8 @@ static char *validate_group(char *group,char *password,int pwlen,int snum) while (pwd = getpwent ()) { if (*(pwd->pw_passwd) && pwd->pw_gid == gptr->gr_gid) { /* This Entry have PASSWORD and same GID then check pwd */ - if (password_ok(NULL, password, pwlen, pwd,False)) { - strcpy(tm, pwd->pw_name); + if (password_ok(NULL, password, pwlen, pwd)) { + fstrcpy(tm, pwd->pw_name); endpwent (); return tm; } @@ -946,7 +651,7 @@ static char *validate_group(char *group,char *password,int pwlen,int snum) check for authority to login to a service with a given username/password ****************************************************************************/ BOOL authorise_login(int snum,char *user,char *password, int pwlen, - BOOL *guest,BOOL *force,int vuid) + BOOL *guest,BOOL *force,uint16 vuid) { BOOL ok = False; @@ -956,7 +661,7 @@ BOOL authorise_login(int snum,char *user,char *password, int pwlen, DEBUG(100,("checking authorisation on user=%s pass=%s\n",user,password)); #endif - /* there are several possabilities: + /* there are several possibilities: 1) login as the given user with given password 2) login as a previously registered username with the given password 3) login as a session list username with the given password @@ -973,18 +678,20 @@ BOOL authorise_login(int snum,char *user,char *password, int pwlen, if (!(GUEST_ONLY(snum) && GUEST_OK(snum))) { + user_struct *vuser = get_valid_user_struct(vuid); + /* check the given username and password */ if (!ok && (*user) && user_ok(user,snum)) { - ok = password_ok(user,password, pwlen, NULL, False); + ok = password_ok(user,password, pwlen, NULL); if (ok) DEBUG(3,("ACCEPTED: given username password ok\n")); } /* check for a previously registered guest username */ - if (!ok && (vuid >= 0) && validated_users[vuid].guest) { - if (user_ok(validated_users[vuid].name,snum) && - password_ok(validated_users[vuid].name, password, pwlen, NULL, False)) { - strcpy(user, validated_users[vuid].name); - validated_users[vuid].guest = False; + if (!ok && (vuser != 0) && vuser->guest) { + if (user_ok(vuser->name,snum) && + password_ok(vuser->name, password, pwlen, NULL)) { + fstrcpy(user, vuser->name); + vuser->guest = False; DEBUG(3,("ACCEPTED: given password with registered user %s\n", user)); ok = True; } @@ -992,38 +699,38 @@ BOOL authorise_login(int snum,char *user,char *password, int pwlen, /* now check the list of session users */ - if (!ok) - { - char *auser; - char *user_list = strdup(session_users); - if (!user_list) return(False); + if (!ok) + { + char *auser; + char *user_list = strdup(session_users); + if (!user_list) return(False); - for (auser=strtok(user_list,LIST_SEP); - !ok && auser; - auser = strtok(NULL,LIST_SEP)) - { - fstring user2; - strcpy(user2,auser); - if (!user_ok(user2,snum)) continue; + for (auser=strtok(user_list,LIST_SEP); + !ok && auser; + auser = strtok(NULL,LIST_SEP)) + { + fstring user2; + fstrcpy(user2,auser); + if (!user_ok(user2,snum)) continue; - if (password_ok(user2,password, pwlen, NULL, False)) { - ok = True; - strcpy(user,user2); - DEBUG(3,("ACCEPTED: session list username and given password ok\n")); - } - } - free(user_list); - } - - /* check for a previously validated username/password pair */ - if (!ok && !lp_revalidate(snum) && - (vuid >= 0) && !validated_users[vuid].guest && - user_ok(validated_users[vuid].name,snum)) { - strcpy(user,validated_users[vuid].name); - *guest = False; - DEBUG(3,("ACCEPTED: validated uid ok as non-guest\n")); - ok = True; + if (password_ok(user2,password, pwlen, NULL)) { + ok = True; + fstrcpy(user,user2); + DEBUG(3,("ACCEPTED: session list username and given password ok\n")); + } } + free(user_list); + } + + /* check for a previously validated username/password pair */ + if (!ok && (!lp_revalidate(snum) || lp_security() > SEC_SHARE) && + (vuser != 0) && !vuser->guest && + user_ok(vuser->name,snum)) { + fstrcpy(user,vuser->name); + *guest = False; + DEBUG(3,("ACCEPTED: validated uid ok as non-guest\n")); + ok = True; + } /* check for a rhosts entry */ if (!ok && user_ok(user,snum) && check_hosts_equiv(user)) { @@ -1049,19 +756,19 @@ BOOL authorise_login(int snum,char *user,char *password, int pwlen, if (auser) { ok = True; - strcpy(user,auser); + fstrcpy(user,auser); DEBUG(3,("ACCEPTED: group username and given password ok\n")); } } else { fstring user2; - strcpy(user2,auser); + fstrcpy(user2,auser); if (user_ok(user2,snum) && - password_ok(user2,password,pwlen,NULL, False)) + password_ok(user2,password,pwlen,NULL)) { ok = True; - strcpy(user,user2); + fstrcpy(user,user2); DEBUG(3,("ACCEPTED: user list username and given password ok\n")); } } @@ -1076,7 +783,7 @@ BOOL authorise_login(int snum,char *user,char *password, int pwlen, StrnCpy(guestname,lp_guestaccount(snum),sizeof(guestname)-1); if (Get_Pwnam(guestname,True)) { - strcpy(user,guestname); + fstrcpy(user,guestname); ok = True; DEBUG(3,("ACCEPTED: guest account and guest ok\n")); } @@ -1147,25 +854,25 @@ static BOOL check_user_equiv(char *user, char *remote, char *equiv_file) } file_host = strtok(bp, " \t\n"); file_user = strtok(NULL, " \t\n"); - DEBUG(7, ("check_user_equiv %s %s\n", file_host, file_user)); + DEBUG(7, ("check_user_equiv %s %s\n", file_host ? file_host : "(null)", + file_user ? file_user : "(null)" )); if (file_host && *file_host) { BOOL host_ok = False; -#ifdef NETGROUP - /* THIS IS UNTESTED!! */ +#if defined(HAVE_NETGROUP) && defined(HAVE_YP_GET_DEFAULT_DOMAIN) if (is_group) { static char *mydomain = NULL; if (!mydomain) yp_get_default_domain(&mydomain); - if (mydomain && innetgr(remote,file_host,user,mydomain)) + if (mydomain && innetgr(file_host,remote,user,mydomain)) host_ok = True; } #else if (is_group) { - DEBUG(1,("Netgroups not configured - add -DNETGROUP and recompile\n")); + DEBUG(1,("Netgroups not configured\n")); continue; } #endif @@ -1210,207 +917,477 @@ BOOL check_hosts_equiv(char *user) pstring rhostsfile; struct passwd *pass = Get_Pwnam(user,True); - extern struct from_host Client_info; - extern int Client; - if (!pass) return(False); - fromhost(Client,&Client_info); - fname = lp_hosts_equiv(); /* note: don't allow hosts.equiv on root */ - if (fname && *fname && (pass->pw_uid != 0)) - { - if (check_user_equiv(user,Client_info.name,fname)) - return(True); - } + if (fname && *fname && (pass->pw_uid != 0)) { + extern int Client; + if (check_user_equiv(user,client_name(Client),fname)) + return(True); + } if (lp_use_rhosts()) { char *home = get_home_dir(user); - if (home) - { - sprintf(rhostsfile, "%s/.rhosts", home); - if (check_user_equiv(user,Client_info.name,rhostsfile)) - return(True); - } + if (home) { + extern int Client; + slprintf(rhostsfile, sizeof(rhostsfile)-1, "%s/.rhosts", home); + if (check_user_equiv(user,client_name(Client),rhostsfile)) + return(True); + } } return(False); } -static int password_client = -1; -static fstring pserver; +/**************************************************************************** +return the client state structure +****************************************************************************/ +struct cli_state *server_client(void) +{ + static struct cli_state pw_cli; + return &pw_cli; +} /**************************************************************************** -attempted support for server level security +support for server level security ****************************************************************************/ -BOOL server_cryptkey(char *buf) +struct cli_state *server_cryptkey(void) { - pstring inbuf,outbuf; - fstring pass_protocol; - extern fstring remote_machine; - char *p; - int len; - fstring desthost; - struct in_addr dest_ip; - extern struct in_addr myip; - int port = 139; - BOOL ret; + struct cli_state *cli; + fstring desthost; + struct in_addr dest_ip; + extern fstring local_machine; + char *p; + BOOL connected_ok = False; + struct nmb_name calling, called; + + cli = server_client(); + + if (!cli_initialise(cli)) + return NULL; + + p = lp_passwordserver(); + while(p && next_token( &p, desthost, LIST_SEP, sizeof(desthost))) { + standard_sub_basic(desthost); + strupper(desthost); + + if(!resolve_name( desthost, &dest_ip, 0x20)) { + DEBUG(1,("server_cryptkey: Can't resolve address for %s\n",desthost)); + continue; + } + + if (ismyip(dest_ip)) { + DEBUG(1,("Password server loop - disabling password server %s\n",desthost)); + continue; + } - if (password_client >= 0) - close(password_client); - password_client = -1; + if (cli_connect(cli, desthost, &dest_ip)) { + DEBUG(3,("connected to password server %s\n",desthost)); + connected_ok = True; + break; + } + } - if (Protocol < PROTOCOL_NT1) { - strcpy(pass_protocol,"LM1.2X002"); - } else { - strcpy(pass_protocol,"NT LM 0.12"); - } + if (!connected_ok) { + DEBUG(0,("password server not available\n")); + cli_shutdown(cli); + return NULL; + } - bzero(inbuf,sizeof(inbuf)); - bzero(outbuf,sizeof(outbuf)); + make_nmb_name(&calling, local_machine, 0x0 , scope); + make_nmb_name(&called , desthost , 0x20, scope); - for (p=strtok(lp_passwordserver(),LIST_SEP); p ; p = strtok(NULL,LIST_SEP)) { - strcpy(desthost,p); - standard_sub_basic(desthost); - strupper(desthost); + if (!cli_session_request(cli, &calling, &called)) + { + DEBUG(1,("%s rejected the session\n",desthost)); + cli_shutdown(cli); + return NULL; + } - dest_ip = *interpret_addr2(desthost); - if (zero_ip(dest_ip)) { - DEBUG(1,("Can't resolve address for %s\n",p)); - continue; - } + DEBUG(3,("got session\n")); - if (memcmp(&dest_ip,&myip,sizeof(dest_ip)) == 0) { - DEBUG(1,("Password server loop - disabling password server %s\n",p)); - continue; - } + if (!cli_negprot(cli)) { + DEBUG(1,("%s rejected the negprot\n",desthost)); + cli_shutdown(cli); + return NULL; + } - password_client = open_socket_out(SOCK_STREAM, &dest_ip, port); - if (password_client >= 0) { - DEBUG(3,("connected to password server %s\n",p)); - StrnCpy(pserver,p,sizeof(pserver)-1); - break; - } - } + if (cli->protocol < PROTOCOL_LANMAN2 || + !(cli->sec_mode & 1)) { + DEBUG(1,("%s isn't in user level security mode\n",desthost)); + cli_shutdown(cli); + return NULL; + } - if (password_client < 0) { - DEBUG(1,("password server not available\n")); - return(False); - } + DEBUG(3,("password server OK\n")); + return cli; +} - /* send a session request (RFC 8002) */ +/**************************************************************************** +validate a password with the password server +****************************************************************************/ +BOOL server_validate(char *user, char *domain, + char *pass, int passlen, + char *ntpass, int ntpasslen) +{ + struct cli_state *cli; + extern fstring local_machine; + static unsigned char badpass[24]; + cli = server_client(); + + if (!cli->initialised) { + DEBUG(1,("password server %s is not connected\n", cli->desthost)); + return(False); + } + + if(badpass[0] == 0) { + memset(badpass, 0x1f, sizeof(badpass)); + } + + if((passlen == sizeof(badpass)) && !memcmp(badpass, pass, passlen)) { + /* Very unlikely, our random bad password is the same as the users + password. */ + memset(badpass, badpass[0]+1, sizeof(badpass)); + } + + /* + * Attempt a session setup with a totally incorrect password. + * If this succeeds with the guest bit *NOT* set then the password + * server is broken and is not correctly setting the guest bit. We + * need to detect this as some versions of NT4.x are broken. JRA. + */ + + if (cli_session_setup(cli, user, (char *)badpass, sizeof(badpass), + (char *)badpass, sizeof(badpass), domain)) { + if ((SVAL(cli->inbuf,smb_vwv2) & 1) == 0) { + DEBUG(0,("server_validate: password server %s allows users as non-guest \ +with a bad password.\n", cli->desthost)); + DEBUG(0,("server_validate: This is broken (and insecure) behaviour. Please do not \ +use this machine as the password server.\n")); + cli_ulogoff(cli); + return False; + } + cli_ulogoff(cli); + } + + /* + * Now we know the password server will correctly set the guest bit, or is + * not guest enabled, we can try with the real password. + */ + + if (!cli_session_setup(cli, user, pass, passlen, ntpass, ntpasslen, domain)) { + DEBUG(1,("password server %s rejected the password\n", cli->desthost)); + return False; + } - /* put in the destination name */ - len = 4; - p = outbuf+len; - name_mangle(desthost,p,' '); - len += name_len(p); + /* if logged in as guest then reject */ + if ((SVAL(cli->inbuf,smb_vwv2) & 1) != 0) { + DEBUG(1,("password server %s gave us guest only\n", cli->desthost)); + cli_ulogoff(cli); + return(False); + } - /* and my name */ - p = outbuf+len; - name_mangle(remote_machine,p,' '); - len += name_len(p); + /* + * This patch from Rob Nielsen <ran@adc.com> makes doing + * the NetWksaUserLogon a dynamic, rather than compile-time + * parameter, defaulting to on. This is somewhat dangerous + * as it allows people to turn off this neccessary check, + * but so many people have had problems with this that I + * think it is a neccessary change. JRA. + */ + + if (lp_net_wksta_user_logon()) { + DEBUG(3,("trying NetWkstaUserLogon with password server %s\n", cli->desthost)); + + if (!cli_send_tconX(cli, "IPC$", "IPC", "", 1)) { + DEBUG(0,("password server %s refused IPC$ connect\n", cli->desthost)); + cli_ulogoff(cli); + return False; + } + + if (!cli_NetWkstaUserLogon(cli,user,local_machine)) { + DEBUG(0,("password server %s failed NetWkstaUserLogon\n", cli->desthost)); + cli_tdis(cli); + cli_ulogoff(cli); + return False; + } - _smb_setlen(outbuf,len); - CVAL(outbuf,0) = 0x81; + if (cli->privilages == 0) { + DEBUG(0,("password server %s gave guest privilages\n", cli->desthost)); + cli_tdis(cli); + cli_ulogoff(cli); + return False; + } - send_smb(password_client,outbuf); - receive_smb(password_client,inbuf,5000); - - if (CVAL(inbuf,0) != 0x82) { - DEBUG(1,("%s rejected the session\n",pserver)); - close(password_client); password_client = -1; - return(False); - } + if (!strequal(cli->eff_name, user)) { + DEBUG(0,("password server %s gave different username %s\n", + cli->desthost, + cli->eff_name)); + cli_tdis(cli); + cli_ulogoff(cli); + return False; + } + cli_tdis(cli); + } + else { + DEBUG(3,("skipping NetWkstaUserLogon with password server %s\n", cli->desthost)); + } - DEBUG(3,("got session\n")); + DEBUG(3,("password server %s accepted the password\n", cli->desthost)); - bzero(outbuf,smb_size); + cli_ulogoff(cli); - /* setup the protocol string */ - set_message(outbuf,0,strlen(pass_protocol)+2,True); - p = smb_buf(outbuf); - *p++ = 2; - strcpy(p,pass_protocol); + return(True); +} - CVAL(outbuf,smb_com) = SMBnegprot; - CVAL(outbuf,smb_flg) = 0x8; - SSVAL(outbuf,smb_flg2,0x1); +/*********************************************************************** + Do the same as security=server, but using NT Domain calls and a session + key from the machine password. +************************************************************************/ - send_smb(password_client,outbuf); - ret = receive_smb(password_client,inbuf,5000); +BOOL domain_client_validate( char *user, char *domain, + char *smb_apasswd, int smb_apasslen, + char *smb_ntpasswd, int smb_ntpasslen) +{ + unsigned char local_challenge[8]; + unsigned char local_lm_response[24]; + unsigned char local_nt_reponse[24]; + unsigned char trust_passwd[16]; + fstring remote_machine; + char *p; + struct in_addr dest_ip; + NET_ID_INFO_CTR ctr; + NET_USER_INFO_3 info3; + struct cli_state cli; + uint32 smb_uid_low; + BOOL connected_ok = False; + struct nmb_name calling, called; + + /* + * Check that the requested domain is not our own machine name. + * If it is, we should never check the PDC here, we use our own local + * password file. + */ - if (!ret || CVAL(inbuf,smb_rcls) || SVAL(inbuf,smb_vwv0)) { - DEBUG(1,("%s rejected the protocol\n",pserver)); - close(password_client); password_client= -1; - return(False); + if(strequal( domain, global_myname)) { + DEBUG(3,("domain_client_validate: Requested domain was for this machine.\n")); + return False; } - if (!(CVAL(inbuf,smb_vwv1) & 1)) { - DEBUG(1,("%s isn't in user level security mode\n",pserver)); - close(password_client); password_client= -1; - return(False); + /* + * Next, check that the passwords given were encrypted. + */ + + if(((smb_apasslen != 24) && (smb_apasslen != 0)) || + ((smb_ntpasslen != 24) && (smb_ntpasslen != 0))) { + + /* + * Not encrypted - do so. + */ + + DEBUG(3,("domain_client_validate: User passwords not in encrypted format.\n")); + generate_random_buffer( local_challenge, 8, False); + SMBencrypt( (uchar *)smb_apasswd, local_challenge, local_lm_response); + SMBNTencrypt((uchar *)smb_ntpasswd, local_challenge, local_nt_reponse); + smb_apasslen = 24; + smb_ntpasslen = 24; + smb_apasswd = (char *)local_lm_response; + smb_ntpasswd = (char *)local_nt_reponse; + } else { + + /* + * Encrypted - get the challenge we sent for these + * responses. + */ + + if (!last_challenge(local_challenge)) { + DEBUG(0,("domain_client_validate: no challenge done - password failed\n")); + return False; + } + } + + /* + * Get the machine account password. + */ + if (!trust_get_passwd( trust_passwd, global_myworkgroup, global_myname)) + { + return False; + } + + /* + * At this point, smb_apasswd points to the lanman response to + * the challenge in local_challenge, and smb_ntpasswd points to + * the NT response to the challenge in local_challenge. Ship + * these over the secure channel to a domain controller and + * see if they were valid. + */ + + ZERO_STRUCT(cli); + + if(cli_initialise(&cli) == False) { + DEBUG(0,("domain_client_validate: unable to initialize client connection.\n")); + return False; } - memcpy(buf,inbuf,smb_len(inbuf)+4); + /* + * Treat each name in the 'password server =' line as a potential + * PDC/BDC. Contact each in turn and try and authenticate. + */ - DEBUG(3,("password server OK\n")); + p = lp_passwordserver(); + while(p && next_token(&p,remote_machine,LIST_SEP,sizeof(remote_machine))) { - return(True); -} + standard_sub_basic(remote_machine); + strupper(remote_machine); + + if(!resolve_name( remote_machine, &dest_ip, 0x20)) { + DEBUG(1,("domain_client_validate: Can't resolve address for %s\n", remote_machine)); + continue; + } + + if (ismyip(dest_ip)) { + DEBUG(1,("domain_client_validate: Password server loop - not using password server %s\n",remote_machine)); + continue; + } + + if (!cli_connect(&cli, remote_machine, &dest_ip)) { + DEBUG(0,("domain_client_validate: unable to connect to SMB server on \ +machine %s. Error was : %s.\n", remote_machine, cli_errstr(&cli) )); + continue; + } + + make_nmb_name(&calling, global_myname , 0x0 , scope); + make_nmb_name(&called , remote_machine, 0x20, scope); -/**************************************************************************** -attempted support for server level security -****************************************************************************/ -BOOL server_validate(char *buf) -{ - pstring inbuf,outbuf; - BOOL ret; + if (!cli_session_request(&cli, &calling, &called)) + { + DEBUG(0,("domain_client_validate: machine %s rejected the session setup. \ +Error was : %s.\n", remote_machine, cli_errstr(&cli) )); + cli_shutdown(&cli); + continue; + } + + cli.protocol = PROTOCOL_NT1; - if (password_client < 0) { - DEBUG(1,("%s not connected\n",pserver)); - return(False); - } + if (!cli_negprot(&cli)) { + DEBUG(0,("domain_client_validate: machine %s rejected the negotiate protocol. \ +Error was : %s.\n", remote_machine, cli_errstr(&cli) )); + cli_shutdown(&cli); + continue; + } + + if (cli.protocol != PROTOCOL_NT1) { + DEBUG(0,("domain_client_validate: machine %s didn't negotiate NT protocol.\n", + remote_machine)); + cli_shutdown(&cli); + continue; + } - bzero(inbuf,sizeof(inbuf)); - memcpy(outbuf,buf,sizeof(outbuf)); + /* + * Do an anonymous session setup. + */ - /* send a session setup command */ - CVAL(outbuf,smb_flg) = 0x8; - SSVAL(outbuf,smb_flg2,0x1); - CVAL(outbuf,smb_vwv0) = 0xFF; + if (!cli_session_setup(&cli, "", "", 0, "", 0, "")) { + DEBUG(0,("domain_client_validate: machine %s rejected the session setup. \ +Error was : %s.\n", remote_machine, cli_errstr(&cli) )); + cli_shutdown(&cli); + continue; + } - set_message(outbuf,smb_numwords(outbuf),smb_buflen(outbuf),False); + if (!(cli.sec_mode & 1)) { + DEBUG(1,("domain_client_validate: machine %s isn't in user level security mode\n", + remote_machine)); + cli_shutdown(&cli); + continue; + } - SCVAL(inbuf,smb_rcls,1); + if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) { + DEBUG(0,("domain_client_validate: machine %s rejected the tconX on the IPC$ share. \ +Error was : %s.\n", remote_machine, cli_errstr(&cli) )); + cli_shutdown(&cli); + continue; + } - send_smb(password_client,outbuf); - ret = receive_smb(password_client,inbuf,5000); + /* + * We have an anonymous connection to IPC$. + */ + connected_ok = True; + break; + } - if (!ret || CVAL(inbuf,smb_rcls) != 0) { - DEBUG(1,("password server %s rejected the password\n",pserver)); - return(False); + if (!connected_ok) { + DEBUG(0,("domain_client_validate: Domain password server not available.\n")); + cli_shutdown(&cli); + return False; } - /* if logged in as guest then reject */ - if ((SVAL(inbuf,smb_vwv2) & 1) != 0) { - DEBUG(1,("password server %s gave us guest only\n",pserver)); - return(False); + /* + * Ok - we have an anonymous connection to the IPC$ share. + * Now start the NT Domain stuff :-). + */ + + if(cli_nt_session_open(&cli, PIPE_NETLOGON) == False) { + DEBUG(0,("domain_client_validate: unable to open the domain client session to \ +machine %s. Error was : %s.\n", remote_machine, cli_errstr(&cli))); + cli_nt_session_close(&cli); + cli_ulogoff(&cli); + cli_shutdown(&cli); + return False; } - DEBUG(3,("password server %s accepted the password\n",pserver)); + if(cli_nt_setup_creds(&cli, trust_passwd) == False) { + DEBUG(0,("domain_client_validate: unable to setup the PDC credentials to machine \ +%s. Error was : %s.\n", remote_machine, cli_errstr(&cli))); + cli_nt_session_close(&cli); + cli_ulogoff(&cli); + cli_shutdown(&cli); + return False; + } -#ifndef KEEP_PASSWORD_SERVER_OPEN - close(password_client); password_client= -1; -#endif + /* We really don't care what LUID we give the user. */ + generate_random_buffer( (unsigned char *)&smb_uid_low, 4, False); + + if(cli_nt_login_network(&cli, domain, user, smb_uid_low, (char *)local_challenge, + ((smb_apasslen != 0) ? smb_apasswd : NULL), + ((smb_ntpasslen != 0) ? smb_ntpasswd : NULL), + &ctr, &info3) == False) { + DEBUG(0,("domain_client_validate: unable to validate password for user %s in domain \ +%s to Domain controller %s. Error was %s.\n", user, domain, remote_machine, cli_errstr(&cli))); + cli_nt_session_close(&cli); + cli_ulogoff(&cli); + cli_shutdown(&cli); + return False; + } - return(True); -} + /* + * Here, if we really want it, we have lots of info about the user in info3. + */ + +#if 0 + /* + * We don't actually need to do this - plus it fails currently with + * NT_STATUS_INVALID_INFO_CLASS - we need to know *exactly* what to + * send here. JRA. + */ + if(cli_nt_logoff(&cli, &ctr) == False) { + DEBUG(0,("domain_client_validate: unable to log off user %s in domain \ +%s to Domain controller %s. Error was %s.\n", user, domain, remote_machine, cli_errstr(&cli))); + cli_nt_session_close(&cli); + cli_ulogoff(&cli); + cli_shutdown(&cli); + return False; + } +#endif /* 0 */ + cli_nt_session_close(&cli); + cli_ulogoff(&cli); + cli_shutdown(&cli); + return True; +} |