r21563: Fix a memleak: We only need dispinfo structs for "our" and for the builtin
[kai/samba.git] / source / rpc_server / srv_samr_nt.c
index 33de292d226e79a81507e7295e4d88f8ea8f0630..7cf75bcd40c162357afa83430822ef1c157e1575 100644 (file)
                ( READ_CONTROL_ACCESS           | \
                  SA_RIGHT_USER_CHANGE_PASSWORD | \
                  SA_RIGHT_USER_SET_LOC_COM )
+#define SAMR_USR_RIGHTS_CANT_WRITE_PW \
+               ( READ_CONTROL_ACCESS | SA_RIGHT_USER_SET_LOC_COM )
 
 #define DISP_INFO_CACHE_TIMEOUT 10
 
 typedef struct disp_info {
-       struct disp_info *next, *prev;
-       TALLOC_CTX *mem_ctx;
        DOM_SID sid; /* identify which domain this is. */
        BOOL builtin_domain; /* Quick flag to check if this is the builtin domain. */
        struct pdb_search *users; /* querydispinfo 1 and 4 */
@@ -63,8 +63,6 @@ typedef struct disp_info {
 /* We keep a static list of these by SID as modern clients close down
    all resources between each request in a complete enumeration. */
 
-static DISP_INFO *disp_info_list;
-
 struct samr_info {
        /* for use by the \PIPE\samr policy */
        DOM_SID sid;
@@ -90,6 +88,11 @@ static struct generic_mapping usr_generic_mapping = {
        GENERIC_RIGHTS_USER_WRITE,
        GENERIC_RIGHTS_USER_EXECUTE,
        GENERIC_RIGHTS_USER_ALL_ACCESS};
+static struct generic_mapping usr_nopwchange_generic_mapping = {
+       GENERIC_RIGHTS_USER_READ,
+       GENERIC_RIGHTS_USER_WRITE,
+       GENERIC_RIGHTS_USER_EXECUTE & ~SA_RIGHT_USER_CHANGE_PASSWORD,
+       GENERIC_RIGHTS_USER_ALL_ACCESS};
 static struct generic_mapping grp_generic_mapping = {
        GENERIC_RIGHTS_GROUP_READ,
        GENERIC_RIGHTS_GROUP_WRITE,
@@ -247,49 +250,59 @@ static NTSTATUS access_check_samr_function(uint32 acc_granted, uint32 acc_requir
  Fetch or create a dispinfo struct.
 ********************************************************************/
 
-static DISP_INFO *get_samr_dispinfo_by_sid(DOM_SID *psid, const char *sid_str)
+static DISP_INFO *get_samr_dispinfo_by_sid(DOM_SID *psid)
 {
-       TALLOC_CTX *mem_ctx;
-       DISP_INFO *dpi;
+       /*
+        * We do a static cache for DISP_INFO's here. Explanation can be found
+        * in Jeremy's checkin message to r11793:
+        *
+        * Fix the SAMR cache so it works across completely insane
+        * client behaviour (ie.:
+        * open pipe/open SAMR handle/enumerate 0 - 1024
+        * close SAMR handle, close pipe.
+        * open pipe/open SAMR handle/enumerate 1024 - 2048...
+        * close SAMR handle, close pipe.
+        * And on ad-nausium. Amazing.... probably object-oriented
+        * client side programming in action yet again.
+        * This change should *massively* improve performance when
+        * enumerating users from an LDAP database.
+        * Jeremy.
+        *
+        * "Our" and the builtin domain are the only ones where we ever
+        * enumerate stuff, so just cache 2 entries.
+        */
+
+       static struct disp_info builtin_dispinfo;
+       static struct disp_info domain_dispinfo;
 
        /* There are two cases to consider here:
           1) The SID is a domain SID and we look for an equality match, or
           2) This is an account SID and so we return the DISP_INFO* for our 
              domain */
 
-       if ( psid && sid_check_is_in_our_domain( psid ) ) {
-               DEBUG(10,("get_samr_dispinfo_by_sid: Replacing %s with our domain SID\n",
-                       sid_str));
-               psid = get_global_sam_sid();
-       }
-
-       for (dpi = disp_info_list; dpi; dpi = dpi->next) {
-               if (sid_equal(psid, &dpi->sid)) {
-                       return dpi;
-               }
-       }
-
-       /* This struct is never free'd - I'm using talloc so we
-          can get a list out of smbd using smbcontrol. There will
-          be one of these per SID we're authorative for. JRA. */
-
-       mem_ctx = talloc_init("DISP_INFO for domain sid %s", sid_str);
-
-       if ((dpi = TALLOC_ZERO_P(mem_ctx, DISP_INFO)) == NULL)
+       if (psid == NULL) {
                return NULL;
+       }
 
-       dpi->mem_ctx = mem_ctx;
+       if (sid_check_is_builtin(psid) || sid_check_is_in_builtin(psid)) {
+               /*
+                * Necessary only once, but it does not really hurt.
+                */
+               sid_copy(&builtin_dispinfo.sid, &global_sid_Builtin);
 
-       if (psid) {
-               sid_copy( &dpi->sid, psid);
-               dpi->builtin_domain = sid_check_is_builtin(psid);
-       } else {
-               dpi->builtin_domain = False;
+               return &builtin_dispinfo;
        }
+               
+       if (sid_check_is_domain(psid) || sid_check_is_in_our_domain(psid)) {
+               /*
+                * Necessary only once, but it does not really hurt.
+                */
+               sid_copy(&domain_dispinfo.sid, get_global_sam_sid());
 
-       DLIST_ADD(disp_info_list, dpi);
+               return &domain_dispinfo;
+       }
 
-       return dpi;
+       return NULL;
 }
 
 /*******************************************************************
@@ -323,12 +336,7 @@ static struct samr_info *get_samr_info_by_sid(DOM_SID *psid)
        }
        info->mem_ctx = mem_ctx;
 
-       info->disp_info = get_samr_dispinfo_by_sid(psid, sid_str);
-
-       if (!info->disp_info) {
-               talloc_destroy(mem_ctx);
-               return NULL;
-       }
+       info->disp_info = get_samr_dispinfo_by_sid(psid);
 
        return info;
 }
@@ -482,7 +490,7 @@ static void samr_clear_sam_passwd(struct samu *sam_pass)
        pdb_set_nt_passwd(sam_pass, NULL, PDB_DEFAULT);
 }
 
-static uint32 count_sam_users(struct disp_info *info, uint16 acct_flags)
+static uint32 count_sam_users(struct disp_info *info, uint32 acct_flags)
 {
        struct samr_displayentry *entry;
 
@@ -656,16 +664,6 @@ NTSTATUS _samr_get_usrdom_pwinfo(pipes_struct *p, SAMR_Q_GET_USRDOM_PWINFO *q_u,
        return r_u->status;
 }
 
-/*******************************************************************
- _samr_set_sec_obj
- ********************************************************************/
-
-NTSTATUS _samr_set_sec_obj(pipes_struct *p, SAMR_Q_SET_SEC_OBJ *q_u, SAMR_R_SET_SEC_OBJ *r_u)
-{
-       DEBUG(0,("_samr_set_sec_obj: Not yet implemented!\n"));
-       return NT_STATUS_NOT_IMPLEMENTED;
-}
-
 /*******************************************************************
 ********************************************************************/
 
@@ -691,6 +689,97 @@ static BOOL get_lsa_policy_samr_sid( pipes_struct *p, POLICY_HND *pol,
        return True;
 }
 
+/*******************************************************************
+ _samr_set_sec_obj
+ ********************************************************************/
+
+NTSTATUS _samr_set_sec_obj(pipes_struct *p, SAMR_Q_SET_SEC_OBJ *q_u, SAMR_R_SET_SEC_OBJ *r_u)
+{
+       DOM_SID pol_sid;
+       uint32 acc_granted, i;
+       SEC_ACL *dacl;
+       BOOL ret;
+       struct samu *sampass=NULL;
+       NTSTATUS status;
+
+       r_u->status = NT_STATUS_OK;
+
+       if (!get_lsa_policy_samr_sid(p, &q_u->pol, &pol_sid, &acc_granted, NULL))
+               return NT_STATUS_INVALID_HANDLE;
+
+       if (!(sampass = samu_new( p->mem_ctx))) {
+               DEBUG(0,("No memory!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* get the user record */
+       become_root();
+       ret = pdb_getsampwsid(sampass, &pol_sid);
+       unbecome_root();
+
+       if (!ret) {
+               DEBUG(4, ("User %s not found\n", sid_string_static(&pol_sid)));
+               TALLOC_FREE(sampass);
+               return NT_STATUS_INVALID_HANDLE;
+       }
+
+       dacl = q_u->buf->sd->dacl;
+       for (i=0; i < dacl->num_aces; i++) {
+               if (sid_equal(&pol_sid, &dacl->aces[i].trustee)) {
+                       ret = pdb_set_pass_can_change(sampass, 
+                               (dacl->aces[i].access_mask & 
+                                SA_RIGHT_USER_CHANGE_PASSWORD) ? 
+                                                     True: False);
+                       break;
+               }
+       }
+
+       if (!ret) {
+               TALLOC_FREE(sampass);
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       status = pdb_update_sam_account(sampass);
+
+       TALLOC_FREE(sampass);
+
+       return status;
+}
+
+/*******************************************************************
+  build correct perms based on policies and password times for _samr_query_sec_obj
+*******************************************************************/
+static BOOL check_change_pw_access(TALLOC_CTX *mem_ctx, DOM_SID *user_sid)
+{
+       struct samu *sampass=NULL;
+       BOOL ret;
+
+       if ( !(sampass = samu_new( mem_ctx )) ) {
+               DEBUG(0,("No memory!\n"));
+               return False;
+       }
+
+       become_root();
+       ret = pdb_getsampwsid(sampass, user_sid);
+       unbecome_root();
+
+       if (ret == False) {
+               DEBUG(4,("User %s not found\n", sid_string_static(user_sid)));
+               TALLOC_FREE(sampass);
+               return False;
+       }
+
+       DEBUG(3,("User:[%s]\n",  pdb_get_username(sampass) ));
+
+       if (pdb_get_pass_can_change(sampass)) {
+               TALLOC_FREE(sampass);
+               return True;
+       }
+       TALLOC_FREE(sampass);
+       return False;
+}
+
+
 /*******************************************************************
  _samr_query_sec_obj
  ********************************************************************/
@@ -731,7 +820,13 @@ NTSTATUS _samr_query_sec_obj(pipes_struct *p, SAMR_Q_QUERY_SEC_OBJ *q_u, SAMR_R_
                /* TODO: different SDs have to be generated for aliases groups and users.
                         Currently all three get a default user SD  */
                DEBUG(10,("_samr_query_sec_obj: querying security on Object with SID: %s\n", sid_to_string(str_sid, &pol_sid)));
-               r_u->status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &usr_generic_mapping, &pol_sid, SAMR_USR_RIGHTS_WRITE_PW);
+               if (check_change_pw_access(p->mem_ctx, &pol_sid)) {
+                       r_u->status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &usr_generic_mapping, 
+                                                         &pol_sid, SAMR_USR_RIGHTS_WRITE_PW);
+               } else {
+                       r_u->status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &usr_nopwchange_generic_mapping, 
+                                                         &pol_sid, SAMR_USR_RIGHTS_CANT_WRITE_PW);
+               }
        } else {
                return NT_STATUS_OBJECT_TYPE_MISMATCH;
        }
@@ -1377,7 +1472,7 @@ NTSTATUS _samr_query_aliasinfo(pipes_struct *p, SAMR_Q_QUERY_ALIASINFO *q_u, SAM
 NTSTATUS _samr_lookup_names(pipes_struct *p, SAMR_Q_LOOKUP_NAMES *q_u, SAMR_R_LOOKUP_NAMES *r_u)
 {
        uint32 rid[MAX_SAM_ENTRIES];
-       enum SID_NAME_USE type[MAX_SAM_ENTRIES];
+       enum lsa_SidType type[MAX_SAM_ENTRIES];
        int i;
        int num_rids = q_u->num_names2;
        DOM_SID pol_sid;
@@ -1435,7 +1530,7 @@ NTSTATUS _samr_lookup_names(pipes_struct *p, SAMR_Q_LOOKUP_NAMES *q_u, SAMR_R_LO
                }
        }
 
-       init_samr_r_lookup_names(p->mem_ctx, r_u, num_rids, rid, (uint32 *)type, r_u->status);
+       init_samr_r_lookup_names(p->mem_ctx, r_u, num_rids, rid, type, r_u->status);
 
        DEBUG(5,("_samr_lookup_names: %d\n", __LINE__));
 
@@ -1618,13 +1713,15 @@ static BOOL make_samr_lookup_rids(TALLOC_CTX *ctx, uint32 num_names,
 NTSTATUS _samr_lookup_rids(pipes_struct *p, SAMR_Q_LOOKUP_RIDS *q_u, SAMR_R_LOOKUP_RIDS *r_u)
 {
        const char **names;
-       uint32 *attrs = NULL;
+       enum lsa_SidType *attrs = NULL;
+       uint32 *wire_attrs = NULL;
        UNIHDR *hdr_name = NULL;
        UNISTR2 *uni_name = NULL;
        DOM_SID pol_sid;
        int num_rids = q_u->num_rids1;
        uint32 acc_granted;
-       
+       int i;
+
        r_u->status = NT_STATUS_OK;
 
        DEBUG(5,("_samr_lookup_rids: %d\n", __LINE__));
@@ -1640,9 +1737,10 @@ NTSTATUS _samr_lookup_rids(pipes_struct *p, SAMR_Q_LOOKUP_RIDS *q_u, SAMR_R_LOOK
        }
 
        names = TALLOC_ZERO_ARRAY(p->mem_ctx, const char *, num_rids);
-       attrs = TALLOC_ZERO_ARRAY(p->mem_ctx, uint32, num_rids);
+       attrs = TALLOC_ZERO_ARRAY(p->mem_ctx, enum lsa_SidType, num_rids);
+       wire_attrs = TALLOC_ZERO_ARRAY(p->mem_ctx, uint32, num_rids);
 
-       if ((num_rids != 0) && ((names == NULL) || (attrs == NULL)))
+       if ((num_rids != 0) && ((names == NULL) || (attrs == NULL) || (wire_attrs==NULL)))
                return NT_STATUS_NO_MEMORY;
 
        become_root();  /* lookup_sid can require root privs */
@@ -1650,11 +1748,20 @@ NTSTATUS _samr_lookup_rids(pipes_struct *p, SAMR_Q_LOOKUP_RIDS *q_u, SAMR_R_LOOK
                                      names, attrs);
        unbecome_root();
 
+       if ( NT_STATUS_EQUAL(r_u->status, NT_STATUS_NONE_MAPPED) && (num_rids == 0) ) {
+               r_u->status = NT_STATUS_OK;
+       }
+
        if(!make_samr_lookup_rids(p->mem_ctx, num_rids, names,
                                  &hdr_name, &uni_name))
                return NT_STATUS_NO_MEMORY;
 
-       init_samr_r_lookup_rids(r_u, num_rids, hdr_name, uni_name, attrs);
+       /* Convert from enum lsa_SidType to uint32 for wire format. */
+       for (i = 0; i < num_rids; i++) {
+               wire_attrs[i] = (uint32)attrs[i];
+       }
+
+       init_samr_r_lookup_rids(r_u, num_rids, hdr_name, uni_name, wire_attrs);
 
        DEBUG(5,("_samr_lookup_rids: %d\n", __LINE__));
 
@@ -1952,7 +2059,7 @@ static NTSTATUS get_user_info_21(TALLOC_CTX *mem_ctx, SAM_USER_INFO_21 *id21,
        
        TALLOC_FREE(sampass);
 
-       return NT_STATUS_OK;
+       return nt_status;
 }
 
 /*******************************************************************
@@ -2073,6 +2180,7 @@ NTSTATUS _samr_query_usergroups(pipes_struct *p, SAMR_Q_QUERY_USERGROUPS *q_u, S
        uint32 acc_granted;
        BOOL ret;
        NTSTATUS result;
+       BOOL success = False;
 
        /*
         * from the SID in the request:
@@ -2117,9 +2225,15 @@ NTSTATUS _samr_query_usergroups(pipes_struct *p, SAMR_Q_QUERY_USERGROUPS *q_u, S
 
        sids = NULL;
 
+       /* make both calls inside the root block */
        become_root();
        result = pdb_enum_group_memberships(p->mem_ctx, sam_pass,
                                            &sids, &unix_gids, &num_groups);
+       if ( NT_STATUS_IS_OK(result) ) {
+               success = sid_peek_check_rid(get_global_sam_sid(), 
+                                            pdb_get_group_sid(sam_pass),
+                                            &primary_group_rid);
+       }
        unbecome_root();
 
        if (!NT_STATUS_IS_OK(result)) {
@@ -2128,15 +2242,7 @@ NTSTATUS _samr_query_usergroups(pipes_struct *p, SAMR_Q_QUERY_USERGROUPS *q_u, S
                return result;
        }
 
-       gids = NULL;
-       num_gids = 0;
-
-       dom_gid.attr = (SE_GROUP_MANDATORY|SE_GROUP_ENABLED_BY_DEFAULT|
-                       SE_GROUP_ENABLED);
-
-       if (!sid_peek_check_rid(get_global_sam_sid(),
-                               pdb_get_group_sid(sam_pass),
-                               &primary_group_rid)) {
+       if ( !success ) {
                DEBUG(5, ("Group sid %s for user %s not in our domain\n",
                          sid_string_static(pdb_get_group_sid(sam_pass)),
                          pdb_get_username(sam_pass)));
@@ -2144,8 +2250,12 @@ NTSTATUS _samr_query_usergroups(pipes_struct *p, SAMR_Q_QUERY_USERGROUPS *q_u, S
                return NT_STATUS_INTERNAL_DB_CORRUPTION;
        }
 
-       dom_gid.g_rid = primary_group_rid;
+       gids = NULL;
+       num_gids = 0;
 
+       dom_gid.attr = (SE_GROUP_MANDATORY|SE_GROUP_ENABLED_BY_DEFAULT|
+                       SE_GROUP_ENABLED);
+       dom_gid.g_rid = primary_group_rid;
        ADD_TO_ARRAY(p->mem_ctx, DOM_GID, dom_gid, &gids, &num_gids);
 
        for (i=0; i<num_groups; i++) {
@@ -2176,10 +2286,12 @@ NTSTATUS _samr_query_usergroups(pipes_struct *p, SAMR_Q_QUERY_USERGROUPS *q_u, S
 }
 
 /*******************************************************************
- _samr_query_dom_info
+ _samr_query_domain_info
  ********************************************************************/
 
-NTSTATUS _samr_query_dom_info(pipes_struct *p, SAMR_Q_QUERY_DOMAIN_INFO *q_u, SAMR_R_QUERY_DOMAIN_INFO *r_u)
+NTSTATUS _samr_query_domain_info(pipes_struct *p, 
+                                SAMR_Q_QUERY_DOMAIN_INFO *q_u, 
+                                SAMR_R_QUERY_DOMAIN_INFO *r_u)
 {
        struct samr_info *info = NULL;
        SAM_UNK_CTR *ctr;
@@ -2208,7 +2320,7 @@ NTSTATUS _samr_query_dom_info(pipes_struct *p, SAMR_Q_QUERY_DOMAIN_INFO *q_u, SA
 
        r_u->status = NT_STATUS_OK;
        
-       DEBUG(5,("_samr_query_dom_info: %d\n", __LINE__));
+       DEBUG(5,("_samr_query_domain_info: %d\n", __LINE__));
        
        /* find the policy handle.  open a policy on it. */
        if (!find_policy_by_hnd(p, &q_u->domain_pol, (void **)(void *)&info)) {
@@ -2296,11 +2408,17 @@ NTSTATUS _samr_query_dom_info(pipes_struct *p, SAMR_Q_QUERY_DOMAIN_INFO *q_u, SA
                        
                        init_unk_info3(&ctr->info.inf3, nt_logout);
                        break;
+               case 0x04:
+                       init_unk_info4(&ctr->info.inf4, lp_serverstring());
+                       break;
                case 0x05:
-                       init_unk_info5(&ctr->info.inf5, global_myname());
+                       init_unk_info5(&ctr->info.inf5, get_global_sam_name());
                        break;
                case 0x06:
-                       init_unk_info6(&ctr->info.inf6);
+                       /* NT returns its own name when a PDC. win2k and later
+                        * only the name of the PDC if itself is a BDC (samba4
+                        * idl) */
+                       init_unk_info6(&ctr->info.inf6, global_myname());
                        break;
                case 0x07:
                        server_role = ROLE_DOMAIN_PDC;
@@ -2357,9 +2475,9 @@ NTSTATUS _samr_query_dom_info(pipes_struct *p, SAMR_Q_QUERY_DOMAIN_INFO *q_u, SA
                }
        
 
-       init_samr_r_query_dom_info(r_u, q_u->switch_value, ctr, NT_STATUS_OK);
+       init_samr_r_query_domain_info(r_u, q_u->switch_value, ctr, NT_STATUS_OK);
        
-       DEBUG(5,("_samr_query_dom_info: %d\n", __LINE__));
+       DEBUG(5,("_samr_query_domain_info: %d\n", __LINE__));
        
        return r_u->status;
 }
@@ -2371,7 +2489,7 @@ NTSTATUS _samr_query_dom_info(pipes_struct *p, SAMR_Q_QUERY_DOMAIN_INFO *q_u, SA
 
 static NTSTATUS can_create(TALLOC_CTX *mem_ctx, const char *new_name)
 {
-       enum SID_NAME_USE type;
+       enum lsa_SidType type;
        BOOL result;
 
        DEBUG(10, ("Checking whether [%s] can be created\n", new_name));
@@ -2486,7 +2604,8 @@ NTSTATUS _samr_create_user(pipes_struct *p, SAMR_Q_CREATE_USER *q_u,
        }
                
        DEBUG(5, ("_samr_create_user: %s can add this account : %s\n",
-               p->pipe_user_name, can_add_account ? "True":"False" ));
+                 uidtoname(p->pipe_user.ut.uid),
+                 can_add_account ? "True":"False" ));
                
        /********** BEGIN Admin BLOCK **********/
 
@@ -2912,8 +3031,9 @@ NTSTATUS _samr_open_alias(pipes_struct *p, SAMR_Q_OPEN_ALIAS *q_u, SAMR_R_OPEN_A
 
        {
                /* Check we actually have the requested alias */
-               enum SID_NAME_USE type;
+               enum lsa_SidType type;
                BOOL result;
+               gid_t gid;
 
                become_root();
                result = lookup_sid(NULL, &sid, NULL, NULL, &type);
@@ -2922,6 +3042,13 @@ NTSTATUS _samr_open_alias(pipes_struct *p, SAMR_Q_OPEN_ALIAS *q_u, SAMR_R_OPEN_A
                if (!result || (type != SID_NAME_ALIAS)) {
                        return NT_STATUS_NO_SUCH_ALIAS;
                }
+
+               /* make sure there is a mapping */
+               
+               if ( !sid_to_gid( &sid, &gid ) ) {
+                       return NT_STATUS_NO_SUCH_ALIAS;
+               }
+
        }
 
        /* associate the alias SID with the new handle. */
@@ -3027,7 +3154,7 @@ static BOOL set_user_info_18(SAM_USER_INFO_18 *id18, struct samu *pwd)
                TALLOC_FREE(pwd);
                return False;
        }
-       if (!pdb_set_pass_changed_now (pwd)) {
+       if (!pdb_set_pass_last_set_time (pwd, time(NULL), PDB_CHANGED)) {
                TALLOC_FREE(pwd);
                return False; 
        }
@@ -3080,9 +3207,11 @@ static NTSTATUS set_user_info_21(TALLOC_CTX *mem_ctx, SAM_USER_INFO_21 *id21,
        }
 
        /* we need to separately check for an account rename first */
+       
        if (rpcstr_pull(new_name, id21->uni_user_name.buffer, 
-                       sizeof(new_name), id21->uni_user_name.uni_str_len*2, 0) && 
-          (!strequal(new_name, pdb_get_username(pwd)))) {
+               sizeof(new_name), id21->uni_user_name.uni_str_len*2, 0) 
+               && (!strequal(new_name, pdb_get_username(pwd)))) 
+       {
 
                /* check to see if the new username already exists.  Note: we can't
                   reliably lock all backends, so there is potentially the 
@@ -3121,11 +3250,16 @@ static NTSTATUS set_user_info_21(TALLOC_CTX *mem_ctx, SAM_USER_INFO_21 *id21,
         * id21.  I don't know if they need to be set.    --jerry
         */
  
-       if (IS_SAM_CHANGED(pwd, PDB_GROUPSID) &&
-           !NT_STATUS_IS_OK(status = pdb_set_unix_primary_group(mem_ctx,
-                                                                pwd))) {
-               return status;
+       if ( IS_SAM_CHANGED(pwd, PDB_GROUPSID) ) {
+               status = pdb_set_unix_primary_group(mem_ctx, pwd);
+               if ( !NT_STATUS_IS_OK(status) ) {
+                       return status;
+               }
        }
+       
+       /* Don't worry about writing out the user account since the
+          primary group SID is generated solely from the user's Unix 
+          primary group. */
 
        /* write the change out */
        if(!NT_STATUS_IS_OK(status = pdb_update_sam_account(pwd))) {
@@ -3176,12 +3310,18 @@ static NTSTATUS set_user_info_23(TALLOC_CTX *mem_ctx, SAM_USER_INFO_23 *id23,
        if (    ( (acct_ctrl &  ACB_DOMTRUST) == ACB_DOMTRUST ) ||
                ( (acct_ctrl &  ACB_WSTRUST) ==  ACB_WSTRUST) ||
                ( (acct_ctrl &  ACB_SVRTRUST) ==  ACB_SVRTRUST) ) {
-               DEBUG(5, ("Changing trust account or non-unix-user password, not updating /etc/passwd\n"));
+               DEBUG(5, ("Changing trust account.  Not updating /etc/passwd\n"));
        } else  {
                /* update the UNIX password */
                if (lp_unix_password_sync() ) {
-                       struct passwd *passwd = Get_Pwnam(pdb_get_username(pwd));
-                       if (!passwd) {
+                       struct passwd *passwd;
+                       if (pdb_get_username(pwd) == NULL) {
+                               DEBUG(1, ("chgpasswd: User without name???\n"));
+                               TALLOC_FREE(pwd);
+                               return NT_STATUS_ACCESS_DENIED;
+                       }
+
+                       if ((passwd = Get_Pwnam(pdb_get_username(pwd))) == NULL) {
                                DEBUG(1, ("chgpasswd: Username does not exist in system !?!\n"));
                        }
                        
@@ -3219,7 +3359,7 @@ static BOOL set_user_info_pw(uint8 *pass, struct samu *pwd)
 {
        uint32 len;
        pstring plaintext_buf;
-       uint16 acct_ctrl;
+       uint32 acct_ctrl;
  
        DEBUG(5, ("Attempting administrator password change for user %s\n",
                  pdb_get_username(pwd)));
@@ -3246,8 +3386,15 @@ static BOOL set_user_info_pw(uint8 *pass, struct samu *pwd)
        } else {
                /* update the UNIX password */
                if (lp_unix_password_sync()) {
-                       struct passwd *passwd = Get_Pwnam(pdb_get_username(pwd));
-                       if (!passwd) {
+                       struct passwd *passwd;
+
+                       if (pdb_get_username(pwd) == NULL) {
+                               DEBUG(1, ("chgpasswd: User without name???\n"));
+                               TALLOC_FREE(pwd);
+                               return False;
+                       }
+
+                       if ((passwd = Get_Pwnam(pdb_get_username(pwd))) == NULL) {
                                DEBUG(1, ("chgpasswd: Username does not exist in system !?!\n"));
                        }
                        
@@ -3273,6 +3420,49 @@ static BOOL set_user_info_pw(uint8 *pass, struct samu *pwd)
        return True;
 }
 
+/*******************************************************************
+ set_user_info_25
+ ********************************************************************/
+
+static NTSTATUS set_user_info_25(TALLOC_CTX *mem_ctx, SAM_USER_INFO_25 *id25,
+                                struct samu *pwd)
+{
+       NTSTATUS status;
+       
+       if (id25 == NULL) {
+               DEBUG(5, ("set_user_info_25: NULL id25\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       copy_id25_to_sam_passwd(pwd, id25);
+       /* write the change out */
+       if(!NT_STATUS_IS_OK(status = pdb_update_sam_account(pwd))) {
+               TALLOC_FREE(pwd);
+               return status;
+       }
+
+       /*
+        * We need to "pdb_update_sam_account" before the unix primary group
+        * is set, because the idealx scripts would also change the
+        * sambaPrimaryGroupSid using the ldap replace method. pdb_ldap uses
+        * the delete explicit / add explicit, which would then fail to find
+        * the previous primaryGroupSid value.
+        */
+
+       if ( IS_SAM_CHANGED(pwd, PDB_GROUPSID) ) {
+               status = pdb_set_unix_primary_group(mem_ctx, pwd);
+               if ( !NT_STATUS_IS_OK(status) ) {
+                       return status;
+               }
+       }
+       
+       /* WARNING: No TALLOC_FREE(pwd), we are about to set the password
+        * hereafter! */
+
+       return NT_STATUS_OK;
+}
+
 /*******************************************************************
  samr_reply_set_userinfo
  ********************************************************************/
@@ -3299,10 +3489,25 @@ NTSTATUS _samr_set_userinfo(pipes_struct *p, SAMR_Q_SET_USERINFO *q_u, SAMR_R_SE
        if (!get_lsa_policy_samr_sid(p, pol, &sid, &acc_granted, &disp_info))
                return NT_STATUS_INVALID_HANDLE;
 
-       /* observed when joining an XP client to a Samba domain */
+       /* This is tricky.  A WinXP domain join sets 
+         (SA_RIGHT_USER_SET_PASSWORD|SA_RIGHT_USER_SET_ATTRIBUTES|SA_RIGHT_USER_ACCT_FLAGS_EXPIRY)
+         The MMC lusrmgr plugin includes these perms and more in the SamrOpenUser().  But the 
+         standard Win32 API calls just ask for SA_RIGHT_USER_SET_PASSWORD in the SamrOpenUser().  
+         This should be enough for levels 18, 24, 25,& 26.  Info level 23 can set more so 
+         we'll use the set from the WinXP join as the basis. */
+       
+       switch (switch_value) {
+       case 18:
+       case 24:
+       case 25:
+       case 26:
+               acc_required = SA_RIGHT_USER_SET_PASSWORD;
+               break;
+       default:
+               acc_required = SA_RIGHT_USER_SET_PASSWORD | SA_RIGHT_USER_SET_ATTRIBUTES | SA_RIGHT_USER_ACCT_FLAGS_EXPIRY;
+               break;
+       }
        
-       acc_required = SA_RIGHT_USER_SET_PASSWORD | SA_RIGHT_USER_SET_ATTRIBUTES | SA_RIGHT_USER_ACCT_FLAGS_EXPIRY;     
-
        if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, acc_required, "_samr_set_userinfo"))) {
                return r_u->status;
        }
@@ -3341,7 +3546,8 @@ NTSTATUS _samr_set_userinfo(pipes_struct *p, SAMR_Q_SET_USERINFO *q_u, SAMR_R_SE
        }
        
        DEBUG(5, ("_samr_set_userinfo: %s does%s possess sufficient rights\n",
-               p->pipe_user_name, has_enough_rights ? "" : " not"));
+                 uidtoname(p->pipe_user.ut.uid),
+                 has_enough_rights ? "" : " not"));
 
        /* ================ BEGIN SeMachineAccountPrivilege BLOCK ================ */
        
@@ -3376,6 +3582,11 @@ NTSTATUS _samr_set_userinfo(pipes_struct *p, SAMR_Q_SET_USERINFO *q_u, SAMR_R_SE
 
                        dump_data(100, (char *)ctr->info.id25->pass, 532);
 
+                       r_u->status = set_user_info_25(p->mem_ctx,
+                                                      ctr->info.id25, pwd);
+                       if (!NT_STATUS_IS_OK(r_u->status)) {
+                               goto done;
+                       }
                        if (!set_user_info_pw(ctr->info.id25->pass, pwd))
                                r_u->status = NT_STATUS_ACCESS_DENIED;
                        break;
@@ -3408,6 +3619,7 @@ NTSTATUS _samr_set_userinfo(pipes_struct *p, SAMR_Q_SET_USERINFO *q_u, SAMR_R_SE
                        r_u->status = NT_STATUS_INVALID_INFO_CLASS;
        }
 
+ done:
        
        if ( has_enough_rights )                                
                unbecome_root();
@@ -3447,9 +3659,14 @@ NTSTATUS _samr_set_userinfo2(pipes_struct *p, SAMR_Q_SET_USERINFO2 *q_u, SAMR_R_
        if (!get_lsa_policy_samr_sid(p, pol, &sid, &acc_granted, &disp_info))
                return NT_STATUS_INVALID_HANDLE;
 
-       /* observed when joining XP client to Samba domain */
                
+#if 0  /* this really should be applied on a per info level basis   --jerry */
+
+       /* observed when joining XP client to Samba domain */
        acc_required = SA_RIGHT_USER_SET_PASSWORD | SA_RIGHT_USER_SET_ATTRIBUTES | SA_RIGHT_USER_ACCT_FLAGS_EXPIRY;
+#else
+       acc_required = SA_RIGHT_USER_SET_ATTRIBUTES;
+#endif
        
        if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, acc_required, "_samr_set_userinfo2"))) {
                return r_u->status;
@@ -3488,7 +3705,8 @@ NTSTATUS _samr_set_userinfo2(pipes_struct *p, SAMR_Q_SET_USERINFO2 *q_u, SAMR_R_
        }
        
        DEBUG(5, ("_samr_set_userinfo2: %s does%s possess sufficient rights\n",
-               p->pipe_user_name, has_enough_rights ? "" : " not"));
+                 uidtoname(p->pipe_user.ut.uid),
+                 has_enough_rights ? "" : " not"));
 
        /* ================ BEGIN SeMachineAccountPrivilege BLOCK ================ */
        
@@ -3648,7 +3866,9 @@ NTSTATUS _samr_query_aliasmem(pipes_struct *p, SAMR_Q_QUERY_ALIASMEM *q_u, SAMR_
 
        DEBUG(10, ("sid is %s\n", sid_string_static(&alias_sid)));
 
+       become_root();
        status = pdb_enum_aliasmem(&alias_sid, &sids, &num_sids);
+       unbecome_root();
 
        if (!NT_STATUS_IS_OK(status)) {
                return status;
@@ -3716,7 +3936,7 @@ NTSTATUS _samr_query_groupmem(pipes_struct *p, SAMR_Q_QUERY_GROUPMEM *q_u, SAMR_
 
        attr=TALLOC_ZERO_ARRAY(p->mem_ctx, uint32, num_members);
        
-       if ((num_members!=0) && (rid==NULL))
+       if ((num_members!=0) && (attr==NULL))
                return NT_STATUS_NO_MEMORY;
        
        for (i=0; i<num_members; i++)
@@ -3928,7 +4148,9 @@ NTSTATUS _samr_delete_dom_user(pipes_struct *p, SAMR_Q_DELETE_DOM_USER *q_u, SAM
        struct samu *sam_pass=NULL;
        uint32 acc_granted;
        BOOL can_add_accounts;
+       uint32 acb_info;
        DISP_INFO *disp_info = NULL;
+       BOOL ret;
 
        DEBUG(5, ("_samr_delete_dom_user: %d\n", __LINE__));
 
@@ -3948,14 +4170,25 @@ NTSTATUS _samr_delete_dom_user(pipes_struct *p, SAMR_Q_DELETE_DOM_USER *q_u, SAM
                return NT_STATUS_NO_MEMORY;
        }
 
-       if(!pdb_getsampwsid(sam_pass, &user_sid)) {
+       become_root();
+       ret = pdb_getsampwsid(sam_pass, &user_sid);
+       unbecome_root();
+
+       if( !ret ) {
                DEBUG(5,("_samr_delete_dom_user:User %s doesn't exist.\n", 
                        sid_string_static(&user_sid)));
                TALLOC_FREE(sam_pass);
                return NT_STATUS_NO_SUCH_USER;
        }
        
-       can_add_accounts = user_has_privileges( p->pipe_user.nt_user_token, &se_add_users );
+       acb_info = pdb_get_acct_ctrl(sam_pass);
+
+       /* For machine accounts it's the SeMachineAccountPrivilege that counts. */
+       if ( acb_info & ACB_WSTRUST ) {
+               can_add_accounts = user_has_privileges( p->pipe_user.nt_user_token, &se_machine_account );
+       } else {
+               can_add_accounts = user_has_privileges( p->pipe_user.nt_user_token, &se_add_users );
+       } 
 
        /******** BEGIN SeAddUsers BLOCK *********/
        
@@ -4068,12 +4301,22 @@ NTSTATUS _samr_delete_dom_alias(pipes_struct *p, SAMR_Q_DELETE_DOM_ALIAS *q_u, S
        if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &alias_sid, &acc_granted, &disp_info)) 
                return NT_STATUS_INVALID_HANDLE;
        
+       /* copy the handle to the outgoing reply */
+
+       memcpy( &r_u->pol, &q_u->alias_pol, sizeof(r_u->pol) );
+
        if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, STD_RIGHT_DELETE_ACCESS, "_samr_delete_dom_alias"))) {
                return r_u->status;
        }
 
        DEBUG(10, ("sid is %s\n", sid_string_static(&alias_sid)));
 
+       /* Don't let Windows delete builtin groups */
+
+       if ( sid_check_is_in_builtin( &alias_sid ) ) {
+               return NT_STATUS_SPECIAL_ACCOUNT;
+       }
+
        if (!sid_check_is_in_our_domain(&alias_sid))
                return NT_STATUS_NO_SUCH_ALIAS;
                
@@ -4322,6 +4565,10 @@ NTSTATUS _samr_query_groupinfo(pipes_struct *p, SAMR_Q_QUERY_GROUPINFO *q_u, SAM
                                      map.comment, num_members);
                        break;
                }
+               case 2:
+                       ctr->switch_value1 = 2;
+                       init_samr_group_info2(&ctr->group.info2, map.nt_name);
+                       break;
                case 3:
                        ctr->switch_value1 = 3;
                        init_samr_group_info3(&ctr->group.info3);
@@ -4330,6 +4577,28 @@ NTSTATUS _samr_query_groupinfo(pipes_struct *p, SAMR_Q_QUERY_GROUPINFO *q_u, SAM
                        ctr->switch_value1 = 4;
                        init_samr_group_info4(&ctr->group.info4, map.comment);
                        break;
+               case 5: {
+                       /*
+                       uint32 *members;
+                       size_t num_members;
+                       */
+
+                       ctr->switch_value1 = 5;
+
+                       /*
+                       become_root();
+                       r_u->status = pdb_enum_group_members(
+                               p->mem_ctx, &group_sid, &members, &num_members);
+                       unbecome_root();
+       
+                       if (!NT_STATUS_IS_OK(r_u->status)) {
+                               return r_u->status;
+                       }
+                       */
+                       init_samr_group_info5(&ctr->group.info5, map.nt_name,
+                                     map.comment, 0 /* num_members */); /* in w2k3 this is always 0 */
+                       break;
+               }
                default:
                        return NT_STATUS_INVALID_INFO_CLASS;
        }
@@ -4428,7 +4697,56 @@ NTSTATUS _samr_set_aliasinfo(pipes_struct *p, SAMR_Q_SET_ALIASINFO *q_u, SAMR_R_
                
        ctr=&q_u->ctr;
 
+       /* get the current group information */
+
+       become_root();
+       ret = pdb_get_aliasinfo( &group_sid, &info );
+       unbecome_root();
+
+       if ( !ret ) {
+               return NT_STATUS_NO_SUCH_ALIAS;
+       }
+
        switch (ctr->level) {
+               case 2:
+               {
+                       fstring group_name, acct_name;
+                       NTSTATUS status;
+
+                       /* We currently do not support renaming groups in the
+                          the BUILTIN domain.  Refer to util_builtin.c to understand 
+                          why.  The eventually needs to be fixed to be like Windows
+                          where you can rename builtin groups, just not delete them */
+
+                       if ( sid_check_is_in_builtin( &group_sid ) ) {
+                               return NT_STATUS_SPECIAL_ACCOUNT;
+                       }
+
+                       /* There has to be a valid name (and it has to be different) */
+
+                       if ( !ctr->alias.info2.name.string ) 
+                               return NT_STATUS_INVALID_PARAMETER;
+
+                       unistr2_to_ascii( acct_name, ctr->alias.info2.name.string, 
+                               sizeof(acct_name)-1 );
+
+                       /* If the name is the same just reply "ok".  Yes this
+                          doesn't allow you to change the case of a group name. */
+
+                       if ( strequal( acct_name, info.acct_name ) )
+                               return NT_STATUS_OK;
+
+                       fstrcpy( info.acct_name, acct_name );
+
+                       /* make sure the name doesn't already exist as a user 
+                          or local group */
+
+                       fstr_sprintf( group_name, "%s\\%s", global_myname(), info.acct_name );
+                       status = can_create( p->mem_ctx, group_name );
+                       if ( !NT_STATUS_IS_OK( status ) ) 
+                               return status;
+                       break;
+               }
                case 3:
                        if ( ctr->alias.info3.description.string ) {
                                unistr2_to_ascii( info.acct_desc, 
@@ -4623,144 +4941,31 @@ NTSTATUS _samr_remove_sid_foreign_domain(pipes_struct *p,
  ********************************************************************/
 
 NTSTATUS _samr_query_domain_info2(pipes_struct *p,
-               SAMR_Q_QUERY_DOMAIN_INFO2 *q_u,
-               SAMR_R_QUERY_DOMAIN_INFO2 *r_u)
+                                 SAMR_Q_QUERY_DOMAIN_INFO2 *q_u,
+                                 SAMR_R_QUERY_DOMAIN_INFO2 *r_u)
 {
-       struct samr_info *info = NULL;
-       SAM_UNK_CTR *ctr;
-       uint32 min_pass_len,pass_hist,password_properties;
-       time_t u_expire, u_min_age;
-       NTTIME nt_expire, nt_min_age;
+       SAMR_Q_QUERY_DOMAIN_INFO q;
+       SAMR_R_QUERY_DOMAIN_INFO r;
 
-       time_t u_lock_duration, u_reset_time;
-       NTTIME nt_lock_duration, nt_reset_time;
-       uint32 lockout;
-       
-       time_t u_logout;
-       NTTIME nt_logout;
-
-       uint32 num_users=0, num_groups=0, num_aliases=0;
-
-       uint32 account_policy_temp;
-
-       time_t seq_num;
-       uint32 server_role;
-
-       if ((ctr = TALLOC_ZERO_P(p->mem_ctx, SAM_UNK_CTR)) == NULL)
-               return NT_STATUS_NO_MEMORY;
-
-       ZERO_STRUCTP(ctr);
-
-       r_u->status = NT_STATUS_OK;
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
 
        DEBUG(5,("_samr_query_domain_info2: %d\n", __LINE__));
 
-       /* find the policy handle.  open a policy on it. */
-       if (!find_policy_by_hnd(p, &q_u->domain_pol, (void **)(void *)&info))
-               return NT_STATUS_INVALID_HANDLE;
+       q.domain_pol = q_u->domain_pol;
+       q.switch_value = q_u->switch_value;
 
-       switch (q_u->switch_value) {
-               case 0x01:
-                       pdb_get_account_policy(AP_MIN_PASSWORD_LEN, &account_policy_temp);
-                       min_pass_len = account_policy_temp;
-
-                       pdb_get_account_policy(AP_PASSWORD_HISTORY, &account_policy_temp);
-                       pass_hist = account_policy_temp;
-
-                       pdb_get_account_policy(AP_USER_MUST_LOGON_TO_CHG_PASS, &account_policy_temp);
-                       password_properties = account_policy_temp;
-
-                       pdb_get_account_policy(AP_MAX_PASSWORD_AGE, &account_policy_temp);
-                       u_expire = account_policy_temp;
-
-                       pdb_get_account_policy(AP_MIN_PASSWORD_AGE, &account_policy_temp);
-                       u_min_age = account_policy_temp;
-
-                       unix_to_nt_time_abs(&nt_expire, u_expire);
-                       unix_to_nt_time_abs(&nt_min_age, u_min_age);
-
-                       init_unk_info1(&ctr->info.inf1, (uint16)min_pass_len, (uint16)pass_hist, 
-                                      password_properties, nt_expire, nt_min_age);
-                       break;
-               case 0x02:
-                       become_root();          
-                       num_users = count_sam_users(info->disp_info, ACB_NORMAL);
-                       num_groups = count_sam_groups(info->disp_info);
-                       num_aliases = count_sam_aliases(info->disp_info);
-                       unbecome_root();
-
-                       pdb_get_account_policy(AP_TIME_TO_LOGOUT, &account_policy_temp);
-                       u_logout = account_policy_temp;
-
-                       unix_to_nt_time_abs(&nt_logout, u_logout);
-
-                       if (!pdb_get_seq_num(&seq_num))
-                               seq_num = time(NULL);
-
-                       server_role = ROLE_DOMAIN_PDC;
-                       if (lp_server_role() == ROLE_DOMAIN_BDC)
-                               server_role = ROLE_DOMAIN_BDC;
-
-                       init_unk_info2(&ctr->info.inf2, lp_serverstring(), lp_workgroup(), global_myname(), seq_num, 
-                                      num_users, num_groups, num_aliases, nt_logout, server_role);
-                       break;
-               case 0x03:
-                       pdb_get_account_policy(AP_TIME_TO_LOGOUT, &account_policy_temp);
-                       u_logout = account_policy_temp;
+       r_u->status = _samr_query_domain_info(p, &q, &r);
 
-                       unix_to_nt_time_abs(&nt_logout, u_logout);
-                       
-                       init_unk_info3(&ctr->info.inf3, nt_logout);
-                       break;
-               case 0x05:
-                       init_unk_info5(&ctr->info.inf5, global_myname());
-                       break;
-               case 0x06:
-                       init_unk_info6(&ctr->info.inf6);
-                       break;
-               case 0x07:
-                       server_role = ROLE_DOMAIN_PDC;
-                       if (lp_server_role() == ROLE_DOMAIN_BDC)
-                               server_role = ROLE_DOMAIN_BDC;
-
-                       init_unk_info7(&ctr->info.inf7, server_role);
-                       break;
-               case 0x08:
-                       if (!pdb_get_seq_num(&seq_num))
-                               seq_num = time(NULL);
-
-                       init_unk_info8(&ctr->info.inf8, (uint32) seq_num);
-                       break;
-               case 0x0c:
-                       pdb_get_account_policy(AP_LOCK_ACCOUNT_DURATION, &account_policy_temp);
-                       u_lock_duration = account_policy_temp;
-                       if (u_lock_duration != -1)
-                               u_lock_duration *= 60;
-
-                       pdb_get_account_policy(AP_RESET_COUNT_TIME, &account_policy_temp);
-                       u_reset_time = account_policy_temp * 60;
-
-                       pdb_get_account_policy(AP_BAD_ATTEMPT_LOCKOUT, &account_policy_temp);
-                       lockout = account_policy_temp;
-       
-                       unix_to_nt_time_abs(&nt_lock_duration, u_lock_duration);
-                       unix_to_nt_time_abs(&nt_reset_time, u_reset_time);
-       
-                       init_unk_info12(&ctr->info.inf12, nt_lock_duration, nt_reset_time, (uint16)lockout);
-                       break;
-               default:
-                       return NT_STATUS_INVALID_INFO_CLASS;
-       }
-
-       init_samr_r_samr_query_domain_info2(r_u, q_u->switch_value, ctr, NT_STATUS_OK);
-
-       DEBUG(5,("_samr_query_domain_info2: %d\n", __LINE__));
+       r_u->ptr_0              = r.ptr_0;
+       r_u->switch_value       = r.switch_value;
+       r_u->ctr                = r.ctr;
 
        return r_u->status;
 }
 
 /*******************************************************************
- _samr_
+ _samr_set_dom_info
  ********************************************************************/
 
 NTSTATUS _samr_set_dom_info(pipes_struct *p, SAMR_Q_SET_DOMAIN_INFO *q_u, SAMR_R_SET_DOMAIN_INFO *r_u)