2 Unix SMB/Netbios implementation.
4 Password and authentication handling
5 Copyright (C) Andrew Tridgell 1992-1997
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #if (defined(NETGROUP) && defined (AUTOMOUNT))
25 #include "rpcsvc/ypclnt.h"
28 extern int DEBUGLEVEL;
31 /* users from session setup */
32 static pstring session_users="";
34 /* these are kept here to keep the string_combinations function simple */
35 static char this_user[100]="";
36 static char this_salt[100]="";
37 static char this_crypted[100]="";
39 /* Data to do lanman1/2 password challenge. */
40 static unsigned char saved_challenge[8];
41 static BOOL challenge_sent=False;
43 /*******************************************************************
44 Get the next challenge value - no repeats.
45 ********************************************************************/
46 void generate_next_challenge(char *challenge)
48 unsigned char buf[16];
49 static int counter = 0;
53 /* get a sort-of random number */
55 v1 = (counter++) + getpid() + tval.tv_sec;
56 v2 = (counter++) * getpid() + tval.tv_usec;
57 SIVAL(challenge,0,v1);
58 SIVAL(challenge,4,v2);
60 /* mash it up with md4 */
61 mdfour(buf, (unsigned char *)challenge, 8);
63 memcpy(saved_challenge, buf, 8);
64 memcpy(challenge,buf,8);
65 challenge_sent = True;
68 /*******************************************************************
69 set the last challenge sent, usually from a password server
70 ********************************************************************/
71 BOOL set_challenge(char *challenge)
73 memcpy(saved_challenge,challenge,8);
74 challenge_sent = True;
78 /*******************************************************************
79 get the last challenge sent
80 ********************************************************************/
81 BOOL last_challenge(char *challenge)
83 if (!challenge_sent) return(False);
84 memcpy(challenge,saved_challenge,8);
88 /* this holds info on user ids that are already validated for this VC */
89 static user_struct *validated_users = NULL;
90 static int num_validated_users = 0;
92 /****************************************************************************
93 check if a uid has been validated, and return an pointer to the user_struct
94 if it has. NULL if not. vuid is biased by an offset. This allows us to
95 tell random client vuid's (normally zero) from valid vuids.
96 ****************************************************************************/
97 user_struct *get_valid_user_struct(uint16 vuid)
99 if (vuid == UID_FIELD_INVALID)
102 if ((vuid >= (uint16)num_validated_users) ||
103 (validated_users[vuid].uid == -1) || (validated_users[vuid].gid == -1))
105 return &validated_users[vuid];
108 /****************************************************************************
110 ****************************************************************************/
111 void invalidate_vuid(uint16 vuid)
113 user_struct *vuser = get_valid_user_struct(vuid);
115 if (vuser == NULL) return;
122 /* same number of igroups as groups as attrs */
125 if (vuser->groups && (vuser->groups != (gid_t *)vuser->igroups))
128 if (vuser->igroups) free(vuser->igroups);
129 if (vuser->attrs ) free(vuser->attrs);
130 if (vuser->sids ) free(vuser->sids);
134 vuser->igroups = NULL;
135 vuser->groups = NULL;
139 /****************************************************************************
140 return a validated username
141 ****************************************************************************/
142 char *validated_username(uint16 vuid)
144 user_struct *vuser = get_valid_user_struct(vuid);
150 /****************************************************************************
151 register a uid/name pair as being valid and that a valid password
152 has been given. vuid is biased by an offset. This allows us to
153 tell random client vuid's (normally zero) from valid vuids.
154 ****************************************************************************/
155 uint16 register_vuid(int uid,int gid, char *name,BOOL guest)
159 #if (defined(NETGROUP) && defined (AUTOMOUNT))
160 int nis_error; /* returned by yp all functions */
161 char *nis_result; /* yp_match inits this */
162 int nis_result_len; /* and set this */
163 char *nis_domain; /* yp_get_default_domain inits this */
164 char *nis_map = (char *)lp_nis_home_map_name();
167 struct passwd *pwfile; /* for getting real name from passwd file */
171 * After observing MS-Exchange services writing to a Samba share
172 * I belive this code is incorrect. Each service does its own
173 * sessionsetup_and_X for the same user, and as each service shuts
174 * down, it does a user_logoff_and_X. As we are consolidating multiple
175 * sessionsetup_and_X's onto the same vuid here, when the first service
176 * shuts down, it invalidates all the open files for the other services.
177 * Hence I am removing this code and forcing each sessionsetup_and_X
179 * Jeremy Allison. (jallison@whistle.com).
183 for(i = 0; i < num_validated_users; i++) {
184 vuser = &validated_users[i];
185 if ( vuser->uid == uid )
186 return (uint16)(i + VUID_OFFSET); /* User already validated */
190 validated_users = (user_struct *)Realloc(validated_users,
192 (num_validated_users+1));
194 if (!validated_users)
196 DEBUG(0,("Failed to realloc users struct!\n"));
197 num_validated_users = 0;
198 return UID_FIELD_INVALID;
201 vuser = &validated_users[num_validated_users];
202 num_validated_users++;
206 vuser->guest = guest;
207 strcpy(vuser->name,name);
213 vuser->groups = NULL;
214 vuser->igroups = NULL;
217 /* Find all the groups this uid is in and store them.
218 Used by become_user() */
219 setup_groups(name,uid,gid,
225 DEBUG(3,("uid %d registered to name %s\n",uid,name));
227 #if (defined(NETGROUP) && defined (AUTOMOUNT))
228 vuser->home_share = NULL;
229 DEBUG(3, ("Setting default HOMESHR to: \\\\logon server\\HOMES\n"));
230 vuser->home_share = Realloc(vuser->home_share, 32);
231 strcpy(vuser->home_share,"\\\\%L\\%U");
233 if ((nis_error = yp_get_default_domain(&nis_domain)) != 0)
234 DEBUG(3, ("YP Error: %s\n", yperr_string(nis_error)));
235 DEBUG(3, ("NIS Domain: %s\n", nis_domain));
237 if ((nis_error = yp_match(nis_domain, nis_map, vuser->name, strlen(vuser->name),
238 &nis_result, &nis_result_len)) != 0)
239 DEBUG(3, ("YP Error: %s\n", yperr_string(nis_error)));
240 if (!nis_error && lp_nis_home_map()) {
241 home_server_len = strcspn(nis_result,":");
242 DEBUG(3, ("NIS lookup succeeded\n\tHome server length: %d\n",home_server_len));
243 vuser->home_share = (char *)Realloc(vuser->home_share, home_server_len+12);
244 DEBUG(3, ("\tAllocated %d bytes for HOMESHR\n",home_server_len+12 ));
245 strcpy(vuser->home_share,"\\\\");
246 strncat(vuser->home_share, nis_result, home_server_len);
247 strcat(vuser->home_share,"\\homes");
248 DEBUG(2,("\tUser = %s\n\tUID = %d\n\tNIS result = %s\n\tHOMESHR = %s\n",
249 vuser->name, vuser->uid, nis_result, vuser->home_share));
253 DEBUG(3, ("Clearing default real name\n"));
254 fstrcpy(vuser->real_name, "<Full Name>\0");
255 if (lp_unix_realname()) {
256 if ((pwfile=getpwnam(vuser->name))!= NULL)
258 DEBUG(3, ("User name: %s\tReal name: %s\n",vuser->name,pwfile->pw_gecos));
259 fstrcpy(vuser->real_name, pwfile->pw_gecos);
263 return (uint16)((num_validated_users - 1) + VUID_OFFSET);
267 /****************************************************************************
268 add a name to the session users list
269 ****************************************************************************/
270 void add_session_user(char *user)
273 StrnCpy(suser,user,sizeof(suser)-1);
275 if (!Get_Pwnam(suser,True)) return;
277 if (suser && *suser && !in_list(suser,session_users,False))
279 if (strlen(suser) + strlen(session_users) + 2 >= sizeof(pstring))
280 DEBUG(1,("Too many session users??\n"));
283 strcat(session_users," ");
284 strcat(session_users,suser);
291 /* a fake shadow password routine which just fills a fake spwd struct
292 * with the sp_pwdp field. (sreiz@aie.nl)
294 static struct spwd *getspnam(char *username) /* fake shadow password routine */
299 static struct spwd static_spwd;
301 static_spwd.sp_pwdp=0;
302 if (!(f=fopen("/etc/master.passwd", "r")))
304 while (fgets(line, 1024, f)) {
305 if (!strncmp(line, username, strlen(username)) &&
306 line[strlen(username)]==':') { /* found entry */
309 p=line+strlen(username)+1;
310 if ((q=strchr(p, ':'))) {
315 static_spwd.sp_pwdp=pw;
321 if (static_spwd.sp_pwdp)
329 /****************************************************************************
330 an enhanced crypt for OSF1
331 ****************************************************************************/
332 static char *osf1_bigcrypt(char *password,char *salt1)
334 static char result[AUTH_MAX_PASSWD_LENGTH] = "";
339 int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
340 if (strlen(password)%AUTH_CLEARTEXT_SEG_CHARS)
343 StrnCpy(salt,salt1,2);
344 StrnCpy(result,salt1,2);
346 for (i=0; i<parts;i++)
350 StrnCpy(salt,&result[2+i*AUTH_CIPHERTEXT_SEG_CHARS],2);
351 p2 += AUTH_CLEARTEXT_SEG_CHARS;
359 /****************************************************************************
360 update the enhanced security database. Only relevant for OSF1 at the moment.
361 ****************************************************************************/
362 static void update_protected_database( char *user, BOOL result)
365 struct pr_passwd *mypasswd;
368 mypasswd = getprpwnam (user);
369 starttime = time (NULL);
373 mypasswd->ufld.fd_slogin = starttime;
374 mypasswd->ufld.fd_nlogins = 0;
376 putprpwnam(user,mypasswd);
378 DEBUG(3,("Update protected database for Account %s after succesful connection\n",user));
382 mypasswd->ufld.fd_ulogin = starttime;
383 mypasswd->ufld.fd_nlogins = mypasswd->ufld.fd_nlogins + 1;
384 if ( mypasswd->ufld.fd_max_tries != 0 && mypasswd->ufld.fd_nlogins > mypasswd->ufld.fd_max_tries )
386 mypasswd->uflg.fg_lock = 0;
387 DEBUG(3,("Account is disabled -- see Account Administrator.\n"));
389 putprpwnam ( user , mypasswd );
390 DEBUG(3,("Update protected database for Account %s after refusing connection\n",user));
393 DEBUG(6,("Updated database with %s %s\n",user,BOOLSTR(result)));
399 /*******************************************************************
400 check on PAM authentication
401 ********************************************************************/
403 /* We first need some helper functions */
404 #include <security/pam_appl.h>
405 /* Static variables used to communicate between the conversation function
406 * and the server_login function
408 static char *PAM_username;
409 static char *PAM_password;
411 /* PAM conversation function
412 * Here we assume (for now, at least) that echo on means login name, and
413 * echo off means password.
415 static int PAM_conv (int num_msg,
416 struct pam_message **msg,
417 struct pam_response **resp,
419 int count = 0, replies = 0;
420 struct pam_response *reply = NULL;
421 int size = sizeof(struct pam_response);
423 #define GET_MEM if (reply) realloc(reply, size); else reply = malloc(size); \
424 if (!reply) return PAM_CONV_ERR; \
425 size += sizeof(struct pam_response)
426 #define COPY_STRING(s) (s) ? strdup(s) : NULL
428 for (count = 0; count < num_msg; count++) {
429 switch (msg[count]->msg_style) {
430 case PAM_PROMPT_ECHO_ON:
432 reply[replies].resp_retcode = PAM_SUCCESS;
433 reply[replies++].resp = COPY_STRING(PAM_username);
436 case PAM_PROMPT_ECHO_OFF:
438 reply[replies].resp_retcode = PAM_SUCCESS;
439 reply[replies++].resp = COPY_STRING(PAM_password);
447 /* Must be an error of some sort... */
452 if (reply) *resp = reply;
455 static struct pam_conv PAM_conversation = {
461 static BOOL pam_auth(char *this_user,char *password)
466 /* Now use PAM to do authentication. For now, we won't worry about
467 * session logging, only authentication. Bail out if there are any
468 * errors. Since this is a limited protocol, and an even more limited
469 * function within a server speaking this protocol, we can't be as
470 * verbose as would otherwise make sense.
471 * Query: should we be using PAM_SILENT to shut PAM up?
473 #define PAM_BAIL if (pam_error != PAM_SUCCESS) { \
474 pam_end(pamh, 0); return False; \
476 PAM_password = password;
477 PAM_username = this_user;
478 pam_error = pam_start("samba", this_user, &PAM_conversation, &pamh);
480 pam_error = pam_authenticate(pamh, 0);
482 /* It is not clear to me that account management is the right thing
483 * to do, but it is not clear that it isn't, either. This can be
484 * removed if no account management should be done. Alternately,
485 * put a pam_allow.so entry in /etc/pam.conf for account handling. */
486 pam_error = pam_acct_mgmt(pamh, 0);
488 pam_end(pamh, PAM_SUCCESS);
489 /* If this point is reached, the user has been authenticated. */
496 /*******************************************************************
497 check on AFS authentication
498 ********************************************************************/
499 static BOOL afs_auth(char *this_user,char *password)
501 long password_expires = 0;
504 /* For versions of AFS prior to 3.3, this routine has few arguments, */
505 /* but since I can't find the old documentation... :-) */
507 if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION+KA_USERAUTH_DOSETPAG,
509 (char *) 0, /* instance */
510 (char *) 0, /* cell */
512 0, /* lifetime, default */
513 &password_expires, /*days 'til it expires */
524 sec_login_handle_t my_dce_sec_context;
525 int dcelogin_atmost_once = 0;
527 /*******************************************************************
528 check on a DCE/DFS authentication
529 ********************************************************************/
530 static BOOL dfs_auth(char *this_user,char *password)
535 boolean32 password_reset;
536 sec_passwd_rec_t my_dce_password;
537 sec_login_auth_src_t auth_src = sec_login_auth_src_network;
538 unsigned char dce_errstr[dce_c_error_string_len];
541 * We only go for a DCE login context if the given password
542 * matches that stored in the local password file..
543 * Assumes local passwd file is kept in sync w/ DCE RGY!
546 if (!strcmp((char *)crypt(password,this_salt),this_crypted) ||
547 dcelogin_atmost_once)
550 if (sec_login_setup_identity(
551 (unsigned char *)this_user,
556 dce_error_inq_text(err, dce_errstr, &err2);
557 DEBUG(0,("DCE Setup Identity for %s failed: %s\n",
558 this_user,dce_errstr));
562 my_dce_password.version_number = sec_passwd_c_version_none;
563 my_dce_password.pepper = NULL;
564 my_dce_password.key.key_type = sec_passwd_plain;
565 my_dce_password.key.tagged_union.plain = (idl_char *)password;
567 if (sec_login_valid_and_cert_ident(my_dce_sec_context,
573 dce_error_inq_text(err, dce_errstr, &err2);
574 DEBUG(0,("DCE Identity Validation failed for principal %s: %s\n",
575 this_user,dce_errstr));
580 sec_login_set_context(my_dce_sec_context, &err);
581 if (err != error_status_ok )
583 dce_error_inq_text(err, dce_errstr, &err2);
584 DEBUG(0,("DCE login failed for principal %s, cant set context: %s\n",
585 this_user,dce_errstr));
586 sec_login_purge_context(my_dce_sec_context, &err);
591 DEBUG(0,("DCE login succeeded for principal %s on pid %d\n",
592 this_user, getpid()));
595 dcelogin_atmost_once = 1;
599 void dfs_unlogin(void)
603 unsigned char dce_errstr[dce_c_error_string_len];
605 sec_login_purge_context(my_dce_sec_context, &err);
606 if (err != error_status_ok )
608 dce_error_inq_text(err, dce_errstr, &err2);
609 DEBUG(0,("DCE purge login context failed for server instance %d: %s\n",
610 getpid(), dce_errstr));
617 /*******************************************************************
618 check on Kerberos authentication
619 ********************************************************************/
620 static BOOL krb5_auth(char *this_user,char *password)
622 krb5_data tgtname = {
627 krb5_context kcontext;
628 krb5_principal kprinc;
629 krb5_principal server;
632 krb5_address **addrs = (krb5_address **)0;
633 krb5_preauthtype *preauth = NULL;
634 krb5_keytab keytab = NULL;
636 krb5_ccache ccache = NULL;
640 if ( retval=krb5_init_context(&kcontext))
645 if ( retval = krb5_timeofday(kcontext, &now) )
650 if ( retval = krb5_cc_default(kcontext, &ccache) )
655 if ( retval = krb5_parse_name(kcontext, this_user, &kprinc) )
660 memset((char *)&kcreds, 0, sizeof(kcreds));
662 kcreds.client = kprinc;
664 if ((retval = krb5_build_principal_ext(kcontext, &server,
665 krb5_princ_realm(kcontext, kprinc)->length,
666 krb5_princ_realm(kcontext, kprinc)->data,
669 krb5_princ_realm(kcontext, kprinc)->length,
670 krb5_princ_realm(kcontext, kprinc)->data,
676 kcreds.server = server;
678 retval = krb5_get_in_tkt_with_password(kcontext,
695 #endif /* KRB5_AUTH */
697 #ifdef LINUX_BIGCRYPT
698 /****************************************************************************
699 an enhanced crypt for Linux to handle password longer than 8 characters
700 ****************************************************************************/
701 static int linux_bigcrypt(char *password,char *salt1, char *crypted)
703 #define LINUX_PASSWORD_SEG_CHARS 8
707 StrnCpy(salt,salt1,2);
710 for ( i=strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
711 char * p = crypt(password,salt) + 2;
712 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
714 password += LINUX_PASSWORD_SEG_CHARS;
715 crypted += strlen(p);
723 /****************************************************************************
724 apply a function to upper/lower case combinations
725 of a string and return true if one of them returns true.
726 try all combinations with N uppercase letters.
727 offset is the first char to try and change (start with 0)
728 it assumes the string starts lowercased
729 ****************************************************************************/
730 static BOOL string_combinations2(char *s,int offset,BOOL (*fn)(),int N)
735 #ifdef PASSWORD_LENGTH
736 len = MIN(len,PASSWORD_LENGTH);
739 if (N <= 0 || offset >= len)
742 for (i=offset;i<(len-(N-1));i++)
745 if (!islower(c)) continue;
747 if (string_combinations2(s,i+1,fn,N-1))
754 /****************************************************************************
755 apply a function to upper/lower case combinations
756 of a string and return true if one of them returns true.
757 try all combinations with up to N uppercase letters.
758 offset is the first char to try and change (start with 0)
759 it assumes the string starts lowercased
760 ****************************************************************************/
761 static BOOL string_combinations(char *s,BOOL (*fn)(),int N)
765 if (string_combinations2(s,0,fn,n)) return(True);
771 /****************************************************************************
772 core of password checking routine
773 ****************************************************************************/
774 BOOL password_check(char *password)
778 /* This falls through if the password check fails
779 - if NO_CRYPT is defined this causes an error msg
780 saying Warning - no crypt available
781 - if NO_CRYPT is NOT defined this is a potential security hole
782 as it may authenticate via the crypt call when PAM
783 settings say it should fail.
784 if (pam_auth(this_user,password)) return(True);
785 Hence we make a direct return to avoid a second chance!!!
787 return (pam_auth(this_user,password));
791 if (afs_auth(this_user,password)) return(True);
795 if (dfs_auth(this_user,password)) return(True);
799 if (krb5_auth(this_user,password)) return(True);
803 if (pwdauth(this_user,password) == 0)
808 return(strcmp(osf1_bigcrypt(password,this_salt),this_crypted) == 0);
812 return (strcmp((char *)crypt16(password, this_salt ),this_crypted) == 0);
815 #ifdef LINUX_BIGCRYPT
816 return(linux_bigcrypt(password,this_salt,this_crypted));
820 DEBUG(1,("Warning - no crypt available\n"));
823 return(strcmp((char *)crypt(password,this_salt),this_crypted) == 0);
827 /****************************************************************************
828 core of smb password checking routine.
829 ****************************************************************************/
830 BOOL smb_password_check(char *password, unsigned char *part_passwd, unsigned char *c8)
832 /* Finish the encryption of part_passwd. */
833 unsigned char p21[21];
834 unsigned char p24[24];
836 if (part_passwd == NULL)
837 DEBUG(10,("No password set - allowing access\n"));
838 /* No password set - always true ! */
839 if (part_passwd == NULL)
843 memcpy(p21,part_passwd,16);
848 DEBUG(100,("Part password (P16) was |"));
849 for(i = 0; i < 16; i++)
850 DEBUG(100,("%X ", (unsigned char)part_passwd[i]));
852 DEBUG(100,("Password from client was |"));
853 for(i = 0; i < 24; i++)
854 DEBUG(100,("%X ", (unsigned char)password[i]));
856 DEBUG(100,("Given challenge was |"));
857 for(i = 0; i < 8; i++)
858 DEBUG(100,("%X ", (unsigned char)c8[i]));
860 DEBUG(100,("Value from encryption was |"));
861 for(i = 0; i < 24; i++)
862 DEBUG(100,("%X ", (unsigned char)p24[i]));
866 return (memcmp(p24, password, 24) == 0);
869 /****************************************************************************
870 check if a username/password is OK
871 ****************************************************************************/
872 BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd)
875 int level = lp_passwordlevel();
878 struct smb_passwd *smb_pass;
879 BOOL challenge_done = False;
881 if (password) password[pwlen] = 0;
884 challenge_done = last_challenge(challenge);
890 DEBUG(100,("checking user=[%s] pass=[",user));
891 for( i = 0; i < 24; i++)
892 DEBUG(100,("%0x ", (unsigned char)password[i]));
895 DEBUG(100,("checking user=[%s] pass=[%s]\n",user,password));
902 if (((!*password) || (!pwlen)) && !lp_null_passwords())
907 pass = (struct passwd *) pwd;
908 user = pass->pw_name;
911 pass = Get_Pwnam(user,True);
913 DEBUG(4,("SMB Password - pwlen = %d, challenge_done = %d\n", pwlen, challenge_done));
915 if ((pwlen == 24) && challenge_done)
917 DEBUG(4,("Checking SMB password for user %s (l=24)\n",user));
921 DEBUG(3,("Couldn't find user %s\n",user));
925 smb_pass = get_smbpwnam(user);
928 DEBUG(3,("Couldn't find user %s in smb_passwd file.\n", user));
932 /* Ensure the uid's match */
933 if (smb_pass->smb_userid != pass->pw_uid)
935 DEBUG(3,("Error : UNIX and SMB uids in password files do not match !\n"));
939 if (Protocol >= PROTOCOL_NT1)
941 /* We have the NT MD4 hash challenge available - see if we can
942 use it (ie. does it exist in the smbpasswd file).
944 if (smb_pass->smb_nt_passwd != NULL)
946 DEBUG(4,("Checking NT MD4 password\n"));
947 if (smb_password_check(password,
948 smb_pass->smb_nt_passwd,
949 (unsigned char *)challenge))
951 update_protected_database(user,True);
954 DEBUG(4,("NT MD4 password check failed\n"));
958 /* Try against the lanman password */
960 if (smb_password_check(password,
961 smb_pass->smb_passwd,
962 (unsigned char *)challenge)) {
963 update_protected_database(user,True);
967 DEBUG(3,("Error smb_password_check failed\n"));
970 DEBUG(4,("Checking password for user %s (l=%d)\n",user,pwlen));
974 DEBUG(3,("Couldn't find user %s\n",user));
982 /* many shadow systems require you to be root to get the password,
983 in most cases this should already be the case when this
984 function is called, except perhaps for IPC password changing
987 spass = getspnam(pass->pw_name);
988 if (spass && spass->sp_pwdp)
989 pass->pw_passwd = spass->sp_pwdp;
991 #elif defined(IA_UINFO)
993 /* Need to get password with SVR4.2's ia_ functions instead of
994 get{sp,pw}ent functions. Required by UnixWare 2.x, tested on
995 version 2.1. (tangent@cyberport.com) */
997 if (ia_openinfo(pass->pw_name, &uinfo) != -1)
998 ia_get_logpwd(uinfo, &(pass->pw_passwd));
1004 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
1005 if (pr_pw && pr_pw->ufld.fd_encrypt)
1006 pass->pw_passwd = pr_pw->ufld.fd_encrypt;
1010 #ifdef HPUX_10_TRUSTED
1012 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
1013 if (pr_pw && pr_pw->ufld.fd_encrypt)
1014 pass->pw_passwd = pr_pw->ufld.fd_encrypt;
1020 struct pr_passwd *mypasswd;
1021 DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n",user));
1022 mypasswd = getprpwnam (user);
1025 strcpy(pass->pw_name,mypasswd->ufld.fd_name);
1026 strcpy(pass->pw_passwd,mypasswd->ufld.fd_encrypt);
1030 DEBUG(5,("No entry for user %s in protected database !\n",user));
1038 AUTHORIZATION *ap = getauthuid( pass->pw_uid );
1041 strcpy( pass->pw_passwd, ap->a_password );
1047 /* extract relevant info */
1048 strcpy(this_user,pass->pw_name);
1049 strcpy(this_salt,pass->pw_passwd);
1050 strcpy(this_crypted,pass->pw_passwd);
1052 if (!*this_crypted) {
1053 if (!lp_null_passwords()) {
1054 DEBUG(2,("Disallowing access to %s due to null password\n",this_user));
1059 DEBUG(3,("Allowing access to %s with null password\n",this_user));
1065 /* try it as it came to us */
1066 if (password_check(password))
1068 update_protected_database(user,True);
1072 /* if the password was given to us with mixed case then we don't
1073 need to proceed as we know it hasn't been case modified by the
1075 if (strhasupper(password) && strhaslower(password))
1078 /* make a copy of it */
1079 StrnCpy(pass2,password,sizeof(pstring)-1);
1081 /* try all lowercase */
1083 if (password_check(password))
1085 update_protected_database(user,True);
1092 update_protected_database(user,False);
1095 strcpy(password,pass2);
1100 /* last chance - all combinations of up to level chars upper! */
1103 if (string_combinations(password,password_check,level))
1105 update_protected_database(user,True);
1109 update_protected_database(user,False);
1112 strcpy(password,pass2);
1119 /****************************************************************************
1120 check if a username is valid
1121 ****************************************************************************/
1122 BOOL user_ok(char *user,int snum)
1124 pstring valid, invalid;
1127 StrnCpy(valid, lp_valid_users(snum), sizeof(pstring));
1128 StrnCpy(invalid, lp_invalid_users(snum), sizeof(pstring));
1130 string_sub(valid,"%S",lp_servicename(snum));
1131 string_sub(invalid,"%S",lp_servicename(snum));
1133 ret = !user_in_list(user,invalid);
1135 if (ret && valid && *valid)
1136 ret = user_in_list(user,valid);
1138 if (ret && lp_onlyuser(snum)) {
1139 char *user_list = lp_username(snum);
1140 string_sub(user_list,"%S",lp_servicename(snum));
1141 ret = user_in_list(user,user_list);
1150 /****************************************************************************
1151 validate a group username entry. Return the username or NULL
1152 ****************************************************************************/
1153 static char *validate_group(char *group,char *password,int pwlen,int snum)
1157 char *host, *user, *domain;
1159 while (getnetgrent(&host, &user, &domain)) {
1161 if (user_ok(user, snum) &&
1162 password_ok(user,password,pwlen,NULL)) {
1174 struct group *gptr = (struct group *)getgrnam(group);
1178 member = gptr->gr_mem;
1179 while (member && *member)
1181 static fstring name;
1182 strcpy(name,*member);
1183 if (user_ok(name,snum) &&
1184 password_ok(name,password,pwlen,NULL))
1188 #ifdef GROUP_CHECK_PWENT
1194 while (pwd = getpwent ()) {
1195 if (*(pwd->pw_passwd) && pwd->pw_gid == gptr->gr_gid) {
1196 /* This Entry have PASSWORD and same GID then check pwd */
1197 if (password_ok(NULL, password, pwlen, pwd)) {
1198 strcpy(tm, pwd->pw_name);
1206 #endif /* GROUP_CHECK_PWENT */
1215 /****************************************************************************
1216 check for authority to login to a service with a given username/password
1217 ****************************************************************************/
1218 BOOL authorise_login(int snum,char *user,char *password, int pwlen,
1219 BOOL *guest,BOOL *force,uint16 vuid)
1226 DEBUG(100,("checking authorisation on user=%s pass=%s\n",user,password));
1229 /* there are several possibilities:
1230 1) login as the given user with given password
1231 2) login as a previously registered username with the given password
1232 3) login as a session list username with the given password
1233 4) login as a previously validated user/password pair
1234 5) login as the "user =" user with given password
1235 6) login as the "user =" user with no password (guest connection)
1236 7) login as guest user with no password
1238 if the service is guest_only then steps 1 to 5 are skipped
1241 if (GUEST_ONLY(snum)) *force = True;
1243 if (!(GUEST_ONLY(snum) && GUEST_OK(snum)))
1246 user_struct *vuser = get_valid_user_struct(vuid);
1248 /* check the given username and password */
1249 if (!ok && (*user) && user_ok(user,snum)) {
1250 ok = password_ok(user,password, pwlen, NULL);
1251 if (ok) DEBUG(3,("ACCEPTED: given username password ok\n"));
1254 /* check for a previously registered guest username */
1255 if (!ok && (vuser != 0) && vuser->guest) {
1256 if (user_ok(vuser->name,snum) &&
1257 password_ok(vuser->name, password, pwlen, NULL)) {
1258 strcpy(user, vuser->name);
1259 vuser->guest = False;
1260 DEBUG(3,("ACCEPTED: given password with registered user %s\n", user));
1266 /* now check the list of session users */
1270 char *user_list = strdup(session_users);
1271 if (!user_list) return(False);
1273 for (auser=strtok(user_list,LIST_SEP);
1275 auser = strtok(NULL,LIST_SEP))
1278 strcpy(user2,auser);
1279 if (!user_ok(user2,snum)) continue;
1281 if (password_ok(user2,password, pwlen, NULL)) {
1284 DEBUG(3,("ACCEPTED: session list username and given password ok\n"));
1290 /* check for a previously validated username/password pair */
1291 if (!ok && !lp_revalidate(snum) &&
1292 (vuser != 0) && !vuser->guest &&
1293 user_ok(vuser->name,snum)) {
1294 strcpy(user,vuser->name);
1296 DEBUG(3,("ACCEPTED: validated uid ok as non-guest\n"));
1300 /* check for a rhosts entry */
1301 if (!ok && user_ok(user,snum) && check_hosts_equiv(user)) {
1303 DEBUG(3,("ACCEPTED: hosts equiv or rhosts entry\n"));
1306 /* check the user= fields and the given password */
1307 if (!ok && lp_username(snum)) {
1310 StrnCpy(user_list,lp_username(snum),sizeof(pstring));
1312 string_sub(user_list,"%S",lp_servicename(snum));
1314 for (auser=strtok(user_list,LIST_SEP);
1316 auser = strtok(NULL,LIST_SEP))
1320 auser = validate_group(auser+1,password,pwlen,snum);
1325 DEBUG(3,("ACCEPTED: group username and given password ok\n"));
1331 strcpy(user2,auser);
1332 if (user_ok(user2,snum) &&
1333 password_ok(user2,password,pwlen,NULL))
1337 DEBUG(3,("ACCEPTED: user list username and given password ok\n"));
1342 } /* not guest only */
1344 /* check for a normal guest connection */
1345 if (!ok && GUEST_OK(snum))
1348 StrnCpy(guestname,lp_guestaccount(snum),sizeof(guestname)-1);
1349 if (Get_Pwnam(guestname,True))
1351 strcpy(user,guestname);
1353 DEBUG(3,("ACCEPTED: guest account and guest ok\n"));
1356 DEBUG(0,("Invalid guest account %s??\n",guestname));
1361 if (ok && !user_ok(user,snum))
1363 DEBUG(0,("rejected invalid user %s\n",user));
1371 /****************************************************************************
1372 read the a hosts.equiv or .rhosts file and check if it
1373 allows this user from this machine
1374 ****************************************************************************/
1375 static BOOL check_user_equiv(char *user, char *remote, char *equiv_file)
1378 int plus_allowed = 1;
1381 FILE *fp = fopen(equiv_file, "r");
1382 DEBUG(5, ("check_user_equiv %s %s %s\n", user, remote, equiv_file));
1383 if (! fp) return False;
1384 while(fgets(buf, sizeof(buf), fp))
1386 trim_string(buf," "," ");
1388 if (buf[0] != '#' && buf[0] != '\n')
1390 BOOL is_group = False;
1393 if (strcmp(buf, "NO_PLUS\n") == 0)
1395 DEBUG(6, ("check_user_equiv NO_PLUS\n"));
1402 if (*bp == '\n' && plus_allowed)
1404 /* a bare plus means everbody allowed */
1405 DEBUG(6, ("check_user_equiv everybody allowed\n"));
1410 else if (buf[0] == '-')
1420 file_host = strtok(bp, " \t\n");
1421 file_user = strtok(NULL, " \t\n");
1422 DEBUG(7, ("check_user_equiv %s %s\n", file_host ? file_host : "(null)",
1423 file_user ? file_user : "(null)" ));
1424 if (file_host && *file_host)
1426 BOOL host_ok = False;
1431 static char *mydomain = NULL;
1433 yp_get_default_domain(&mydomain);
1434 if (mydomain && innetgr(file_host,remote,user,mydomain))
1440 DEBUG(1,("Netgroups not configured - add -DNETGROUP and recompile\n"));
1445 /* is it this host */
1446 /* the fact that remote has come from a call of gethostbyaddr
1447 * means that it may have the fully qualified domain name
1448 * so we could look up the file version to get it into
1449 * a canonical form, but I would rather just type it
1450 * in full in the equiv file
1452 if (!host_ok && !is_group && strequal(remote, file_host))
1458 /* is it this user */
1459 if (file_user == 0 || strequal(user, file_user))
1462 DEBUG(5, ("check_user_equiv matched %s%s %s\n",
1463 (plus ? "+" : "-"), file_host,
1464 (file_user ? file_user : "")));
1465 return (plus ? True : False);
1476 /****************************************************************************
1477 check for a possible hosts equiv or rhosts entry for the user
1478 ****************************************************************************/
1479 BOOL check_hosts_equiv(char *user)
1483 struct passwd *pass = Get_Pwnam(user,True);
1488 fname = lp_hosts_equiv();
1490 /* note: don't allow hosts.equiv on root */
1491 if (fname && *fname && (pass->pw_uid != 0))
1493 if (check_user_equiv(user,client_name(),fname))
1497 if (lp_use_rhosts())
1499 char *home = get_home_dir(user);
1502 sprintf(rhostsfile, "%s/.rhosts", home);
1503 if (check_user_equiv(user,client_name(),rhostsfile))
1512 int password_client = -1;
1513 static fstring pserver;
1514 static char *secserver_inbuf = NULL;
1516 /****************************************************************************
1517 attempted support for server level security
1518 ****************************************************************************/
1519 BOOL server_cryptkey(char *buf)
1522 fstring pass_protocol;
1523 extern fstring remote_machine;
1527 struct in_addr dest_ip;
1528 int port = SMB_PORT;
1531 if(secserver_inbuf == NULL) {
1532 secserver_inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
1533 if(secserver_inbuf == NULL) {
1534 DEBUG(0,("server_cryptkey: malloc fail for input buffer.\n"));
1539 if (password_client >= 0)
1540 close(password_client);
1541 password_client = -1;
1543 if (Protocol < PROTOCOL_NT1) {
1544 strcpy(pass_protocol,"LM1.2X002");
1546 strcpy(pass_protocol,"NT LM 0.12");
1549 bzero(secserver_inbuf,BUFFER_SIZE + SAFETY_MARGIN);
1550 bzero(outbuf,sizeof(outbuf));
1552 for (p=strtok(lp_passwordserver(),LIST_SEP); p ; p = strtok(NULL,LIST_SEP)) {
1554 standard_sub_basic(desthost);
1557 dest_ip = *interpret_addr2(desthost);
1558 if (zero_ip(dest_ip)) {
1559 DEBUG(1,("Can't resolve address for %s\n",p));
1563 if (ismyip(dest_ip)) {
1564 DEBUG(1,("Password server loop - disabling password server %s\n",p));
1568 password_client = open_socket_out(SOCK_STREAM, &dest_ip, port, SHORT_CONNECT_TIMEOUT);
1569 if (password_client >= 0) {
1570 DEBUG(3,("connected to password server %s\n",p));
1571 StrnCpy(pserver,p,sizeof(pserver)-1);
1576 if (password_client < 0) {
1577 DEBUG(1,("password server not available\n"));
1582 /* send a session request (RFC 8002) */
1584 /* put in the destination name */
1587 name_mangle(desthost,p,' ');
1592 /* Fix from Frank Varnavas <varnavas@ny.ubs.com>.
1593 We cannot use the same name as the client to
1594 the NT password server, as NT will drop client
1595 connections if the same client name connects
1596 twice. Instead, synthesize a name from our pid.
1597 and the remote machine name.
1600 char buf2[32]; /* create name as PIDname */
1601 sprintf(buf2,"%d", getpid());
1602 strncpy(&buf2[strlen(buf2)], remote_machine, 31 - strlen(buf2));
1604 DEBUG(1,("negprot w/password server as %s\n",buf2));
1605 name_mangle(buf2,p,' ');
1609 _smb_setlen(outbuf,len);
1610 CVAL(outbuf,0) = 0x81;
1612 send_smb(password_client,outbuf);
1615 if (!receive_smb(password_client,secserver_inbuf,5000) ||
1616 CVAL(secserver_inbuf,0) != 0x82) {
1617 DEBUG(1,("%s rejected the session\n",pserver));
1618 close(password_client); password_client = -1;
1622 DEBUG(3,("got session\n"));
1624 bzero(outbuf,smb_size);
1626 /* setup the protocol string */
1627 set_message(outbuf,0,strlen(pass_protocol)+2,True);
1628 p = smb_buf(outbuf);
1630 strcpy(p,pass_protocol);
1632 CVAL(outbuf,smb_com) = SMBnegprot;
1633 CVAL(outbuf,smb_flg) = 0x8;
1634 SSVAL(outbuf,smb_flg2,0x1);
1636 send_smb(password_client,outbuf);
1637 ret = receive_smb(password_client,secserver_inbuf,5000);
1639 if (!ret || CVAL(secserver_inbuf,smb_rcls) || SVAL(secserver_inbuf,smb_vwv0)) {
1640 DEBUG(1,("%s rejected the protocol\n",pserver));
1641 close(password_client); password_client= -1;
1645 if (!(CVAL(secserver_inbuf,smb_vwv1) & 1)) {
1646 DEBUG(1,("%s isn't in user level security mode\n",pserver));
1647 close(password_client); password_client= -1;
1651 memcpy(buf,secserver_inbuf,smb_len(secserver_inbuf)+4);
1653 DEBUG(3,("password server OK\n"));
1658 /****************************************************************************
1659 attempted support for server level security
1660 ****************************************************************************/
1661 BOOL server_validate(char *buf)
1666 if(secserver_inbuf == NULL) {
1667 secserver_inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
1668 if(secserver_inbuf == NULL) {
1669 DEBUG(0,("server_validate: malloc fail for input buffer.\n"));
1674 if (password_client < 0) {
1675 DEBUG(1,("%s not connected\n",pserver));
1679 bzero(secserver_inbuf,BUFFER_SIZE + SAFETY_MARGIN);
1680 memcpy(outbuf,buf,sizeof(outbuf));
1682 /* send a session setup command */
1683 CVAL(outbuf,smb_flg) = 0x8;
1684 SSVAL(outbuf,smb_flg2,0x1);
1685 CVAL(outbuf,smb_vwv0) = 0xFF;
1687 set_message(outbuf,smb_numwords(outbuf),smb_buflen(outbuf),False);
1689 SCVAL(secserver_inbuf,smb_rcls,1);
1691 send_smb(password_client,outbuf);
1692 ret = receive_smb(password_client,secserver_inbuf,5000);
1694 if (!ret || CVAL(secserver_inbuf,smb_rcls) != 0) {
1695 DEBUG(1,("password server %s rejected the password\n",pserver));
1699 /* if logged in as guest then reject */
1700 if ((SVAL(secserver_inbuf,smb_vwv2) & 1) != 0) {
1701 DEBUG(1,("password server %s gave us guest only\n",pserver));
1705 DEBUG(3,("password server %s accepted the password\n",pserver));
1707 #if !KEEP_PASSWORD_SERVER_OPEN
1708 close(password_client); password_client= -1;