#if HAVE_GSSAPI && HAVE_PAC_SUPPORT
+#define LOGON_EXTRA_SIDS 0x0020
+#define LOGON_RESOURCE_GROUPS 0x0200
+
static int bpos;
static krb5_data *ad_data;
static unsigned char *p;
return ad_groups;
}
+static char *
+get_resource_group_domain_sid(const uint32_t ResourceGroupDomainSid, size_t &length)
+{
+ if (ResourceGroupDomainSid != 0) {
+ uint8_t rev;
+ uint64_t idauth;
+ char dli[256];
+
+ align(4);
+
+ // ResourceGroupDomainSid structure:
+ // 4 bytes nauth
+ // 1 byte revision = 1
+ // 1 byte nsub (it is equal to the number of dashes minus two)
+ // 6 bytes idauth (for NT Authority it is 5)
+ // 4 bytes sauth1
+ // ... nauth timss
+ // 4 bytes sauthN
+
+ uint32_t nauth = get4byt();
+
+ // check if nauth math will produce invalid length values on 32-bit
+ static uint32_t maxGidCount = (UINT32_MAX - 4 - 1 - 1 - 6)/4;
+ if (nauth > maxGidCount) {
+ debug((char *) "%s| %s: ERROR: Too many subAuths in the ResourceGroupDomainSID: nauth = %d > %d\n",
+ LogTime(), PROGRAM, nauth, maxGidCount);
+ return nullptr;
+ }
+
+ // length = revision[1byte]+nsub[1byte]+idauth[6bytes]+nauth*sauth[4bytes]
+ length = 1 + 1 + 6 + nauth*4;
+
+ auto sid = static_cast<char *>(xcalloc(length, 1));
+ // 1 byte revision
+ // 1 byte nsub
+ // 6 bytes+nauth*4bytes idauth+sauths
+ memcpy((void *)&sid[0], (const void*)&p[bpos], 1);
+ memcpy((void *)&sid[1], (const void*)&p[bpos+1], 1);
+ sid[1]++; // ++ as it will be used in a rid concatenation
+ memcpy((void *)&sid[2], (const void*)&p[bpos+2], 6 + nauth*4);
+
+ /* mainly for debug only */
+ rev = get1byt();
+ bpos = bpos + 1; /* nsub */
+ idauth = get6byt_be();
+
+ int rv = snprintf(dli, sizeof(dli), "S-%d-%lu", rev, (long unsigned int)idauth);
+ assert(rv > 0);
+ for (int l=0; l<(int)nauth; l++) {
+ uint32_t sauth;
+ sauth = get4byt();
+ rv = snprintf((char *)&dli[strlen(dli)], sizeof(dli) - strlen(dli), "-%u", sauth);
+ assert(rv > 0);
+ }
+ debug((char *) "%s| %s: INFO: Got ResourceGroupDomainSid %s\n", LogTime(), PROGRAM, dli);
+ return sid;
+ }
+
+ length = 0;
+ return nullptr;
+}
+
+static bool
+get_resource_groups(char *ad_groups, uint32_t ResourceGroupDomainSid, uint32_t ResourceGroupIds, uint32_t ResourceGroupCount)
+{
+ if (!ad_groups) {
+ debug((char *) "%s| %s: ERR: No space to store resource groups\n",
+ LogTime(), PROGRAM);
+ return false;
+ }
+
+ size_t group_domain_sid_len = 0;
+ const auto resource_group_domain_sid = get_resource_group_domain_sid(ResourceGroupDomainSid, group_domain_sid_len);
+ if (!resource_group_domain_sid)
+ return false;
+
+ if (ResourceGroupIds != 0) {
+ align(4);
+ uint32_t ngroup = get4byt();
+ if (ngroup != ResourceGroupCount) {
+ debug((char *) "%s| %s: ERROR: Group encoding error => ResourceGroupCount: %d != Array size: %d\n",
+ LogTime(), PROGRAM, ResourceGroupCount, ngroup);
+ xfree(resource_group_domain_sid);
+ return false;
+ }
+ debug((char *) "%s| %s: INFO: Found %d Resource Group rids\n", LogTime(), PROGRAM, ResourceGroupCount);
+
+ // prepare a group template which begins with the resource_group_domain_sid
+ size_t length = group_domain_sid_len + 4; // +4 for a rid concatenation
+ auto *st = static_cast<char *>(xcalloc(length, 1));
+
+ memcpy((void *)st, (const void*)resource_group_domain_sid, group_domain_sid_len); // template
+
+ for (int l=0; l < (int)ResourceGroupCount; l++) {
+ uint32_t sauth;
+ memcpy((void *)&st[group_domain_sid_len], (const void*)&p[bpos], 4); // rid concatenation
+
+ if (!pstrcat(ad_groups, " group=")) {
+ debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n",
+ LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
+ }
+
+ struct base64_encode_ctx ctx;
+ base64_encode_init(&ctx);
+ const uint32_t expectedSz = base64_encode_len(length) + 1 /* terminator */;
+ char *b64buf = static_cast<char *>(xcalloc(expectedSz, 1));
+ size_t blen = base64_encode_update(&ctx, b64buf, length, reinterpret_cast<uint8_t*>(st));
+ blen += base64_encode_final(&ctx, b64buf + blen);
+ b64buf[expectedSz - 1] = '\0';
+ if (!pstrcat(ad_groups, reinterpret_cast<char*>(b64buf))) {
+ debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n",
+ LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
+ }
+ xfree(b64buf);
+
+ sauth = get4byt();
+ debug((char *) "%s| %s: Info: Got rid: %u\n", LogTime(), PROGRAM, sauth);
+ /* attribute */
+ bpos = bpos + 4;
+ }
+
+ xfree(st);
+ }
+
+ xfree(resource_group_domain_sid);
+ return true;
+}
+
char *
get_ad_groups(char *ad_groups, krb5_context context, krb5_pac pac)
{
uint32_t GroupIds=0;
uint32_t LogonDomainId=0;
uint32_t SidCount=0;
+ uint32_t UserFlags=0;
uint32_t ExtraSids=0;
- /*
uint32_t ResourceGroupDomainSid=0;
uint32_t ResourceGroupCount=0;
uint32_t ResourceGroupIds=0;
- */
char **Rids=nullptr;
int l=0;
bpos = bpos+12;
GroupCount = get4byt();
GroupIds = get4byt();
- /* 4 bytes UserFlags
- * 16 bytes UserSessionKey
- */
- bpos = bpos+20;
+ UserFlags = get4byt();
+ /// 16 bytes UserSessionKey
+ bpos = bpos+16;
getustr(&LogonServer);
getustr(&LogonDomainName);
LogonDomainId = get4byt();
bpos = bpos+40;
SidCount = get4byt();
ExtraSids = get4byt();
- /* 4 bytes ResourceGroupDomainSid
- * 4 bytes ResourceGroupCount
- * 4 bytes ResourceGroupIds
- */
- bpos = bpos+12;
+
+ ResourceGroupDomainSid = get4byt();
+ ResourceGroupCount = get4byt();
+ ResourceGroupIds = get4byt();
+
/*
* Read all data from structure => Now check pointers
*/
if (checkustr(&LogonDomainName)<0)
goto k5clean;
ad_groups = getdomaingids(ad_groups,LogonDomainId,Rids,GroupCount);
- if ((ad_groups = getextrasids(ad_groups,ExtraSids,SidCount))==nullptr)
- goto k5clean;
+
+ // https://learn.microsoft.com/en-us/previous-versions/aa302203(v=msdn.10)?redirectedfrom=MSDN#top-level-pac-structure
+ if ((UserFlags&LOGON_EXTRA_SIDS) != 0) {
+ // EXTRA_SIDS structures are present and valid
+ debug((char *) "%s| %s: Info: EXTRA_SIDS are present\n", LogTime(), PROGRAM);
+ if ((ad_groups = getextrasids(ad_groups,ExtraSids,SidCount)) == nullptr)
+ goto k5clean;
+ }
+
+ if ((UserFlags&LOGON_RESOURCE_GROUPS) != 0 && ResourceGroupDomainSid && ResourceGroupIds && ResourceGroupCount) {
+ // RESOURCE_GROUPS structures are present and valid
+ debug((char *) "%s| %s: Info: RESOURCE_GROUPS are present\n", LogTime(), PROGRAM);
+ if (!get_resource_groups(ad_groups, ResourceGroupDomainSid, ResourceGroupIds, ResourceGroupCount))
+ goto k5clean;
+ }
debug((char *) "%s| %s: INFO: Read %d of %d bytes \n", LogTime(), PROGRAM, bpos, (int)ad_data->length);
+
if (Rids) {
for ( l=0; l<(int)GroupCount; l++) {
xfree(Rids[l]);
}
krb5_free_data(context, ad_data);
return ad_groups;
+
k5clean:
if (Rids) {
for ( l=0; l<(int)GroupCount; l++) {