Index: include/smb.h =================================================================== --- include/smb.h (revision 10033) +++ include/smb.h (working copy) @@ -228,6 +228,7 @@ #define ACB_SVRTRUST 0x0100 /* 1 = Server trust account (BDC) */ #define ACB_PWNOEXP 0x0200 /* 1 = User password does not expire */ #define ACB_AUTOLOCK 0x0400 /* 1 = Account auto locked */ +#define ACB_PWLOCK 0x0800 /* 1 = Password is locked and connot be changed remotely */ #define MAX_HOURS_LEN 32 Index: passdb/passdb.c =================================================================== --- passdb/passdb.c (revision 10033) +++ passdb/passdb.c (working copy) @@ -479,6 +479,7 @@ if (acct_ctrl & ACB_AUTOLOCK ) acct_str[i++] = 'L'; if (acct_ctrl & ACB_PWNOEXP ) acct_str[i++] = 'X'; if (acct_ctrl & ACB_DOMTRUST ) acct_str[i++] = 'I'; + if (acct_ctrl & ACB_PWLOCK ) acct_str[i++] = 'P'; for ( ; i < length - 2 ; i++ ) acct_str[i] = ' '; @@ -520,6 +521,7 @@ case 'L': { acct_ctrl |= ACB_AUTOLOCK ; break; /* 'L'ocked account. */ } case 'X': { acct_ctrl |= ACB_PWNOEXP ; break; /* No 'X'piry on password */ } case 'I': { acct_ctrl |= ACB_DOMTRUST ; break; /* 'I'nterdomain trust account. */ } + case 'P': { acct_ctrl |= ACB_PWLOCK ; break; /* 'P'assword may not change. */ } case ' ': { break; } case ':': case '\n': Index: smbd/chgpasswd.c =================================================================== --- smbd/chgpasswd.c (revision 10033) +++ smbd/chgpasswd.c (working copy) @@ -654,6 +654,12 @@ return False; } + if (acct_ctrl & ACB_PWLOCK) { + DEBUG(0,("change_lanman_password: account %s may not change password.\n", + pdb_get_username(sampass))); + return False; + } + if (pwd == NULL) { if (acct_ctrl & ACB_PWNOTREQ) { uchar no_pw[14]; @@ -778,11 +784,18 @@ acct_ctrl = pdb_get_acct_ctrl(sampass); if (acct_ctrl & ACB_DISABLED) { - DEBUG(2,("check_lanman_password: account %s disabled.\n", user)); + DEBUG(2,("check_oem_password: account %s disabled.\n", user)); pdb_free_sam(&sampass); return NT_STATUS_ACCOUNT_DISABLED; } + if (acct_ctrl & ACB_PWLOCK) { + DEBUG(2,("check_oem_password: account %s may not change password.\n", user)); + pdb_free_sam(&sampass); + return NT_STATUS_ACCESS_DENIED; + } + + if ((acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords()) { /* construct a null password (in case one is needed */ no_pw[0] = 0; Index: rpc_server/srv_samr_nt.c =================================================================== --- rpc_server/srv_samr_nt.c (revision 10033) +++ rpc_server/srv_samr_nt.c (working copy) @@ -78,18 +78,25 @@ static NTSTATUS make_samr_object_sd( TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size, struct generic_mapping *map, - DOM_SID *sid, uint32 sid_access ) + DOM_SID *sid, uint32 sid_access, BOOL pw_can_change ) { DOM_SID domadmin_sid; SEC_ACE ace[5]; /* at most 5 entries */ SEC_ACCESS mask; size_t i = 0; + uint32 tmp_mask; SEC_ACL *psa = NULL; /* basic access for Everyone */ - - init_sec_access(&mask, map->generic_execute | map->generic_read ); + tmp_mask = map->generic_execute | map->generic_read; + + if (pw_can_change) + tmp_mask |= SA_RIGHT_USER_CHANGE_PASSWORD; + else + tmp_mask &= ~SA_RIGHT_USER_CHANGE_PASSWORD; + + init_sec_access(&mask, tmp_mask ); init_sec_ace(&ace[i++], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0); /* add Full Access 'BUILTIN\Administrators' and 'BUILTIN\Account Operators */ @@ -365,7 +372,7 @@ /*check if access can be granted as requested by client. */ - make_samr_object_sd( p->mem_ctx, &psd, &sd_size, &dom_generic_mapping, NULL, 0 ); + make_samr_object_sd( p->mem_ctx, &psd, &sd_size, &dom_generic_mapping, NULL, 0, True ); se_map_generic( &des_access, &dom_generic_mapping ); se_priv_copy( &se_rights, &se_machine_account ); @@ -421,19 +428,7 @@ 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; -} - - -/******************************************************************* ********************************************************************/ static BOOL get_lsa_policy_samr_sid( pipes_struct *p, POLICY_HND *pol, @@ -453,7 +448,118 @@ return True; } + /******************************************************************* +********************************************************************/ + +static const SEC_ACE *acl_find_ace(const SEC_ACL *acl, const DOM_SID *sid) +{ + uint32 i; + const SEC_ACE *ace; + if (!acl || !acl->ace || !sid) + return NULL; + ace = acl->ace; + for (i = acl->num_aces; i; i--, ace++) + { + if (sid_equal(&ace->trustee, sid)) + return ace; + } + return NULL; +} + + +/******************************************************************* + _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; + fstring sid_str; + NTSTATUS status; + SAM_USER_INFO_16 id16; + SAM_ACCOUNT *smbpass = NULL; + BOOL ret; + const SEC_ACE *ace; + + DEBUG(10,("_samr_set_sec_obj:\n")); + + ZERO_STRUCT(id16); + + if (!q_u->buf || !q_u->buf->sec) + return NT_STATUS_INVALID_PARAMETER; + + if (q_u->sec_info != 0x00000004) + return NT_STATUS_NOT_IMPLEMENTED; + + ace = acl_find_ace(q_u->buf->sec->dacl, &global_sid_World); + if (!ace) { + DEBUG(0, ("_samr_set_sec_obj: Didn't find everyone ace\n")); + return NT_STATUS_NOT_IMPLEMENTED; + } + + /* find sid from handle */ + if (!get_lsa_policy_samr_sid(p, &q_u->pol, &pol_sid, &acc_granted)) + return NT_STATUS_INVALID_HANDLE; + + + /* check rights (FIXME) */ + status = access_check_samr_function( acc_granted, + SA_RIGHT_DOMAIN_OPEN_ACCOUNT, "_samr_open_user" ); + + if (!NT_STATUS_IS_OK(status)) + return status; + + sid_to_string(sid_str, &pol_sid); + + DEBUG(10,("_samr_set_sec_obj: will have to look for %s\n", sid_str)); + + + /* query pdb to get the user */ + status = pdb_init_sam_talloc(p->mem_ctx, &smbpass); + + if ( !NT_STATUS_IS_OK(status) ) + return status; + + become_root(); + ret = pdb_getsampwsid(smbpass, &pol_sid); + unbecome_root(); + + if (ret==False) { + DEBUG(0,("_samr_set_sec_obj: User %s not found\n", sid_str)); + pdb_free_sam(&smbpass); + return NT_STATUS_NO_SUCH_USER; + } + + DEBUG(10,("_samr_set_sec_obj: User:[%s]\n", pdb_get_username(smbpass) )); + + + /* manipulate the acctflags */ + init_sam_user_info16(&id16, pdb_get_acct_ctrl(smbpass) ); + + DEBUG(10,("_samr_set_sec_obj: acb_info was: 0x%08x\n", id16.acb_info)); + + if (ace->info.mask & SA_RIGHT_USER_CHANGE_PASSWORD) { + DEBUG(10,("_samr_set_sec_obj: user may change his password, clearing bit if necessary\n")); + id16.acb_info &= ~ACB_PWLOCK; + } else { + DEBUG(10,("_samr_set_sec_obj: user may not change his password\n")); + id16.acb_info |= ACB_PWLOCK; + } + + DEBUG(10,("_samr_set_sec_obj: acb_info is now: 0x%08x\n", id16.acb_info)); + if (!set_user_info_16(&id16, smbpass)) { + pdb_free_sam(&smbpass); + return NT_STATUS_ACCESS_DENIED; + } + + pdb_free_sam(&smbpass); + return NT_STATUS_OK; +} + + +/******************************************************************* _samr_query_sec_obj ********************************************************************/ @@ -481,27 +587,57 @@ if (pol_sid.sid_rev_num == 0) { DEBUG(5,("_samr_query_sec_obj: querying security on SAM\n")); - r_u->status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &sam_generic_mapping, NULL, 0); + r_u->status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &sam_generic_mapping, NULL, 0, True); } else if (sid_equal(&pol_sid,get_global_sam_sid())) /* check if it is our domain SID */ { DEBUG(5,("_samr_query_sec_obj: querying security on Domain with SID: %s\n", sid_to_string(str_sid, &pol_sid))); - r_u->status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &dom_generic_mapping, NULL, 0); + r_u->status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &dom_generic_mapping, NULL, 0, True); } else if (sid_equal(&pol_sid,&global_sid_Builtin)) /* check if it is the Builtin Domain */ { /* TODO: Builtin probably needs a different SD with restricted write access*/ DEBUG(5,("_samr_query_sec_obj: querying security on Builtin Domain with SID: %s\n", sid_to_string(str_sid, &pol_sid))); - r_u->status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &dom_generic_mapping, NULL, 0); + r_u->status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &dom_generic_mapping, NULL, 0, True); } else if (sid_check_is_in_our_domain(&pol_sid) || sid_check_is_in_builtin(&pol_sid)) { /* TODO: different SDs have to be generated for aliases groups and users. Currently all three get a default user SD */ + + SAM_ACCOUNT *smbpass = NULL; + BOOL ret; + BOOL may_change = True; + + /* query pdb to get the user */ + r_u->status = pdb_init_sam_talloc(p->mem_ctx, &smbpass); + + if ( !NT_STATUS_IS_OK(r_u->status) ) + return r_u->status; + + become_root(); + ret = pdb_getsampwsid(smbpass, &pol_sid); + unbecome_root(); + + if (ret == False) { + DEBUG(0,("_samr_query_sec_obj: User %s not found\n", str_sid)); + pdb_free_sam(&smbpass); + return NT_STATUS_NO_SUCH_USER; + } + + DEBUG(10,("_samr_query_sec_obj: User:[%s]\n", pdb_get_username(smbpass) )); + + if (pdb_get_acct_ctrl(smbpass) & ACB_PWLOCK ) + may_change = False; + + pdb_free_sam(&smbpass); + + 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); + r_u->status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &usr_generic_mapping, &pol_sid, SAMR_USR_RIGHTS_WRITE_PW, + may_change); } else return NT_STATUS_OBJECT_TYPE_MISMATCH; @@ -1339,7 +1475,7 @@ /* check if access can be granted as requested by client. */ - make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &usr_generic_mapping, &sid, SAMR_USR_RIGHTS_WRITE_PW); + make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &usr_generic_mapping, &sid, SAMR_USR_RIGHTS_WRITE_PW, True); se_map_generic(&des_access, &usr_generic_mapping); se_priv_copy( &se_rights, &se_machine_account ); @@ -2059,7 +2195,7 @@ sid_copy(&sid, pdb_get_user_sid(sam_pass)); - make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &usr_generic_mapping, &sid, SAMR_USR_RIGHTS_WRITE_PW); + make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &usr_generic_mapping, &sid, SAMR_USR_RIGHTS_WRITE_PW, True); se_map_generic(&des_access, &usr_generic_mapping); nt_status = access_check_samr_object(psd, p->pipe_user.nt_user_token, @@ -2160,7 +2296,7 @@ return r_u->status; } - make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &sam_generic_mapping, NULL, 0); + make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &sam_generic_mapping, NULL, 0, True); se_map_generic(&des_access, &sam_generic_mapping); nt_status = access_check_samr_object(psd, p->pipe_user.nt_user_token, @@ -2211,7 +2347,7 @@ return r_u->status; } - make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &sam_generic_mapping, NULL, 0); + make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &sam_generic_mapping, NULL, 0, True); se_map_generic(&des_access, &sam_generic_mapping); nt_status = access_check_samr_object(psd, p->pipe_user.nt_user_token, @@ -2385,7 +2521,7 @@ /*check if access can be granted as requested by client. */ - make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &ali_generic_mapping, NULL, 0); + make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &ali_generic_mapping, NULL, 0, True); se_map_generic(&des_access,&ali_generic_mapping); se_priv_copy( &se_rights, &se_add_users ); @@ -2420,7 +2556,7 @@ set_user_info_16 ********************************************************************/ -static BOOL set_user_info_16(const SAM_USER_INFO_16 *id16, SAM_ACCOUNT *pwd) +BOOL set_user_info_16(const SAM_USER_INFO_16 *id16, SAM_ACCOUNT *pwd) { if (id16 == NULL) { DEBUG(5, ("set_user_info_16: NULL id16\n")); @@ -4070,7 +4206,7 @@ return status; /*check if access can be granted as requested by client. */ - make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &grp_generic_mapping, NULL, 0); + make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &grp_generic_mapping, NULL, 0, True); se_map_generic(&des_access,&grp_generic_mapping); se_priv_copy( &se_rights, &se_add_users );