support_krb5.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  * -----------------------------------------------------------------------------
31  */
32 
33 #include "squid.h"
34 #include "util.h"
35 
36 #if HAVE_LDAP && HAVE_KRB5
37 
38 #include "support.h"
39 
40 #if HAVE_KRB5
41 extern struct kstruct kparam;
42 #endif
43 
44 #define KT_PATH_MAX 256
45 
46 void
47 krb5_cleanup()
48 {
49  if (kparam.context)
50  for (int i=0; i<MAX_DOMAINS; i++) {
51  if (kparam.cc[i])
52  krb5_cc_destroy(kparam.context, kparam.cc[i]);
53  safe_free(kparam.mem_ccache[i]);
54  }
55  krb5_free_context(kparam.context);
56 }
57 
58 static void
59 k5_error2(const char* msg, char* msg2, krb5_error_code code)
60 {
61  const char *errmsg;
62  errmsg = krb5_get_error_message(kparam.context, code);
63  error((char *) "%s| %s: ERROR: %s%s : %s\n", LogTime(), PROGRAM, msg, msg2, errmsg);
64 #if HAVE_KRB5_FREE_ERROR_MESSAGE
65  krb5_free_error_message(kparam.context, errmsg);
66 #elif HAVE_KRB5_FREE_ERROR_STRING
67  krb5_free_error_string(kparam.context, (char *)errmsg);
68 #else
69  xfree(errmsg);
70 #endif
71 }
72 
73 static void
74 k5_error(const char* msg, krb5_error_code code)
75 {
76  k5_error2(msg, (char *)"", code);
77 }
78 
79 /*
80  * create Kerberos memory cache
81  */
82 int
83 krb5_create_cache(char *domain)
84 {
85 
86  krb5_keytab keytab = NULL;
87  krb5_keytab_entry entry;
88  krb5_kt_cursor cursor;
89  krb5_cc_cursor ccursor;
90  krb5_creds *creds = NULL;
91  krb5_principal *principal_list = NULL;
92  krb5_principal principal = NULL;
93  char *service;
94  char *keytab_name = NULL, *principal_name = NULL, *mem_cache = NULL;
95  char buf[KT_PATH_MAX], *p;
96  size_t j,nprinc = 0;
97  int retval = 0;
98  krb5_error_code code = 0;
99  int ccindex=-1;
100 
101  if (!domain || !strcmp(domain, ""))
102  return (1);
103 
104  /*
105  * prepare memory credential cache
106  */
107 #if !HAVE_KRB5_MEMORY_CACHE || HAVE_SUN_LDAP_SDK
108  mem_cache = (char *) xmalloc(strlen("FILE:/tmp/squid_ldap_") + strlen(domain) + 1 + 16);
109  snprintf(mem_cache, strlen("FILE:/tmp/squid_ldap_") + strlen(domain) + 1 + 16, "FILE:/tmp/squid_ldap_%s_%d", domain, (int) getpid());
110 #else
111  mem_cache = (char *) xmalloc(strlen("MEMORY:squid_ldap_") + strlen(domain) + 1 + 16);
112  snprintf(mem_cache, strlen("MEMORY:squid_ldap_") + strlen(domain) + 1 + 16, "MEMORY:squid_ldap_%s_%d", domain, (int) getpid());
113 #endif
114 
115  setenv("KRB5CCNAME", mem_cache, 1);
116  debug((char *) "%s| %s: DEBUG: Set credential cache to %s\n", LogTime(), PROGRAM, mem_cache);
117  for (int i=0; i<MAX_DOMAINS; i++) {
118  if (kparam.mem_ccache[i] && !strcmp(mem_cache,kparam.mem_ccache[i])) {
119  ccindex=i;
120  break;
121  }
122  }
123  if ( ccindex == -1 ) {
124  kparam.mem_ccache[kparam.ncache]=xstrdup(mem_cache);
125  ccindex=kparam.ncache;
126  kparam.ncache++;
127  if ( kparam.ncache == MAX_DOMAINS ) {
128  error((char *) "%s| %s: ERROR: Too many domains to support: # domains %d\n", LogTime(), PROGRAM, kparam.ncache);
129  retval = 1;
130  goto cleanup;
131  }
132  code = krb5_cc_resolve(kparam.context, mem_cache, &kparam.cc[ccindex]);
133  if (code) {
134  k5_error("Error while resolving memory ccache",code);
135  retval = 1;
136  goto cleanup;
137  }
138  }
139  /*
140  * getting default principal from cache
141  */
142 
143  code = krb5_cc_get_principal(kparam.context, kparam.cc[ccindex], &principal);
144  if (code) {
145  if (principal)
146  krb5_free_principal(kparam.context, principal);
147  principal = NULL;
148  k5_error("No default principal found in ccache",code);
149  } else {
150  /*
151  * Look for krbtgt and check if it is expired (or soon to be expired)
152  */
153  code = krb5_cc_start_seq_get(kparam.context, kparam.cc[ccindex], &ccursor);
154  if (code) {
155  k5_error("Error while starting ccache scan",code);
156  code = krb5_cc_close (kparam.context, kparam.cc[ccindex]);
157  if (code) {
158  k5_error("Error while closing ccache",code);
159  }
160  if (kparam.cc[ccindex]) {
161  code = krb5_cc_destroy(kparam.context, kparam.cc[ccindex]);
162  if (code) {
163  k5_error("Error while destroying ccache",code);
164  }
165  }
166  } else {
167  krb5_error_code code2 = 0;
168  creds = static_cast<krb5_creds *>(xcalloc(1,sizeof(*creds)));
169  while ((krb5_cc_next_cred(kparam.context, kparam.cc[ccindex], &ccursor, creds)) == 0) {
170  code2 = krb5_unparse_name(kparam.context, creds->server, &principal_name);
171  if (code2) {
172  k5_error("Error while unparsing principal",code2);
173  code = krb5_cc_destroy(kparam.context, kparam.cc[ccindex]);
174  if (code) {
175  k5_error("Error while destroying ccache",code);
176  }
177  assert(creds != NULL);
178  krb5_free_creds(kparam.context, creds);
179  creds = NULL;
180  safe_free(principal_name);
181  debug((char *) "%s| %s: DEBUG: Reset credential cache to %s\n", LogTime(), PROGRAM, mem_cache);
182  code = krb5_cc_resolve(kparam.context, mem_cache, &kparam.cc[ccindex]);
183  if (code) {
184  k5_error("Error while resolving memory ccache",code);
185  retval = 1;
186  goto cleanup;
187  }
188  code =1;
189  break;
190  }
191  if (!strncmp(KRB5_TGS_NAME,principal_name,KRB5_TGS_NAME_SIZE)) {
192  time_t now;
193  static krb5_deltat skew=MAX_SKEW;
194 
195  debug((char *) "%s| %s: DEBUG: Found %s in cache : %s\n", LogTime(), PROGRAM,KRB5_TGS_NAME,principal_name);
196  /*
197  * Check time
198  */
199  time(&now);
200  debug((char *) "%s| %s: DEBUG: credential time diff %d\n", LogTime(), PROGRAM, (int)(creds->times.endtime - now));
201  if (creds->times.endtime - now < 2*skew) {
202  debug((char *) "%s| %s: DEBUG: credential will soon expire %d\n", LogTime(), PROGRAM, (int)(creds->times.endtime - now));
203  if (principal)
204  krb5_free_principal(kparam.context, principal);
205  principal = NULL;
206  code = krb5_cc_destroy(kparam.context, kparam.cc[ccindex]);
207  if (code) {
208  k5_error("Error while destroying ccache",code);
209  }
210  assert(creds != NULL);
211  krb5_free_creds(kparam.context, creds);
212  creds = NULL;
213  safe_free(principal_name);
214  debug((char *) "%s| %s: DEBUG: Reset credential cache to %s\n", LogTime(), PROGRAM, mem_cache);
215  code = krb5_cc_resolve(kparam.context, mem_cache, &kparam.cc[ccindex]);
216  if (code) {
217  k5_error("Error while resolving ccache",code);
218  retval = 1;
219  goto cleanup;
220  }
221  code = 1;
222  } else {
223  safe_free(principal_name);
224  }
225  break;
226  }
227  assert(creds != NULL);
228  krb5_free_creds(kparam.context, creds);
229  creds = static_cast<krb5_creds *>(xcalloc(1, sizeof(*creds)));
230  safe_free(principal_name);
231  }
232  if (creds)
233  krb5_free_creds(kparam.context, creds);
234  creds = NULL;
235  code2 = krb5_cc_end_seq_get(kparam.context, kparam.cc[ccindex], &ccursor);
236  if (code2) {
237  k5_error("Error while ending ccache scan",code2);
238  retval = 1;
239  goto cleanup;
240  }
241  }
242  }
243  if (code) {
244  /*
245  * getting default keytab name
246  */
247 
248  debug((char *) "%s| %s: DEBUG: Get default keytab file name\n", LogTime(), PROGRAM);
249  krb5_kt_default_name(kparam.context, buf, KT_PATH_MAX);
250  p = strchr(buf, ':'); /* Find the end if "FILE:" */
251  if (p)
252  ++p; /* step past : */
253  keytab_name = xstrdup(p ? p : buf);
254  debug((char *) "%s| %s: DEBUG: Got default keytab file name %s\n", LogTime(), PROGRAM, keytab_name);
255 
256  code = krb5_kt_resolve(kparam.context, keytab_name, &keytab);
257  if (code) {
258  k5_error2("Error while resolving keytab ",keytab_name,code);
259  retval = 1;
260  goto cleanup;
261  }
262  code = krb5_kt_start_seq_get(kparam.context, keytab, &cursor);
263  if (code) {
264  k5_error("Error while starting keytab scan",code);
265  retval = 1;
266  goto cleanup;
267  }
268  debug((char *) "%s| %s: DEBUG: Get principal name from keytab %s\n", LogTime(), PROGRAM, keytab_name);
269 
270  nprinc = 0;
271  while ((code = krb5_kt_next_entry(kparam.context, keytab, &entry, &cursor)) == 0) {
272  int found = 0;
273 
274  principal_list = (krb5_principal *) xrealloc(principal_list, sizeof(krb5_principal) * (nprinc + 1));
275  krb5_copy_principal(kparam.context, entry.principal, &principal_list[nprinc++]);
276 #if USE_HEIMDAL_KRB5
277  debug((char *) "%s| %s: DEBUG: Keytab entry has realm name: %s\n", LogTime(), PROGRAM, entry.principal->realm);
278 #else
279  debug((char *) "%s| %s: DEBUG: Keytab entry has realm name: %s\n", LogTime(), PROGRAM, krb5_princ_realm(kparam.context, entry.principal)->data);
280 #endif
281 #if USE_HEIMDAL_KRB5
282  if (!strcasecmp(domain, entry.principal->realm))
283 #else
284  if (!strcasecmp(domain, krb5_princ_realm(kparam.context, entry.principal)->data))
285 #endif
286  {
287  code = krb5_unparse_name(kparam.context, entry.principal, &principal_name);
288  if (code) {
289  k5_error("Error while unparsing principal name",code);
290  } else {
291  debug((char *) "%s| %s: DEBUG: Found principal name: %s\n", LogTime(), PROGRAM, principal_name);
292  found = 1;
293  }
294  }
295 #if USE_HEIMDAL_KRB5 || ( HAVE_KRB5_KT_FREE_ENTRY && HAVE_DECL_KRB5_KT_FREE_ENTRY )
296  code = krb5_kt_free_entry(kparam.context, &entry);
297 #else
298  code = krb5_free_keytab_entry_contents(kparam.context, &entry);
299 #endif
300  if (code) {
301  k5_error("Error while freeing keytab entry",code);
302  retval = 1;
303  break;
304  }
305  if (found) {
306  debug((char *) "%s| %s: DEBUG: Got principal name %s\n", LogTime(), PROGRAM, principal_name);
307  /*
308  * build principal
309  */
310  code = krb5_parse_name(kparam.context, principal_name, &principal);
311  if (code) {
312  k5_error2("Error while parsing name ", principal_name,code);
313  safe_free(principal_name);
314  if (principal)
315  krb5_free_principal(kparam.context, principal);
316  found = 0;
317  continue;
318  }
319  creds = (krb5_creds *) xcalloc(1,sizeof(*creds));
320 
321  /*
322  * get credentials
323  */
324 #if HAVE_GET_INIT_CREDS_KEYTAB
325  code = krb5_get_init_creds_keytab(kparam.context, creds, principal, keytab, 0, NULL, NULL);
326 #else
327  service = (char *) xmalloc(strlen("krbtgt") + 2 * strlen(domain) + 3);
328  snprintf(service, strlen("krbtgt") + 2 * strlen(domain) + 3, "krbtgt/%s@%s", domain, domain);
329  creds->client = principal;
330  code = krb5_parse_name(kparam.context, service, &creds->server);
331  xfree(service);
332  code = krb5_get_in_tkt_with_keytab(kparam.context, 0, NULL, NULL, NULL, keytab, NULL, creds, 0);
333 #endif
334 
335  if (code) {
336  k5_error("Error while initialising credentials from keytab",code);
337  safe_free(principal_name);
338  if (principal)
339  krb5_free_principal(kparam.context, principal);
340  if (creds)
341  krb5_free_creds(kparam.context, creds);
342  creds = NULL;
343  found = 0;
344  continue;
345  }
346  code = krb5_cc_initialize(kparam.context, kparam.cc[ccindex], principal);
347  if (code) {
348  k5_error("Error while initialising memory caches",code);
349  safe_free(principal_name);
350  if (principal)
351  krb5_free_principal(kparam.context, principal);
352  if (creds)
353  krb5_free_creds(kparam.context, creds);
354  creds = NULL;
355  found = 0;
356  continue;
357  }
358  code = krb5_cc_store_cred(kparam.context, kparam.cc[ccindex], creds);
359  if (code) {
360  k5_error("Error while storing credentials",code);
361  if (principal)
362  krb5_free_principal(kparam.context, principal);
363  safe_free(principal_name);
364  if (creds)
365  krb5_free_creds(kparam.context, creds);
366  creds = NULL;
367  found = 0;
368  continue;
369  }
370  debug((char *) "%s| %s: DEBUG: Stored credentials\n", LogTime(), PROGRAM);
371  break;
372  }
373  }
374 
375  if (code && code != KRB5_KT_END) {
376  k5_error("Error while scanning keytab",code);
377  retval = 1;
378  goto cleanup;
379  }
380  code = krb5_kt_end_seq_get(kparam.context, keytab, &cursor);
381  if (code) {
382  k5_error("Error while ending keytab scan",code);
383  retval = 1;
384  goto cleanup;
385  }
386 
387  /*
388  * if no principal name found in keytab for domain use the prinipal name which can get a TGT
389  */
390  if (!principal_name) {
391  size_t i;
392  debug((char *) "%s| %s: DEBUG: Did not find a principal in keytab for domain %s.\n", LogTime(), PROGRAM, domain);
393  debug((char *) "%s| %s: DEBUG: Try to get principal of trusted domain.\n", LogTime(), PROGRAM);
394 
395  for (i = 0; i < nprinc; ++i) {
396  krb5_creds *tgt_creds = NULL;
397  creds = (krb5_creds *) xmalloc(sizeof(*creds));
398  memset(creds, 0, sizeof(*creds));
399  /*
400  * get credentials
401  */
402  code = krb5_unparse_name(kparam.context, principal_list[i], &principal_name);
403  if (code) {
404  k5_error("Error while unparsing principal name",code);
405  goto loop_end;
406  }
407  debug((char *) "%s| %s: DEBUG: Keytab entry has principal: %s\n", LogTime(), PROGRAM, principal_name);
408 
409 #if HAVE_GET_INIT_CREDS_KEYTAB
410  code = krb5_get_init_creds_keytab(kparam.context, creds, principal_list[i], keytab, 0, NULL, NULL);
411 #else
412  service = (char *) xmalloc(strlen("krbtgt") + 2 * strlen(domain) + 3);
413  snprintf(service, strlen("krbtgt") + 2 * strlen(domain) + 3, "krbtgt/%s@%s", domain, domain);
414  creds->client = principal_list[i];
415  code = krb5_parse_name(kparam.context, service, &creds->server);
416  xfree(service);
417  code = krb5_get_in_tkt_with_keytab(kparam.context, 0, NULL, NULL, NULL, keytab, NULL, creds, 0);
418 #endif
419  if (code) {
420  k5_error("Error while initialising credentials from keytab",code);
421  goto loop_end;
422  }
423  code = krb5_cc_initialize(kparam.context, kparam.cc[ccindex], principal_list[i]);
424  if (code) {
425  k5_error("Error while initialising memory caches",code);
426  goto loop_end;
427  }
428  code = krb5_cc_store_cred(kparam.context, kparam.cc[ccindex], creds);
429  if (code) {
430  k5_error("Error while storing credentials",code);
431  goto loop_end;
432  }
433  if (creds->server)
434  krb5_free_principal(kparam.context, creds->server);
435 #if USE_HEIMDAL_KRB5
436  service = (char *) xmalloc(strlen("krbtgt") + strlen(domain) + strlen(principal_list[i]->realm) + 3);
437  snprintf(service, strlen("krbtgt") + strlen(domain) + strlen(principal_list[i]->realm) + 3, "krbtgt/%s@%s", domain, principal_list[i]->realm);
438 #else
439  service = (char *) xmalloc(strlen("krbtgt") + strlen(domain) + strlen(krb5_princ_realm(kparam.context, principal_list[i])->data) + 3);
440  snprintf(service, strlen("krbtgt") + strlen(domain) + strlen(krb5_princ_realm(kparam.context, principal_list[i])->data) + 3, "krbtgt/%s@%s", domain, krb5_princ_realm(kparam.context, principal_list[i])->data);
441 #endif
442  code = krb5_parse_name(kparam.context, service, &creds->server);
443  xfree(service);
444  if (code) {
445  k5_error("Error while initialising TGT credentials",code);
446  goto loop_end;
447  }
448  code = krb5_get_credentials(kparam.context, 0, kparam.cc[ccindex], creds, &tgt_creds);
449  if (code) {
450  k5_error("Error while getting tgt",code);
451  goto loop_end;
452  } else {
453  debug((char *) "%s| %s: DEBUG: Found trusted principal name: %s\n", LogTime(), PROGRAM, principal_name);
454  if (tgt_creds)
455  krb5_free_creds(kparam.context, tgt_creds);
456  tgt_creds = NULL;
457  break;
458  }
459 
460 loop_end:
461  safe_free(principal_name);
462  if (tgt_creds)
463  krb5_free_creds(kparam.context, tgt_creds);
464  tgt_creds = NULL;
465  if (creds)
466  krb5_free_creds(kparam.context, creds);
467  creds = NULL;
468 
469  }
470 
471  if (creds)
472  krb5_free_creds(kparam.context, creds);
473  creds = NULL;
474  }
475  } else {
476  debug((char *) "%s| %s: DEBUG: Got principal from ccache\n", LogTime(), PROGRAM);
477  /*
478  * get credentials
479  */
480  code = krb5_unparse_name(kparam.context, principal, &principal_name);
481  if (code) {
482  k5_error("Error while unparsing principal name",code);
483  retval = 1;
484  goto cleanup;
485  }
486  debug((char *) "%s| %s: DEBUG: ccache has principal: %s\n", LogTime(), PROGRAM, principal_name);
487  }
488 
489  if (!principal_name) {
490  debug((char *) "%s| %s: DEBUG: Got no principal name\n", LogTime(), PROGRAM);
491  retval = 1;
492  }
493 cleanup:
494  if (keytab)
495  krb5_kt_close(kparam.context, keytab);
496  xfree(keytab_name);
497  xfree(principal_name);
498  xfree(mem_cache);
499  if (principal)
500  krb5_free_principal(kparam.context, principal);
501  for (j = 0; j < nprinc; ++j) {
502  if (principal_list[j])
503  krb5_free_principal(kparam.context, principal_list[j]);
504  }
505  xfree(principal_list);
506  if (creds)
507  krb5_free_creds(kparam.context, creds);
508  return (retval);
509 }
510 #endif
511 
const char * LogTime(void)
#define assert(EX)
Definition: assert.h:17
#define xcalloc
Definition: membanger.c:57
void error(char *format,...)
int i
Definition: membanger.c:49
#define xstrdup
#define safe_free(x)
Definition: xalloc.h:73
char * p
Definition: membanger.c:43
void * xrealloc(void *s, size_t sz)
Definition: xalloc.cc:137
static int debug
Definition: tcp-banger3.c:105
#define xmalloc
#define PROGRAM
Definition: support.h:188
static hrtime_t now
Definition: ProfStats.cc:256
#define xfree
#define NULL
Definition: types.h:166

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors