negotiate_kerberos_pac.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
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
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"
39#include "rfc1738.h"
40
41#include "negotiate_kerberos.h"
42
43#if HAVE_GSSAPI && HAVE_PAC_SUPPORT
44
45static int bpos;
46static krb5_data *ad_data;
47static unsigned char *p;
48
49extern int
50check_k5_err(krb5_context context, const char *function, krb5_error_code code);
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
117static char *
118pstrcpy( char *src, const char *dst)
119{
120 if (dst) {
121 if (strlen(dst)>MAX_PAC_GROUP_SIZE)
122 return nullptr;
123 else
124 return strcpy(src,dst);
125 } else
126 return src;
127}
128
129static char *
130pstrcat( char *src, const char *dst)
131{
132 if (dst) {
133 if (strlen(src)+strlen(dst)+1>MAX_PAC_GROUP_SIZE)
134 return nullptr;
135 else
136 return strcat(src,dst);
137 } else
138 return src;
139}
140
141int
142checkustr(RPC_UNICODE_STRING *string)
143{
144
145 if (string->pointer != 0) {
146 uint32_t size,off,len;
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;
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);
178 return nullptr;
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++) {
184 uint32_t sauth;
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{
199 if (!ad_groups) {
200 debug((char *) "%s| %s: ERR: No space to store groups\n",
201 LogTime(), PROGRAM);
202 return nullptr;
203 }
204
205 if (DomainLogonId!= 0) {
206 uint8_t rev;
207 uint64_t idauth;
208 char dli[256];
209 char *ag;
210 int l;
211
212 align(4);
213
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);
221 return nullptr;
222 }
223 size_t length = 1+1+6+nauth*4;
224
225 /* prepend rids with DomainID */
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) {
234 if (!pstrcpy(ad_groups,"group=")) {
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 {
239 if (!pstrcat(ad_groups," group=")) {
240 debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n",
241 LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
242 }
243 }
244 struct base64_encode_ctx ctx;
245 base64_encode_init(&ctx);
246 const uint32_t expectedSz = base64_encode_len(length+4) +1 /* terminator */;
247 char *b64buf = static_cast<char *>(xcalloc(expectedSz, 1));
248 size_t blen = base64_encode_update(&ctx, b64buf, length+4, reinterpret_cast<uint8_t*>(ag));
249 blen += base64_encode_final(&ctx, b64buf+blen);
250 b64buf[expectedSz-1] = '\0';
251 if (!pstrcat(ad_groups, b64buf)) {
252 debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n",
253 LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
254 }
255 xfree(b64buf);
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++ ) {
266 uint32_t sauth;
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;
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);
289 return nullptr;
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];
301
302 if (pa[l] != 0) {
303 uint8_t rev;
304 uint64_t idauth;
305
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);
314 return nullptr;
315 }
316
317 size_t length = 1+1+6+nauth*4;
318 ag = (char *)xcalloc((length)*sizeof(char),1);
319 memcpy((void *)ag,(const void*)&p[bpos],length);
320 if (!ad_groups) {
321 debug((char *) "%s| %s: ERR: No space to store groups\n",
322 LogTime(), PROGRAM);
323 xfree(pa);
324 xfree(ag);
325 return nullptr;
326 } else {
327 if (!pstrcat(ad_groups," group=")) {
328 debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n",
329 LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
330 }
331 }
332
333 struct base64_encode_ctx ctx;
334 base64_encode_init(&ctx);
335 const uint32_t expectedSz = base64_encode_len(length) +1 /* terminator */;
336 char *b64buf = static_cast<char *>(xcalloc(expectedSz, 1));
337 size_t blen = base64_encode_update(&ctx, b64buf, length, reinterpret_cast<uint8_t*>(ag));
338 blen += base64_encode_final(&ctx, b64buf+blen);
339 b64buf[expectedSz-1] = '\0';
340 if (!pstrcat(ad_groups, reinterpret_cast<char*>(b64buf))) {
341 debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n",
342 LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups);
343 }
344 xfree(b64buf);
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);
352 for (int k=0; k<(int)nauth; k++ ) {
353 uint32_t sauth;
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 */
387 char **Rids=nullptr;
388 int l=0;
389
390 if (!ad_groups) {
391 debug((char *) "%s| %s: ERR: No space to store groups\n",
392 LogTime(), PROGRAM);
393 return nullptr;
394 }
395
396 ad_data = (krb5_data *)xcalloc(1,sizeof(krb5_data));
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
405 debug((char *) "%s| %s: INFO: Got PAC data of length %d\n",
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);
483 if ((ad_groups = getextrasids(ad_groups,ExtraSids,SidCount))==nullptr)
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);
503 return nullptr;
504}
505#endif
506
int size
Definition: ModDevPoll.cc:75
#define PROGRAM
Definition: support.h:166
const char * LogTime(void)
void base64_encode_init(struct base64_encode_ctx *ctx)
Definition: base64.c:232
size_t base64_encode_update(struct base64_encode_ctx *ctx, char *dst, size_t length, const uint8_t *src)
Definition: base64.c:265
size_t base64_encode_final(struct base64_encode_ctx *ctx, char *dst)
Definition: base64.c:308
#define base64_encode_len(length)
Definition: base64.h:169
void debug(const char *format,...)
Definition: debug.cc:19
int check_k5_err(krb5_context context, const char *msg, krb5_error_code code)
#define xfree
#define xmalloc
int code
Definition: smb-errors.c:145
int unsigned int
Definition: stub_fd.cc:19
#define UINT32_MAX
Definition: types.h:66
void * xcalloc(size_t n, size_t sz)
Definition: xalloc.cc:71

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors