diff options
author | Joseph Sutton <josephsutton@catalyst.net.nz> | 2022-12-02 10:49:20 +1300 |
---|---|---|
committer | Andrew Bartlett <abartlet@samba.org> | 2023-02-08 00:03:39 +0000 |
commit | e3fdb2d00152d86558a2ba29b92fd36440055461 (patch) | |
tree | 185f5cf6fa8b9c065116c9a18a71b5b118aff7a8 /auth | |
parent | 14d94460ca1ef22269373f36ec50df248aca9465 (diff) | |
download | samba-e3fdb2d00152d86558a2ba29b92fd36440055461.tar.gz |
s4:kdc: Add resource SID compression
The domain-local groups that are added to the PAC of a service ticket
are now, if the service doesn't disclaim support for SID compression,
placed into the resource groups structure in PAC_LOGON_INFO.
In a TGS exchange directed to a KDC, rather than to a service, the
resource groups structure is simply copied into the updated PAC without
any processing being done.
Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Diffstat (limited to 'auth')
-rw-r--r-- | auth/auth_sam_reply.c | 299 | ||||
-rw-r--r-- | auth/auth_sam_reply.h | 7 |
2 files changed, 267 insertions, 39 deletions
diff --git a/auth/auth_sam_reply.c b/auth/auth_sam_reply.c index b9d06161cb1..850ccae980b 100644 --- a/auth/auth_sam_reply.c +++ b/auth/auth_sam_reply.c @@ -25,6 +25,165 @@ #include "libcli/security/security.h" #include "auth/auth_sam_reply.h" +/* Returns true if this SID belongs in SamBaseInfo, otherwise false. */ +static bool is_base_sid(const struct auth_SidAttr *sid, + const struct dom_sid *domain_sid) +{ + if (sid->attrs & SE_GROUP_RESOURCE) { + /* + * Resource groups don't belong in the base + * RIDs, they're handled elsewhere. + */ + return false; + } + + /* + * This SID belongs in the base structure only if it's in the account's + * domain. + */ + return dom_sid_in_domain(domain_sid, &sid->sid); +} + +/* Stores a SID in a previously allocated array. */ +static NTSTATUS store_extra_sid(struct netr_SidAttr *sids, + uint32_t *sidcount, + const uint32_t allocated_sids, + const struct auth_SidAttr *sid) +{ + /* Check we aren't about to overflow our allocation. */ + if (*sidcount >= allocated_sids) { + return NT_STATUS_INVALID_PARAMETER; + } + + sids[*sidcount].sid = dom_sid_dup(sids, &sid->sid); + if (sids[*sidcount].sid == NULL) { + return NT_STATUS_NO_MEMORY; + } + sids[*sidcount].attributes = sid->attrs; + *sidcount += 1; + + return NT_STATUS_OK; +} + +/* + * Stores a resource SID in a previously allocated array, either Extra SIDs or + * Resource SIDs. Any SID within the domain of the first SID so added is stored + * there, while remaining SIDs are stored in Extra SIDs. + */ +static NTSTATUS store_resource_sid(struct netr_SidAttr *sids, + uint32_t *sidcount, + const uint32_t allocated_sids, + const struct auth_SidAttr *sid, + struct PAC_DOMAIN_GROUP_MEMBERSHIP *resource_groups, + const uint32_t allocated_resource_groups) +{ + NTSTATUS status; + + struct dom_sid *resource_domain = NULL; + uint32_t rid; + + if (resource_groups == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* Split the SID into domain and RID. */ + status = dom_sid_split_rid(resource_groups, &sid->sid, &resource_domain, &rid); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (resource_groups->domain_sid == NULL) { + /* + * There is no domain SID set. Set it to the domain of this SID. + */ + resource_groups->domain_sid = resource_domain; + } else { + /* + * A domain SID has already been set. Check whether this SID's + * domain matches. + * + * Assuming that resource SIDs have been obtained with + * dsdb_expand_nested_groups(), they should all be within the + * same domain (ours), so unless something has gone horribly + * wrong, we should always find that they match. + */ + bool match = dom_sid_equal(resource_groups->domain_sid, resource_domain); + talloc_free(resource_domain); + if (!match) { + /* + * It doesn't match, so we can't store this SID here. It + * will have to go in Extra SIDs. + */ + return store_extra_sid(sids, sidcount, allocated_sids, sid); + } + } + + /* Store the SID in Resource SIDs. */ + + /* Check we aren't about to overflow our allocation. */ + if (resource_groups->groups.count >= allocated_resource_groups) { + return NT_STATUS_INVALID_PARAMETER; + } + + resource_groups->groups.rids[resource_groups->groups.count].rid = rid; + resource_groups->groups.rids[resource_groups->groups.count].attributes = sid->attrs; + resource_groups->groups.count++; + + return NT_STATUS_OK; +} + +/* + * Stores a SID in a previously allocated array, or excludes it if we are not + * storing resource groups. It will be placed in either Extra SIDs or Resource + * SIDs, depending on which is appropriate. + */ +static NTSTATUS store_sid(struct netr_SidAttr *sids, + uint32_t *sidcount, + const uint32_t allocated_sids, + const struct auth_SidAttr *sid, + struct PAC_DOMAIN_GROUP_MEMBERSHIP *resource_groups, + const uint32_t allocated_resource_groups, + const enum auth_group_inclusion group_inclusion) +{ + /* See if it's a resource SID. */ + if (sid->attrs & SE_GROUP_RESOURCE) { + /* + * If this is the SID of a resource group, determine whether it + * should be included or filtered out. + */ + switch (group_inclusion) { + case AUTH_INCLUDE_RESOURCE_GROUPS: + /* Include this SID in Extra SIDs. */ + break; + case AUTH_INCLUDE_RESOURCE_GROUPS_COMPRESSED: + /* + * Try to include this SID in Resource Groups. If this + * can't be arranged, we shall fall back to Extra + * SIDs. + */ + return store_resource_sid(sids, + sidcount, + allocated_sids, + sid, + resource_groups, + allocated_resource_groups); + case AUTH_EXCLUDE_RESOURCE_GROUPS: + /* Ignore this SID. */ + return NT_STATUS_OK; + default: + /* This means we have a bug. */ + DBG_ERR("invalid group inclusion parameter: %u\n", group_inclusion); + return NT_STATUS_INVALID_PARAMETER; + } + } + + /* Just store the SID in Extra SIDs. */ + return store_extra_sid(sids, + sidcount, + allocated_sids, + sid); +} + static NTSTATUS auth_convert_user_info_dc_sambaseinfo(TALLOC_CTX *mem_ctx, const struct auth_user_info_dc *user_info_dc, struct netr_SamBaseInfo *sam) @@ -99,14 +258,9 @@ static NTSTATUS auth_convert_user_info_dc_sambaseinfo(TALLOC_CTX *mem_ctx, for (i=PRIMARY_GROUP_SID_INDEX; i<user_info_dc->num_sids; i++) { struct auth_SidAttr *group_sid = &user_info_dc->sids[i]; - if (group_sid->attrs & SE_GROUP_RESOURCE) { - /* - * Resource groups don't belong in the base - * RIDs, they're handled elsewhere. - */ - continue; - } - if (!dom_sid_in_domain(sam->domain_sid, &group_sid->sid)) { + + bool belongs_in_base = is_base_sid(group_sid, sam->domain_sid); + if (!belongs_in_base) { /* We handle this elsewhere */ continue; } @@ -140,62 +294,103 @@ static NTSTATUS auth_convert_user_info_dc_sambaseinfo(TALLOC_CTX *mem_ctx, return NT_STATUS_OK; } -/* Note that the validity of the _sam6 structure is only as long as - * the user_info_dc it was generated from */ +/* Note that the validity of the _sam6 and resource_groups structures is only as + * long as the user_info_dc it was generated from */ NTSTATUS auth_convert_user_info_dc_saminfo6(TALLOC_CTX *mem_ctx, const struct auth_user_info_dc *user_info_dc, enum auth_group_inclusion group_inclusion, - struct netr_SamInfo6 **_sam6) + struct netr_SamInfo6 **_sam6, + struct PAC_DOMAIN_GROUP_MEMBERSHIP **_resource_groups) { NTSTATUS status; struct netr_SamInfo6 *sam6 = NULL; + struct PAC_DOMAIN_GROUP_MEMBERSHIP *resource_groups = NULL; size_t i; + const uint32_t allocated_sids = user_info_dc->num_sids; + uint32_t allocated_resource_groups = 0; + sam6 = talloc_zero(mem_ctx, struct netr_SamInfo6); if (sam6 == NULL) { return NT_STATUS_NO_MEMORY; } + if (_resource_groups == NULL) { + if (group_inclusion == AUTH_INCLUDE_RESOURCE_GROUPS_COMPRESSED) { + DBG_ERR("_resource_groups parameter not provided to receive resource groups!\n"); + TALLOC_FREE(sam6); + return NT_STATUS_INVALID_PARAMETER; + } + } else if (group_inclusion == AUTH_INCLUDE_RESOURCE_GROUPS_COMPRESSED) { + *_resource_groups = NULL; + + /* Allocate resource groups structure. */ + resource_groups = talloc_zero(mem_ctx, struct PAC_DOMAIN_GROUP_MEMBERSHIP); + if (resource_groups == NULL) { + TALLOC_FREE(sam6); + return NT_STATUS_NO_MEMORY; + } + + /* + * Allocate enough space to store user_info_dc->num_sids + * RIDs in the worst case. + */ + allocated_resource_groups = user_info_dc->num_sids; + resource_groups->groups.rids = talloc_zero_array(resource_groups, + struct samr_RidWithAttribute, + allocated_resource_groups); + if (resource_groups->groups.rids == NULL) { + TALLOC_FREE(sam6); + TALLOC_FREE(resource_groups); + return NT_STATUS_NO_MEMORY; + } + } else { + /* No resource groups will be provided. */ + *_resource_groups = NULL; + } + status = auth_convert_user_info_dc_sambaseinfo(sam6, user_info_dc, &sam6->base); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(sam6); + TALLOC_FREE(resource_groups); return status; } - sam6->sids = talloc_array(sam6, struct netr_SidAttr, - user_info_dc->num_sids); + /* + * Allocate enough space to store user_info_dc->num_sids SIDs in the + * worst case. + */ + sam6->sids = talloc_zero_array(sam6, struct netr_SidAttr, + allocated_sids); if (sam6->sids == NULL) { TALLOC_FREE(sam6); + TALLOC_FREE(resource_groups); return NT_STATUS_NO_MEMORY; } /* We don't put the user and group SIDs in there */ for (i=2; i<user_info_dc->num_sids; i++) { - if (user_info_dc->sids[i].attrs & SE_GROUP_RESOURCE) { - /* - * If it's a resource group, check whether it should be - * included or filtered out. - */ - switch (group_inclusion) { - case AUTH_INCLUDE_RESOURCE_GROUPS: - /* Include it. */ - break; - case AUTH_EXCLUDE_RESOURCE_GROUPS: - /* Ignore it. */ - continue; - } - } else if (dom_sid_in_domain(sam6->base.domain_sid, &user_info_dc->sids[i].sid)) { + struct auth_SidAttr *group_sid = &user_info_dc->sids[i]; + bool belongs_in_base = is_base_sid(group_sid, sam6->base.domain_sid); + if (belongs_in_base) { + /* We already handled this in the base. */ continue; } - sam6->sids[sam6->sidcount].sid = dom_sid_dup(sam6->sids, &user_info_dc->sids[i].sid); - if (sam6->sids[sam6->sidcount].sid == NULL) { + + status = store_sid(sam6->sids, + &sam6->sidcount, + allocated_sids, + group_sid, + resource_groups, + allocated_resource_groups, + group_inclusion); + if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(sam6); - return NT_STATUS_NO_MEMORY; + TALLOC_FREE(resource_groups); + return status; } - sam6->sids[sam6->sidcount].attributes = user_info_dc->sids[i].attrs; - sam6->sidcount += 1; } if (sam6->sidcount) { sam6->base.user_flags |= NETLOGON_EXTRA_SIDS; @@ -208,6 +403,7 @@ NTSTATUS auth_convert_user_info_dc_saminfo6(TALLOC_CTX *mem_ctx, user_info_dc->info->dns_domain_name); if (sam6->dns_domainname.string == NULL) { TALLOC_FREE(sam6); + TALLOC_FREE(resource_groups); return NT_STATUS_NO_MEMORY; } } @@ -217,11 +413,19 @@ NTSTATUS auth_convert_user_info_dc_saminfo6(TALLOC_CTX *mem_ctx, user_info_dc->info->user_principal_name); if (sam6->principal_name.string == NULL) { TALLOC_FREE(sam6); + TALLOC_FREE(resource_groups); return NT_STATUS_NO_MEMORY; } } *_sam6 = sam6; + if (resource_groups != NULL) { + if (resource_groups->groups.count > 0) { + *_resource_groups = resource_groups; + } else { + TALLOC_FREE(resource_groups); + } + } return NT_STATUS_OK; } @@ -242,7 +446,8 @@ NTSTATUS auth_convert_user_info_dc_saminfo2(TALLOC_CTX *mem_ctx, } status = auth_convert_user_info_dc_saminfo6(sam2, user_info_dc, - group_inclusion, &sam6); + group_inclusion, &sam6, + NULL); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(sam2); return status; @@ -258,7 +463,8 @@ NTSTATUS auth_convert_user_info_dc_saminfo2(TALLOC_CTX *mem_ctx, NTSTATUS auth_convert_user_info_dc_saminfo3(TALLOC_CTX *mem_ctx, const struct auth_user_info_dc *user_info_dc, enum auth_group_inclusion group_inclusion, - struct netr_SamInfo3 **_sam3) + struct netr_SamInfo3 **_sam3, + struct PAC_DOMAIN_GROUP_MEMBERSHIP **_resource_groups) { NTSTATUS status; struct netr_SamInfo6 *sam6 = NULL; @@ -270,7 +476,8 @@ NTSTATUS auth_convert_user_info_dc_saminfo3(TALLOC_CTX *mem_ctx, } status = auth_convert_user_info_dc_saminfo6(sam3, user_info_dc, - group_inclusion, &sam6); + group_inclusion, &sam6, + _resource_groups); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(sam3); return status; @@ -579,11 +786,15 @@ NTSTATUS make_user_info_dc_netlogon_validation(TALLOC_CTX *mem_ctx, } /** - * Make a user_info_dc struct from the PAC_LOGON_INFO supplied in the krb5 logon + * Make a user_info_dc struct from the PAC_LOGON_INFO supplied in the krb5 + * logon. For group_inclusion, pass AUTH_INCLUDE_RESOURCE_GROUPS if SIDs from + * the resource groups are to be included in the resulting structure, and pass + * AUTH_EXCLUDE_RESOURCE_GROUPS otherwise. */ NTSTATUS make_user_info_dc_pac(TALLOC_CTX *mem_ctx, const struct PAC_LOGON_INFO *pac_logon_info, const struct PAC_UPN_DNS_INFO *pac_upn_dns_info, + const enum auth_group_inclusion group_inclusion, struct auth_user_info_dc **_user_info_dc) { uint32_t i; @@ -603,7 +814,21 @@ NTSTATUS make_user_info_dc_pac(TALLOC_CTX *mem_ctx, } if (pac_logon_info->info3.base.user_flags & NETLOGON_RESOURCE_GROUPS) { - rg = &pac_logon_info->resource_groups; + switch (group_inclusion) { + case AUTH_INCLUDE_RESOURCE_GROUPS: + /* Take resource groups from the PAC. */ + rg = &pac_logon_info->resource_groups; + break; + case AUTH_EXCLUDE_RESOURCE_GROUPS: + /* + * The PAC is from a TGT, or we don't want to process + * its resource groups. + */ + break; + default: + DBG_ERR("invalid group inclusion parameter: %u\n", group_inclusion); + return NT_STATUS_INVALID_PARAMETER; + } } if (rg != NULL && rg->groups.count > 0) { diff --git a/auth/auth_sam_reply.h b/auth/auth_sam_reply.h index 4eebf0b06e3..57a98249b08 100644 --- a/auth/auth_sam_reply.h +++ b/auth/auth_sam_reply.h @@ -48,7 +48,8 @@ struct auth_user_info *auth_user_info_copy(TALLOC_CTX *mem_ctx, NTSTATUS auth_convert_user_info_dc_saminfo6(TALLOC_CTX *mem_ctx, const struct auth_user_info_dc *user_info_dc, enum auth_group_inclusion group_inclusion, - struct netr_SamInfo6 **_sam6); + struct netr_SamInfo6 **_sam6, + struct PAC_DOMAIN_GROUP_MEMBERSHIP **_resource_groups); NTSTATUS auth_convert_user_info_dc_saminfo2(TALLOC_CTX *mem_ctx, const struct auth_user_info_dc *user_info_dc, enum auth_group_inclusion group_inclusion, @@ -56,7 +57,8 @@ NTSTATUS auth_convert_user_info_dc_saminfo2(TALLOC_CTX *mem_ctx, NTSTATUS auth_convert_user_info_dc_saminfo3(TALLOC_CTX *mem_ctx, const struct auth_user_info_dc *user_info_dc, enum auth_group_inclusion group_inclusion, - struct netr_SamInfo3 **_sam3); + struct netr_SamInfo3 **_sam3, + struct PAC_DOMAIN_GROUP_MEMBERSHIP **_resource_groups); /** * Make a user_info_dc struct from the info3 returned by a domain logon @@ -74,6 +76,7 @@ NTSTATUS make_user_info_dc_netlogon_validation(TALLOC_CTX *mem_ctx, NTSTATUS make_user_info_dc_pac(TALLOC_CTX *mem_ctx, const struct PAC_LOGON_INFO *pac_logon_info, const struct PAC_UPN_DNS_INFO *pac_upn_dns_info, + enum auth_group_inclusion group_inclusion, struct auth_user_info_dc **_user_info_dc); /* The following definitions come from auth/wbc_auth_util.c */ |