password.c: Fixes to allow Win95 clients to have lm encrypted passwords
[samba.git] / source3 / smbd / password.c
index 4f9f91d76d06a4a4c15c123df1dfb38b5095cc2b..1924a32780e80bbdd3222acd6e4c469357c8f738 100644 (file)
@@ -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
 
 #include "includes.h"
 
+#if (defined(NETGROUP) && defined (AUTOMOUNT))
+#include "rpcsvc/ypclnt.h"
+#endif
+
 extern int DEBUGLEVEL;
 extern int Protocol;
 
+BOOL global_machine_pasword_needs_changing;
+
 /* users from session setup */
 static pstring session_users="";
 
+extern pstring global_myname;
+extern fstring global_myworkgroup;
+
 /* 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]="";
 
-#ifdef SMB_PASSWD
 /* Data to do lanman1/2 password challenge. */
 static unsigned char saved_challenge[8];
 static BOOL challenge_sent=False;
@@ -42,17 +50,34 @@ Get the next challenge value - no repeats.
 ********************************************************************/
 void generate_next_challenge(char *challenge)
 {
-  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",(char *)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;
 }
 
 /*******************************************************************
@@ -68,13 +93,12 @@ BOOL set_challenge(char *challenge)
 /*******************************************************************
 get the last challenge sent
 ********************************************************************/
-BOOL last_challenge(char *challenge)
+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;
@@ -87,10 +111,10 @@ tell random client vuid's (normally zero) from valid vuids.
 ****************************************************************************/
 user_struct *get_valid_user_struct(uint16 vuid)
 {
-  if(vuid == UID_FIELD_INVALID)
+  if (vuid == UID_FIELD_INVALID)
     return NULL;
   vuid -= VUID_OFFSET;
-  if((vuid >= (uint16)num_validated_users) || 
+  if ((vuid >= (uint16)num_validated_users) || 
      (validated_users[vuid].uid == -1) || (validated_users[vuid].gid == -1))
     return NULL;
   return &validated_users[vuid];
@@ -102,19 +126,28 @@ invalidate a uid
 void invalidate_vuid(uint16 vuid)
 {
   user_struct *vuser = get_valid_user_struct(vuid);
-  if(vuser == 0)
-    return;
+
+  if (vuser == NULL) return;
 
   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;
+
+  vuser->n_sids = 0;
+
+  /* same number of igroups as groups as attrs */
+  vuser->n_groups = 0;
+
+  if (vuser->groups && (vuser->groups != (gid_t *)vuser->igroups))
+       free(vuser->groups);
+
+  if (vuser->igroups) free(vuser->igroups);
+  if (vuser->attrs  ) free(vuser->attrs);
+  if (vuser->sids   ) free(vuser->sids);
+
+  vuser->attrs   = NULL;
+  vuser->sids    = NULL;
+  vuser->igroups = NULL;
+  vuser->groups  = NULL;
 }
 
 
@@ -124,26 +157,131 @@ return a validated username
 char *validated_username(uint16 vuid)
 {
   user_struct *vuser = get_valid_user_struct(vuid);
-  if(vuser == 0)
+  if (vuser == NULL)
     return 0;
   return(vuser->name);
 }
 
+
+/****************************************************************************
+Setup the groups a user belongs to.
+****************************************************************************/
+int setup_groups(char *user, int uid, int gid, int *p_ngroups, 
+                int **p_igroups, gid_t **p_groups,
+         int **p_attrs)
+{
+  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));
+       }
+    }
+  else
+    {
+      int i,ngroups;
+      int *igroups;
+      int *attrs;
+      gid_t grp = 0;
+      ngroups = getgroups(0,&grp);
+      if (ngroups <= 0)
+        ngroups = 32;
+      igroups = (int *)malloc(sizeof(int)*ngroups);
+      attrs   = (int *)malloc(sizeof(int)*ngroups);
+      for (i=0;i<ngroups;i++)
+      {
+        attrs  [i] = 0x7; /* XXXX don't know what NT user attributes are yet! */
+        igroups[i] = 0x42424242;
+      }
+      ngroups = getgroups(ngroups,(gid_t *)igroups);
+
+      if (igroups[0] == 0x42424242)
+        ngroups = 0;
+
+      *p_ngroups = ngroups;
+      *p_attrs   = attrs;
+
+      /* The following bit of code is very strange. It is due to the
+         fact that some OSes use int* and some use gid_t* for
+         getgroups, and some (like SunOS) use both, one in prototypes,
+         and one in man pages and the actual code. Thus we detect it
+         dynamically using some very ugly code */
+      if (ngroups > 0)
+        {
+         /* does getgroups return ints or gid_t ?? */
+         static BOOL groups_use_ints = True;
+
+         if (groups_use_ints && 
+             ngroups == 1 && 
+             SVAL(igroups,2) == 0x4242)
+           groups_use_ints = False;
+         
+          for (i=0;groups_use_ints && i<ngroups;i++)
+            if (igroups[i] == 0x42424242)
+             groups_use_ints = False;
+             
+          if (groups_use_ints)
+          {
+             *p_igroups = igroups;
+             *p_groups = (gid_t *)igroups;       
+          }
+          else
+          {
+             gid_t *groups = (gid_t *)igroups;
+             igroups = (int *)malloc(sizeof(int)*ngroups);
+             for (i=0;i<ngroups;i++)
+          {
+               igroups[i] = groups[i];
+          }
+             *p_igroups = igroups;
+             *p_groups = (gid_t *)groups;
+           }
+       }
+      DEBUG(3,("%s is in %d groups\n",user,ngroups));
+      for (i=0;i<ngroups;i++)
+        DEBUG(3,("%d ",igroups[i]));
+      DEBUG(3,("\n"));
+    }
+  return 0;
+}
+
+
 /****************************************************************************
 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(int uid,int gid, char *name,BOOL guest)
+uint16 register_vuid(int uid,int gid, char *unix_name, char *requested_name, BOOL guest)
 {
   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
+  /*
+   * 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).
+   */
 
   int i;
   for(i = 0; i < num_validated_users; i++) {
     vuser = &validated_users[i];
-    if( vuser->uid == uid )
-      return i; /* User already validated */
+    if ( vuser->uid == uid )
+      return (uint16)(i + VUID_OFFSET); /* User already validated */
   }
