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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors