Changes from APPLIANCE_HEAD:
[ira/wip.git] / source3 / smbd / password.c
index 6d3a2d1a317db9f9512f7dfeb341ba9320d35692..69ba0421555cea539c248c9e6e6a15453a5270c3 100644 (file)
@@ -93,8 +93,9 @@ static BOOL last_challenge(unsigned char *challenge)
 }
 
 /* this holds info on user ids that are already validated for this VC */
-static user_struct *validated_users = NULL;
-static int num_validated_users = 0;
+static user_struct *validated_users;
+static int next_vuid = VUID_OFFSET;
+static int num_validated_vuids;
 
 /****************************************************************************
 check if a uid has been validated, and return an pointer to the user_struct
@@ -103,13 +104,21 @@ tell random client vuid's (normally zero) from valid vuids.
 ****************************************************************************/
 user_struct *get_valid_user_struct(uint16 vuid)
 {
-  if (vuid == UID_FIELD_INVALID)
-    return NULL;
-  vuid -= VUID_OFFSET;
-  if ((vuid >= (uint16)num_validated_users) || 
-     (validated_users[vuid].uid == (uid_t)-1) || (validated_users[vuid].gid == (gid_t)-1))
-    return NULL;
-  return &validated_users[vuid];
+       user_struct *usp;
+       int count=0;
+
+       if (vuid == UID_FIELD_INVALID)
+               return NULL;
+
+       for (usp=validated_users;usp;usp=usp->next,count++) {
+               if (vuid == usp->vuid) {
+                       if (count > 10)
+                DLIST_PROMOTE(validated_users, usp);
+                       return usp;
+               }
+       }
+
+       return NULL;
 }
 
 /****************************************************************************
@@ -122,18 +131,12 @@ void invalidate_vuid(uint16 vuid)
        if (vuser == NULL)
                return;
 
-       vuser->uid = (uid_t)-1;
-       vuser->gid = (gid_t)-1;
-
-       /* same number of igroups as groups */
-       vuser->n_groups = 0;
-
-       if (vuser->groups)
-               free((char *)vuser->groups);
-
-       vuser->groups  = NULL;
+       DLIST_REMOVE(validated_users, vuser);
 
+       safe_free(vuser->groups);
        delete_nt_token(&vuser->nt_user_token);
+       safe_free(vuser);
+       num_validated_vuids--;
 }
 
 /****************************************************************************
@@ -159,63 +162,80 @@ char *validated_domain(uint16 vuid)
 }
 
 
-/****************************************************************************
- Initialize the groups a user belongs to.
-****************************************************************************/
-
-BOOL initialize_groups(char *user, uid_t uid, gid_t gid)
-{
-       become_root();
-       if (initgroups(user,gid) == -1) {
-               DEBUG(0,("Unable to initgroups. Error was %s\n", strerror(errno) ));
-               if (getuid() == 0) {
-                       if (gid < 0 || gid > 32767 || uid < 0 || uid > 32767) {
-                               DEBUG(0,("This is probably a problem with the account %s\n", user));
-                       }
-               }
-               unbecome_root();
-               return False;
-       }
-       unbecome_root();
-       return True;
-}
-
 /****************************************************************************
  Create the SID list for this user.
 ****************************************************************************/
 