+#endif
 
   validated_users = (user_struct *)Realloc(validated_users,
                           sizeof(user_struct)*
@@ -152,6 +290,7 @@ uint16 register_vuid(int uid,int gid, char *name,BOOL guest)
   if (!validated_users)
     {
       DEBUG(0,("Failed to realloc users struct!\n"));
+      num_validated_users = 0;
       return UID_FIELD_INVALID;
     }
 
@@ -161,20 +300,36 @@ uint16 register_vuid(int uid,int gid, char *name,BOOL guest)
   vuser->uid = uid;
   vuser->gid = gid;
   vuser->guest = guest;
-  strcpy(vuser->name,name);
+  fstrcpy(vuser->name,unix_name);
+  fstrcpy(vuser->requested_name,requested_name);
+
+  vuser->n_sids = 0;
+  vuser->sids   = NULL;
 
-  vuser->user_ngroups = 0;
-  vuser->user_groups = NULL;
-  vuser->user_igroups = NULL;
+  vuser->n_groups = 0;
+  vuser->groups  = NULL;
+  vuser->igroups = NULL;
+  vuser->attrs    = NULL;
 
   /* 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);
-
-  DEBUG(3,("uid %d registered to name %s\n",uid,name));
+  setup_groups(unix_name,uid,gid,
+              &vuser->n_groups,
+              &vuser->igroups,
+              &vuser->groups,
+              &vuser->attrs);
+
+  DEBUG(3,("uid %d registered to name %s\n",uid,unix_name));
+
+  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 (uint16)((num_validated_users - 1) + VUID_OFFSET);
 }
@@ -196,8 +351,8 @@ void add_session_user(char *user)
        DEBUG(1,("Too many session users??\n"));
       else
        {
-         strcat(session_users," ");
-         strcat(session_users,suser);
+         pstrcat(session_users," ");
+         pstrcat(session_users,suser);
        }
     }
 }
@@ -211,7 +366,7 @@ static struct spwd *getspnam(char *username) /* fake shadow password routine */
 {
        FILE *f;
        char line[1024];
-       static char pw[20];
+       static fstring pw;
        static struct spwd static_spwd;
 
        static_spwd.sp_pwdp=0;
@@ -227,7 +382,7 @@ static struct spwd *getspnam(char *username) /* fake shadow password routine */
                                *q=0;
                                if (q-p+1>20)
                                        break;
-                               strcpy(pw, p);
+                               fstrcpy(pw, p);
                                static_spwd.sp_pwdp=pw;
                        }
                        break;
@@ -262,7 +417,7 @@ static char *osf1_bigcrypt(char *password,char *salt1)
   for (i=0; i<parts;i++)
     {
       p1 = crypt(p2,salt);
-      strcat(result,p1+2);
+      strncat(result,p1+2,AUTH_MAX_PASSWD_LENGTH-strlen(p1+2)-1);
       StrnCpy(salt,&result[2+i*AUTH_CIPHERTEXT_SEG_CHARS],2);
       p2 += AUTH_CLEARTEXT_SEG_CHARS;
     }
@@ -271,6 +426,31 @@ static char *osf1_bigcrypt(char *password,char *salt1)
 }
 #endif
 
