]> git.ipfire.org Git - thirdparty/squid.git/blame - helpers/negotiate_auth/kerberos/negotiate_kerberos_pac.cc
SourceFormat Enforcement
[thirdparty/squid.git] / helpers / negotiate_auth / kerberos / negotiate_kerberos_pac.cc
CommitLineData
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
47static int bpos;
48static krb5_data *ad_data;
49static unsigned char *p;
50
685277d8
MM
51extern int
52check_k5_err(krb5_context context, const char *function, krb5_error_code code);
4ebcf1ce
MM
53
54void
55align(int n)
56{
57 if ( bpos % n != 0 ) {
58 int al;
59 al = (bpos/n);
60 bpos = bpos+(bpos-n*al);
61 }
62}
63
64void
65getustr(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
75uint64_t
76get6byt_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
86uint32_t
87get4byt(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
97uint16_t
98get2byt(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
108uint8_t
109get1byt(void)
110{
111 uint8_t var;
112
113 var=(uint8_t)((p[bpos]<<0));
114 bpos = bpos+1;
115
116 return var;
117}
118
119char *
685277d8 120pstrcpy( 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
131char *
685277d8 132pstrcat( 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
143int
144checkustr(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
168char **
169getgids(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
198char *
199getdomaingids(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
257char *
258getextrasids(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
330char *
331get_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;
454k5clean:
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