]> 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 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
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
117char *
685277d8 118pstrcpy( 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
129char *
685277d8 130pstrcat( 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
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);
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
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);
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
269char *
270getextrasids(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
352char *
353get_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;
482k5clean:
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