+/****************************************************************************
+update the encrypted smbpasswd file from the plaintext username and password
+*****************************************************************************/
+BOOL update_smbpassword_file( char *user, fstring password)
+{
+  struct smb_passwd *smbpw;
+  BOOL ret;
+
+  become_root(0);
+  smbpw = getsmbpwnam(user);
+  unbecome_root(0);
+
+  if(smbpw == NULL)
+  {
+    DEBUG(0,("update_smbpassword_file: 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,("update_smbpasswd_file: change_oem_password returned False\n"));
+
+  return ret;
+}
 
 /****************************************************************************
 update the enhanced security database. Only relevant for OSF1 at the moment.
@@ -311,6 +491,109 @@ static void update_protected_database( char *user, BOOL result)
 }
 
 
+#ifdef USE_PAM
+/*******************************************************************
+check on PAM authentication
+********************************************************************/
+
+/* We first need some helper functions */
+#include <security/pam_appl.h>
+/* Static variables used to communicate between the conversation function
+ * and the server_login function
+ */
+static char *PAM_username;
+static char *PAM_password;
+
+/* PAM conversation function
+ * Here we assume (for now, at least) that echo on means login name, and
+ * echo off means password.
+ */
+static int PAM_conv (int num_msg,
+                     const struct pam_message **msg,
+                     struct pam_response **resp,
+                     void *appdata_ptr) {
+  int replies = 0;
+  struct pam_response *reply = NULL;
+
+  #define COPY_STRING(s) (s) ? strdup(s) : NULL
+
+  reply = malloc(sizeof(struct pam_response) * num_msg);
+  if (!reply) return PAM_CONV_ERR;
+
+  for (replies = 0; replies < num_msg; replies++) {
+    switch (msg[replies]->msg_style) {
+      case PAM_PROMPT_ECHO_ON:
+        reply[replies].resp_retcode = PAM_SUCCESS;
+        reply[replies].resp = COPY_STRING(PAM_username);
+          /* PAM frees resp */
+        break;
+      case PAM_PROMPT_ECHO_OFF:
+        reply[replies].resp_retcode = PAM_SUCCESS;
+        reply[replies].resp = COPY_STRING(PAM_password);
+          /* PAM frees resp */
+        break;
+      case PAM_TEXT_INFO:
+       /* fall through */
+      case PAM_ERROR_MSG:
+        /* ignore it... */
+        reply[replies].resp_retcode = PAM_SUCCESS;
+        reply[replies].resp = NULL;
+        break;
+      default:
+        /* Must be an error of some sort... */
+        free (reply);
+        return PAM_CONV_ERR;
+    }
+  }
+  if (reply) *resp = reply;
+  return PAM_SUCCESS;
+}
+static struct pam_conv PAM_conversation = {
+    &PAM_conv,
+    NULL
+};
+
+
+static BOOL pam_auth(char *this_user,char *password)
+{
+  pam_handle_t *pamh;
+  int pam_error;
+
+  /* Now use PAM to do authentication.  For now, we won't worry about
+   * session logging, only authentication.  Bail out if there are any
+   * errors.  Since this is a limited protocol, and an even more limited
+   * function within a server speaking this protocol, we can't be as
+   * verbose as would otherwise make sense.
+   * Query: should we be using PAM_SILENT to shut PAM up?
+   */
+  #define PAM_BAIL if (pam_error != PAM_SUCCESS) { \
+     pam_end(pamh, 0); return False; \
+   }
+  PAM_password = password;
+  PAM_username = this_user;
+  pam_error = pam_start("samba", this_user, &PAM_conversation, &pamh);
+  PAM_BAIL;
+/* Setting PAM_SILENT stops generation of error messages to syslog
+ * to enable debugging on Red Hat Linux set:
+ * /etc/pam.d/samba:
+ *     auth required /lib/security/pam_pwdb.so nullok shadow audit
+ * _OR_ change PAM_SILENT to 0 to force detailed reporting (logging)
+ */
+  pam_error = pam_authenticate(pamh, PAM_SILENT);
+  PAM_BAIL;
+  /* It is not clear to me that account management is the right thing
+   * to do, but it is not clear that it isn't, either.  This can be
+   * removed if no account management should be done.  Alternately,
+   * put a pam_allow.so entry in /etc/pam.conf for account handling. */
+  pam_error = pam_acct_mgmt(pamh, PAM_SILENT);
+  PAM_BAIL;
+  pam_end(pamh, PAM_SUCCESS);
+  /* If this point is reached, the user has been authenticated. */
+  return(True);
+}
+#endif
+
+
 #ifdef AFS_AUTH
 /*******************************************************************
 check on AFS authentication
@@ -362,9 +645,14 @@ static BOOL dfs_auth(char *this_user,char *password)
    * Assumes local passwd file is kept in sync w/ DCE RGY!
    */
 
-  if (!strcmp((char *)crypt(password,this_salt),this_crypted) ||
-      dcelogin_atmost_once)
-    return(False);
+  /* Fix for original (broken) code from Brett Wooldridge <brettw@austin.ibm.com> */
+  if (dcelogin_atmost_once)
+    return (False);
+  /* This can be ifdefed as the DCE check below is stricter... */
+#ifndef NO_CRYPT
+  if ( strcmp((char *)crypt(password,this_salt),this_crypted) )
+    return (False);
+#endif
 
   if (sec_login_setup_identity(
                               (unsigned char *)this_user,
@@ -432,6 +720,112 @@ void dfs_unlogin(void)
 
 #endif
 
+#ifdef KRB5_AUTH
+/*******************************************************************
+check on Kerberos authentication
+********************************************************************/
+static BOOL krb5_auth(char *this_user,char *password)
+{
+       krb5_data tgtname = {
+               0,
+               KRB5_TGS_NAME_SIZE,
+               KRB5_TGS_NAME
+       };
+       krb5_context kcontext;
+       krb5_principal kprinc;
+       krb5_principal server;
+       krb5_creds kcreds;
+       int options = 0;
+       krb5_address **addrs = (krb5_address **)0;
+       krb5_preauthtype *preauth = NULL;
+       krb5_keytab keytab = NULL;
+       krb5_timestamp now;
+       krb5_ccache ccache = NULL;
+       int retval;
+       char *name;
+
+       if ( retval=krb5_init_context(&kcontext))
+       {
+               return(False);
+       }
+
+       if ( retval = krb5_timeofday(kcontext, &now) )
+       {
+               return(False);
+       }
+
+       if ( retval = krb5_cc_default(kcontext, &ccache) )
+       {
+               return(False);
+       }
+       
+       if ( retval = krb5_parse_name(kcontext, this_user, &kprinc) )
+       {
+               return(False);
+       }
+
+       memset((char *)&kcreds, 0, sizeof(kcreds));
+
+       kcreds.client = kprinc;
+       
+       if ((retval = krb5_build_principal_ext(kcontext, &server,
+               krb5_princ_realm(kcontext, kprinc)->length,
+               krb5_princ_realm(kcontext, kprinc)->data,
+               tgtname.length,
+               tgtname.data,
+               krb5_princ_realm(kcontext, kprinc)->length,
+               krb5_princ_realm(kcontext, kprinc)->data,
+               0)))
+       {
+               return(False);
+       }
+
+       kcreds.server = server;
+
+       retval = krb5_get_in_tkt_with_password(kcontext,
+               options,
+               addrs,
+               NULL,
+               preauth,
+               password,
+               0,
+               &kcreds,
+               0);
+
+       if ( retval )
+       {
+               return(False);
+       }
+
+       return(True);
+}
+#endif /* KRB5_AUTH */
+
+#ifdef KRB4_AUTH
+/*******************************************************************
+check on Kerberos authentication
+********************************************************************/
+static BOOL krb4_auth(char *this_user,char *password)
+{
+  char realm[REALM_SZ];
+  char tkfile[MAXPATHLEN];
+  
+  if (krb_get_lrealm(realm, 1) != KSUCCESS)
+    (void) safe_strcpy(realm, KRB_REALM, sizeof (realm) - 1);
+  
+  (void) slprintf(tkfile, sizeof(tkfile) - 1, "/tmp/samba_tkt_%d", getpid());
+  
+  krb_set_tkt_string(tkfile);
+  if (krb_verify_user(this_user, "", realm,
+                     password, 0,
+                     "rmcd") == KSUCCESS) {
+    unlink(tkfile);
+    return 1;
+  }
+  unlink(tkfile);
+  return 0;
+}
+#endif /* KRB4_AUTH */
 
 #ifdef LINUX_BIGCRYPT
 /****************************************************************************
@@ -448,7 +842,7 @@ static int linux_bigcrypt(char *password,char *salt1, char *crypted)
   
   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)
+    if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
       return(0);
     password += LINUX_PASSWORD_SEG_CHARS;
     crypted  += strlen(p);
@@ -466,7 +860,7 @@ try all combinations with N uppercase letters.
 offset is the first char to try and change (start with 0)
 it assumes the string starts lowercased
 ****************************************************************************/
-static BOOL string_combinations2(char *s,int offset,BOOL (*fn)(),int N)
+static BOOL string_combinations2(char *s,int offset,BOOL (*fn)(char *),int N)
 {
   int len = strlen(s);
   int i;
@@ -497,7 +891,7 @@ 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)
+static BOOL string_combinations(char *s,BOOL (*fn)(char *),int N)
 {
   int n;
   for (n=1;n<=N;n++)
@@ -512,6 +906,20 @@ core of password checking routine
 ****************************************************************************/
 BOOL password_check(char *password)
 {
+
+#ifdef USE_PAM
+/* This falls through if the password check fails
+       - if NO_CRYPT is defined this causes an error msg
+               saying Warning - no crypt available
+       - if NO_CRYPT is NOT defined this is a potential security hole
+               as it may authenticate via the crypt call when PAM
+               settings say it should fail.
+  if (pam_auth(this_user,password)) return(True);
+Hence we make a direct return to avoid a second chance!!!
+*/
+  return (pam_auth(this_user,password));
+#endif
+
 #ifdef AFS_AUTH
   if (afs_auth(this_user,password)) return(True);
 #endif
@@ -520,13 +928,28 @@ BOOL password_check(char *password)
   if (dfs_auth(this_user,password)) return(True);
 #endif 
 
+#ifdef KRB5_AUTH
+  if (krb5_auth(this_user,password)) return(True);
+#endif
+
+#ifdef KRB4_AUTH
+  if (krb4_auth(this_user,password)) return(True);
+#endif
+
 #ifdef PWDAUTH
   if (pwdauth(this_user,password) == 0)
     return(True);
 #endif
 
 #ifdef OSF1_ENH_SEC
-  return(strcmp(osf1_bigcrypt(password,this_salt),this_crypted) == 0);
+  {
+    BOOL ret = (strcmp(osf1_bigcrypt(password,this_salt),this_crypted) == 0);
+    if(!ret) {
+      DEBUG(2,("password_check: OSF1_ENH_SEC failed. Trying normal crypt.\n"));
+      ret = (strcmp((char *)crypt(password,this_salt),this_crypted) == 0);
+    }
+    return ret;
+  }
 #endif
 
 #ifdef ULTRIX_AUTH
@@ -537,6 +960,10 @@ BOOL password_check(char *password)
   return(linux_bigcrypt(password,this_salt,this_crypted));
 #endif
 
+#ifdef HPUX_10_TRUSTED
+  return(strcmp(bigcrypt(password,this_salt),this_crypted) == 0);
+#endif
+
 #ifdef NO_CRYPT
   DEBUG(1,("Warning - no crypt available\n"));
   return(False);
@@ -545,7 +972,6 @@ BOOL password_check(char *password)
 #endif
 }
 
-#ifdef SMB_PASSWD
 /****************************************************************************
 core of smb password checking routine.
 ****************************************************************************/
@@ -555,10 +981,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);
@@ -587,7 +1013,68 @@ BOOL smb_password_check(char *password, unsigned char *part_passwd, unsigned cha
 #endif
   return (memcmp(p24, password, 24) == 0);
 }
-#endif
+
+/****************************************************************************
+ Do a specific test for an smb password being correct, given a smb_password and
+ the lanman and NT responses.
+****************************************************************************/
+
+BOOL smb_password_ok(struct smb_passwd *smb_pass,
+                     uchar lm_pass[24], uchar nt_pass[24])
+{
+  uchar challenge[8];
+
+  if (!lm_pass || !smb_pass) return(False);
+
+  if(smb_pass->acct_ctrl & ACB_DISABLED)
+  {
+    DEBUG(3,("smb_password_ok: account for user %s was disabled.\n", smb_pass->smb_name));
+    return(False);
+  }
+
+  if (!last_challenge(challenge))
+  {
+    DEBUG(1,("smb_password_ok: no challenge done - password failed\n"));
+    return False;
+  }
+
+  DEBUG(4,("smb_password_ok: Checking SMB password for user %s\n", smb_pass->smb_name));
+
+  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).
+     */
+    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,("smb_password_ok: NT MD4 password check succeeded\n"));
+      return(True);
+    }
+    DEBUG(4,("smb_password_ok: NT MD4 password check failed\n"));
+  }
+
+  /* Try against the lanman password. smb_pass->smb_passwd == NULL means
+     no password, allow access. */
+
+  DEBUG(4,("Checking LM MD4 password\n"));
+
+  if((smb_pass->smb_passwd == NULL) && (smb_pass->acct_ctrl & ACB_PWNOTREQ))
+  {
+    DEBUG(4,("smb_password_ok: no password required for user %s\n", smb_pass->smb_name));
+    return True;
+  }
+
+  if((smb_pass->smb_passwd != NULL) && smb_password_check((char *)lm_pass, (uchar *)smb_pass->smb_passwd, challenge))
+  {
+    DEBUG(4,("smb_password_ok: LM MD4 password check succeeded\n"));
+    return(True);
+  }
+
+  DEBUG(4,("smb_password_ok: LM MD4 password check failed\n"));
+
+  return False;
+}
 
 /****************************************************************************
 check if a username/password is OK
@@ -597,21 +1084,17 @@ BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd)
   pstring pass2;
   int level = lp_passwordlevel();
   struct passwd *pass;
-#ifdef SMB_PASSWD
-  char challenge[8];
+  uchar challenge[8];
   struct smb_passwd *smb_pass;
+  BOOL update_encrypted = lp_update_encrypted();
   BOOL challenge_done = False;
-#endif
 
   if (password) password[pwlen] = 0;
 
-#ifdef SMB_PASSWD
   if (pwlen == 24)
     challenge_done = last_challenge(challenge);
-#endif
 
 #if DEBUG_PASSWORD
-#ifdef SMB_PASSWD
   if (challenge_done)
     {
       int i;      
@@ -619,10 +1102,9 @@ BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd)
       for( i = 0; i < 24; i++)
        DEBUG(100,("%0x ", (unsigned char)password[i]));
       DEBUG(100,("]\n"));
+    } else {
+           DEBUG(100,("checking user=[%s] pass=[%s]\n",user,password));
     }
-  else
-#endif
-    DEBUG(100,("checking user=[%s] pass=[%s]\n",user,password));
 #endif
 
   if (!password)
@@ -639,11 +1121,9 @@ BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd)
   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)
+  if ((pwlen == 24) && challenge_done)
     {
       DEBUG(4,("Checking SMB password for user %s (l=24)\n",user));
 
@@ -653,51 +1133,36 @@ BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd)
          return(False);
        }
 
-      smb_pass = get_smbpwnam(user);
-      if(!smb_pass)
+      smb_pass = getsmbpwnam(user);
+
+      if (!smb_pass)
        {
          DEBUG(3,("Couldn't find user %s in smb_passwd file.\n", user));
          return(False);
        }
 
+      /* Quit if the account was disabled. */
+      if(smb_pass->acct_ctrl & ACB_DISABLED)
+        {
+          DEBUG(3,("password_ok: account for user %s was disabled.\n", user));
+          return(False);
+        }
+
       /* Ensure the uid's match */