-NT_USER_TOKEN *create_nt_token(uid_t uid, gid_t gid, int ngroups, gid_t *groups)
+NT_USER_TOKEN *create_nt_token(uid_t uid, gid_t gid, int ngroups, gid_t *groups, BOOL is_guest)
 {
+       extern DOM_SID global_sid_World;
+       extern DOM_SID global_sid_Network;
+       extern DOM_SID global_sid_Builtin_Guests;
+       extern DOM_SID global_sid_Authenticated_Users;
        NT_USER_TOKEN *token;
        DOM_SID *psids;
        int i, psid_ndx = 0;
+       size_t num_sids = 0;
+       fstring sid_str;
 
        if ((token = (NT_USER_TOKEN *)malloc( sizeof(NT_USER_TOKEN) ) ) == NULL)
                return NULL;
 
        ZERO_STRUCTP(token);
 
-       if ((token->user_sids = (DOM_SID *)malloc( (ngroups + 2)*sizeof(DOM_SID))) == NULL) {
+       /* We always have uid/gid plus World and Network and Authenticated Users or Guest SIDs. */
+       num_sids = 5 + ngroups;
+
+       if ((token->user_sids = (DOM_SID *)malloc( num_sids*sizeof(DOM_SID))) == NULL) {
                free(token);
                return NULL;
        }
 
        psids = token->user_sids;
 
-       token->num_sids = 2;
+       /*
+        * Note - user SID *MUST* be first in token !
+        * se_access_check depends on this.
+        */
 
-       uid_to_sid( &psids[0], uid);
-       gid_to_sid( &psids[1], gid);
+       uid_to_sid( &psids[psid_ndx++], uid);
+
+       /*
+        * Primary group SID is second in token. Convention.
+        */
+
+       gid_to_sid( &psids[psid_ndx++], gid);
+
+       /* Now add the group SIDs. */
 
        for (i = 0; i < ngroups; i++) {
                if (groups[i] != gid) {
-                       gid_to_sid( &psids[psid_ndx+2], groups[i]);
-                       psid_ndx++;
-                       token->num_sids++;
+                       gid_to_sid( &psids[psid_ndx++], groups[i]);
                }
        }
-       for (i = 0; i < ngroups; i++)
-               gid_to_sid( &psids[i+2], groups[i]);
+
+       /*
+        * Finally add the "standard" SIDs.
+        * The only difference between guest and "anonymous" (which we
+        * don't really support) is the addition of Authenticated_Users.
+        */
+
+       sid_copy( &psids[psid_ndx++], &global_sid_World);
+       sid_copy( &psids[psid_ndx++], &global_sid_Network);
+
+       if (is_guest)
+               sid_copy( &psids[psid_ndx++], &global_sid_Builtin_Guests);
+       else
+               sid_copy( &psids[psid_ndx++], &global_sid_Authenticated_Users);
+
+       token->num_sids = psid_ndx;
+
+       /* Dump list of sids in token */
+
+       for (i = 0; i < token->num_sids; i++) {
+               DEBUG(5, ("user token sid %s\n", 
+                         sid_to_string(sid_str, &token->user_sids[i])));
+       }
 
        return token;
 }
@@ -229,59 +249,75 @@ tell random client vuid's (normally zero) from valid vuids.
 uint16 register_vuid(uid_t uid,gid_t gid, char *unix_name, char *requested_name, 
                     char *domain,BOOL guest)
 {
-  user_struct *vuser;
-  struct passwd *pwfile; /* for getting real name from passwd file */
+       user_struct *vuser = NULL;
+       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;
+       /* Ensure no vuid gets registered in share level security. */
+       if(lp_security() == SEC_SHARE)
+               return UID_FIELD_INVALID;
 
-  validated_users = (user_struct *)Realloc(validated_users,
-                          sizeof(user_struct)*
-                          (num_validated_users+1));
-  
-  if (!validated_users) {
-      DEBUG(0,("Failed to realloc users struct!\n"));
-      num_validated_users = 0;
-      return UID_FIELD_INVALID;
-  }
+       /* Limit allowed vuids to 16bits - VUID_OFFSET. */
+       if (num_validated_vuids >= 0xFFFF-VUID_OFFSET)
+               return UID_FIELD_INVALID;
 
-  vuser = &validated_users[num_validated_users];
-  num_validated_users++;
+       if((vuser = (user_struct *)malloc( sizeof(user_struct) )) == NULL) {
+               DEBUG(0,("Failed to malloc users struct!\n"));
+               return UID_FIELD_INVALID;
+       }
 
-  vuser->uid = uid;
-  vuser->gid = gid;
-  vuser->guest = guest;
-  fstrcpy(vuser->user.unix_name,unix_name);
-  fstrcpy(vuser->user.smb_name,requested_name);
-  fstrcpy(vuser->user.domain,domain);
+       ZERO_STRUCTP(vuser);
 
-  vuser->n_groups = 0;
-  vuser->groups  = NULL;
+       DEBUG(10,("register_vuid: (%u,%u) %s %s %s guest=%d\n", (unsigned int)uid, (unsigned int)gid,
+                               unix_name, requested_name, domain, guest ));
 
-  /* Find all the groups this uid is in and store them. 
-     Used by become_user() */
-  initialise_groups(unix_name, uid, gid);
-  initialize_groups(unix_name, uid, gid);
-  get_current_groups( &vuser->n_groups, &vuser->groups);
+       /* Allocate a free vuid. Yes this is a linear search... :-) */
+       while( get_valid_user_struct(next_vuid) != NULL ) {
+               next_vuid++;
+               /* Check for vuid wrap. */
+               if (next_vuid == UID_FIELD_INVALID)
+                       next_vuid = VUID_OFFSET;
+       }
 
-  /* Create an NT_USER_TOKEN struct for this user. */
-  vuser->nt_user_token = create_nt_token(uid,gid, vuser->n_groups, vuser->groups);
+       DEBUG(10,("register_vuid: allocated vuid = %u\n", (unsigned int)next_vuid ));
 
-  DEBUG(3,("uid %d registered to name %s\n",(int)uid,unix_name));
+       vuser->vuid = next_vuid;
+       vuser->uid = uid;
+       vuser->gid = gid;
+       vuser->guest = guest;
+       fstrcpy(vuser->user.unix_name,unix_name);
+       fstrcpy(vuser->user.smb_name,requested_name);
+       fstrcpy(vuser->user.domain,domain);
 
-  DEBUG(3, ("Clearing default real name\n"));
-  fstrcpy(vuser->user.full_name, "<Full Name>");
-  if (lp_unix_realname()) {
-    if ((pwfile=sys_getpwnam(vuser->user.unix_name))!= NULL) {
-      DEBUG(3, ("User name: %s\tReal name: %s\n",vuser->user.unix_name,pwfile->pw_gecos));
-      fstrcpy(vuser->user.full_name, pwfile->pw_gecos);
-    }
-  }
+       vuser->n_groups = 0;
+       vuser->groups  = NULL;
+
+       /* Find all the groups this uid is in and store them. 
+               Used by become_user() */
+       initialise_groups(unix_name, uid, gid);
+       get_current_groups( &vuser->n_groups, &vuser->groups);
+
+       /* Create an NT_USER_TOKEN struct for this user. */
+       vuser->nt_user_token = create_nt_token(uid,gid, vuser->n_groups, vuser->groups, guest);
+
+       next_vuid++;
+       num_validated_vuids++;
+
+       DLIST_ADD(validated_users, vuser);
 
-  memset(&vuser->dc, '\0', sizeof(vuser->dc));
+       DEBUG(3,("uid %d registered to name %s\n",(int)uid,unix_name));
 
-  return (uint16)((num_validated_users - 1) + VUID_OFFSET);
+       DEBUG(3, ("Clearing default real name\n"));
+       fstrcpy(vuser->user.full_name, "<Full Name>");
+       if (lp_unix_realname()) {
+               if ((pwfile=sys_getpwnam(vuser->user.unix_name))!= NULL) {
+                       DEBUG(3, ("User name: %s\tReal name: %s\n",vuser->user.unix_name,pwfile->pw_gecos));
+                       fstrcpy(vuser->user.full_name, pwfile->pw_gecos);
+               }
+       }
+
+       memset(&vuser->dc, '\0', sizeof(vuser->dc));
+
+       return vuser->vuid;
 }
 
 
@@ -313,15 +349,15 @@ update the encrypted smbpasswd file from the plaintext username and password
 *****************************************************************************/
 static BOOL update_smbpassword_file(char *user, char *password)
 {
-       struct smb_passwd *smbpw;
-       BOOL ret;
+       SAM_ACCOUNT     *sampass = NULL;
+       BOOL            ret;
        
        become_root();
-       smbpw = getsmbpwnam(user);
+       sampass = pdb_getsampwnam(user);
        unbecome_root();
 
-       if(smbpw == NULL) {
-               DEBUG(0,("getsmbpwnam returned NULL\n"));
+       if(sampass == NULL) {
+               DEBUG(0,("pdb_getsampwnam returned NULL\n"));
                return False;
        }
 
@@ -329,11 +365,11 @@ static BOOL update_smbpassword_file(char *user, char *password)
         * Remove the account disabled flag - we are updating the
         * users password from a login.
         */
-       smbpw->acct_ctrl &= ~ACB_DISABLED;
+       pdb_set_acct_ctrl(sampass, pdb_get_acct_ctrl(sampass) & ~ACB_DISABLED);
 
        /* Here, the flag is one, because we want to ignore the
            XXXXXXX'd out password */
-       ret = change_oem_password( smbpw, password, True);
+       ret = change_oem_password( sampass, password, True);
        if (ret == False) {
                DEBUG(3,("change_oem_password returned False\n"));
        }
@@ -341,10 +377,6 @@ static BOOL update_smbpassword_file(char *user, char *password)
        return ret;
 }
 
-
-
-
-
 /****************************************************************************
 core of smb password checking routine.
 ****************************************************************************/
@@ -354,11 +386,13 @@ 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)
+
+    /* No password set - always true ! */
     return 1;
+  }
 
   memset(p21,'\0',21);
   memcpy(p21,part_passwd,16);
