diff options
Diffstat (limited to 'source3/rpc_server')
-rw-r--r-- | source3/rpc_server/srv_lsa_hnd.c | 129 | ||||
-rw-r--r-- | source3/rpc_server/srv_lsa_nt.c | 10 | ||||
-rw-r--r-- | source3/rpc_server/srv_samr_nt.c | 625 | ||||
-rw-r--r-- | source3/rpc_server/srv_spoolss_nt.c | 207 |
4 files changed, 598 insertions, 373 deletions
diff --git a/source3/rpc_server/srv_lsa_hnd.c b/source3/rpc_server/srv_lsa_hnd.c index e853bb2047..21b297af2d 100644 --- a/source3/rpc_server/srv_lsa_hnd.c +++ b/source3/rpc_server/srv_lsa_hnd.c @@ -24,6 +24,26 @@ #undef DBGC_CLASS #define DBGC_CLASS DBGC_RPC_SRV +/* + * Handle database - stored per pipe. + */ + +struct policy { + struct policy *next, *prev; + + struct policy_handle pol_hnd; + + uint32_t access_granted; + + void *data_ptr; +}; + +struct handle_list { + struct policy *Policy; /* List of policies. */ + size_t count; /* Current number of handles. */ + size_t pipe_ref_count; /* Number of pipe handles referring to this list. */ +}; + /* This is the max handles across all instances of a pipe name. */ #ifndef MAX_OPEN_POLS #define MAX_OPEN_POLS 1024 @@ -40,6 +60,14 @@ static bool is_samr_lsa_pipe(const struct ndr_syntax_id *syntax) || ndr_syntax_id_equal(syntax, &ndr_table_lsarpc.syntax_id)); } +size_t num_pipe_handles(struct handle_list *list) +{ + if (list == NULL) { + return 0; + } + return list->count; +} + /**************************************************************************** Initialise a policy handle list on a pipe. Handle list is shared between all pipes of the same name. @@ -112,7 +140,9 @@ bool init_pipe_handle_list(pipes_struct *p, const struct ndr_syntax_id *syntax) data_ptr is TALLOC_FREE()'ed ****************************************************************************/ -bool create_policy_hnd(pipes_struct *p, struct policy_handle *hnd, void *data_ptr) +static struct policy *create_policy_hnd_internal(pipes_struct *p, + struct policy_handle *hnd, + void *data_ptr) { static uint32 pol_hnd_low = 0; static uint32 pol_hnd_high = 0; @@ -123,13 +153,13 @@ bool create_policy_hnd(pipes_struct *p, struct policy_handle *hnd, void *data_pt if (p->pipe_handles->count > MAX_OPEN_POLS) { DEBUG(0,("create_policy_hnd: ERROR: too many handles (%d) on this pipe.\n", (int)p->pipe_handles->count)); - return False; + return NULL; } pol = TALLOC_ZERO_P(NULL, struct policy); if (!pol) { DEBUG(0,("create_policy_hnd: ERROR: out of memory!\n")); - return False; + return NULL; } if (data_ptr != NULL) { @@ -160,14 +190,22 @@ bool create_policy_hnd(pipes_struct *p, struct policy_handle *hnd, void *data_pt DEBUG(4,("Opened policy hnd[%d] ", (int)p->pipe_handles->count)); dump_data(4, (uint8 *)hnd, sizeof(*hnd)); - return True; + return pol; +} + +bool create_policy_hnd(pipes_struct *p, struct policy_handle *hnd, + void *data_ptr) +{ + return create_policy_hnd_internal(p, hnd, data_ptr) != NULL; } /**************************************************************************** find policy by handle - internal version. ****************************************************************************/ -static struct policy *find_policy_by_hnd_internal(pipes_struct *p, struct policy_handle *hnd, void **data_p) +static struct policy *find_policy_by_hnd_internal(pipes_struct *p, + const struct policy_handle *hnd, + void **data_p) { struct policy *pol; size_t i; @@ -197,7 +235,8 @@ static struct policy *find_policy_by_hnd_internal(pipes_struct *p, struct policy find policy by handle ****************************************************************************/ -bool find_policy_by_hnd(pipes_struct *p, struct policy_handle *hnd, void **data_p) +bool find_policy_by_hnd(pipes_struct *p, const struct policy_handle *hnd, + void **data_p) { return find_policy_by_hnd_internal(p, hnd, data_p) == NULL ? False : True; } @@ -277,3 +316,81 @@ bool pipe_access_check(pipes_struct *p) return True; } + +void *_policy_handle_create(struct pipes_struct *p, struct policy_handle *hnd, + uint32_t access_granted, size_t data_size, + const char *type, NTSTATUS *pstatus) +{ + struct policy *pol; + void *data; + + if (p->pipe_handles->count > MAX_OPEN_POLS) { + DEBUG(0, ("policy_handle_create: ERROR: too many handles (%d) " + "on pipe %s.\n", (int)p->pipe_handles->count, + get_pipe_name_from_iface(&p->syntax))); + *pstatus = NT_STATUS_INSUFFICIENT_RESOURCES; + return NULL; + } + + data = talloc_size(talloc_tos(), data_size); + if (data == NULL) { + *pstatus = NT_STATUS_NO_MEMORY; + return NULL; + } + talloc_set_name(data, "%s", type); + + pol = create_policy_hnd_internal(p, hnd, data); + if (pol == NULL) { + TALLOC_FREE(data); + *pstatus = NT_STATUS_NO_MEMORY; + return NULL; + } + pol->access_granted = access_granted; + *pstatus = NT_STATUS_OK; + return data; +} + +void *_policy_handle_find(struct pipes_struct *p, + const struct policy_handle *hnd, + uint32_t access_required, + uint32_t *paccess_granted, + const char *name, const char *location, + NTSTATUS *pstatus) +{ + struct policy *pol; + void *data; + + pol = find_policy_by_hnd_internal(p, hnd, &data); + if (pol == NULL) { + *pstatus = NT_STATUS_INVALID_HANDLE; + return NULL; + } + if (strcmp(name, talloc_get_name(data)) != 0) { + DEBUG(10, ("expected %s, got %s\n", name, + talloc_get_name(data))); + *pstatus = NT_STATUS_INVALID_HANDLE; + return NULL; + } + if ((access_required & pol->access_granted) != access_required) { + if (geteuid() == sec_initial_uid()) { + DEBUG(4, ("%s: ACCESS should be DENIED (granted: " + "%#010x; required: %#010x)\n", location, + pol->access_granted, access_required)); + DEBUGADD(4,("but overwritten by euid == 0\n")); + goto okay; + } + DEBUG(2,("%s: ACCESS DENIED (granted: %#010x; required: " + "%#010x)\n", location, pol->access_granted, + access_required)); + *pstatus = NT_STATUS_ACCESS_DENIED; + return NULL; + } + + okay: + DEBUG(10, ("found handle of type %s\n", talloc_get_name(data))); + if (paccess_granted != NULL) { + *paccess_granted = pol->access_granted; + } + *pstatus = NT_STATUS_OK; + return data; +} diff --git a/source3/rpc_server/srv_lsa_nt.c b/source3/rpc_server/srv_lsa_nt.c index 0ce2b40f65..9481c206f6 100644 --- a/source3/rpc_server/srv_lsa_nt.c +++ b/source3/rpc_server/srv_lsa_nt.c @@ -827,7 +827,15 @@ NTSTATUS _lsa_LookupSids(pipes_struct *p, &names, &mapped_count); - if (NT_STATUS_IS_ERR(status)) { + /* Only return here when there is a real error. + NT_STATUS_NONE_MAPPED is a special case as it indicates that none of + the requested sids could be resolved. Older versions of XP (pre SP3) + rely that we return with the string representations of those SIDs in + that case. If we don't, XP crashes - Guenther + */ + + if (NT_STATUS_IS_ERR(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { return status; } diff --git a/source3/rpc_server/srv_samr_nt.c b/source3/rpc_server/srv_samr_nt.c index 187c721dfb..b54ed717a3 100644 --- a/source3/rpc_server/srv_samr_nt.c +++ b/source3/rpc_server/srv_samr_nt.c @@ -49,9 +49,17 @@ #define MAX_SAM_ENTRIES_W2K 0x400 /* 1024 */ #define MAX_SAM_ENTRIES_W95 50 +struct samr_connect_info { + uint8_t dummy; +}; + +struct samr_domain_info { + struct dom_sid sid; + struct disp_info *disp_info; +}; + typedef struct disp_info { 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 */ struct pdb_search *machines; /* querydispinfo 2 */ struct pdb_search *groups; /* querydispinfo 3 and 5, enumgroups */ @@ -70,7 +78,6 @@ typedef struct disp_info { struct samr_info { /* for use by the \PIPE\samr policy */ DOM_SID sid; - bool builtin_domain; /* Quick flag to check if this is the builtin domain. */ uint32 status; /* some sort of flag. best to record it. comes from opnum 0x39 */ uint32 acc_granted; DISP_INFO *disp_info; @@ -298,7 +305,7 @@ static void map_max_allowed_access(const NT_USER_TOKEN *token, Fetch or create a dispinfo struct. ********************************************************************/ -static DISP_INFO *get_samr_dispinfo_by_sid(DOM_SID *psid) +static DISP_INFO *get_samr_dispinfo_by_sid(const struct dom_sid *psid) { /* * We do a static cache for DISP_INFO's here. Explanation can be found @@ -377,26 +384,20 @@ static struct samr_info *get_samr_info_by_sid(TALLOC_CTX *mem_ctx, DOM_SID *psid) { struct samr_info *info; - fstring sid_str; - if (psid) { - sid_to_fstring(sid_str, psid); - } else { - fstrcpy(sid_str,"(NULL)"); - } - - if ((info = TALLOC_ZERO_P(mem_ctx, struct samr_info)) == NULL) { + info = talloc_zero(mem_ctx, struct samr_info); + if (info == NULL) { return NULL; } talloc_set_destructor(info, samr_info_destructor); - DEBUG(10,("get_samr_info_by_sid: created new info for sid %s\n", sid_str)); + DEBUG(10, ("get_samr_info_by_sid: created new info for sid %s\n", + sid_string_dbg(psid))); + if (psid) { sid_copy( &info->sid, psid); - info->builtin_domain = sid_check_is_builtin(psid); } else { DEBUG(10,("get_samr_info_by_sid: created new info for NULL sid.\n")); - info->builtin_domain = False; } info->disp_info = get_samr_dispinfo_by_sid(psid); @@ -481,8 +482,10 @@ static void set_disp_info_cache_timeout(DISP_INFO *disp_info, time_t secs_fromno We must also remove the timeout handler. ********************************************************************/ -static void force_flush_samr_cache(DISP_INFO *disp_info) +static void force_flush_samr_cache(const struct dom_sid *sid) { + struct disp_info *disp_info = get_samr_dispinfo_by_sid(sid); + if ((disp_info == NULL) || (disp_info->cache_timeout_event == NULL)) { return; } @@ -512,7 +515,7 @@ static uint32 count_sam_users(struct disp_info *info, uint32 acct_flags) { struct samr_displayentry *entry; - if (info->builtin_domain) { + if (sid_check_is_builtin(&info->sid)) { /* No users in builtin. */ return 0; } @@ -536,7 +539,7 @@ static uint32 count_sam_groups(struct disp_info *info) { struct samr_displayentry *entry; - if (info->builtin_domain) { + if (sid_check_is_builtin(&info->sid)) { /* No groups in builtin. */ return 0; } @@ -597,7 +600,8 @@ NTSTATUS _samr_Close(pipes_struct *p, struct samr_Close *r) NTSTATUS _samr_OpenDomain(pipes_struct *p, struct samr_OpenDomain *r) { - struct samr_info *info; + struct samr_connect_info *cinfo; + struct samr_domain_info *dinfo; SEC_DESC *psd = NULL; uint32 acc_granted; uint32 des_access = r->in.access_mask; @@ -607,15 +611,11 @@ NTSTATUS _samr_OpenDomain(pipes_struct *p, /* find the connection policy handle. */ - if ( !find_policy_by_hnd(p, r->in.connect_handle, (void**)(void *)&info) ) - return NT_STATUS_INVALID_HANDLE; - - status = access_check_samr_function(info->acc_granted, - SAMR_ACCESS_OPEN_DOMAIN, - "_samr_OpenDomain" ); - - if ( !NT_STATUS_IS_OK(status) ) + cinfo = policy_handle_find(p, r->in.connect_handle, 0, NULL, + struct samr_connect_info, &status); + if (!NT_STATUS_IS_OK(status)) { return status; + } /*check if access can be granted as requested by client. */ map_max_allowed_access(p->server_info->ptok, &des_access); @@ -638,14 +638,13 @@ NTSTATUS _samr_OpenDomain(pipes_struct *p, return NT_STATUS_NO_SUCH_DOMAIN; } - /* associate the domain SID with the (unique) handle. */ - if ((info = get_samr_info_by_sid(p->mem_ctx, r->in.sid))==NULL) - return NT_STATUS_NO_MEMORY; - info->acc_granted = acc_granted; - - /* get a (unique) handle. open a policy on it. */ - if (!create_policy_hnd(p, r->out.domain_handle, info)) - return NT_STATUS_OBJECT_NAME_NOT_FOUND; + dinfo = policy_handle_create(p, r->out.domain_handle, acc_granted, + struct samr_domain_info, &status); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + dinfo->sid = *r->in.sid; + dinfo->disp_info = get_samr_dispinfo_by_sid(r->in.sid); DEBUG(5,("_samr_OpenDomain: %d\n", __LINE__)); @@ -964,7 +963,7 @@ NTSTATUS _samr_EnumDomainUsers(pipes_struct *p, struct samr_EnumDomainUsers *r) { NTSTATUS status; - struct samr_info *info = NULL; + struct samr_domain_info *dinfo; int num_account; uint32 enum_context = *r->in.resume_handle; enum remote_arch_types ra_type = get_remote_arch(); @@ -974,20 +973,16 @@ NTSTATUS _samr_EnumDomainUsers(pipes_struct *p, struct samr_SamArray *samr_array = NULL; struct samr_SamEntry *samr_entries = NULL; - /* find the policy handle. open a policy on it. */ - if (!find_policy_by_hnd(p, r->in.domain_handle, (void **)(void *)&info)) - return NT_STATUS_INVALID_HANDLE; + DEBUG(5,("_samr_EnumDomainUsers: %d\n", __LINE__)); - status = access_check_samr_function(info->acc_granted, - SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS, - "_samr_EnumDomainUsers"); + dinfo = policy_handle_find(p, r->in.domain_handle, + SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS, NULL, + struct samr_domain_info, &status); if (!NT_STATUS_IS_OK(status)) { return status; } - DEBUG(5,("_samr_EnumDomainUsers: %d\n", __LINE__)); - - if (info->builtin_domain) { + if (sid_check_is_builtin(&dinfo->sid)) { /* No users in builtin. */ *r->out.resume_handle = *r->in.resume_handle; DEBUG(5,("_samr_EnumDomainUsers: No users in BUILTIN\n")); @@ -1004,24 +999,24 @@ NTSTATUS _samr_EnumDomainUsers(pipes_struct *p, /* AS ROOT !!!! */ - if ((info->disp_info->enum_users != NULL) && - (info->disp_info->enum_acb_mask != r->in.acct_flags)) { - TALLOC_FREE(info->disp_info->enum_users); + if ((dinfo->disp_info->enum_users != NULL) && + (dinfo->disp_info->enum_acb_mask != r->in.acct_flags)) { + TALLOC_FREE(dinfo->disp_info->enum_users); } - if (info->disp_info->enum_users == NULL) { - info->disp_info->enum_users = pdb_search_users( - info->disp_info, r->in.acct_flags); - info->disp_info->enum_acb_mask = r->in.acct_flags; + if (dinfo->disp_info->enum_users == NULL) { + dinfo->disp_info->enum_users = pdb_search_users( + dinfo->disp_info, r->in.acct_flags); + dinfo->disp_info->enum_acb_mask = r->in.acct_flags; } - if (info->disp_info->enum_users == NULL) { + if (dinfo->disp_info->enum_users == NULL) { /* END AS ROOT !!!! */ unbecome_root(); return NT_STATUS_ACCESS_DENIED; } - num_account = pdb_search_entries(info->disp_info->enum_users, + num_account = pdb_search_entries(dinfo->disp_info->enum_users, enum_context, max_entries, &entries); @@ -1050,7 +1045,7 @@ NTSTATUS _samr_EnumDomainUsers(pipes_struct *p, } /* Ensure we cache this enumeration. */ - set_disp_info_cache_timeout(info->disp_info, DISP_INFO_CACHE_TIMEOUT); + set_disp_info_cache_timeout(dinfo->disp_info, DISP_INFO_CACHE_TIMEOUT); DEBUG(5, ("_samr_EnumDomainUsers: %d\n", __LINE__)); @@ -1107,26 +1102,22 @@ NTSTATUS _samr_EnumDomainGroups(pipes_struct *p, struct samr_EnumDomainGroups *r) { NTSTATUS status; - struct samr_info *info = NULL; + struct samr_domain_info *dinfo; struct samr_displayentry *groups; uint32 num_groups; struct samr_SamArray *samr_array = NULL; struct samr_SamEntry *samr_entries = NULL; - /* find the policy handle. open a policy on it. */ - if (!find_policy_by_hnd(p, r->in.domain_handle, (void **)(void *)&info)) - return NT_STATUS_INVALID_HANDLE; - - status = access_check_samr_function(info->acc_granted, - SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS, - "_samr_EnumDomainGroups"); + dinfo = policy_handle_find(p, r->in.domain_handle, + SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS, NULL, + struct samr_domain_info, &status); if (!NT_STATUS_IS_OK(status)) { return status; } DEBUG(5,("_samr_EnumDomainGroups: %d\n", __LINE__)); - if (info->builtin_domain) { + if (sid_check_is_builtin(&dinfo->sid)) { /* No groups in builtin. */ *r->out.resume_handle = *r->in.resume_handle; DEBUG(5,("_samr_EnumDomainGroups: No groups in BUILTIN\n")); @@ -1142,22 +1133,22 @@ NTSTATUS _samr_EnumDomainGroups(pipes_struct *p, become_root(); - if (info->disp_info->groups == NULL) { - info->disp_info->groups = pdb_search_groups(info->disp_info); + if (dinfo->disp_info->groups == NULL) { + dinfo->disp_info->groups = pdb_search_groups(dinfo->disp_info); - if (info->disp_info->groups == NULL) { + if (dinfo->disp_info->groups == NULL) { unbecome_root(); return NT_STATUS_ACCESS_DENIED; } } - num_groups = pdb_search_entries(info->disp_info->groups, + num_groups = pdb_search_entries(dinfo->disp_info->groups, *r->in.resume_handle, MAX_SAM_ENTRIES, &groups); unbecome_root(); /* Ensure we cache this enumeration. */ - set_disp_info_cache_timeout(info->disp_info, DISP_INFO_CACHE_TIMEOUT); + set_disp_info_cache_timeout(dinfo->disp_info, DISP_INFO_CACHE_TIMEOUT); make_group_sam_entry_list(p->mem_ctx, &samr_entries, num_groups, groups); @@ -1182,26 +1173,22 @@ NTSTATUS _samr_EnumDomainAliases(pipes_struct *p, struct samr_EnumDomainAliases *r) { NTSTATUS status; - struct samr_info *info; + struct samr_domain_info *dinfo; struct samr_displayentry *aliases; uint32 num_aliases = 0; struct samr_SamArray *samr_array = NULL; struct samr_SamEntry *samr_entries = NULL; - /* find the policy handle. open a policy on it. */ - if (!find_policy_by_hnd(p, r->in.domain_handle, (void **)(void *)&info)) - return NT_STATUS_INVALID_HANDLE; - - DEBUG(5,("_samr_EnumDomainAliases: sid %s\n", - sid_string_dbg(&info->sid))); - - status = access_check_samr_function(info->acc_granted, - SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS, - "_samr_EnumDomainAliases"); + dinfo = policy_handle_find(p, r->in.domain_handle, + SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS, NULL, + struct samr_domain_info, &status); if (!NT_STATUS_IS_OK(status)) { return status; } + DEBUG(5,("_samr_EnumDomainAliases: sid %s\n", + sid_string_dbg(&dinfo->sid))); + samr_array = TALLOC_ZERO_P(p->mem_ctx, struct samr_SamArray); if (!samr_array) { return NT_STATUS_NO_MEMORY; @@ -1209,22 +1196,22 @@ NTSTATUS _samr_EnumDomainAliases(pipes_struct *p, become_root(); - if (info->disp_info->aliases == NULL) { - info->disp_info->aliases = pdb_search_aliases( - info->disp_info, &info->sid); - if (info->disp_info->aliases == NULL) { + if (dinfo->disp_info->aliases == NULL) { + dinfo->disp_info->aliases = pdb_search_aliases( + dinfo->disp_info, &dinfo->sid); + if (dinfo->disp_info->aliases == NULL) { unbecome_root(); return NT_STATUS_ACCESS_DENIED; } } - num_aliases = pdb_search_entries(info->disp_info->aliases, + num_aliases = pdb_search_entries(dinfo->disp_info->aliases, *r->in.resume_handle, MAX_SAM_ENTRIES, &aliases); unbecome_root(); /* Ensure we cache this enumeration. */ - set_disp_info_cache_timeout(info->disp_info, DISP_INFO_CACHE_TIMEOUT); + set_disp_info_cache_timeout(dinfo->disp_info, DISP_INFO_CACHE_TIMEOUT); make_group_sam_entry_list(p->mem_ctx, &samr_entries, num_aliases, aliases); @@ -1447,7 +1434,7 @@ NTSTATUS _samr_QueryDisplayInfo(pipes_struct *p, struct samr_QueryDisplayInfo *r) { NTSTATUS status; - struct samr_info *info = NULL; + struct samr_domain_info *dinfo; uint32 struct_size=0x20; /* W2K always reply that, client doesn't care */ uint32 max_entries = r->in.max_entries; @@ -1465,18 +1452,9 @@ NTSTATUS _samr_QueryDisplayInfo(pipes_struct *p, DEBUG(5,("_samr_QueryDisplayInfo: %d\n", __LINE__)); - /* find the policy handle. open a policy on it. */ - if (!find_policy_by_hnd(p, r->in.domain_handle, (void **)(void *)&info)) - return NT_STATUS_INVALID_HANDLE; - - if (info->builtin_domain) { - DEBUG(5,("_samr_QueryDisplayInfo: Nothing in BUILTIN\n")); - return NT_STATUS_OK; - } - - status = access_check_samr_function(info->acc_granted, - SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS, - "_samr_QueryDisplayInfo"); + dinfo = policy_handle_find(p, r->in.domain_handle, + SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS, NULL, + struct samr_domain_info, &status); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -1541,10 +1519,10 @@ NTSTATUS _samr_QueryDisplayInfo(pipes_struct *p, switch (r->in.level) { case 0x1: case 0x4: - if (info->disp_info->users == NULL) { - info->disp_info->users = pdb_search_users( - info->disp_info, ACB_NORMAL); - if (info->disp_info->users == NULL) { + if (dinfo->disp_info->users == NULL) { + dinfo->disp_info->users = pdb_search_users( + dinfo->disp_info, ACB_NORMAL); + if (dinfo->disp_info->users == NULL) { unbecome_root(); return NT_STATUS_ACCESS_DENIED; } @@ -1555,15 +1533,15 @@ NTSTATUS _samr_QueryDisplayInfo(pipes_struct *p, (unsigned int)enum_context )); } - num_account = pdb_search_entries(info->disp_info->users, + num_account = pdb_search_entries(dinfo->disp_info->users, enum_context, max_entries, &entries); break; case 0x2: - if (info->disp_info->machines == NULL) { - info->disp_info->machines = pdb_search_users( - info->disp_info, ACB_WSTRUST|ACB_SVRTRUST); - if (info->disp_info->machines == NULL) { + if (dinfo->disp_info->machines == NULL) { + dinfo->disp_info->machines = pdb_search_users( + dinfo->disp_info, ACB_WSTRUST|ACB_SVRTRUST); + if (dinfo->disp_info->machines == NULL) { unbecome_root(); return NT_STATUS_ACCESS_DENIED; } @@ -1574,16 +1552,16 @@ NTSTATUS _samr_QueryDisplayInfo(pipes_struct *p, (unsigned int)enum_context )); } - num_account = pdb_search_entries(info->disp_info->machines, + num_account = pdb_search_entries(dinfo->disp_info->machines, enum_context, max_entries, &entries); break; case 0x3: case 0x5: - if (info->disp_info->groups == NULL) { - info->disp_info->groups = pdb_search_groups( - info->disp_info); - if (info->disp_info->groups == NULL) { + if (dinfo->disp_info->groups == NULL) { + dinfo->disp_info->groups = pdb_search_groups( + dinfo->disp_info); + if (dinfo->disp_info->groups == NULL) { unbecome_root(); return NT_STATUS_ACCESS_DENIED; } @@ -1594,7 +1572,7 @@ NTSTATUS _samr_QueryDisplayInfo(pipes_struct *p, (unsigned int)enum_context )); } - num_account = pdb_search_entries(info->disp_info->groups, + num_account = pdb_search_entries(dinfo->disp_info->groups, enum_context, max_entries, &entries); break; @@ -1651,7 +1629,7 @@ NTSTATUS _samr_QueryDisplayInfo(pipes_struct *p, } /* Ensure we cache this enumeration. */ - set_disp_info_cache_timeout(info->disp_info, DISP_INFO_CACHE_TIMEOUT); + set_disp_info_cache_timeout(dinfo->disp_info, DISP_INFO_CACHE_TIMEOUT); DEBUG(5, ("_samr_QueryDisplayInfo: %d\n", __LINE__)); @@ -1776,25 +1754,20 @@ NTSTATUS _samr_QueryAliasInfo(pipes_struct *p, NTSTATUS _samr_LookupNames(pipes_struct *p, struct samr_LookupNames *r) { + struct samr_domain_info *dinfo; NTSTATUS status; uint32 *rid; enum lsa_SidType *type; int i; int num_rids = r->in.num_names; - DOM_SID pol_sid; - uint32 acc_granted; struct samr_Ids rids, types; uint32_t num_mapped = 0; DEBUG(5,("_samr_LookupNames: %d\n", __LINE__)); - if (!get_lsa_policy_samr_sid(p, r->in.domain_handle, &pol_sid, &acc_granted, NULL)) { - return NT_STATUS_OBJECT_TYPE_MISMATCH; - } - - status = access_check_samr_function(acc_granted, - 0, /* Don't know the acc_bits yet */ - "_samr_LookupNames"); + dinfo = policy_handle_find(p, r->in.domain_handle, + 0 /* Don't know the acc_bits yet */, NULL, + struct samr_domain_info, &status); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -1811,7 +1784,7 @@ NTSTATUS _samr_LookupNames(pipes_struct *p, NT_STATUS_HAVE_NO_MEMORY(type); DEBUG(5,("_samr_LookupNames: looking name on SID %s\n", - sid_string_dbg(&pol_sid))); + sid_string_dbg(&dinfo->sid))); for (i = 0; i < num_rids; i++) { @@ -1820,7 +1793,7 @@ NTSTATUS _samr_LookupNames(pipes_struct *p, rid[i] = 0xffffffff; - if (sid_check_is_builtin(&pol_sid)) { + if (sid_check_is_builtin(&dinfo->sid)) { if (lookup_builtin_name(r->in.names[i].string, &rid[i])) { @@ -2037,13 +2010,12 @@ static bool make_samr_lookup_rids(TALLOC_CTX *ctx, uint32 num_names, NTSTATUS _samr_LookupRids(pipes_struct *p, struct samr_LookupRids *r) { + struct samr_domain_info *dinfo; NTSTATUS status; const char **names; enum lsa_SidType *attrs = NULL; uint32 *wire_attrs = NULL; - DOM_SID pol_sid; int num_rids = (int)r->in.num_rids; - uint32 acc_granted; int i; struct lsa_Strings names_array; struct samr_Ids types_array; @@ -2051,13 +2023,9 @@ NTSTATUS _samr_LookupRids(pipes_struct *p, DEBUG(5,("_samr_LookupRids: %d\n", __LINE__)); - /* find the policy handle. open a policy on it. */ - if (!get_lsa_policy_samr_sid(p, r->in.domain_handle, &pol_sid, &acc_granted, NULL)) - return NT_STATUS_INVALID_HANDLE; - - status = access_check_samr_function(acc_granted, - 0, /* Don't know the acc_bits yet */ - "_samr_LookupRids"); + dinfo = policy_handle_find(p, r->in.domain_handle, + 0 /* Don't know the acc_bits yet */, NULL, + struct samr_domain_info, &status); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -2082,7 +2050,7 @@ NTSTATUS _samr_LookupRids(pipes_struct *p, } become_root(); /* lookup_sid can require root privs */ - status = pdb_lookup_rids(&pol_sid, num_rids, r->in.rids, + status = pdb_lookup_rids(&dinfo->sid, num_rids, r->in.rids, names, attrs); unbecome_root(); @@ -2123,7 +2091,8 @@ NTSTATUS _samr_OpenUser(pipes_struct *p, { struct samu *sampass=NULL; DOM_SID sid; - struct samr_info *info = NULL; + struct samr_domain_info *dinfo; + struct samr_info *info; SEC_DESC *psd = NULL; uint32 acc_granted; uint32 des_access = r->in.access_mask; @@ -2131,18 +2100,14 @@ NTSTATUS _samr_OpenUser(pipes_struct *p, bool ret; NTSTATUS nt_status; SE_PRIV se_rights; + NTSTATUS status; - /* find the domain policy handle and get domain SID / access bits in the domain policy. */ - - if ( !get_lsa_policy_samr_sid(p, r->in.domain_handle, &sid, &acc_granted, NULL) ) - return NT_STATUS_INVALID_HANDLE; - - nt_status = access_check_samr_function(acc_granted, - SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, - "_samr_OpenUser" ); - - if ( !NT_STATUS_IS_OK(nt_status) ) - return nt_status; + dinfo = policy_handle_find(p, r->in.domain_handle, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, NULL, + struct samr_domain_info, &status); + if (!NT_STATUS_IS_OK(status)) { + return status; + } if ( !(sampass = samu_new( p->mem_ctx )) ) { return NT_STATUS_NO_MEMORY; @@ -2150,7 +2115,7 @@ NTSTATUS _samr_OpenUser(pipes_struct *p, /* append the user's RID to it */ - if (!sid_append_rid(&sid, r->in.rid)) + if (!sid_compose(&sid, &dinfo->sid, r->in.rid)) return NT_STATUS_NO_SUCH_USER; /* check if access can be granted as requested by client. */ @@ -2788,7 +2753,7 @@ NTSTATUS _samr_QueryDomainInfo(pipes_struct *p, struct samr_QueryDomainInfo *r) { NTSTATUS status = NT_STATUS_OK; - struct samr_info *info = NULL; + struct samr_domain_info *dinfo; union samr_DomainInfo *dom_info; time_t u_expire, u_min_age; @@ -2802,23 +2767,18 @@ NTSTATUS _samr_QueryDomainInfo(pipes_struct *p, DEBUG(5,("_samr_QueryDomainInfo: %d\n", __LINE__)); + dinfo = policy_handle_find(p, r->in.domain_handle, + SAMR_ACCESS_LOOKUP_DOMAIN, NULL, + struct samr_domain_info, &status); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + dom_info = TALLOC_ZERO_P(p->mem_ctx, union samr_DomainInfo); if (!dom_info) { return NT_STATUS_NO_MEMORY; } - /* find the policy handle. open a policy on it. */ - if (!find_policy_by_hnd(p, r->in.domain_handle, (void **)(void *)&info)) { - return NT_STATUS_INVALID_HANDLE; - } - - status = access_check_samr_function(info->acc_granted, - SAMR_ACCESS_OPEN_DOMAIN, - "_samr_QueryDomainInfo" ); - - if ( !NT_STATUS_IS_OK(status) ) - return status; - switch (r->in.level) { case 0x01: @@ -2860,9 +2820,12 @@ NTSTATUS _samr_QueryDomainInfo(pipes_struct *p, /* AS ROOT !!! */ - dom_info->general.num_users = count_sam_users(info->disp_info, ACB_NORMAL); - dom_info->general.num_groups = count_sam_groups(info->disp_info); - dom_info->general.num_aliases = count_sam_aliases(info->disp_info); + dom_info->general.num_users = count_sam_users( + dinfo->disp_info, ACB_NORMAL); + dom_info->general.num_groups = count_sam_groups( + dinfo->disp_info); + dom_info->general.num_aliases = count_sam_aliases( + dinfo->disp_info); pdb_get_account_policy(AP_TIME_TO_LOGOUT, &u_logout); @@ -3032,7 +2995,8 @@ NTSTATUS _samr_CreateUser2(pipes_struct *p, const char *account = NULL; DOM_SID sid; uint32_t acb_info = r->in.acct_flags; - struct samr_info *info = NULL; + struct samr_domain_info *dinfo; + struct samr_info *info; NTSTATUS nt_status; uint32 acc_granted; SEC_DESC *psd; @@ -3041,25 +3005,19 @@ NTSTATUS _samr_CreateUser2(pipes_struct *p, uint32 des_access = GENERIC_RIGHTS_USER_ALL_ACCESS; bool can_add_account = False; SE_PRIV se_rights; - DISP_INFO *disp_info = NULL; - /* Get the domain SID stored in the domain policy */ - if (!get_lsa_policy_samr_sid(p, r->in.domain_handle, &sid, &acc_granted, - &disp_info)) - return NT_STATUS_INVALID_HANDLE; + dinfo = policy_handle_find(p, r->in.domain_handle, + SAMR_DOMAIN_ACCESS_CREATE_USER, NULL, + struct samr_domain_info, &nt_status); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } - if (disp_info->builtin_domain) { + if (sid_check_is_builtin(&dinfo->sid)) { DEBUG(5,("_samr_CreateUser2: Refusing user create in BUILTIN\n")); return NT_STATUS_ACCESS_DENIED; } - nt_status = access_check_samr_function(acc_granted, - SAMR_DOMAIN_ACCESS_CREATE_USER, - "_samr_CreateUser2"); - if (!NT_STATUS_IS_OK(nt_status)) { - return nt_status; - } - if (!(acb_info == ACB_NORMAL || acb_info == ACB_DOMTRUST || acb_info == ACB_WSTRUST || acb_info == ACB_SVRTRUST)) { /* Match Win2k, and return NT_STATUS_INVALID_PARAMETER if @@ -3161,7 +3119,7 @@ NTSTATUS _samr_CreateUser2(pipes_struct *p, } /* After a "set" ensure we have no cached display info. */ - force_flush_samr_cache(info->disp_info); + force_flush_samr_cache(&sid); *r->out.access_granted = acc_granted; @@ -3195,8 +3153,11 @@ NTSTATUS _samr_CreateUser(pipes_struct *p, NTSTATUS _samr_Connect(pipes_struct *p, struct samr_Connect *r) { - struct samr_info *info = NULL; + struct samr_connect_info *info; + uint32_t acc_granted; + struct policy_handle hnd; uint32 des_access = r->in.access_mask; + NTSTATUS status; /* Access check */ @@ -3205,12 +3166,6 @@ NTSTATUS _samr_Connect(pipes_struct *p, return NT_STATUS_ACCESS_DENIED; } - /* set up the SAMR connect_anon response */ - - /* associate the user's SID with the new handle. */ - if ((info = get_samr_info_by_sid(p->mem_ctx, NULL)) == NULL) - return NT_STATUS_NO_MEMORY; - /* don't give away the farm but this is probably ok. The SAMR_ACCESS_ENUM_DOMAINS was observed from a win98 client trying to enumerate users (when configured user level access control on shares) --jerry */ @@ -3218,12 +3173,20 @@ NTSTATUS _samr_Connect(pipes_struct *p, map_max_allowed_access(p->server_info->ptok, &des_access); se_map_generic( &des_access, &sam_generic_mapping ); - info->acc_granted = des_access & (SAMR_ACCESS_ENUM_DOMAINS|SAMR_ACCESS_OPEN_DOMAIN); - /* get a (unique) handle. open a policy on it. */ - if (!create_policy_hnd(p, r->out.connect_handle, info)) - return NT_STATUS_OBJECT_NAME_NOT_FOUND; + acc_granted = des_access & (SAMR_ACCESS_ENUM_DOMAINS + |SAMR_ACCESS_LOOKUP_DOMAIN); + + /* set up the SAMR connect_anon response */ + + info = policy_handle_create(p, &hnd, acc_granted, + struct samr_connect_info, + &status); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + *r->out.connect_handle = hnd; return NT_STATUS_OK; } @@ -3234,7 +3197,8 @@ NTSTATUS _samr_Connect(pipes_struct *p, NTSTATUS _samr_Connect2(pipes_struct *p, struct samr_Connect2 *r) { - struct samr_info *info = NULL; + struct samr_connect_info *info = NULL; + struct policy_handle hnd; SEC_DESC *psd = NULL; uint32 acc_granted; uint32 des_access = r->in.access_mask; @@ -3277,20 +3241,16 @@ NTSTATUS _samr_Connect2(pipes_struct *p, if ( !NT_STATUS_IS_OK(nt_status) ) return nt_status; - /* associate the user's SID and access granted with the new handle. */ - if ((info = get_samr_info_by_sid(p->mem_ctx, NULL)) == NULL) - return NT_STATUS_NO_MEMORY; - - info->acc_granted = acc_granted; - info->status = r->in.access_mask; /* this looks so wrong... - gd */ - - /* get a (unique) handle. open a policy on it. */ - if (!create_policy_hnd(p, r->out.connect_handle, info)) - return NT_STATUS_OBJECT_NAME_NOT_FOUND; + info = policy_handle_create(p, &hnd, acc_granted, + struct samr_connect_info, &nt_status); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } DEBUG(5,("%s: %d\n", fn, __LINE__)); - return nt_status; + *r->out.connect_handle = hnd; + return NT_STATUS_OK; } /**************************************************************** @@ -3361,20 +3321,18 @@ NTSTATUS _samr_Connect5(pipes_struct *p, NTSTATUS _samr_LookupDomain(pipes_struct *p, struct samr_LookupDomain *r) { - NTSTATUS status = NT_STATUS_OK; - struct samr_info *info; + NTSTATUS status; + struct samr_connect_info *info; const char *domain_name; DOM_SID *sid = NULL; - if (!find_policy_by_hnd(p, r->in.connect_handle, (void**)(void *)&info)) - return NT_STATUS_INVALID_HANDLE; - /* win9x user manager likes to use SAMR_ACCESS_ENUM_DOMAINS here. Reverted that change so we will work with RAS servers again */ - status = access_check_samr_function(info->acc_granted, - SAMR_ACCESS_OPEN_DOMAIN, - "_samr_LookupDomain"); + info = policy_handle_find(p, r->in.connect_handle, + SAMR_ACCESS_LOOKUP_DOMAIN, NULL, + struct samr_connect_info, + &status); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -3413,17 +3371,14 @@ NTSTATUS _samr_EnumDomains(pipes_struct *p, struct samr_EnumDomains *r) { NTSTATUS status; - struct samr_info *info; + struct samr_connect_info *info; uint32_t num_entries = 2; struct samr_SamEntry *entry_array = NULL; struct samr_SamArray *sam; - if (!find_policy_by_hnd(p, r->in.connect_handle, (void**)(void *)&info)) - return NT_STATUS_INVALID_HANDLE; - - status = access_check_samr_function(info->acc_granted, - SAMR_ACCESS_ENUM_DOMAINS, - "_samr_EnumDomains"); + info = policy_handle_find(p, r->in.connect_handle, + SAMR_ACCESS_ENUM_DOMAINS, NULL, + struct samr_connect_info, &status); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -3465,6 +3420,7 @@ NTSTATUS _samr_OpenAlias(pipes_struct *p, DOM_SID sid; uint32 alias_rid = r->in.rid; struct samr_info *info = NULL; + struct samr_domain_info *dinfo; SEC_DESC *psd = NULL; uint32 acc_granted; uint32 des_access = r->in.access_mask; @@ -3472,21 +3428,16 @@ NTSTATUS _samr_OpenAlias(pipes_struct *p, NTSTATUS status; SE_PRIV se_rights; - /* find the domain policy and get the SID / access bits stored in the domain policy */ - - if ( !get_lsa_policy_samr_sid(p, r->in.domain_handle, &sid, &acc_granted, NULL) ) - return NT_STATUS_INVALID_HANDLE; - - status = access_check_samr_function(acc_granted, - SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, - "_samr_OpenAlias"); - - if ( !NT_STATUS_IS_OK(status) ) + dinfo = policy_handle_find(p, r->in.domain_handle, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, NULL, + struct samr_domain_info, &status); + if (!NT_STATUS_IS_OK(status)) { return status; + } /* append the alias' RID to it */ - if (!sid_append_rid(&sid, alias_rid)) + if (!sid_compose(&sid, &dinfo->sid, alias_rid)) return NT_STATUS_NO_SUCH_ALIAS; /*check if access can be granted as requested by client. */ @@ -4288,7 +4239,7 @@ NTSTATUS _samr_SetUserInfo(pipes_struct *p, /* ================ END SeMachineAccountPrivilege BLOCK ================ */ if (NT_STATUS_IS_OK(status)) { - force_flush_samr_cache(disp_info); + force_flush_samr_cache(&sid); } return status; @@ -4319,36 +4270,25 @@ NTSTATUS _samr_GetAliasMembership(pipes_struct *p, { size_t num_alias_rids; uint32 *alias_rids; - struct samr_info *info = NULL; + struct samr_domain_info *dinfo; size_t i; - NTSTATUS ntstatus1; - NTSTATUS ntstatus2; + NTSTATUS status; DOM_SID *members; DEBUG(5,("_samr_GetAliasMembership: %d\n", __LINE__)); - /* find the policy handle. open a policy on it. */ - if (!find_policy_by_hnd(p, r->in.domain_handle, (void **)(void *)&info)) - return NT_STATUS_INVALID_HANDLE; - - ntstatus1 = access_check_samr_function(info->acc_granted, - SAMR_DOMAIN_ACCESS_LOOKUP_ALIAS, - "_samr_GetAliasMembership"); - ntstatus2 = access_check_samr_function(info->acc_granted, - SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, - "_samr_GetAliasMembership"); - - if (!NT_STATUS_IS_OK(ntstatus1) || !NT_STATUS_IS_OK(ntstatus2)) { - if (!(NT_STATUS_EQUAL(ntstatus1,NT_STATUS_ACCESS_DENIED) && NT_STATUS_IS_OK(ntstatus2)) && - !(NT_STATUS_EQUAL(ntstatus1,NT_STATUS_ACCESS_DENIED) && NT_STATUS_IS_OK(ntstatus1))) { - return (NT_STATUS_IS_OK(ntstatus1)) ? ntstatus2 : ntstatus1; - } + dinfo = policy_handle_find(p, r->in.domain_handle, + SAMR_DOMAIN_ACCESS_LOOKUP_ALIAS + | SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, NULL, + struct samr_domain_info, &status); + if (!NT_STATUS_IS_OK(status)) { + return status; } - if (!sid_check_is_domain(&info->sid) && - !sid_check_is_builtin(&info->sid)) + if (!sid_check_is_domain(&dinfo->sid) && + !sid_check_is_builtin(&dinfo->sid)) return NT_STATUS_OBJECT_TYPE_MISMATCH; if (r->in.sids->num_sids) { @@ -4367,13 +4307,13 @@ NTSTATUS _samr_GetAliasMembership(pipes_struct *p, num_alias_rids = 0; become_root(); - ntstatus1 = pdb_enum_alias_memberships(p->mem_ctx, &info->sid, members, - r->in.sids->num_sids, - &alias_rids, &num_alias_rids); + status = pdb_enum_alias_memberships(p->mem_ctx, &dinfo->sid, members, + r->in.sids->num_sids, + &alias_rids, &num_alias_rids); unbecome_root(); - if (!NT_STATUS_IS_OK(ntstatus1)) { - return ntstatus1; + if (!NT_STATUS_IS_OK(status)) { + return status; } r->out.rids->count = num_alias_rids; @@ -4560,7 +4500,7 @@ NTSTATUS _samr_AddAliasMember(pipes_struct *p, /******** END SeAddUsers BLOCK *********/ if (NT_STATUS_IS_OK(status)) { - force_flush_samr_cache(disp_info); + force_flush_samr_cache(&alias_sid); } return status; @@ -4610,7 +4550,7 @@ NTSTATUS _samr_DeleteAliasMember(pipes_struct *p, /******** END SeAddUsers BLOCK *********/ if (NT_STATUS_IS_OK(status)) { - force_flush_samr_cache(disp_info); + force_flush_samr_cache(&alias_sid); } return status; @@ -4664,7 +4604,7 @@ NTSTATUS _samr_AddGroupMember(pipes_struct *p, /******** END SeAddUsers BLOCK *********/ - force_flush_samr_cache(disp_info); + force_flush_samr_cache(&group_sid); return status; } @@ -4722,7 +4662,7 @@ NTSTATUS _samr_DeleteGroupMember(pipes_struct *p, /******** END SeAddUsers BLOCK *********/ - force_flush_samr_cache(disp_info); + force_flush_samr_cache(&group_sid); return status; } @@ -4812,7 +4752,7 @@ NTSTATUS _samr_DeleteUser(pipes_struct *p, ZERO_STRUCTP(r->out.user_handle); - force_flush_samr_cache(disp_info); + force_flush_samr_cache(&user_sid); return NT_STATUS_OK; } @@ -4878,7 +4818,7 @@ NTSTATUS _samr_DeleteDomainGroup(pipes_struct *p, if (!close_policy_hnd(p, r->in.group_handle)) return NT_STATUS_OBJECT_NAME_INVALID; - force_flush_samr_cache(disp_info); + force_flush_samr_cache(&group_sid); return NT_STATUS_OK; } @@ -4949,7 +4889,7 @@ NTSTATUS _samr_DeleteDomAlias(pipes_struct *p, if (!close_policy_hnd(p, r->in.alias_handle)) return NT_STATUS_OBJECT_NAME_INVALID; - force_flush_samr_cache(disp_info); + force_flush_samr_cache(&alias_sid); return NT_STATUS_OK; } @@ -4963,27 +4903,21 @@ NTSTATUS _samr_CreateDomainGroup(pipes_struct *p, { NTSTATUS status; - DOM_SID dom_sid; DOM_SID info_sid; const char *name; + struct samr_domain_info *dinfo; struct samr_info *info; - uint32 acc_granted; SE_PRIV se_rights; bool can_add_accounts; - DISP_INFO *disp_info = NULL; - - /* Find the policy handle. Open a policy on it. */ - if (!get_lsa_policy_samr_sid(p, r->in.domain_handle, &dom_sid, &acc_granted, &disp_info)) - return NT_STATUS_INVALID_HANDLE; - status = access_check_samr_function(acc_granted, - SAMR_DOMAIN_ACCESS_CREATE_GROUP, - "_samr_CreateDomainGroup"); + dinfo = policy_handle_find(p, r->in.domain_handle, + SAMR_DOMAIN_ACCESS_CREATE_GROUP, NULL, + struct samr_domain_info, &status); if (!NT_STATUS_IS_OK(status)) { return status; } - if (!sid_equal(&dom_sid, get_global_sam_sid())) + if (!sid_equal(&dinfo->sid, get_global_sam_sid())) return NT_STATUS_ACCESS_DENIED; name = r->in.name->string; @@ -5018,7 +4952,7 @@ NTSTATUS _samr_CreateDomainGroup(pipes_struct *p, if ( !NT_STATUS_IS_OK(status) ) return status; - sid_compose(&info_sid, get_global_sam_sid(), *r->out.rid); + sid_compose(&info_sid, &dinfo->sid, *r->out.rid); if ((info = get_samr_info_by_sid(p->mem_ctx, &info_sid)) == NULL) return NT_STATUS_NO_MEMORY; @@ -5031,7 +4965,7 @@ NTSTATUS _samr_CreateDomainGroup(pipes_struct *p, if (!create_policy_hnd(p, r->out.group_handle, info)) return NT_STATUS_OBJECT_NAME_NOT_FOUND; - force_flush_samr_cache(disp_info); + force_flush_samr_cache(&info_sid); return NT_STATUS_OK; } @@ -5043,29 +4977,23 @@ NTSTATUS _samr_CreateDomainGroup(pipes_struct *p, NTSTATUS _samr_CreateDomAlias(pipes_struct *p, struct samr_CreateDomAlias *r) { - DOM_SID dom_sid; DOM_SID info_sid; const char *name = NULL; + struct samr_domain_info *dinfo; struct samr_info *info; - uint32 acc_granted; gid_t gid; NTSTATUS result; SE_PRIV se_rights; bool can_add_accounts; - DISP_INFO *disp_info = NULL; - - /* Find the policy handle. Open a policy on it. */ - if (!get_lsa_policy_samr_sid(p, r->in.domain_handle, &dom_sid, &acc_granted, &disp_info)) - return NT_STATUS_INVALID_HANDLE; - result = access_check_samr_function(acc_granted, - SAMR_DOMAIN_ACCESS_CREATE_ALIAS, - "_samr_CreateDomAlias"); + dinfo = policy_handle_find(p, r->in.domain_handle, + SAMR_DOMAIN_ACCESS_CREATE_ALIAS, NULL, + struct samr_domain_info, &result); if (!NT_STATUS_IS_OK(result)) { return result; } - if (!sid_equal(&dom_sid, get_global_sam_sid())) + if (!sid_equal(&dinfo->sid, get_global_sam_sid())) return NT_STATUS_ACCESS_DENIED; name = r->in.alias_name->string; @@ -5097,8 +5025,7 @@ NTSTATUS _samr_CreateDomAlias(pipes_struct *p, return result; } - sid_copy(&info_sid, get_global_sam_sid()); - sid_append_rid(&info_sid, *r->out.rid); + sid_compose(&info_sid, &dinfo->sid, *r->out.rid); if (!sid_to_gid(&info_sid, &gid)) { DEBUG(10, ("Could not find alias just created\n")); @@ -5123,7 +5050,7 @@ NTSTATUS _samr_CreateDomAlias(pipes_struct *p, if (!create_policy_hnd(p, r->out.alias_handle, info)) return NT_STATUS_OBJECT_NAME_NOT_FOUND; - force_flush_samr_cache(disp_info); + force_flush_samr_cache(&info_sid); return NT_STATUS_OK; } @@ -5293,7 +5220,7 @@ NTSTATUS _samr_SetGroupInfo(pipes_struct *p, /******** End SeAddUsers BLOCK *********/ if (NT_STATUS_IS_OK(status)) { - force_flush_samr_cache(disp_info); + force_flush_samr_cache(&group_sid); } return status; @@ -5395,7 +5322,7 @@ NTSTATUS _samr_SetAliasInfo(pipes_struct *p, /******** End SeAddUsers BLOCK *********/ if (NT_STATUS_IS_OK(status)) - force_flush_samr_cache(disp_info); + force_flush_samr_cache(&group_sid); return status; } @@ -5444,28 +5371,24 @@ NTSTATUS _samr_OpenGroup(pipes_struct *p, struct samr_OpenGroup *r) { - DOM_SID sid; DOM_SID info_sid; GROUP_MAP map; + struct samr_domain_info *dinfo; struct samr_info *info; SEC_DESC *psd = NULL; uint32 acc_granted; uint32 des_access = r->in.access_mask; size_t sd_size; NTSTATUS status; - fstring sid_string; bool ret; SE_PRIV se_rights; - if (!get_lsa_policy_samr_sid(p, r->in.domain_handle, &sid, &acc_granted, NULL)) - return NT_STATUS_INVALID_HANDLE; - - status = access_check_samr_function(acc_granted, - SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, - "_samr_OpenGroup"); - - if ( !NT_STATUS_IS_OK(status) ) + dinfo = policy_handle_find(p, r->in.domain_handle, + SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT, NULL, + struct samr_domain_info, &status); + if (!NT_STATUS_IS_OK(status)) { return status; + } /*check if access can be granted as requested by client. */ map_max_allowed_access(p->server_info->ptok, &des_access); @@ -5484,19 +5407,18 @@ NTSTATUS _samr_OpenGroup(pipes_struct *p, /* this should not be hard-coded like this */ - if (!sid_equal(&sid, get_global_sam_sid())) + if (!sid_equal(&dinfo->sid, get_global_sam_sid())) return NT_STATUS_ACCESS_DENIED; - sid_copy(&info_sid, get_global_sam_sid()); - sid_append_rid(&info_sid, r->in.rid); - sid_to_fstring(sid_string, &info_sid); + sid_compose(&info_sid, &dinfo->sid, r->in.rid); if ((info = get_samr_info_by_sid(p->mem_ctx, &info_sid)) == NULL) return NT_STATUS_NO_MEMORY; info->acc_granted = acc_granted; - DEBUG(10, ("_samr_OpenGroup:Opening SID: %s\n", sid_string)); + DEBUG(10, ("_samr_OpenGroup:Opening SID: %s\n", + sid_string_dbg(&info_sid))); /* check if that group really exists */ become_root(); @@ -5519,31 +5441,23 @@ NTSTATUS _samr_OpenGroup(pipes_struct *p, NTSTATUS _samr_RemoveMemberFromForeignDomain(pipes_struct *p, struct samr_RemoveMemberFromForeignDomain *r) { - DOM_SID delete_sid, domain_sid; - uint32 acc_granted; + struct samr_domain_info *dinfo; NTSTATUS result; - DISP_INFO *disp_info = NULL; - - sid_copy( &delete_sid, r->in.sid ); DEBUG(5,("_samr_RemoveMemberFromForeignDomain: removing SID [%s]\n", - sid_string_dbg(&delete_sid))); + sid_string_dbg(r->in.sid))); /* Find the policy handle. Open a policy on it. */ - if (!get_lsa_policy_samr_sid(p, r->in.domain_handle, &domain_sid, - &acc_granted, &disp_info)) - return NT_STATUS_INVALID_HANDLE; - - result = access_check_samr_function(acc_granted, - STD_RIGHT_DELETE_ACCESS, - "_samr_RemoveMemberFromForeignDomain"); - - if (!NT_STATUS_IS_OK(result)) + dinfo = policy_handle_find(p, r->in.domain_handle, + STD_RIGHT_DELETE_ACCESS, NULL, + struct samr_domain_info, &result); + if (!NT_STATUS_IS_OK(result)) { return result; + } DEBUG(8, ("_samr_RemoveMemberFromForeignDomain: sid is %s\n", - sid_string_dbg(&domain_sid))); + sid_string_dbg(&dinfo->sid))); /* we can only delete a user from a group since we don't have nested groups anyways. So in the latter case, just say OK */ @@ -5559,16 +5473,16 @@ NTSTATUS _samr_RemoveMemberFromForeignDomain(pipes_struct *p, * only application of this call. To verify this, let people report * other cases. */ - if (!sid_check_is_builtin(&domain_sid)) { + if (!sid_check_is_builtin(&dinfo->sid)) { DEBUG(1,("_samr_RemoveMemberFromForeignDomain: domain_sid = %s, " "global_sam_sid() = %s\n", - sid_string_dbg(&domain_sid), + sid_string_dbg(&dinfo->sid), sid_string_dbg(get_global_sam_sid()))); DEBUGADD(1,("please report to samba-technical@samba.org!\n")); return NT_STATUS_OK; } - force_flush_samr_cache(disp_info); + force_flush_samr_cache(&dinfo->sid); result = NT_STATUS_OK; @@ -5599,7 +5513,7 @@ NTSTATUS _samr_QueryDomainInfo2(pipes_struct *p, NTSTATUS _samr_SetDomainInfo(pipes_struct *p, struct samr_SetDomainInfo *r) { - struct samr_info *info = NULL; + struct samr_domain_info *dinfo; time_t u_expire, u_min_age; time_t u_logout; time_t u_lock_duration, u_reset_time; @@ -5607,10 +5521,6 @@ NTSTATUS _samr_SetDomainInfo(pipes_struct *p, DEBUG(5,("_samr_SetDomainInfo: %d\n", __LINE__)); - /* find the policy handle. open a policy on it. */ - if (!find_policy_by_hnd(p, r->in.domain_handle, (void **)(void *)&info)) - return NT_STATUS_INVALID_HANDLE; - /* We do have different access bits for info * levels here, but we're really just looking for * GENERIC_RIGHTS_DOMAIN_WRITE access. Unfortunately @@ -5618,12 +5528,12 @@ NTSTATUS _samr_SetDomainInfo(pipes_struct *p, * assume if we have SAMR_DOMAIN_ACCESS_SET_INFO_1 * set we are ok. */ - result = access_check_samr_function(info->acc_granted, - SAMR_DOMAIN_ACCESS_SET_INFO_1, - "_samr_SetDomainInfo"); - - if (!NT_STATUS_IS_OK(result)) + dinfo = policy_handle_find(p, r->in.domain_handle, + SAMR_DOMAIN_ACCESS_SET_INFO_1, NULL, + struct samr_domain_info, &result); + if (!NT_STATUS_IS_OK(result)) { return result; + } DEBUG(5,("_samr_SetDomainInfo: level: %d\n", r->in.level)); @@ -5676,7 +5586,7 @@ NTSTATUS _samr_SetDomainInfo(pipes_struct *p, NTSTATUS _samr_GetDisplayEnumerationIndex(pipes_struct *p, struct samr_GetDisplayEnumerationIndex *r) { - struct samr_info *info = NULL; + struct samr_domain_info *dinfo; uint32_t max_entries = (uint32_t) -1; uint32_t enum_context = 0; int i; @@ -5686,14 +5596,9 @@ NTSTATUS _samr_GetDisplayEnumerationIndex(pipes_struct *p, DEBUG(5,("_samr_GetDisplayEnumerationIndex: %d\n", __LINE__)); - /* find the policy handle. open a policy on it. */ - if (!find_policy_by_hnd(p, r->in.domain_handle, (void **)(void *)&info)) { - return NT_STATUS_INVALID_HANDLE; - } - - status = access_check_samr_function(info->acc_granted, - SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS, - "_samr_GetDisplayEnumerationIndex"); + dinfo = policy_handle_find(p, r->in.domain_handle, + SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS, NULL, + struct samr_domain_info, &status); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -5711,10 +5616,10 @@ NTSTATUS _samr_GetDisplayEnumerationIndex(pipes_struct *p, switch (r->in.level) { case 1: - if (info->disp_info->users == NULL) { - info->disp_info->users = pdb_search_users( - info->disp_info, ACB_NORMAL); - if (info->disp_info->users == NULL) { + if (dinfo->disp_info->users == NULL) { + dinfo->disp_info->users = pdb_search_users( + dinfo->disp_info, ACB_NORMAL); + if (dinfo->disp_info->users == NULL) { unbecome_root(); return NT_STATUS_ACCESS_DENIED; } @@ -5726,15 +5631,15 @@ NTSTATUS _samr_GetDisplayEnumerationIndex(pipes_struct *p, "using cached user enumeration at index %u\n", (unsigned int)enum_context)); } - num_account = pdb_search_entries(info->disp_info->users, + num_account = pdb_search_entries(dinfo->disp_info->users, enum_context, max_entries, &entries); break; case 2: - if (info->disp_info->machines == NULL) { - info->disp_info->machines = pdb_search_users( - info->disp_info, ACB_WSTRUST|ACB_SVRTRUST); - if (info->disp_info->machines == NULL) { + if (dinfo->disp_info->machines == NULL) { + dinfo->disp_info->machines = pdb_search_users( + dinfo->disp_info, ACB_WSTRUST|ACB_SVRTRUST); + if (dinfo->disp_info->machines == NULL) { unbecome_root(); return NT_STATUS_ACCESS_DENIED; } @@ -5746,15 +5651,15 @@ NTSTATUS _samr_GetDisplayEnumerationIndex(pipes_struct *p, "using cached machine enumeration at index %u\n", (unsigned int)enum_context)); } - num_account = pdb_search_entries(info->disp_info->machines, + num_account = pdb_search_entries(dinfo->disp_info->machines, enum_context, max_entries, &entries); break; case 3: - if (info->disp_info->groups == NULL) { - info->disp_info->groups = pdb_search_groups( - info->disp_info); - if (info->disp_info->groups == NULL) { + if (dinfo->disp_info->groups == NULL) { + dinfo->disp_info->groups = pdb_search_groups( + dinfo->disp_info); + if (dinfo->disp_info->groups == NULL) { unbecome_root(); return NT_STATUS_ACCESS_DENIED; } @@ -5766,7 +5671,7 @@ NTSTATUS _samr_GetDisplayEnumerationIndex(pipes_struct *p, "using cached group enumeration at index %u\n", (unsigned int)enum_context)); } - num_account = pdb_search_entries(info->disp_info->groups, + num_account = pdb_search_entries(dinfo->disp_info->groups, enum_context, max_entries, &entries); break; @@ -5779,7 +5684,7 @@ NTSTATUS _samr_GetDisplayEnumerationIndex(pipes_struct *p, unbecome_root(); /* Ensure we cache this enumeration. */ - set_disp_info_cache_timeout(info->disp_info, DISP_INFO_CACHE_TIMEOUT); + set_disp_info_cache_timeout(dinfo->disp_info, DISP_INFO_CACHE_TIMEOUT); DEBUG(10,("_samr_GetDisplayEnumerationIndex: looking for :%s\n", r->in.name->string)); diff --git a/source3/rpc_server/srv_spoolss_nt.c b/source3/rpc_server/srv_spoolss_nt.c index d114152f64..629e41c003 100644 --- a/source3/rpc_server/srv_spoolss_nt.c +++ b/source3/rpc_server/srv_spoolss_nt.c @@ -591,7 +591,8 @@ static bool open_printer_hnd(pipes_struct *p, struct policy_handle *hnd, new_printer->access_granted = access_granted; - DEBUG(5, ("%d printer handles active\n", (int)p->pipe_handles->count )); + DEBUG(5, ("%d printer handles active\n", + (int)num_pipe_handles(p->pipe_handles))); return true; } @@ -4846,6 +4847,121 @@ static WERROR fill_printer_driver_info3(TALLOC_CTX *mem_ctx, } /******************************************************************** + * fill a spoolss_DriverInfo4 struct + ********************************************************************/ + +static WERROR fill_printer_driver_info4(TALLOC_CTX *mem_ctx, + struct spoolss_DriverInfo4 *r, + const NT_PRINTER_DRIVER_INFO_LEVEL *driver, + const char *servername) +{ + const char *cservername = canon_servername(servername); + + r->version = driver->info_3->cversion; + + r->driver_name = talloc_strdup(mem_ctx, driver->info_3->name); + W_ERROR_HAVE_NO_MEMORY(r->driver_name); + r->architecture = talloc_strdup(mem_ctx, driver->info_3->environment); + W_ERROR_HAVE_NO_MEMORY(r->architecture); + + if (strlen(driver->info_3->driverpath)) { + r->driver_path = talloc_asprintf(mem_ctx, "\\\\%s%s", + cservername, driver->info_3->driverpath); + } else { + r->driver_path = talloc_strdup(mem_ctx, ""); + } + W_ERROR_HAVE_NO_MEMORY(r->driver_path); + + if (strlen(driver->info_3->datafile)) { + r->data_file = talloc_asprintf(mem_ctx, "\\\\%s%s", + cservername, driver->info_3->datafile); + } else { + r->data_file = talloc_strdup(mem_ctx, ""); + } + W_ERROR_HAVE_NO_MEMORY(r->data_file); + + if (strlen(driver->info_3->configfile)) { + r->config_file = talloc_asprintf(mem_ctx, "\\\\%s%s", + cservername, driver->info_3->configfile); + } else { + r->config_file = talloc_strdup(mem_ctx, ""); + } + W_ERROR_HAVE_NO_MEMORY(r->config_file); + + if (strlen(driver->info_3->helpfile)) { + r->help_file = talloc_asprintf(mem_ctx, "\\\\%s%s", + cservername, driver->info_3->helpfile); + } else { + r->help_file = talloc_strdup(mem_ctx, ""); + } + W_ERROR_HAVE_NO_MEMORY(r->help_file); + + r->dependent_files = string_array_from_driver_info(mem_ctx, + driver->info_3->dependentfiles, + cservername); + + + r->monitor_name = talloc_strdup(mem_ctx, driver->info_3->monitorname); + W_ERROR_HAVE_NO_MEMORY(r->monitor_name); + r->default_datatype = talloc_strdup(mem_ctx, driver->info_3->defaultdatatype); + W_ERROR_HAVE_NO_MEMORY(r->default_datatype); + + r->previous_names = string_array_from_driver_info(mem_ctx, + NULL, + cservername); + + return WERR_OK; +} + +/******************************************************************** + * fill a spoolss_DriverInfo5 struct + ********************************************************************/ + +static WERROR fill_printer_driver_info5(TALLOC_CTX *mem_ctx, + struct spoolss_DriverInfo5 *r, + const NT_PRINTER_DRIVER_INFO_LEVEL *driver, + const char *servername) +{ + const char *cservername = canon_servername(servername); + + r->version = driver->info_3->cversion; + + r->driver_name = talloc_strdup(mem_ctx, driver->info_3->name); + W_ERROR_HAVE_NO_MEMORY(r->driver_name); + r->architecture = talloc_strdup(mem_ctx, driver->info_3->environment); + W_ERROR_HAVE_NO_MEMORY(r->architecture); + + if (strlen(driver->info_3->driverpath)) { + r->driver_path = talloc_asprintf(mem_ctx, "\\\\%s%s", + cservername, driver->info_3->driverpath); + } else { + r->driver_path = talloc_strdup(mem_ctx, ""); + } + W_ERROR_HAVE_NO_MEMORY(r->driver_path); + + if (strlen(driver->info_3->datafile)) { + r->data_file = talloc_asprintf(mem_ctx, "\\\\%s%s", + cservername, driver->info_3->datafile); + } else { + r->data_file = talloc_strdup(mem_ctx, ""); + } + W_ERROR_HAVE_NO_MEMORY(r->data_file); + + if (strlen(driver->info_3->configfile)) { + r->config_file = talloc_asprintf(mem_ctx, "\\\\%s%s", + cservername, driver->info_3->configfile); + } else { + r->config_file = talloc_strdup(mem_ctx, ""); + } + W_ERROR_HAVE_NO_MEMORY(r->config_file); + + r->driver_attributes = 0; + r->config_version = 0; + r->driver_version = 0; + + return WERR_OK; +} +/******************************************************************** * fill a spoolss_DriverInfo6 struct ********************************************************************/ @@ -4893,7 +5009,7 @@ static WERROR fill_printer_driver_info6(TALLOC_CTX *mem_ctx, } else { r->help_file = talloc_strdup(mem_ctx, ""); } - W_ERROR_HAVE_NO_MEMORY(r->config_file); + W_ERROR_HAVE_NO_MEMORY(r->help_file); r->monitor_name = talloc_strdup(mem_ctx, driver->info_3->monitorname); W_ERROR_HAVE_NO_MEMORY(r->monitor_name); @@ -6673,6 +6789,18 @@ static WERROR enumprinterdrivers_level(TALLOC_CTX *mem_ctx, result = fill_printer_driver_info3(info, &info[count+i].info3, &driver, servername); break; + case 4: + result = fill_printer_driver_info4(info, &info[count+i].info4, + &driver, servername); + break; + case 5: + result = fill_printer_driver_info5(info, &info[count+i].info5, + &driver, servername); + break; + case 6: + result = fill_printer_driver_info6(info, &info[count+i].info6, + &driver, servername); + break; default: result = WERR_UNKNOWN_LEVEL; break; @@ -6745,6 +6873,49 @@ static WERROR enumprinterdrivers_level3(TALLOC_CTX *mem_ctx, info_p, count); } +/**************************************************************************** + Enumerates all printer drivers at level 4. +****************************************************************************/ + +static WERROR enumprinterdrivers_level4(TALLOC_CTX *mem_ctx, + const char *servername, + const char *architecture, + union spoolss_DriverInfo **info_p, + uint32_t *count) +{ + return enumprinterdrivers_level(mem_ctx, servername, architecture, 4, + info_p, count); +} + +/**************************************************************************** + Enumerates all printer drivers at level 5. +****************************************************************************/ + +static WERROR enumprinterdrivers_level5(TALLOC_CTX *mem_ctx, + const char *servername, + const char *architecture, + union spoolss_DriverInfo **info_p, + uint32_t *count) +{ + return enumprinterdrivers_level(mem_ctx, servername, architecture, 5, + info_p, count); +} + +/**************************************************************************** + Enumerates all printer drivers at level 6. +****************************************************************************/ + +static WERROR enumprinterdrivers_level6(TALLOC_CTX *mem_ctx, + const char *servername, + const char *architecture, + union spoolss_DriverInfo **info_p, + uint32_t *count) +{ + return enumprinterdrivers_level(mem_ctx, servername, architecture, 6, + info_p, count); +} + + /**************************************************************** _spoolss_EnumPrinterDrivers ****************************************************************/ @@ -6789,6 +6960,21 @@ WERROR _spoolss_EnumPrinterDrivers(pipes_struct *p, r->in.environment, r->out.info, r->out.count); break; + case 4: + result = enumprinterdrivers_level4(p->mem_ctx, cservername, + r->in.environment, + r->out.info, r->out.count); + break; + case 5: + result = enumprinterdrivers_level5(p->mem_ctx, cservername, + r->in.environment, + r->out.info, r->out.count); + break; + case 6: + result = enumprinterdrivers_level6(p->mem_ctx, cservername, + r->in.environment, + r->out.info, r->out.count); + break; default: return WERR_UNKNOWN_LEVEL; } @@ -8111,7 +8297,7 @@ WERROR _spoolss_AddForm(pipes_struct *p, /* if the user is not root, doesn't have SE_PRINT_OPERATOR privilege, and not a printer admin, then fail */ - if ((p->server_info->utok.uid != 0) && + if ((p->server_info->utok.uid != sec_initial_uid()) && !user_has_privileges(p->server_info->ptok, &se_printop) && !token_contains_name_in_list(uidtoname(p->server_info->utok.uid), NULL, NULL, @@ -8135,7 +8321,9 @@ WERROR _spoolss_AddForm(pipes_struct *p, goto done; } + become_root(); write_ntforms(&list, count); + unbecome_root(); /* * ChangeID must always be set if this is a printer @@ -8168,6 +8356,7 @@ WERROR _spoolss_DeleteForm(pipes_struct *p, WERROR status = WERR_OK; NT_PRINTER_INFO_LEVEL *printer = NULL; SE_PRIV se_printop = SE_PRINT_OPERATOR; + bool ret = false; DEBUG(5,("_spoolss_DeleteForm\n")); @@ -8189,7 +8378,7 @@ WERROR _spoolss_DeleteForm(pipes_struct *p, goto done; } - if ((p->server_info->utok.uid != 0) && + if ((p->server_info->utok.uid != sec_initial_uid()) && !user_has_privileges(p->server_info->ptok, &se_printop) && !token_contains_name_in_list(uidtoname(p->server_info->utok.uid), NULL, NULL, @@ -8209,8 +8398,12 @@ WERROR _spoolss_DeleteForm(pipes_struct *p, count = get_ntforms(&list); - if ( !delete_a_form(&list, form_name, &count, &status )) + become_root(); + ret = delete_a_form(&list, form_name, &count, &status); + unbecome_root(); + if (ret == false) { goto done; + } /* * ChangeID must always be set if this is a printer @@ -8268,7 +8461,7 @@ WERROR _spoolss_SetForm(pipes_struct *p, /* if the user is not root, doesn't have SE_PRINT_OPERATOR privilege, and not a printer admin, then fail */ - if ((p->server_info->utok.uid != 0) && + if ((p->server_info->utok.uid != sec_initial_uid()) && !user_has_privileges(p->server_info->ptok, &se_printop) && !token_contains_name_in_list(uidtoname(p->server_info->utok.uid), NULL, NULL, @@ -8286,7 +8479,9 @@ WERROR _spoolss_SetForm(pipes_struct *p, count = get_ntforms(&list); update_a_form(&list, form, count); + become_root(); write_ntforms(&list, count); + unbecome_root(); /* * ChangeID must always be set if this is a printer |