-      if(smb_pass->smb_userid != pass->pw_uid)
+      if (smb_pass->smb_userid != pass->pw_uid)
        {
          DEBUG(3,("Error : UNIX and SMB uids in password files do not match !\n"));
          return(False);
        }
 
-       if(Protocol >= PROTOCOL_NT1)
-       {
-               /* 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, 
-                                       (char *)challenge))
-                 {
-               update_protected_database(user,True);
-               return(True);
-         }
-                 DEBUG(4,("NT MD4 password check failed\n"));
-               }
-       }
-
-       /* Try against the lanman password */
-
-      if (smb_password_check(password, 
-                            smb_pass->smb_passwd,
-                            (char *)challenge)) {
-       update_protected_database(user,True);
-       return(True);
-      }
+      if(smb_password_ok( smb_pass, (unsigned char *)password,(uchar *)password))
+        {
+          update_protected_database(user,True);
+          return(True);
+        }
 
-       DEBUG(3,("Error smb_password_check failed\n"));
+      DEBUG(3,("Error smb_password_check failed\n"));
     }
-#endif 
 
   DEBUG(4,("Checking password for user %s (l=%d)\n",user,pwlen));
 
@@ -720,6 +1185,15 @@ BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd)
     if (spass && spass->sp_pwdp)
       pass->pw_passwd = spass->sp_pwdp;
   }