@@ -391,19 +425,22 @@ BOOL smb_password_check(char *password, unsigned char *part_passwd, unsigned cha
  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 chal[8],
+BOOL smb_password_ok(SAM_ACCOUNT *sampass, uchar chal[8],
                      uchar lm_pass[24], uchar nt_pass[24])
 {
        uchar challenge[8];
+       char* user_name;
+       BYTE *nt_pw, *lm_pw;
 
-       if (!lm_pass || !smb_pass) return(False);
+       if (!lm_pass || !sampass) 
+               return(False);
 
-       DEBUG(4,("Checking SMB password for user %s\n", 
-                smb_pass->smb_name));
+       user_name = pdb_get_username(sampass);
+       
+       DEBUG(4,("Checking SMB password for user %s\n",user_name));
 
-       if(smb_pass->acct_ctrl & ACB_DISABLED) {
-               DEBUG(1,("account for user %s was disabled.\n", 
-                        smb_pass->smb_name));
+       if(pdb_get_acct_ctrl(sampass) & ACB_DISABLED) {
+               DEBUG(1,("account for user %s was disabled.\n", user_name));
                return(False);
        }
 
@@ -422,35 +459,36 @@ BOOL smb_password_ok(struct smb_passwd *smb_pass, uchar chal[8],
                memcpy(challenge, chal, 8);
        }
 
-       if ((Protocol >= PROTOCOL_NT1) && (smb_pass->smb_nt_passwd != NULL)) {
+       nt_pw = pdb_get_nt_passwd(sampass);
+       
+       if ((Protocol >= PROTOCOL_NT1) && (nt_pw != 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)) {
+               if (smb_password_check((char *)nt_pass, (uchar *)nt_pw, challenge)) 
+               {
                        DEBUG(4,("NT MD4 password check succeeded\n"));
                        return(True);
                }
                DEBUG(4,("NT MD4 password check failed\n"));
        }
 
-       /* Try against the lanman password. smb_pass->smb_passwd == NULL means
-          no password, allow access. */
+       /* Try against the lanman password. pdb_get_lanman_passwd(sampass) == 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,("no password required for user %s\n",
-                        smb_pass->smb_name));
+       lm_pw = pdb_get_lanman_passwd(sampass);
+       
+       if((lm_pw == NULL) && (pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ)) 
+       {
+               DEBUG(4,("no password required for user %s\n",user_name));
                return True;
        }
 
-       if((smb_pass->smb_passwd != NULL) && 
-          smb_password_check((char *)lm_pass, 
-                             (uchar *)smb_pass->smb_passwd, challenge)) {
+       if((lm_pw != NULL) && smb_password_check((char *)lm_pass,(uchar *)lm_pw, challenge)) 
+       {
                DEBUG(4,("LM MD4 password check succeeded\n"));
                return(True);
        }
@@ -467,18 +505,19 @@ SMB hash
 return True if the password is correct, False otherwise
 ****************************************************************************/
 
-BOOL pass_check_smb(char *user, char *domain,
-               uchar *chal, uchar *lm_pwd, uchar *nt_pwd,
-               struct passwd *pwd)
+BOOL pass_check_smb(char *user, char *domain, uchar *chal, 
+                    uchar *lm_pwd, uchar *nt_pwd, struct passwd *pwd)
 {
        struct passwd *pass;
-       struct smb_passwd *smb_pass;
+       SAM_ACCOUNT *sampass;
 
        if (!lm_pwd || !nt_pwd)
        {
                return(False);
        }
 
+       /* FIXME! this code looks to be unnecessary now that the passdb
+          validates that the username exists and has a valid uid */
        if (pwd != NULL && user == NULL)
        {
                pass = (struct passwd *) pwd;
@@ -486,6 +525,8 @@ BOOL pass_check_smb(char *user, char *domain,
        }
        else
        {
+               /* I don't get this call here.  I think it should be moved.
+                  Need to check on it.     --jerry */
                pass = smb_getpwnam(user,True);
        }
 
@@ -495,34 +536,45 @@ BOOL pass_check_smb(char *user, char *domain,
                return(False);
        }
 
-       smb_pass = getsmbpwnam(user);
-
-       if (smb_pass == NULL)
+       /* get the account information */
+       sampass = pdb_getsampwnam(user);
+       if (sampass == NULL)
        {
-               DEBUG(1,("Couldn't find user '%s' in smb_passwd file.\n", user));
+               DEBUG(1,("Couldn't find user '%s' in passdb file.\n", user));
                return(False);
        }
 
        /* Quit if the account was disabled. */
-       if(smb_pass->acct_ctrl & ACB_DISABLED) {
+       if(pdb_get_acct_ctrl(sampass) & ACB_DISABLED) {
                DEBUG(1,("Account for user '%s' was disabled.\n", user));
                return(False);
        }
 
-       /* Ensure the uid's match */
+       /* Ensure the uid's match 
+          FIXME!  This also seems unnecessary --jerry */
+#if 0  /* GWC */
        if (smb_pass->smb_userid != pass->pw_uid)
        {
                DEBUG(0,("Error : UNIX and SMB uids in password files do not match for user '%s'!\n", user));
                return(False);
        }
+#endif
 
-       if (lm_pwd[0] == '\0' && (smb_pass->acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords())
+       if (pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ) 
        {
-               DEBUG(3,("Account for user '%s' has no password and null passwords are allowed.\n", smb_pass->smb_name));
-               return(True);
+               if (lp_null_passwords()) 
+               {
+                       DEBUG(3,("Account for user '%s' has no password and null passwords are allowed.\n", user));
+                       return(True);
+               } 
+               else 
+               {
+                       DEBUG(3,("Account for user '%s' has no password and null passwords are NOT allowed.\n", user));
+                       return(False);
+               }               
        }
 
-       if (smb_password_ok(smb_pass, chal, lm_pwd, nt_pwd))
+       if (smb_password_ok(sampass, chal, lm_pwd, nt_pwd))
        {
                return(True);
        }
@@ -820,7 +872,6 @@ BOOL authorise_login(int snum,char *user,char *password, int pwlen,
       else
        DEBUG(0,("Invalid guest account %s??\n",guestname));
       *guest = True;
-      *force = True;
     }
 
   if (ok && !user_ok(user,snum))
@@ -842,7 +893,7 @@ static BOOL check_user_equiv(char *user, char *remote, char *equiv_file)
   int plus_allowed = 1;
   char *file_host;
   char *file_user;
-  char **lines = file_lines_load(equiv_file, NULL);
+  char **lines = file_lines_load(equiv_file, NULL, False);
   int i;
 
   DEBUG(5, ("check_user_equiv %s %s %s\n", user, remote, equiv_file));
@@ -1301,14 +1352,27 @@ static BOOL attempt_connect_to_dc(struct cli_state *pcli, struct in_addr *ip, un
  We have been asked to dynamcially determine the IP addresses of
  the PDC and BDC's for this DOMAIN, and query them in turn.
 ************************************************************************/
-static BOOL find_connect_pdc(struct cli_state *pcli, unsigned char *trust_passwd)
+static BOOL find_connect_pdc(struct cli_state *pcli, unsigned char *trust_passwd, time_t last_change_time)
 {
        struct in_addr *ip_list = NULL;
        int count = 0;
        int i;
        BOOL connected_ok = False;
+       time_t time_now = time(NULL);
+       BOOL use_pdc_only = False;
+
+       /*
+        * If the time the machine password has changed
+        * was less than an hour ago then we need to contact
+        * the PDC only, as we cannot be sure domain replication
+        * has yet taken place. Bug found by Gerald (way to go
+        * Gerald !). JRA.
+        */
+
+       if (time_now - last_change_time < 3600)
+               use_pdc_only = True;
 
-       if (!get_dc_list(lp_workgroup(), &ip_list, &count))
+       if (!get_dc_list(use_pdc_only, lp_workgroup(), &ip_list, &count))
                return False;
 
        /*
@@ -1380,6 +1444,7 @@ BOOL domain_client_validate( char *user, char *domain,
   struct cli_state cli;
   uint32 smb_uid_low;
   BOOL connected_ok = False;
+  time_t last_change_time;
 
   if(user_exists != NULL)
     *user_exists = True; /* Only set false on a very specific error. */
@@ -1430,9 +1495,10 @@ BOOL domain_client_validate( char *user, char *domain,
   /*
    * Get the machine account password for our primary domain
    */
-  if (!secrets_fetch_trust_account_password(lp_workgroup(), trust_passwd, NULL))
+  if (!secrets_fetch_trust_account_password(lp_workgroup(), trust_passwd, &last_change_time))
   {
-    return False;
+         DEBUG(0, ("domain_client_validate: could not fetch trust account password for domain %s\n", lp_workgroup()));
+         return False;
   }
 
   /*
@@ -1457,7 +1523,7 @@ BOOL domain_client_validate( char *user, char *domain,
   while (!connected_ok &&
         next_token(&p,remote_machine,LIST_SEP,sizeof(remote_machine))) {
          if(strequal(remote_machine, "*")) {
-                 connected_ok = find_connect_pdc(&cli, trust_passwd);
+                 connected_ok = find_connect_pdc(&cli, trust_passwd, last_change_time);
          } else {
                  connected_ok = connect_to_domain_password_server(&cli, remote_machine, trust_passwd);
          }