]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/negotiate/kerberos/negotiate_kerberos_pac.cc
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / auth / negotiate / kerberos / negotiate_kerberos_pac.cc
CommitLineData
ca02e0ec 1/*
b8ae064d 2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
ca02e0ec
AJ
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
4ebcf1ce
MM
9/*
10 * -----------------------------------------------------------------------------
11 *
12 * Author: Markus Moeller (markus_moeller at compuserve.com)
13 *
14 * Copyright (C) 2007 Markus Moeller. All rights reserved.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
29 *
30 * As a special exemption, M Moeller gives permission to link this program
31 * with MIT, Heimdal or other GSS/Kerberos libraries, and distribute
32 * the resulting executable, without including the source code for
33 * the Libraries in the source distribution.
34 *
35 * -----------------------------------------------------------------------------
36 */
37
38#include "squid.h"
602d9612 39#include "rfc1738.h"
4ebcf1ce
MM
40
41#include "negotiate_kerberos.h"
42
f95eb8d8 43#if HAVE_GSSAPI && HAVE_PAC_SUPPORT
4ebcf1ce
MM
44
45static int bpos;
46static krb5_data *ad_data;
47static unsigned char *p;
48
685277d8
MM
49extern int
50check_k5_err(krb5_context context, const char *function, krb5_error_code code);
4ebcf1ce
MM
51
52void
53align(int n)
54{
55 if ( bpos % n != 0 ) {
56 int al;
57 al = (bpos/n);
58 bpos = bpos+(bpos-n*al);
59 }
60}
61
62void
63getustr(RPC_UNICODE_STRING *string)
64{
65
66 string->length = (uint16_t)((p[bpos]<<0) | (p[bpos+1]<<8));
67 string->maxlength = (uint16_t)((p[bpos+2]<<0) | (p[bpos+2+1]<<8));
68 string->pointer = (uint32_t)((p[bpos+4]<<0) | (p[bpos+4+1]<<8) | (p[bpos+4+2]<<16) | (p[bpos+4+3]<<24));
69 bpos = bpos+8;
70
71}
72
73uint64_t
74get6byt_be(void)
75{
76 uint64_t var;
77
78 var = ((uint64_t)p[bpos+5]<<0) | ((uint64_t)p[bpos+4]<<8) | ((uint64_t)p[bpos+3]<<16) | ((uint64_t)p[bpos+2]<<24) | ((uint64_t)p[bpos+1]<<32) | ((uint64_t)p[bpos]<<40);
79 bpos = bpos+6;
80
81 return var;
82}
83
84uint32_t
85get4byt(void)
86{
87 uint32_t var;
88
89 var=(uint32_t)((p[bpos]<<0) | (p[bpos+1]<<8) | (p[bpos+2]<<16) | (p[bpos+3]<<24));
90 bpos = bpos+4;
91
92 return var;
93}
94
95uint16_t
96get2byt(void)
97{
98 uint16_t var;
99
100 var=(uint16_t)((p[bpos]<<0) | (p[bpos+1]<<8));
101 bpos = bpos+2;
102
103 return var;
104}
105
106uint8_t
107get1byt(void)
108{
109 uint8_t var;
110
111 var=(uint8_t)((p[bpos]<<0));
112 bpos = bpos+1;
113
114 return var;
115}
116
8b082ed9 117static char *
685277d8 118pstrcpy( char *src, const char *dst)
4ebcf1ce
MM
119{
120 if (dst) {
121 if (strlen(dst)>MAX_PAC_GROUP_SIZE)
aee3523a 122 return nullptr;
4ebcf1ce
MM
123 else
124 return strcpy(src,dst);
125 } else
126 return src;
127}
128
8b082ed9 129static char *
685277d8 130pstrcat( char *src, const char *dst)
4ebcf1ce
MM
131{
132 if (dst) {
133 if (strlen(src)+strlen(dst)+1>MAX_PAC_GROUP_SIZE)
aee3523a 134 return nullptr;
4ebcf1ce
MM
135 else
136 return strcat(src,dst);
137 } else
138 return src;
139}
140
141int
142checkustr(RPC_UNICODE_STRING *string)
143{
4ebcf1ce
MM
144
145 if (string->pointer != 0) {
4616aac3 146 uint32_t size,off,len;
4ebcf1ce
MM
147 align(4);
148 size = (uint32_t)((p[bpos]<<0) | (p[bpos+1]<<8) | (p[bpos+2]<<16) | (p[bpos+3]<<24));
149 bpos = bpos+4;
150 off = (uint32_t)((p[bpos]<<0) | (p[bpos+1]<<8) | (p[bpos+2]<<16) | (p[bpos+3]<<24));
151 bpos = bpos+4;
152 len = (uint32_t)((p[bpos]<<0) | (p[bpos+1]<<8) | (p[bpos+2]<<16) | (p[bpos+3]<<24));
153 bpos = bpos+4;
154 if (len > size || off != 0 ||
155 string->length > string->maxlength || len != string->length/2) {
156 debug((char *) "%s| %s: ERROR: RPC_UNICODE_STRING encoding error => size: %d len: %d/%d maxlength: %d offset: %d\n",
157 LogTime(), PROGRAM, size, len, string->length, string->maxlength, off);
158 return -1;
159 }
160 /* UNICODE string */
161 bpos = bpos+string->length;
162 }
163 return 0;
164}
165
166char **
167getgids(char **Rids, uint32_t GroupIds, uint32_t GroupCount)
168{
169 if (GroupIds!= 0) {
170 uint32_t ngroup;
4ebcf1ce
MM
171 int l;
172
173 align(4);
174 ngroup = get4byt();
175 if ( ngroup != GroupCount) {
176 debug((char *) "%s| %s: ERROR: Group encoding error => GroupCount: %d Array size: %d\n",
177 LogTime(), PROGRAM, GroupCount, ngroup);
aee3523a 178 return nullptr;
4ebcf1ce
MM
179 }
180 debug((char *) "%s| %s: INFO: Found %d rids\n", LogTime(), PROGRAM, GroupCount);
181
182 Rids=(char **)xcalloc(GroupCount*sizeof(char*),1);
183 for ( l=0; l<(int)GroupCount; l++) {
4616aac3 184 uint32_t sauth;
4ebcf1ce
MM
185 Rids[l]=(char *)xcalloc(4*sizeof(char),1);
186 memcpy((void *)Rids[l],(void *)&p[bpos],4);
187 sauth = get4byt();
188 debug((char *) "%s| %s: Info: Got rid: %u\n", LogTime(), PROGRAM, sauth);
189 /* attribute */
190 bpos = bpos+4;
191 }
192 }
193 return Rids;
194}
195
196char *
197getdomaingids(char *ad_groups, uint32_t DomainLogonId, char **Rids, uint32_t GroupCount)
198{
4616aac3
MM
199 if (!ad_groups) {
200 debug((char *) "%s| %s: ERR: No space to store groups\n",
201 LogTime(), PROGRAM);
aee3523a 202 return nullptr;
4616aac3
MM
203 }
204
4ebcf1ce 205 if (DomainLogonId!= 0) {
4ebcf1ce
MM
206 uint8_t rev;
207 uint64_t idauth;
4ebcf1ce
MM
208 char dli[256];
209 char *ag;
4ebcf1ce
MM
210 int l;
211
212 align(4);
213
4474192d
AJ
214 uint32_t nauth = get4byt();
215
216 // check if nauth math will produce invalid length values on 32-bit
217 static uint32_t maxGidCount = (UINT32_MAX-1-1-6)/4;
218 if (nauth > maxGidCount) {
219 debug((char *) "%s| %s: ERROR: Too many groups ! count > %d : %s\n",
220 LogTime(), PROGRAM, maxGidCount, ad_groups);
aee3523a 221 return nullptr;
4474192d
AJ
222 }
223 size_t length = 1+1+6+nauth*4;
4ebcf1ce
MM
224
225 /* prepend rids with DomainID */
4ebcf1ce
MM
226 for (l=0; l<(int)GroupCount; l++) {
227 ag=(char *)xcalloc((length+4)*sizeof(char),1);
228 memcpy((void *)ag,(const void*)&p[bpos],1);
229 memcpy((void *)&ag[1],(const void*)&p[bpos+1],1);
230 ag[1] = ag[1]+1;
231 memcpy((void *)&ag[2],(const void*)&p[bpos+2],6+nauth*4);
232 memcpy((void *)&ag[length],(const void*)Rids[l],4);
233 if (l==0) {
685277d8 234 if (!pstrcpy(ad_groups,"group=")) {
4ebcf1ce
MM
235 debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n",
236 LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
237 }
238 } else {
685277d8 239 if (!pstrcat(ad_groups," group=")) {
4ebcf1ce
MM
240 debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n",
241 LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
242 }
243 }
aadbbd7d
AJ
244 struct base64_encode_ctx ctx;
245 base64_encode_init(&ctx);
786ee90a 246 const uint32_t expectedSz = base64_encode_len(length+4) +1 /* terminator */;
1d11e9b3 247 char *b64buf = static_cast<char *>(xcalloc(expectedSz, 1));
aadbbd7d
AJ
248 size_t blen = base64_encode_update(&ctx, b64buf, length+4, reinterpret_cast<uint8_t*>(ag));
249 blen += base64_encode_final(&ctx, b64buf+blen);
786ee90a 250 b64buf[expectedSz-1] = '\0';
1d11e9b3 251 if (!pstrcat(ad_groups, b64buf)) {
4ebcf1ce
MM
252 debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n",
253 LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
254 }
aadbbd7d 255 xfree(b64buf);
4ebcf1ce
MM
256 xfree(ag);
257 }
258
259 /* mainly for debug only */
260 rev = get1byt();
261 bpos = bpos + 1; /*nsub*/
262 idauth = get6byt_be();
263
264 snprintf(dli,sizeof(dli),"S-%d-%lu",rev,(long unsigned int)idauth);
265 for ( l=0; l<(int)nauth; l++ ) {
4616aac3 266 uint32_t sauth;
4ebcf1ce
MM
267 sauth = get4byt();
268 snprintf((char *)&dli[strlen(dli)],sizeof(dli)-strlen(dli),"-%u",sauth);
269 }
270 debug((char *) "%s| %s: INFO: Got DomainLogonId %s\n", LogTime(), PROGRAM, dli);
271 }
272 return ad_groups;
273}
274
275char *
276getextrasids(char *ad_groups, uint32_t ExtraSids, uint32_t SidCount)
277{
278 if (ExtraSids!= 0) {
279 uint32_t ngroup;
280 uint32_t *pa;
281 char *ag;
4ebcf1ce
MM
282 int l;
283
284 align(4);
285 ngroup = get4byt();
286 if ( ngroup != SidCount) {
287 debug((char *) "%s| %s: ERROR: Group encoding error => SidCount: %d Array size: %d\n",
288 LogTime(), PROGRAM, SidCount, ngroup);
aee3523a 289 return nullptr;
4ebcf1ce
MM
290 }
291 debug((char *) "%s| %s: INFO: Found %d ExtraSIDs\n", LogTime(), PROGRAM, SidCount);
292
293 pa=(uint32_t *)xmalloc(SidCount*sizeof(uint32_t));
294 for ( l=0; l < (int)SidCount; l++ ) {
295 pa[l] = get4byt();
296 bpos = bpos+4; /* attr */
297 }
298
299 for ( l=0; l<(int)SidCount; l++ ) {
300 char es[256];
4ebcf1ce
MM
301
302 if (pa[l] != 0) {
4616aac3
MM
303 uint8_t rev;
304 uint64_t idauth;
305
4474192d
AJ
306 uint32_t nauth = get4byt();
307
308 // check if nauth math will produce invalid length values on 32-bit
309 static uint32_t maxGidCount = (UINT32_MAX-1-1-6)/4;
310 if (nauth > maxGidCount) {
311 debug((char *) "%s| %s: ERROR: Too many extra groups ! count > %d : %s\n",
312 LogTime(), PROGRAM, maxGidCount, ad_groups);
313 xfree(pa);
aee3523a 314 return nullptr;
4474192d 315 }
4ebcf1ce 316
4474192d 317 size_t length = 1+1+6+nauth*4;
4ebcf1ce
MM
318 ag = (char *)xcalloc((length)*sizeof(char),1);
319 memcpy((void *)ag,(const void*)&p[bpos],length);
320 if (!ad_groups) {
4616aac3
MM
321 debug((char *) "%s| %s: ERR: No space to store groups\n",
322 LogTime(), PROGRAM);
323 xfree(pa);
324 xfree(ag);
aee3523a 325 return nullptr;
4ebcf1ce 326 } else {
685277d8 327 if (!pstrcat(ad_groups," group=")) {
4ebcf1ce
MM
328 debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n",
329 LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
330 }
331 }
aadbbd7d
AJ
332
333 struct base64_encode_ctx ctx;
334 base64_encode_init(&ctx);
786ee90a 335 const uint32_t expectedSz = base64_encode_len(length) +1 /* terminator */;
1d11e9b3 336 char *b64buf = static_cast<char *>(xcalloc(expectedSz, 1));
aadbbd7d
AJ
337 size_t blen = base64_encode_update(&ctx, b64buf, length, reinterpret_cast<uint8_t*>(ag));
338 blen += base64_encode_final(&ctx, b64buf+blen);
786ee90a 339 b64buf[expectedSz-1] = '\0';
aadbbd7d 340 if (!pstrcat(ad_groups, reinterpret_cast<char*>(b64buf))) {
4ebcf1ce
MM
341 debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n",
342 LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
343 }
aadbbd7d 344 xfree(b64buf);
4ebcf1ce
MM
345 xfree(ag);
346
347 rev = get1byt();
348 bpos = bpos + 1; /* nsub */
349 idauth = get6byt_be();
350
351 snprintf(es,sizeof(es),"S-%d-%lu",rev,(long unsigned int)idauth);
4616aac3
MM
352 for (int k=0; k<(int)nauth; k++ ) {
353 uint32_t sauth;
4ebcf1ce
MM
354 sauth = get4byt();
355 snprintf((char *)&es[strlen(es)],sizeof(es)-strlen(es),"-%u",sauth);
356 }
357 debug((char *) "%s| %s: INFO: Got ExtraSid %s\n", LogTime(), PROGRAM, es);
358 }
359 }
360 xfree(pa);
361 }
362 return ad_groups;
363}
364
365char *
366get_ad_groups(char *ad_groups, krb5_context context, krb5_pac pac)
367{
368 krb5_error_code ret;
369 RPC_UNICODE_STRING EffectiveName;
370 RPC_UNICODE_STRING FullName;
371 RPC_UNICODE_STRING LogonScript;
372 RPC_UNICODE_STRING ProfilePath;
373 RPC_UNICODE_STRING HomeDirectory;
374 RPC_UNICODE_STRING HomeDirectoryDrive;
375 RPC_UNICODE_STRING LogonServer;
376 RPC_UNICODE_STRING LogonDomainName;
377 uint32_t GroupCount=0;
378 uint32_t GroupIds=0;
379 uint32_t LogonDomainId=0;
380 uint32_t SidCount=0;
381 uint32_t ExtraSids=0;
382 /*
383 uint32_t ResourceGroupDomainSid=0;
384 uint32_t ResourceGroupCount=0;
385 uint32_t ResourceGroupIds=0;
386 */
aee3523a 387 char **Rids=nullptr;
4ebcf1ce
MM
388 int l=0;
389
4616aac3
MM
390 if (!ad_groups) {
391 debug((char *) "%s| %s: ERR: No space to store groups\n",
085c4f21 392 LogTime(), PROGRAM);
aee3523a 393 return nullptr;
4616aac3
MM
394 }
395
1a22a39e 396 ad_data = (krb5_data *)xcalloc(1,sizeof(krb5_data));
4ebcf1ce
MM
397
398#define KERB_LOGON_INFO 1
399 ret = krb5_pac_get_buffer(context, pac, KERB_LOGON_INFO, ad_data);
400 if (check_k5_err(context, "krb5_pac_get_buffer", ret))
401 goto k5clean;
402
403 p = (unsigned char *)ad_data->data;
404
61beade2 405 debug((char *) "%s| %s: INFO: Got PAC data of length %d\n",
4ebcf1ce
MM
406 LogTime(), PROGRAM, (int)ad_data->length);
407
408 /* Skip 16 bytes icommon RPC header
409 * Skip 4 bytes RPC unique pointer referent
410 * http://msdn.microsoft.com/en-gb/library/cc237933.aspx
411 */
412 /* Some data are pointers to data which follows the main KRB5 LOGON structure =>
413 * So need to read the data
414 * some logical consistency checks are done when analysineg the pointer data
415 */
416 bpos = 20;
417 /* 8 bytes LogonTime
418 * 8 bytes LogoffTime
419 * 8 bytes KickOffTime
420 * 8 bytes PasswordLastSet
421 * 8 bytes PasswordCanChange
422 * 8 bytes PasswordMustChange
423 */
424 bpos = bpos+48;
425 getustr(&EffectiveName);
426 getustr(&FullName);
427 getustr(&LogonScript);
428 getustr(&ProfilePath);
429 getustr(&HomeDirectory);
430 getustr(&HomeDirectoryDrive);
431 /* 2 bytes LogonCount
432 * 2 bytes BadPasswordCount
433 * 4 bytes UserID
434 * 4 bytes PrimaryGroupId
435 */
436 bpos = bpos+12;
437 GroupCount = get4byt();
438 GroupIds = get4byt();
439 /* 4 bytes UserFlags
440 * 16 bytes UserSessionKey
441 */
442 bpos = bpos+20;
443 getustr(&LogonServer);
444 getustr(&LogonDomainName);
445 LogonDomainId = get4byt();
446 /* 8 bytes Reserved1
447 * 4 bytes UserAccountControl
448 * 4 bytes SubAuthStatus
449 * 8 bytes LastSuccessfullLogon
450 * 8 bytes LastFailedLogon
451 * 4 bytes FailedLogonCount
452 * 4 bytes Reserved2
453 */
454 bpos = bpos+40;
455 SidCount = get4byt();
456 ExtraSids = get4byt();
457 /* 4 bytes ResourceGroupDomainSid
458 * 4 bytes ResourceGroupCount
459 * 4 bytes ResourceGroupIds
460 */
461 bpos = bpos+12;
462 /*
463 * Read all data from structure => Now check pointers
464 */
465 if (checkustr(&EffectiveName)<0)
466 goto k5clean;
467 if (checkustr(&FullName)<0)
468 goto k5clean;
469 if (checkustr(&LogonScript)<0)
470 goto k5clean;
471 if (checkustr(&ProfilePath)<0)
472 goto k5clean;
473 if (checkustr(&HomeDirectory)<0)
474 goto k5clean;
475 if (checkustr(&HomeDirectoryDrive)<0)
476 goto k5clean;
477 Rids = getgids(Rids,GroupIds,GroupCount);
478 if (checkustr(&LogonServer)<0)
479 goto k5clean;
480 if (checkustr(&LogonDomainName)<0)
481 goto k5clean;
482 ad_groups = getdomaingids(ad_groups,LogonDomainId,Rids,GroupCount);
aee3523a 483 if ((ad_groups = getextrasids(ad_groups,ExtraSids,SidCount))==nullptr)
4ebcf1ce
MM
484 goto k5clean;
485
486 debug((char *) "%s| %s: INFO: Read %d of %d bytes \n", LogTime(), PROGRAM, bpos, (int)ad_data->length);
487 if (Rids) {
488 for ( l=0; l<(int)GroupCount; l++) {
489 xfree(Rids[l]);
490 }
491 xfree(Rids);
492 }
493 krb5_free_data(context, ad_data);
494 return ad_groups;
495k5clean:
496 if (Rids) {
497 for ( l=0; l<(int)GroupCount; l++) {
498 xfree(Rids[l]);
499 }
500 xfree(Rids);
501 }
502 krb5_free_data(context, ad_data);
aee3523a 503 return nullptr;
4ebcf1ce
MM
504}
505#endif
f53969cc 506