+#elif defined(IA_UINFO)
+  {
+      /* Need to get password with SVR4.2's ia_ functions instead of
+         get{sp,pw}ent functions. Required by UnixWare 2.x, tested on 
+         version 2.1. (tangent@cyberport.com) */
+      uinfo_t uinfo;
+      if (ia_openinfo(pass->pw_name, &uinfo) != -1)
+        ia_get_logpwd(uinfo, &(pass->pw_passwd));
+  }
 #endif
 
 #ifdef SecureWare
@@ -745,8 +1219,8 @@ BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd)
     mypasswd = getprpwnam (user);
     if ( mypasswd )
       { 
-       strcpy(pass->pw_name,mypasswd->ufld.fd_name);
-       strcpy(pass->pw_passwd,mypasswd->ufld.fd_encrypt);
+       fstrcpy(pass->pw_name,mypasswd->ufld.fd_name);
+       fstrcpy(pass->pw_passwd,mypasswd->ufld.fd_encrypt);
       }
     else
       {
@@ -761,16 +1235,20 @@ BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd)
     AUTHORIZATION *ap = getauthuid( pass->pw_uid );
     if (ap)
       {
-       strcpy( pass->pw_passwd, ap->a_password );
+       fstrcpy( pass->pw_passwd, ap->a_password );
        endauthent();
       }
   }
 #endif
 
   /* extract relevant info */
