negotiate_kerberos_pac.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2017 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 
45 static int bpos;
46 static krb5_data *ad_data;
47 static unsigned char *p;
48 
49 extern int
50 check_k5_err(krb5_context context, const char *function, krb5_error_code code);
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 *
118 pstrcpy( char *src, const char *dst)
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 *
130 pstrcat( char *src, const char *dst)
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 {
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 
166 char **
167 getgids(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 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++) {
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 
196 char *
197 getdomaingids(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 NULL;
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 NULL;
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  uint8_t *b64buf = (uint8_t *)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, reinterpret_cast<char*>(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 
275 char *
276 getextrasids(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 NULL;
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 NULL;
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 NULL;
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  uint8_t *b64buf = (uint8_t *)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 
365 char *
366 get_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=NULL;
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 NULL;
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 lengh %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))==NULL)
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;
495 k5clean:
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 NULL;
504 }
505 #endif
506 
const char * LogTime(void)
#define xcalloc
Definition: membanger.c:57
int check_k5_err(krb5_context context, const char *msg, krb5_error_code code)
char * p
Definition: membanger.c:43
size_t base64_encode_final(struct base64_encode_ctx *ctx, uint8_t *dst)
DST should point to an area of size at least BASE64_ENCODE_FINAL_LENGTH.
Definition: base64.c:253
static int debug
Definition: tcp-banger3.c:105
void base64_encode_init(struct base64_encode_ctx *ctx)
Definition: base64.c:182
int unsigned int const char *desc STUB void int len
Definition: stub_fd.cc:20
unsigned char code
Definition: html_quote.c:20
bool SIGHDLR int STUB void int
Definition: stub_tools.cc:68
#define xmalloc
#define PROGRAM
Definition: support.h:188
size_t base64_encode_update(struct base64_encode_ctx *ctx, uint8_t *dst, size_t length, const uint8_t *src)
Definition: base64.c:213
#define xfree
#define UINT32_MAX
Definition: types.h:72
#define NULL
Definition: types.h:166
int size
Definition: ModDevPoll.cc:77
#define base64_encode_len(length)
Definition: base64.h:93

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors