]>
Commit | Line | Data |
---|---|---|
ca02e0ec | 1 | /* |
ef57eb7b | 2 | * Copyright (C) 1996-2016 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 | |
45 | static int bpos; | |
46 | static krb5_data *ad_data; | |
47 | static unsigned char *p; | |
48 | ||
685277d8 MM |
49 | extern int |
50 | check_k5_err(krb5_context context, const char *function, krb5_error_code code); | |
4ebcf1ce MM |
51 | |
52 | void | |
53 | align(int n) | |
54 | { | |
55 | if ( bpos % n != 0 ) { | |
56 | int al; | |
57 | al = (bpos/n); | |
58 | bpos = bpos+(bpos-n*al); | |
59 | } | |
60 | } | |
61 | ||
62 | void | |
63 | getustr(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 | ||
73 | uint64_t | |
74 | get6byt_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 | ||
84 | uint32_t | |
85 | get4byt(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 | ||
95 | uint16_t | |
96 | get2byt(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 | ||
106 | uint8_t | |
107 | get1byt(void) | |
108 | { | |
109 | uint8_t var; | |
110 | ||
111 | var=(uint8_t)((p[bpos]<<0)); | |
112 | bpos = bpos+1; | |
113 | ||
114 | return var; | |
115 | } | |
116 | ||
117 | char * | |
685277d8 | 118 | pstrcpy( char *src, const char *dst) |
4ebcf1ce MM |
119 | { |
120 | if (dst) { | |
121 | if (strlen(dst)>MAX_PAC_GROUP_SIZE) | |
122 | return NULL; | |
123 | else | |
124 | return strcpy(src,dst); | |
125 | } else | |
126 | return src; | |
127 | } | |
128 | ||
129 | char * | |
685277d8 | 130 | pstrcat( char *src, const char *dst) |
4ebcf1ce MM |
131 | { |
132 | if (dst) { | |
133 | if (strlen(src)+strlen(dst)+1>MAX_PAC_GROUP_SIZE) | |
134 | return NULL; | |
135 | else | |
136 | return strcat(src,dst); | |
137 | } else | |
138 | return src; | |
139 | } | |
140 | ||
141 | int | |
142 | checkustr(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 | ||
166 | char ** | |
167 | getgids(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); | |
178 | return NULL; | |
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 | ||
196 | char * | |
197 | getdomaingids(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); | |
202 | return NULL; | |
203 | } | |
204 | ||
4ebcf1ce MM |
205 | if (DomainLogonId!= 0) { |
206 | uint32_t nauth; | |
207 | uint8_t rev; | |
208 | uint64_t idauth; | |
4ebcf1ce MM |
209 | char dli[256]; |
210 | char *ag; | |
211 | size_t length; | |
212 | int l; | |
213 | ||
214 | align(4); | |
215 | ||
216 | nauth = get4byt(); | |
217 | ||
218 | /* prepend rids with DomainID */ | |
219 | length=1+1+6+nauth*4; | |
220 | for (l=0; l<(int)GroupCount; l++) { | |
221 | ag=(char *)xcalloc((length+4)*sizeof(char),1); | |
222 | memcpy((void *)ag,(const void*)&p[bpos],1); | |
223 | memcpy((void *)&ag[1],(const void*)&p[bpos+1],1); | |
224 | ag[1] = ag[1]+1; | |
225 | memcpy((void *)&ag[2],(const void*)&p[bpos+2],6+nauth*4); | |
226 | memcpy((void *)&ag[length],(const void*)Rids[l],4); | |
227 | if (l==0) { | |
685277d8 | 228 | if (!pstrcpy(ad_groups,"group=")) { |
4ebcf1ce MM |
229 | debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", |
230 | LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups); | |
231 | } | |
232 | } else { | |
685277d8 | 233 | if (!pstrcat(ad_groups," group=")) { |
4ebcf1ce MM |
234 | debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", |
235 | LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups); | |
236 | } | |
237 | } | |
aadbbd7d AJ |
238 | struct base64_encode_ctx ctx; |
239 | base64_encode_init(&ctx); | |
786ee90a AJ |
240 | const uint32_t expectedSz = base64_encode_len(length+4) +1 /* terminator */; |
241 | uint8_t *b64buf = (uint8_t *)xcalloc(expectedSz, 1); | |
aadbbd7d AJ |
242 | size_t blen = base64_encode_update(&ctx, b64buf, length+4, reinterpret_cast<uint8_t*>(ag)); |
243 | blen += base64_encode_final(&ctx, b64buf+blen); | |
786ee90a | 244 | b64buf[expectedSz-1] = '\0'; |
aadbbd7d | 245 | if (!pstrcat(ad_groups, reinterpret_cast<char*>(b64buf))) { |
4ebcf1ce MM |
246 | debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", |
247 | LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups); | |
248 | } | |
aadbbd7d | 249 | xfree(b64buf); |
4ebcf1ce MM |
250 | xfree(ag); |
251 | } | |
252 | ||
253 | /* mainly for debug only */ | |
254 | rev = get1byt(); | |
255 | bpos = bpos + 1; /*nsub*/ | |
256 | idauth = get6byt_be(); | |
257 | ||
258 | snprintf(dli,sizeof(dli),"S-%d-%lu",rev,(long unsigned int)idauth); | |
259 | for ( l=0; l<(int)nauth; l++ ) { | |
4616aac3 | 260 | uint32_t sauth; |
4ebcf1ce MM |
261 | sauth = get4byt(); |
262 | snprintf((char *)&dli[strlen(dli)],sizeof(dli)-strlen(dli),"-%u",sauth); | |
263 | } | |
264 | debug((char *) "%s| %s: INFO: Got DomainLogonId %s\n", LogTime(), PROGRAM, dli); | |
265 | } | |
266 | return ad_groups; | |
267 | } | |
268 | ||
269 | char * | |
270 | getextrasids(char *ad_groups, uint32_t ExtraSids, uint32_t SidCount) | |
271 | { | |
272 | if (ExtraSids!= 0) { | |
273 | uint32_t ngroup; | |
274 | uint32_t *pa; | |
275 | char *ag; | |
276 | size_t length; | |
277 | int l; | |
278 | ||
279 | align(4); | |
280 | ngroup = get4byt(); | |
281 | if ( ngroup != SidCount) { | |
282 | debug((char *) "%s| %s: ERROR: Group encoding error => SidCount: %d Array size: %d\n", | |
283 | LogTime(), PROGRAM, SidCount, ngroup); | |
284 | return NULL; | |
285 | } | |
286 | debug((char *) "%s| %s: INFO: Found %d ExtraSIDs\n", LogTime(), PROGRAM, SidCount); | |
287 | ||
288 | pa=(uint32_t *)xmalloc(SidCount*sizeof(uint32_t)); | |
289 | for ( l=0; l < (int)SidCount; l++ ) { | |
290 | pa[l] = get4byt(); | |
291 | bpos = bpos+4; /* attr */ | |
292 | } | |
293 | ||
294 | for ( l=0; l<(int)SidCount; l++ ) { | |
295 | char es[256]; | |
4ebcf1ce MM |
296 | |
297 | if (pa[l] != 0) { | |
4616aac3 MM |
298 | uint32_t nauth; |
299 | uint8_t rev; | |
300 | uint64_t idauth; | |
301 | ||
4ebcf1ce MM |
302 | nauth = get4byt(); |
303 | ||
304 | length = 1+1+6+nauth*4; | |
305 | ag = (char *)xcalloc((length)*sizeof(char),1); | |
306 | memcpy((void *)ag,(const void*)&p[bpos],length); | |
307 | if (!ad_groups) { | |
4616aac3 MM |
308 | debug((char *) "%s| %s: ERR: No space to store groups\n", |
309 | LogTime(), PROGRAM); | |
310 | xfree(pa); | |
311 | xfree(ag); | |
312 | return NULL; | |
4ebcf1ce | 313 | } else { |
685277d8 | 314 | if (!pstrcat(ad_groups," group=")) { |
4ebcf1ce MM |
315 | debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", |
316 | LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups); | |
317 | } | |
318 | } | |
aadbbd7d AJ |
319 | |
320 | struct base64_encode_ctx ctx; | |
321 | base64_encode_init(&ctx); | |
786ee90a AJ |
322 | const uint32_t expectedSz = base64_encode_len(length) +1 /* terminator */; |
323 | uint8_t *b64buf = (uint8_t *)xcalloc(expectedSz, 1); | |
aadbbd7d AJ |
324 | size_t blen = base64_encode_update(&ctx, b64buf, length, reinterpret_cast<uint8_t*>(ag)); |
325 | blen += base64_encode_final(&ctx, b64buf+blen); | |
786ee90a | 326 | b64buf[expectedSz-1] = '\0'; |
aadbbd7d | 327 | if (!pstrcat(ad_groups, reinterpret_cast<char*>(b64buf))) { |
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 | } | |
aadbbd7d | 331 | xfree(b64buf); |
4ebcf1ce MM |
332 | xfree(ag); |
333 | ||
334 | rev = get1byt(); | |
335 | bpos = bpos + 1; /* nsub */ | |
336 | idauth = get6byt_be(); | |
337 | ||
338 | snprintf(es,sizeof(es),"S-%d-%lu",rev,(long unsigned int)idauth); | |
4616aac3 MM |
339 | for (int k=0; k<(int)nauth; k++ ) { |
340 | uint32_t sauth; | |
4ebcf1ce MM |
341 | sauth = get4byt(); |
342 | snprintf((char *)&es[strlen(es)],sizeof(es)-strlen(es),"-%u",sauth); | |
343 | } | |
344 | debug((char *) "%s| %s: INFO: Got ExtraSid %s\n", LogTime(), PROGRAM, es); | |
345 | } | |
346 | } | |
347 | xfree(pa); | |
348 | } | |
349 | return ad_groups; | |
350 | } | |
351 | ||
352 | char * | |
353 | get_ad_groups(char *ad_groups, krb5_context context, krb5_pac pac) | |
354 | { | |
355 | krb5_error_code ret; | |
356 | RPC_UNICODE_STRING EffectiveName; | |
357 | RPC_UNICODE_STRING FullName; | |
358 | RPC_UNICODE_STRING LogonScript; | |
359 | RPC_UNICODE_STRING ProfilePath; | |
360 | RPC_UNICODE_STRING HomeDirectory; | |
361 | RPC_UNICODE_STRING HomeDirectoryDrive; | |
362 | RPC_UNICODE_STRING LogonServer; | |
363 | RPC_UNICODE_STRING LogonDomainName; | |
364 | uint32_t GroupCount=0; | |
365 | uint32_t GroupIds=0; | |
366 | uint32_t LogonDomainId=0; | |
367 | uint32_t SidCount=0; | |
368 | uint32_t ExtraSids=0; | |
369 | /* | |
370 | uint32_t ResourceGroupDomainSid=0; | |
371 | uint32_t ResourceGroupCount=0; | |
372 | uint32_t ResourceGroupIds=0; | |
373 | */ | |
374 | char **Rids=NULL; | |
375 | int l=0; | |
376 | ||
4616aac3 MM |
377 | if (!ad_groups) { |
378 | debug((char *) "%s| %s: ERR: No space to store groups\n", | |
085c4f21 | 379 | LogTime(), PROGRAM); |
4616aac3 MM |
380 | return NULL; |
381 | } | |
382 | ||
1a22a39e | 383 | ad_data = (krb5_data *)xcalloc(1,sizeof(krb5_data)); |
4ebcf1ce MM |
384 | |
385 | #define KERB_LOGON_INFO 1 | |
386 | ret = krb5_pac_get_buffer(context, pac, KERB_LOGON_INFO, ad_data); | |
387 | if (check_k5_err(context, "krb5_pac_get_buffer", ret)) | |
388 | goto k5clean; | |
389 | ||
390 | p = (unsigned char *)ad_data->data; | |
391 | ||
392 | debug((char *) "%s| %s: INFO: Got PAC data of lengh %d\n", | |
393 | LogTime(), PROGRAM, (int)ad_data->length); | |
394 | ||
395 | /* Skip 16 bytes icommon RPC header | |
396 | * Skip 4 bytes RPC unique pointer referent | |
397 | * http://msdn.microsoft.com/en-gb/library/cc237933.aspx | |
398 | */ | |
399 | /* Some data are pointers to data which follows the main KRB5 LOGON structure => | |
400 | * So need to read the data | |
401 | * some logical consistency checks are done when analysineg the pointer data | |
402 | */ | |
403 | bpos = 20; | |
404 | /* 8 bytes LogonTime | |
405 | * 8 bytes LogoffTime | |
406 | * 8 bytes KickOffTime | |
407 | * 8 bytes PasswordLastSet | |
408 | * 8 bytes PasswordCanChange | |
409 | * 8 bytes PasswordMustChange | |
410 | */ | |
411 | bpos = bpos+48; | |
412 | getustr(&EffectiveName); | |
413 | getustr(&FullName); | |
414 | getustr(&LogonScript); | |
415 | getustr(&ProfilePath); | |
416 | getustr(&HomeDirectory); | |
417 | getustr(&HomeDirectoryDrive); | |
418 | /* 2 bytes LogonCount | |
419 | * 2 bytes BadPasswordCount | |
420 | * 4 bytes UserID | |
421 | * 4 bytes PrimaryGroupId | |
422 | */ | |
423 | bpos = bpos+12; | |
424 | GroupCount = get4byt(); | |
425 | GroupIds = get4byt(); | |
426 | /* 4 bytes UserFlags | |
427 | * 16 bytes UserSessionKey | |
428 | */ | |
429 | bpos = bpos+20; | |
430 | getustr(&LogonServer); | |
431 | getustr(&LogonDomainName); | |
432 | LogonDomainId = get4byt(); | |
433 | /* 8 bytes Reserved1 | |
434 | * 4 bytes UserAccountControl | |
435 | * 4 bytes SubAuthStatus | |
436 | * 8 bytes LastSuccessfullLogon | |
437 | * 8 bytes LastFailedLogon | |
438 | * 4 bytes FailedLogonCount | |
439 | * 4 bytes Reserved2 | |
440 | */ | |
441 | bpos = bpos+40; | |
442 | SidCount = get4byt(); | |
443 | ExtraSids = get4byt(); | |
444 | /* 4 bytes ResourceGroupDomainSid | |
445 | * 4 bytes ResourceGroupCount | |
446 | * 4 bytes ResourceGroupIds | |
447 | */ | |
448 | bpos = bpos+12; | |
449 | /* | |
450 | * Read all data from structure => Now check pointers | |
451 | */ | |
452 | if (checkustr(&EffectiveName)<0) | |
453 | goto k5clean; | |
454 | if (checkustr(&FullName)<0) | |
455 | goto k5clean; | |
456 | if (checkustr(&LogonScript)<0) | |
457 | goto k5clean; | |
458 | if (checkustr(&ProfilePath)<0) | |
459 | goto k5clean; | |
460 | if (checkustr(&HomeDirectory)<0) | |
461 | goto k5clean; | |
462 | if (checkustr(&HomeDirectoryDrive)<0) | |
463 | goto k5clean; | |
464 | Rids = getgids(Rids,GroupIds,GroupCount); | |
465 | if (checkustr(&LogonServer)<0) | |
466 | goto k5clean; | |
467 | if (checkustr(&LogonDomainName)<0) | |
468 | goto k5clean; | |
469 | ad_groups = getdomaingids(ad_groups,LogonDomainId,Rids,GroupCount); | |
470 | if ((ad_groups = getextrasids(ad_groups,ExtraSids,SidCount))==NULL) | |
471 | goto k5clean; | |
472 | ||
473 | debug((char *) "%s| %s: INFO: Read %d of %d bytes \n", LogTime(), PROGRAM, bpos, (int)ad_data->length); | |
474 | if (Rids) { | |
475 | for ( l=0; l<(int)GroupCount; l++) { | |
476 | xfree(Rids[l]); | |
477 | } | |
478 | xfree(Rids); | |
479 | } | |
480 | krb5_free_data(context, ad_data); | |
481 | return ad_groups; | |
482 | k5clean: | |
483 | if (Rids) { | |
484 | for ( l=0; l<(int)GroupCount; l++) { | |
485 | xfree(Rids[l]); | |
486 | } | |
487 | xfree(Rids); | |
488 | } | |
489 | krb5_free_data(context, ad_data); | |
490 | return NULL; | |
491 | } | |
492 | #endif | |
f53969cc | 493 |