-  strcpy(this_user,pass->pw_name);  
-  strcpy(this_salt,pass->pw_passwd);
-  strcpy(this_crypted,pass->pw_passwd);
+  fstrcpy(this_user,pass->pw_name);  
+  fstrcpy(this_salt,pass->pw_passwd);
+#ifdef HPUX
+  /* The crypt on HPUX won't work with more than 2 salt characters. */
+  this_salt[2] = 0;
+#endif /* HPUX */
+  fstrcpy(this_crypted,pass->pw_passwd);
  
   if (!*this_crypted) {
     if (!lp_null_passwords()) {
@@ -789,6 +1267,8 @@ BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd)
   if (password_check(password))
     {
       update_protected_database(user,True);
+      if (update_encrypted)
+        update_smbpassword_file(user,password);
       return(True);
     }
 
@@ -806,16 +1286,18 @@ BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd)
   if (password_check(password))
     {
       update_protected_database(user,True);
+      if (update_encrypted)
+        update_smbpassword_file(user,password);
       return(True);
     }
 
   /* give up? */
-  if(level < 1)
+  if (level < 1)
     {
       update_protected_database(user,False);
 
       /* restore it */
-      strcpy(password,pass2);
+      fstrcpy(password,pass2);
 
       return(False);
     }
@@ -826,13 +1308,15 @@ BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd)
   if (string_combinations(password,password_check,level))
     {
       update_protected_database(user,True);
+      if (update_encrypted)
+        update_smbpassword_file(user,password);
       return(True);
     }
 
   update_protected_database(user,False);
   
   /* restore it */
-  strcpy(password,pass2);
+  fstrcpy(password,pass2);
   
   return(False);
 }
@@ -902,7 +1386,7 @@ 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))
              return(&name[0]);
@@ -918,7 +1402,7 @@ static char *validate_group(char *group,char *password,int pwlen,int snum)
            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)) {
-               strcpy(tm, pwd->pw_name);
+               fstrcpy(tm, pwd->pw_name);
                endpwent ();
                return tm;
              }
@@ -949,7 +1433,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
@@ -978,7 +1462,7 @@ BOOL authorise_login(int snum,char *user,char *password, int pwlen,
       if (!ok && (vuser != 0) && vuser->guest) {         
        if (user_ok(vuser->name,snum) &&
            password_ok(vuser->name, password, pwlen, NULL)) {
-         strcpy(user, vuser->name);
+         fstrcpy(user, vuser->name);
          vuser->guest = False;
          DEBUG(3,("ACCEPTED: given password with registered user %s\n", user));
          ok = True;
@@ -987,38 +1471,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)) {
-               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) &&
-         (vuser != 0) && !vuser->guest &&
-         user_ok(vuser->name,snum)) {
-       strcpy(user,vuser->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)) {
@@ -1044,19 +1528,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))
                  {
                    ok = True;
-                   strcpy(user,user2);
+                   fstrcpy(user,user2);
                    DEBUG(3,("ACCEPTED: user list username and given password ok\n"));
                  }
              }
@@ -1071,7 +1555,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"));
        }
@@ -1142,7 +1626,8 @@ 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;
@@ -1210,196 +1695,475 @@ BOOL check_hosts_equiv(char *user)
   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_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_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);
 }
 
 
-int password_client = -1;
-static fstring pserver;
+static struct cli_state pw_cli;
 
 /****************************************************************************
-attempted support for server level security 
+return the client state structure
 ****************************************************************************/
-BOOL server_cryptkey(char *buf)
+struct cli_state *server_client(void)
 {
-  pstring inbuf,outbuf;
-  fstring pass_protocol;
-  extern fstring remote_machine;
-  char *p;
-  int len;
-  fstring desthost;
-  struct in_addr dest_ip;
-  int port = SMB_PORT;
-  BOOL ret;
+       return &pw_cli;
+}
 
