r21563: Fix a memleak: We only need dispinfo structs for "our" and for the builtin
[kai/samba.git] / source3 / rpc_server / srv_samr_nt.c
index d2a844778677324854aee33b0b0a8acb7f58ad90..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;
 }
@@ -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 */
@@ -1658,7 +1756,12 @@ NTSTATUS _samr_lookup_rids(pipes_struct *p, SAMR_Q_LOOKUP_RIDS *q_u, SAMR_R_LOOK
                                  &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__));
 
@@ -1956,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;
 }
 
 /*******************************************************************
@@ -2077,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:
@@ -2121,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)) {
@@ -2132,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)));
@@ -2148,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++) {
@@ -2302,6 +2408,9 @@ NTSTATUS _samr_query_domain_info(pipes_struct *p,
                        
                        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, get_global_sam_name());
                        break;
@@ -2380,7 +2489,7 @@ NTSTATUS _samr_query_domain_info(pipes_struct *p,
 
 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));
@@ -2495,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 **********/
 
@@ -2921,7 +3031,7 @@ 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;
 
@@ -3044,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; 
        }
@@ -3097,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 
@@ -3198,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"));
                        }
                        
@@ -3268,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"));
                        }
                        
@@ -3295,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
  ********************************************************************/
@@ -3321,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;
        }
@@ -3363,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 ================ */
        
@@ -3398,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;
@@ -3430,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();
@@ -3515,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 ================ */
        
@@ -3675,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;
@@ -3743,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++)
@@ -3957,6 +4150,7 @@ NTSTATUS _samr_delete_dom_user(pipes_struct *p, SAMR_Q_DELETE_DOM_USER *q_u, SAM
        BOOL can_add_accounts;
        uint32 acb_info;
        DISP_INFO *disp_info = NULL;
+       BOOL ret;
 
        DEBUG(5, ("_samr_delete_dom_user: %d\n", __LINE__));
 
@@ -3976,7 +4170,11 @@ 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);
@@ -4501,12 +4699,20 @@ NTSTATUS _samr_set_aliasinfo(pipes_struct *p, SAMR_Q_SET_ALIASINFO *q_u, SAMR_R_
 
        /* get the current group information */
 
-       if ( !pdb_get_aliasinfo( &group_sid, &info ) ) {
+       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
@@ -4516,13 +4722,31 @@ NTSTATUS _samr_set_aliasinfo(pipes_struct *p, SAMR_Q_SET_ALIASINFO *q_u, SAMR_R_
                                return NT_STATUS_SPECIAL_ACCOUNT;
                        }
 
-                       if ( ctr->alias.info2.name.string ) {
-                               unistr2_to_ascii( info.acct_name, ctr->alias.info2.name.string, 
-                                       sizeof(info.acct_name)-1 );
-                       }
-                       else
-                               fstrcpy( info.acct_name, "" );
+                       /* 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, 
@@ -4720,138 +4944,22 @@ NTSTATUS _samr_query_domain_info2(pipes_struct *p,
                                  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;
-
-       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);
+       SAMR_Q_QUERY_DOMAIN_INFO q;
+       SAMR_R_QUERY_DOMAIN_INFO r;
 
-       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;
-
-       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);
+       q.domain_pol = q_u->domain_pol;
+       q.switch_value = q_u->switch_value;
 
-                       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);
+       r_u->status = _samr_query_domain_info(p, &q, &r);
 
-                       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;
-
-                       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, get_global_sam_name());
-                       break;
-               case 0x06:
-                       /* 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;
-                       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_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;
 }