]>
Commit | Line | Data |
---|---|---|
ca02e0ec AJ |
1 | /* |
2 | * Copyright (C) 1996-2014 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 | ||
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" | |
4ebcf1ce MM |
39 | #include "compat/getaddrinfo.h" |
40 | #include "compat/getnameinfo.h" | |
602d9612 | 41 | #include "rfc1738.h" |
4ebcf1ce MM |
42 | |
43 | #include "negotiate_kerberos.h" | |
44 | ||
45 | #if HAVE_PAC_SUPPORT | |
46 | ||
47 | static int bpos; | |
48 | static krb5_data *ad_data; | |
49 | static unsigned char *p; | |
50 | ||
685277d8 MM |
51 | extern int |
52 | check_k5_err(krb5_context context, const char *function, krb5_error_code code); | |
4ebcf1ce MM |
53 | |
54 | void | |
55 | align(int n) | |
56 | { | |
57 | if ( bpos % n != 0 ) { | |
58 | int al; | |
59 | al = (bpos/n); | |
60 | bpos = bpos+(bpos-n*al); | |
61 | } | |
62 | } | |
63 | ||
64 | void | |
65 | getustr(RPC_UNICODE_STRING *string) | |
66 | { | |
67 | ||
68 | string->length = (uint16_t)((p[bpos]<<0) | (p[bpos+1]<<8)); | |
69 | string->maxlength = (uint16_t)((p[bpos+2]<<0) | (p[bpos+2+1]<<8)); | |
70 | string->pointer = (uint32_t)((p[bpos+4]<<0) | (p[bpos+4+1]<<8) | (p[bpos+4+2]<<16) | (p[bpos+4+3]<<24)); | |
71 | bpos = bpos+8; | |
72 | ||
73 | } | |
74 | ||
75 | uint64_t | |
76 | get6byt_be(void) | |
77 | { | |
78 | uint64_t var; | |
79 | ||
80 | 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); | |
81 | bpos = bpos+6; | |
82 | ||
83 | return var; | |
84 | } | |
85 | ||
86 | uint32_t | |
87 | get4byt(void) | |
88 | { | |
89 | uint32_t var; | |
90 | ||
91 | var=(uint32_t)((p[bpos]<<0) | (p[bpos+1]<<8) | (p[bpos+2]<<16) | (p[bpos+3]<<24)); | |
92 | bpos = bpos+4; | |
93 | ||
94 | return var; | |
95 | } | |
96 | ||
97 | uint16_t | |
98 | get2byt(void) | |
99 | { | |
100 | uint16_t var; | |
101 | ||
102 | var=(uint16_t)((p[bpos]<<0) | (p[bpos+1]<<8)); | |
103 | bpos = bpos+2; | |
104 | ||
105 | return var; | |
106 | } | |
107 | ||
108 | uint8_t | |
109 | get1byt(void) | |
110 | { | |
111 | uint8_t var; | |
112 | ||
113 | var=(uint8_t)((p[bpos]<<0)); | |
114 | bpos = bpos+1; | |
115 | ||
116 | return var; | |
117 | } | |
118 | ||
119 | char * | |
685277d8 | 120 | pstrcpy( char *src, const char *dst) |
4ebcf1ce MM |
121 | { |
122 | if (dst) { | |
123 | if (strlen(dst)>MAX_PAC_GROUP_SIZE) | |
124 | return NULL; | |
125 | else | |
126 | return strcpy(src,dst); | |
127 | } else | |
128 | return src; | |
129 | } | |
130 | ||
131 | char * | |
685277d8 | 132 | pstrcat( char *src, const char *dst) |
4ebcf1ce MM |
133 | { |
134 | if (dst) { | |
135 | if (strlen(src)+strlen(dst)+1>MAX_PAC_GROUP_SIZE) | |
136 | return NULL; | |
137 | else | |
138 | return strcat(src,dst); | |
139 | } else | |
140 | return src; | |
141 | } | |
142 | ||
143 | int | |
144 | checkustr(RPC_UNICODE_STRING *string) | |
145 | { | |
146 | uint32_t size,off,len; | |
147 | ||
148 | if (string->pointer != 0) { | |
149 | align(4); | |
150 | size = (uint32_t)((p[bpos]<<0) | (p[bpos+1]<<8) | (p[bpos+2]<<16) | (p[bpos+3]<<24)); | |
151 | bpos = bpos+4; | |
152 | off = (uint32_t)((p[bpos]<<0) | (p[bpos+1]<<8) | (p[bpos+2]<<16) | (p[bpos+3]<<24)); | |
153 | bpos = bpos+4; | |
154 | len = (uint32_t)((p[bpos]<<0) | (p[bpos+1]<<8) | (p[bpos+2]<<16) | (p[bpos+3]<<24)); | |
155 | bpos = bpos+4; | |
156 | if (len > size || off != 0 || | |
157 | string->length > string->maxlength || len != string->length/2) { | |
158 | debug((char *) "%s| %s: ERROR: RPC_UNICODE_STRING encoding error => size: %d len: %d/%d maxlength: %d offset: %d\n", | |
159 | LogTime(), PROGRAM, size, len, string->length, string->maxlength, off); | |
160 | return -1; | |
161 | } | |
162 | /* UNICODE string */ | |
163 | bpos = bpos+string->length; | |
164 | } | |
165 | return 0; | |
166 | } | |
167 | ||
168 | char ** | |
169 | getgids(char **Rids, uint32_t GroupIds, uint32_t GroupCount) | |
170 | { | |
171 | if (GroupIds!= 0) { | |
172 | uint32_t ngroup; | |
173 | uint32_t sauth; | |
174 | int l; | |
175 | ||
176 | align(4); | |
177 | ngroup = get4byt(); | |
178 | if ( ngroup != GroupCount) { | |
179 | debug((char *) "%s| %s: ERROR: Group encoding error => GroupCount: %d Array size: %d\n", | |
180 | LogTime(), PROGRAM, GroupCount, ngroup); | |
181 | return NULL; | |
182 | } | |
183 | debug((char *) "%s| %s: INFO: Found %d rids\n", LogTime(), PROGRAM, GroupCount); | |
184 | ||
185 | Rids=(char **)xcalloc(GroupCount*sizeof(char*),1); | |
186 | for ( l=0; l<(int)GroupCount; l++) { | |
187 | Rids[l]=(char *)xcalloc(4*sizeof(char),1); | |
188 | memcpy((void *)Rids[l],(void *)&p[bpos],4); | |
189 | sauth = get4byt(); | |
190 | debug((char *) "%s| %s: Info: Got rid: %u\n", LogTime(), PROGRAM, sauth); | |
191 | /* attribute */ | |
192 | bpos = bpos+4; | |
193 | } | |
194 | } | |
195 | return Rids; | |
196 | } | |
197 | ||
198 | char * | |
199 | getdomaingids(char *ad_groups, uint32_t DomainLogonId, char **Rids, uint32_t GroupCount) | |
200 | { | |
201 | if (DomainLogonId!= 0) { | |
202 | uint32_t nauth; | |
203 | uint8_t rev; | |
204 | uint64_t idauth; | |
205 | uint32_t sauth; | |
206 | char dli[256]; | |
207 | char *ag; | |
208 | size_t length; | |
209 | int l; | |
210 | ||
211 | align(4); | |
212 | ||
213 | nauth = get4byt(); | |
214 | ||
215 | /* prepend rids with DomainID */ | |
216 | length=1+1+6+nauth*4; | |
217 | for (l=0; l<(int)GroupCount; l++) { | |
218 | ag=(char *)xcalloc((length+4)*sizeof(char),1); | |
219 | memcpy((void *)ag,(const void*)&p[bpos],1); | |
220 | memcpy((void *)&ag[1],(const void*)&p[bpos+1],1); | |
221 | ag[1] = ag[1]+1; | |
222 | memcpy((void *)&ag[2],(const void*)&p[bpos+2],6+nauth*4); | |
223 | memcpy((void *)&ag[length],(const void*)Rids[l],4); | |
224 | if (l==0) { | |
685277d8 | 225 | if (!pstrcpy(ad_groups,"group=")) { |
4ebcf1ce MM |
226 | debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", |
227 | LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups); | |
228 | } | |
229 | } else { | |
685277d8 | 230 | if (!pstrcat(ad_groups," group=")) { |
4ebcf1ce MM |
231 | debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", |
232 | LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups); | |
233 | } | |
234 | } | |
685277d8 | 235 | if (!pstrcat(ad_groups,base64_encode_bin(ag, (int)(length+4)))) { |
4ebcf1ce MM |
236 | debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", |
237 | LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups); | |
238 | } | |
239 | xfree(ag); | |
240 | } | |
241 | ||
242 | /* mainly for debug only */ | |
243 | rev = get1byt(); | |
244 | bpos = bpos + 1; /*nsub*/ | |
245 | idauth = get6byt_be(); | |
246 | ||
247 | snprintf(dli,sizeof(dli),"S-%d-%lu",rev,(long unsigned int)idauth); | |
248 | for ( l=0; l<(int)nauth; l++ ) { | |
249 | sauth = get4byt(); | |
250 | snprintf((char *)&dli[strlen(dli)],sizeof(dli)-strlen(dli),"-%u",sauth); | |
251 | } | |
252 | debug((char *) "%s| %s: INFO: Got DomainLogonId %s\n", LogTime(), PROGRAM, dli); | |
253 | } | |
254 | return ad_groups; | |
255 | } | |
256 | ||
257 | char * | |
258 | getextrasids(char *ad_groups, uint32_t ExtraSids, uint32_t SidCount) | |
259 | { | |
260 | if (ExtraSids!= 0) { | |
261 | uint32_t ngroup; | |
262 | uint32_t *pa; | |
263 | char *ag; | |
264 | size_t length; | |
265 | int l; | |
266 | ||
267 | align(4); | |
268 | ngroup = get4byt(); | |
269 | if ( ngroup != SidCount) { | |
270 | debug((char *) "%s| %s: ERROR: Group encoding error => SidCount: %d Array size: %d\n", | |
271 | LogTime(), PROGRAM, SidCount, ngroup); | |
272 | return NULL; | |
273 | } | |
274 | debug((char *) "%s| %s: INFO: Found %d ExtraSIDs\n", LogTime(), PROGRAM, SidCount); | |
275 | ||
276 | pa=(uint32_t *)xmalloc(SidCount*sizeof(uint32_t)); | |
277 | for ( l=0; l < (int)SidCount; l++ ) { | |
278 | pa[l] = get4byt(); | |
279 | bpos = bpos+4; /* attr */ | |
280 | } | |
281 | ||
282 | for ( l=0; l<(int)SidCount; l++ ) { | |
283 | char es[256]; | |
284 | uint32_t nauth; | |
285 | uint8_t rev; | |
286 | uint64_t idauth; | |
287 | uint32_t sauth; | |
288 | int k; | |
289 | ||
290 | if (pa[l] != 0) { | |
291 | nauth = get4byt(); | |
292 | ||
293 | length = 1+1+6+nauth*4; | |
294 | ag = (char *)xcalloc((length)*sizeof(char),1); | |
295 | memcpy((void *)ag,(const void*)&p[bpos],length); | |
296 | if (!ad_groups) { | |
685277d8 | 297 | if (!pstrcpy(ad_groups,"group=")) { |
4ebcf1ce MM |
298 | debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", |
299 | LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups); | |
300 | } | |
301 | } else { | |
685277d8 | 302 | if (!pstrcat(ad_groups," group=")) { |
4ebcf1ce MM |
303 | debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", |
304 | LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups); | |
305 | } | |
306 | } | |
685277d8 | 307 | if (!pstrcat(ad_groups,base64_encode_bin(ag, (int)length))) { |
4ebcf1ce MM |
308 | debug((char *) "%s| %s: WARN: Too many groups ! size > %d : %s\n", |
309 | LogTime(), PROGRAM, MAX_PAC_GROUP_SIZE, ad_groups); | |
310 | } | |
311 | xfree(ag); | |
312 | ||
313 | rev = get1byt(); | |
314 | bpos = bpos + 1; /* nsub */ | |
315 | idauth = get6byt_be(); | |
316 | ||
317 | snprintf(es,sizeof(es),"S-%d-%lu",rev,(long unsigned int)idauth); | |
318 | for ( k=0; k<(int)nauth; k++ ) { | |
319 | sauth = get4byt(); | |
320 | snprintf((char *)&es[strlen(es)],sizeof(es)-strlen(es),"-%u",sauth); | |
321 | } | |
322 | debug((char *) "%s| %s: INFO: Got ExtraSid %s\n", LogTime(), PROGRAM, es); | |
323 | } | |
324 | } | |
325 | xfree(pa); | |
326 | } | |
327 | return ad_groups; | |
328 | } | |
329 | ||
330 | char * | |
331 | get_ad_groups(char *ad_groups, krb5_context context, krb5_pac pac) | |
332 | { | |
333 | krb5_error_code ret; | |
334 | RPC_UNICODE_STRING EffectiveName; | |
335 | RPC_UNICODE_STRING FullName; | |
336 | RPC_UNICODE_STRING LogonScript; | |
337 | RPC_UNICODE_STRING ProfilePath; | |
338 | RPC_UNICODE_STRING HomeDirectory; | |
339 | RPC_UNICODE_STRING HomeDirectoryDrive; | |
340 | RPC_UNICODE_STRING LogonServer; | |
341 | RPC_UNICODE_STRING LogonDomainName; | |
342 | uint32_t GroupCount=0; | |
343 | uint32_t GroupIds=0; | |
344 | uint32_t LogonDomainId=0; | |
345 | uint32_t SidCount=0; | |
346 | uint32_t ExtraSids=0; | |
347 | /* | |
348 | uint32_t ResourceGroupDomainSid=0; | |
349 | uint32_t ResourceGroupCount=0; | |
350 | uint32_t ResourceGroupIds=0; | |
351 | */ | |
352 | char **Rids=NULL; | |
353 | int l=0; | |
354 | ||
1a22a39e | 355 | ad_data = (krb5_data *)xcalloc(1,sizeof(krb5_data)); |
4ebcf1ce MM |
356 | |
357 | #define KERB_LOGON_INFO 1 | |
358 | ret = krb5_pac_get_buffer(context, pac, KERB_LOGON_INFO, ad_data); | |
359 | if (check_k5_err(context, "krb5_pac_get_buffer", ret)) | |
360 | goto k5clean; | |
361 | ||
362 | p = (unsigned char *)ad_data->data; | |
363 | ||
364 | debug((char *) "%s| %s: INFO: Got PAC data of lengh %d\n", | |
365 | LogTime(), PROGRAM, (int)ad_data->length); | |
366 | ||
367 | /* Skip 16 bytes icommon RPC header | |
368 | * Skip 4 bytes RPC unique pointer referent | |
369 | * http://msdn.microsoft.com/en-gb/library/cc237933.aspx | |
370 | */ | |
371 | /* Some data are pointers to data which follows the main KRB5 LOGON structure => | |
372 | * So need to read the data | |
373 | * some logical consistency checks are done when analysineg the pointer data | |
374 | */ | |
375 | bpos = 20; | |
376 | /* 8 bytes LogonTime | |
377 | * 8 bytes LogoffTime | |
378 | * 8 bytes KickOffTime | |
379 | * 8 bytes PasswordLastSet | |
380 | * 8 bytes PasswordCanChange | |
381 | * 8 bytes PasswordMustChange | |
382 | */ | |
383 | bpos = bpos+48; | |
384 | getustr(&EffectiveName); | |
385 | getustr(&FullName); | |
386 | getustr(&LogonScript); | |
387 | getustr(&ProfilePath); | |
388 | getustr(&HomeDirectory); | |
389 | getustr(&HomeDirectoryDrive); | |
390 | /* 2 bytes LogonCount | |
391 | * 2 bytes BadPasswordCount | |
392 | * 4 bytes UserID | |
393 | * 4 bytes PrimaryGroupId | |
394 | */ | |
395 | bpos = bpos+12; | |
396 | GroupCount = get4byt(); | |
397 | GroupIds = get4byt(); | |
398 | /* 4 bytes UserFlags | |
399 | * 16 bytes UserSessionKey | |
400 | */ | |
401 | bpos = bpos+20; | |
402 | getustr(&LogonServer); | |
403 | getustr(&LogonDomainName); | |
404 | LogonDomainId = get4byt(); | |
405 | /* 8 bytes Reserved1 | |
406 | * 4 bytes UserAccountControl | |
407 | * 4 bytes SubAuthStatus | |
408 | * 8 bytes LastSuccessfullLogon | |
409 | * 8 bytes LastFailedLogon | |
410 | * 4 bytes FailedLogonCount | |
411 | * 4 bytes Reserved2 | |
412 | */ | |
413 | bpos = bpos+40; | |
414 | SidCount = get4byt(); | |
415 | ExtraSids = get4byt(); | |
416 | /* 4 bytes ResourceGroupDomainSid | |
417 | * 4 bytes ResourceGroupCount | |
418 | * 4 bytes ResourceGroupIds | |
419 | */ | |
420 | bpos = bpos+12; | |
421 | /* | |
422 | * Read all data from structure => Now check pointers | |
423 | */ | |
424 | if (checkustr(&EffectiveName)<0) | |
425 | goto k5clean; | |
426 | if (checkustr(&FullName)<0) | |
427 | goto k5clean; | |
428 | if (checkustr(&LogonScript)<0) | |
429 | goto k5clean; | |
430 | if (checkustr(&ProfilePath)<0) | |
431 | goto k5clean; | |
432 | if (checkustr(&HomeDirectory)<0) | |
433 | goto k5clean; | |
434 | if (checkustr(&HomeDirectoryDrive)<0) | |
435 | goto k5clean; | |
436 | Rids = getgids(Rids,GroupIds,GroupCount); | |
437 | if (checkustr(&LogonServer)<0) | |
438 | goto k5clean; | |
439 | if (checkustr(&LogonDomainName)<0) | |
440 | goto k5clean; | |
441 | ad_groups = getdomaingids(ad_groups,LogonDomainId,Rids,GroupCount); | |
442 | if ((ad_groups = getextrasids(ad_groups,ExtraSids,SidCount))==NULL) | |
443 | goto k5clean; | |
444 | ||
445 | debug((char *) "%s| %s: INFO: Read %d of %d bytes \n", LogTime(), PROGRAM, bpos, (int)ad_data->length); | |
446 | if (Rids) { | |
447 | for ( l=0; l<(int)GroupCount; l++) { | |
448 | xfree(Rids[l]); | |
449 | } | |
450 | xfree(Rids); | |
451 | } | |
452 | krb5_free_data(context, ad_data); | |
453 | return ad_groups; | |
454 | k5clean: | |
455 | if (Rids) { | |
456 | for ( l=0; l<(int)GroupCount; l++) { | |
457 | xfree(Rids[l]); | |
458 | } | |
459 | xfree(Rids); | |
460 | } | |
461 | krb5_free_data(context, ad_data); | |
462 | return NULL; | |
463 | } | |
464 | #endif | |
f53969cc | 465 |