-  if (password_client >= 0)
-    close(password_client);
-  password_client = -1;
+/****************************************************************************
+support for server level security 
+****************************************************************************/
+struct cli_state *server_cryptkey(void)
+{
+       fstring desthost;
+       struct in_addr dest_ip;
+       extern fstring local_machine;
+       char *p;
+        BOOL connected_ok = False;
+
+       if (!cli_initialise(&pw_cli))
+               return NULL;
+
+        p = lp_passwordserver();
+        while(p && next_token( &p, desthost, LIST_SEP)) {
+               standard_sub_basic(desthost);
+               strupper(desthost);
+
+                if(!resolve_name( desthost, &dest_ip)) {
+                        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 (Protocol < PROTOCOL_NT1) {
-    strcpy(pass_protocol,"LM1.2X002");
-  } else {
-    strcpy(pass_protocol,"NT LM 0.12");
-  }
+               if (cli_connect(&pw_cli, desthost, &dest_ip)) {
+                       DEBUG(3,("connected to password server %s\n",desthost));
+                       connected_ok = True;
+                       break;
+               }
+       }
 
-  bzero(inbuf,sizeof(inbuf));
-  bzero(outbuf,sizeof(outbuf));
+       if (!connected_ok) {
+               DEBUG(0,("password server not available\n"));
+               cli_shutdown(&pw_cli);
+               return NULL;
+       }
 
-  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(&pw_cli, desthost, 0x20, local_machine)) {
+               DEBUG(1,("%s rejected the session\n",desthost));
+               cli_shutdown(&pw_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 (ismyip(dest_ip)) {
-      DEBUG(1,("Password server loop - disabling password server %s\n",p));
-      continue;
-    }
+       if (!cli_negprot(&pw_cli)) {
+               DEBUG(1,("%s rejected the negprot\n",desthost));
+               cli_shutdown(&pw_cli);
+               return NULL;
+       }
 
-    password_client = open_socket_out(SOCK_STREAM, &dest_ip, port, SHORT_CONNECT_TIMEOUT);
-    if (password_client >= 0) {
-      DEBUG(3,("connected to password server %s\n",p));
-      StrnCpy(pserver,p,sizeof(pserver)-1);
-      break;
-    }
-  }
+       if (pw_cli.protocol < PROTOCOL_LANMAN2 ||
+           !(pw_cli.sec_mode & 1)) {
+               DEBUG(1,("%s isn't in user level security mode\n",desthost));
+               cli_shutdown(&pw_cli);
+               return NULL;
+       }
 
-  if (password_client < 0) {
-    DEBUG(1,("password server not available\n"));
-    return(False);
-  }
+       DEBUG(3,("password server OK\n"));
 
+       return &pw_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)
+{
+       extern fstring local_machine;
+        static unsigned char badpass[24];
+
+       if (!pw_cli.initialised) {
+               DEBUG(1,("password server %s is not connected\n", pw_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(&pw_cli, user, (char *)badpass, sizeof(badpass), 
+                              (char *)badpass, sizeof(badpass), domain)) {
+         if ((SVAL(pw_cli.inbuf,smb_vwv2) & 1) == 0) {
+            DEBUG(0,("server_validate: password server %s allows users as non-guest \
+with a bad password.\n", pw_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(&pw_cli);
+            return False;
+          }
+          cli_ulogoff(&pw_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(&pw_cli, user, pass, passlen, ntpass, ntpasslen, domain)) {
+               DEBUG(1,("password server %s rejected the password\n", pw_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(pw_cli.inbuf,smb_vwv2) & 1) != 0) {
+               DEBUG(1,("password server %s gave us guest only\n", pw_cli.desthost));
+                cli_ulogoff(&pw_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", pw_cli.desthost));
+
+                if (!cli_send_tconX(&pw_cli, "IPC$", "IPC", "", 1)) {
+                        DEBUG(0,("password server %s refused IPC$ connect\n", pw_cli.desthost));
+                        cli_ulogoff(&pw_cli);
+                        return False;
+                }
+
+               if (!cli_NetWkstaUserLogon(&pw_cli,user,local_machine)) {
+                       DEBUG(0,("password server %s failed NetWkstaUserLogon\n", pw_cli.desthost));
+                       cli_tdis(&pw_cli);
+                        cli_ulogoff(&pw_cli);
+                       return False;
+               }
 
-  _smb_setlen(outbuf,len);
-  CVAL(outbuf,0) = 0x81;
+               if (pw_cli.privilages == 0) {
+                       DEBUG(0,("password server %s gave guest privilages\n", pw_cli.desthost));
+                       cli_tdis(&pw_cli);
+                        cli_ulogoff(&pw_cli);
+                       return False;
+               }
 
-  send_smb(password_client,outbuf);
-  
-  if (!receive_smb(password_client,inbuf,5000) ||
-      CVAL(inbuf,0) != 0x82) {
-    DEBUG(1,("%s rejected the session\n",pserver));
-    close(password_client); password_client = -1;
-    return(False);
+               if (!strequal(pw_cli.eff_name, user)) {
+                       DEBUG(0,("password server %s gave different username %s\n", 
+                               pw_cli.desthost,
+                               pw_cli.eff_name));
+                       cli_tdis(&pw_cli);
+                        cli_ulogoff(&pw_cli);
+                       return False;
+               }
+                cli_tdis(&pw_cli);
+       }
+        else {
+               DEBUG(3,("skipping NetWkstaUserLogon with password server %s\n", pw_cli.desthost));
+        }
+
+       DEBUG(3,("password server %s accepted the password\n", pw_cli.desthost));
+
+        cli_ulogoff(&pw_cli);
+
+       return(True);
+}
+
+/***********************************************************************
+ Do the same as security=server, but using NT Domain calls and a session
+ key from the machine password.
+************************************************************************/
+
+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];
+  time_t lct;
+  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;
+
+  /* 
+   * 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(strequal( domain, global_myname)) {
+    DEBUG(3,("domain_client_validate: Requested domain was for this machine.\n"));
+    return False;
   }
 
-  DEBUG(3,("got session\n"));
+  /*
+   * Next, check that the passwords given were encrypted.
+   */
 
-  bzero(outbuf,smb_size);
+  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 {
 
-  /* setup the protocol string */
-  set_message(outbuf,0,strlen(pass_protocol)+2,True);
-  p = smb_buf(outbuf);
-  *p++ = 2;
-  strcpy(p,pass_protocol);
+    /*
+     * Encrypted - get the challenge we sent for these
+     * responses.
+     */
 
-  CVAL(outbuf,smb_com) = SMBnegprot;
-  CVAL(outbuf,smb_flg) = 0x8;
-  SSVAL(outbuf,smb_flg2,0x1);
+    if (!last_challenge(local_challenge)) {
+      DEBUG(0,("domain_client_validate: no challenge done - password failed\n"));
+      return False;
+    }
+  }
 
-  send_smb(password_client,outbuf);
-  ret = receive_smb(password_client,inbuf,5000);
+  /*
+   * Get the machine account password.
+   */
+  if(!trust_password_lock( global_myworkgroup, global_myname, False)) {
+    DEBUG(0,("domain_client_validate: unable to open the machine account password file for \
+machine %s in domain %s.\n", global_myname, global_myworkgroup ));
+    return False;
+  }
 
-  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(get_trust_account_password( trust_passwd, &lct) == False) {
+    DEBUG(0,("domain_client_validate: unable to read the machine account password for \
+machine %s in domain %s.\n", global_myname, global_myworkgroup ));
+    trust_password_unlock();
+    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);
+  trust_password_unlock();
+
+  /* 
+   * Here we should check the last change time to see if the machine
+   * password needs changing..... TODO... JRA. 
+   */
+
+  if(time(NULL) > lct + lp_machine_password_timeout())
+    global_machine_pasword_needs_changing = True;
+
+  /*
+   * 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.
+   */
+
+  memset(&cli, '\0', sizeof(struct cli_state));
+  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)) {                       
 
-  return(True);
-}
+    standard_sub_basic(remote_machine);
+    strupper(remote_machine);
+    if(!resolve_name( remote_machine, &dest_ip)) {
+      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;
+    }
+    
+    if (!cli_session_request(&cli, remote_machine, 0x20, global_myname)) {
+      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;
 
-/****************************************************************************
-attempted support for server level security 
-****************************************************************************/
-BOOL server_validate(char *buf)
-{
-  pstring inbuf,outbuf;  
-  BOOL ret;
+    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;
+    }
 
-  if (password_client < 0) {
-    DEBUG(1,("%s not connected\n",pserver));
-    return(False);
-  }  
+    /* 
+     * Do an anonymous session setup.
+     */
+
+    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;
+    }      
 
-  bzero(inbuf,sizeof(inbuf));
-  memcpy(outbuf,buf,sizeof(outbuf));
+    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;
+    }
 
-  /* send a session setup command */
-  CVAL(outbuf,smb_flg) = 0x8;
-  SSVAL(outbuf,smb_flg2,0x1);
-  CVAL(outbuf,smb_vwv0) = 0xFF;
+    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;
+    }
 
-  set_message(outbuf,smb_numwords(outbuf),smb_buflen(outbuf),False);
+    /*
+     * We have an anonymous connection to IPC$.
+     */
+    connected_ok = True;
+    break;
+  }
 
-  SCVAL(inbuf,smb_rcls,1);
+  if (!connected_ok) {
+    DEBUG(0,("domain_client_validate: Domain password server not available.\n"));
+    cli_shutdown(&cli);
+    return False;
+  }
 
-  send_smb(password_client,outbuf);
-  ret = receive_smb(password_client,inbuf,5000);
+  /*
+   * Ok - we have an anonymous connection to the IPC$ share.
+   * Now start the NT Domain stuff :-).
+   */
 
-  if (!ret || CVAL(inbuf,smb_rcls) != 0) {
-    DEBUG(1,("password server %s rejected the password\n",pserver));
-    return(False);
+  if(cli_nt_session_open(&cli, PIPE_NETLOGON, False) == 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; 
   }
 
-  /* 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);
+  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;
   }
 
-  DEBUG(3,("password server %s accepted the password\n",pserver));
+  /* 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;
+  }
 
-#if !KEEP_PASSWORD_SERVER_OPEN
-  close(password_client); password_client= -1;
-#endif
+  /*
+   * Here, if we really want it, we have lots of info about the user in info3.
+   */
 
-  return(True);
-}
+#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;
+}