/* * Copyright (C) 1996-2023 The Squid Software Foundation and contributors * * Squid software is distributed under GPLv2+ license and includes * contributions from numerous individuals and organizations. * Please see the COPYING and CONTRIBUTORS files for details. */ /* * ---------------------------------------------------------------------------- * * Author: Markus Moeller (markus_moeller at compuserve.com) * * Copyright (C) 2007 Markus Moeller. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * ----------------------------------------------------------------------------- */ #include "squid.h" #include "util.h" #if HAVE_LDAP && HAVE_KRB5 #include "support.h" #if HAVE_KRB5 extern struct kstruct kparam; #endif #define KT_PATH_MAX 256 void krb5_cleanup() { if (kparam.context) for (int i=0; i(xcalloc(1,sizeof(*creds))); while ((krb5_cc_next_cred(kparam.context, kparam.cc[ccindex], &ccursor, creds)) == 0) { code2 = krb5_unparse_name(kparam.context, creds->server, &principal_name); if (code2) { k5_error("Error while unparsing principal", code2); code = krb5_cc_destroy(kparam.context, kparam.cc[ccindex]); if (code) { k5_error("Error while destroying ccache", code); } assert(creds != NULL); krb5_free_creds(kparam.context, creds); creds = NULL; safe_free(principal_name); debug((char *) "%s| %s: DEBUG: Reset credential cache to %s\n", LogTime(), PROGRAM, mem_cache); code = krb5_cc_resolve(kparam.context, mem_cache, &kparam.cc[ccindex]); if (code) { k5_error("Error while resolving memory ccache", code); retval = 1; goto cleanup; } code =1; break; } if (!strncmp(KRB5_TGS_NAME,principal_name,KRB5_TGS_NAME_SIZE)) { time_t now; static krb5_deltat skew=MAX_SKEW; debug((char *) "%s| %s: DEBUG: Found %s in cache : %s\n", LogTime(), PROGRAM,KRB5_TGS_NAME,principal_name); /* * Check time */ time(&now); debug((char *) "%s| %s: DEBUG: credential time diff %d\n", LogTime(), PROGRAM, (int)(creds->times.endtime - now)); if (creds->times.endtime - now < 2*skew) { debug((char *) "%s| %s: DEBUG: credential will soon expire %d\n", LogTime(), PROGRAM, (int)(creds->times.endtime - now)); if (principal) krb5_free_principal(kparam.context, principal); principal = NULL; code = krb5_cc_destroy(kparam.context, kparam.cc[ccindex]); if (code) { k5_error("Error while destroying ccache", code); } assert(creds != NULL); krb5_free_creds(kparam.context, creds); creds = NULL; safe_free(principal_name); debug((char *) "%s| %s: DEBUG: Reset credential cache to %s\n", LogTime(), PROGRAM, mem_cache); code = krb5_cc_resolve(kparam.context, mem_cache, &kparam.cc[ccindex]); if (code) { k5_error("Error while resolving ccache", code); retval = 1; goto cleanup; } code = 1; } else { safe_free(principal_name); } break; } assert(creds != NULL); krb5_free_creds(kparam.context, creds); creds = static_cast(xcalloc(1, sizeof(*creds))); safe_free(principal_name); } if (creds) krb5_free_creds(kparam.context, creds); creds = NULL; code2 = krb5_cc_end_seq_get(kparam.context, kparam.cc[ccindex], &ccursor); if (code2) { k5_error("Error while ending ccache scan", code2); retval = 1; goto cleanup; } } } if (code) { /* * getting default keytab name */ debug((char *) "%s| %s: DEBUG: Get default keytab file name\n", LogTime(), PROGRAM); krb5_kt_default_name(kparam.context, buf, KT_PATH_MAX); p = strchr(buf, ':'); /* Find the end if "FILE:" */ if (p) ++p; /* step past : */ keytab_name = xstrdup(p ? p : buf); debug((char *) "%s| %s: DEBUG: Got default keytab file name %s\n", LogTime(), PROGRAM, keytab_name); code = krb5_kt_resolve(kparam.context, keytab_name, &keytab); if (code) { k5_error2("Error while resolving keytab ", keytab_name,code); retval = 1; goto cleanup; } code = krb5_kt_start_seq_get(kparam.context, keytab, &cursor); if (code) { k5_error("Error while starting keytab scan", code); retval = 1; goto cleanup; } debug((char *) "%s| %s: DEBUG: Get principal name from keytab %s\n", LogTime(), PROGRAM, keytab_name); nprinc = 0; while ((code = krb5_kt_next_entry(kparam.context, keytab, &entry, &cursor)) == 0) { int found = 0; principal_list = (krb5_principal *) xrealloc(principal_list, sizeof(krb5_principal) * (nprinc + 1)); krb5_copy_principal(kparam.context, entry.principal, &principal_list[nprinc++]); #if USE_HEIMDAL_KRB5 debug((char *) "%s| %s: DEBUG: Keytab entry has realm name: %s\n", LogTime(), PROGRAM, entry.principal->realm); #else debug((char *) "%s| %s: DEBUG: Keytab entry has realm name: %s\n", LogTime(), PROGRAM, krb5_princ_realm(kparam.context, entry.principal)->data); #endif #if USE_HEIMDAL_KRB5 if (!strcasecmp(domain, entry.principal->realm)) #else if (!strcasecmp(domain, krb5_princ_realm(kparam.context, entry.principal)->data)) #endif { code = krb5_unparse_name(kparam.context, entry.principal, &principal_name); if (code) { k5_error("Error while unparsing principal name", code); } else { debug((char *) "%s| %s: DEBUG: Found principal name: %s\n", LogTime(), PROGRAM, principal_name); found = 1; if (service_principal_name && strcasecmp(principal_name,service_principal_name) != 0 ) { debug((char *) "%s| %s: DEBUG: principal name does not match parameter: %s\n", LogTime(), PROGRAM, service_principal_name); safe_free(principal_name); found = 0; } } } #if USE_HEIMDAL_KRB5 || ( HAVE_KRB5_KT_FREE_ENTRY && HAVE_DECL_KRB5_KT_FREE_ENTRY ) code = krb5_kt_free_entry(kparam.context, &entry); #else code = krb5_free_keytab_entry_contents(kparam.context, &entry); #endif if (code) { k5_error("Error while freeing keytab entry", code); retval = 1; break; } if (found) { debug((char *) "%s| %s: DEBUG: Got principal name %s\n", LogTime(), PROGRAM, principal_name); /* * build principal */ code = krb5_parse_name(kparam.context, principal_name, &principal); if (code) { k5_error2("Error while parsing name ", principal_name,code); safe_free(principal_name); if (principal) krb5_free_principal(kparam.context, principal); found = 0; continue; } creds = (krb5_creds *) xcalloc(1,sizeof(*creds)); /* * get credentials */ #if HAVE_GET_INIT_CREDS_KEYTAB code = krb5_get_init_creds_keytab(kparam.context, creds, principal, keytab, 0, NULL, NULL); #else service = (char *) xmalloc(strlen("krbtgt") + 2 * strlen(domain) + 3); snprintf(service, strlen("krbtgt") + 2 * strlen(domain) + 3, "krbtgt/%s@%s", domain, domain); creds->client = principal; code = krb5_parse_name(kparam.context, service, &creds->server); xfree(service); code = krb5_get_in_tkt_with_keytab(kparam.context, 0, NULL, NULL, NULL, keytab, NULL, creds, 0); #endif if (code) { k5_error("Error while initialising credentials from keytab", code); safe_free(principal_name); if (principal) krb5_free_principal(kparam.context, principal); if (creds) krb5_free_creds(kparam.context, creds); creds = NULL; found = 0; continue; } code = krb5_cc_initialize(kparam.context, kparam.cc[ccindex], principal); if (code) { k5_error("Error while initialising cache", code); safe_free(principal_name); if (principal) krb5_free_principal(kparam.context, principal); if (creds) krb5_free_creds(kparam.context, creds); creds = NULL; found = 0; continue; } code = krb5_cc_store_cred(kparam.context, kparam.cc[ccindex], creds); if (code) { k5_error("Error while storing credentials", code); if (principal) krb5_free_principal(kparam.context, principal); safe_free(principal_name); if (creds) krb5_free_creds(kparam.context, creds); creds = NULL; found = 0; continue; } debug((char *) "%s| %s: DEBUG: Stored credentials\n", LogTime(), PROGRAM); break; } } if (code && code != KRB5_KT_END) { k5_error("Error while scanning keytab", code); retval = 1; goto cleanup; } code = krb5_kt_end_seq_get(kparam.context, keytab, &cursor); if (code) { k5_error("Error while ending keytab scan", code); retval = 1; goto cleanup; } /* * if no principal name found in keytab for domain use the prinipal name which can get a TGT */ if (!principal_name && !service_principal_name) { size_t i; debug((char *) "%s| %s: DEBUG: Did not find a principal in keytab for domain %s.\n", LogTime(), PROGRAM, domain); debug((char *) "%s| %s: DEBUG: Try to get principal of trusted domain.\n", LogTime(), PROGRAM); for (i = 0; i < nprinc; ++i) { krb5_creds *tgt_creds = NULL; creds = (krb5_creds *) xmalloc(sizeof(*creds)); memset(creds, 0, sizeof(*creds)); /* * get credentials */ code = krb5_unparse_name(kparam.context, principal_list[i], &principal_name); if (code) { k5_error("Error while unparsing principal name", code); goto loop_end; } debug((char *) "%s| %s: DEBUG: Keytab entry has principal: %s\n", LogTime(), PROGRAM, principal_name); #if HAVE_GET_INIT_CREDS_KEYTAB code = krb5_get_init_creds_keytab(kparam.context, creds, principal_list[i], keytab, 0, NULL, NULL); #else service = (char *) xmalloc(strlen("krbtgt") + 2 * strlen(domain) + 3); snprintf(service, strlen("krbtgt") + 2 * strlen(domain) + 3, "krbtgt/%s@%s", domain, domain); creds->client = principal_list[i]; code = krb5_parse_name(kparam.context, service, &creds->server); xfree(service); code = krb5_get_in_tkt_with_keytab(kparam.context, 0, NULL, NULL, NULL, keytab, NULL, creds, 0); #endif if (code) { k5_error("Error while initialising credentials from keytab", code); goto loop_end; } code = krb5_cc_initialize(kparam.context, kparam.cc[ccindex], principal_list[i]); if (code) { k5_error("Error while initialising memory caches", code); goto loop_end; } code = krb5_cc_store_cred(kparam.context, kparam.cc[ccindex], creds); if (code) { k5_error("Error while storing credentials", code); goto loop_end; } if (creds->server) krb5_free_principal(kparam.context, creds->server); #if USE_HEIMDAL_KRB5 service = (char *) xmalloc(strlen("krbtgt") + strlen(domain) + strlen(principal_list[i]->realm) + 3); snprintf(service, strlen("krbtgt") + strlen(domain) + strlen(principal_list[i]->realm) + 3, "krbtgt/%s@%s", domain, principal_list[i]->realm); #else service = (char *) xmalloc(strlen("krbtgt") + strlen(domain) + strlen(krb5_princ_realm(kparam.context, principal_list[i])->data) + 3); 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); #endif code = krb5_parse_name(kparam.context, service, &creds->server); xfree(service); if (code) { k5_error("Error while initialising TGT credentials", code); goto loop_end; } // overwrite limitation of enctypes #if USE_HEIMDAL_KRB5 creds->session.keytype = 0; if (creds->session.keyvalue.length > 0) krb5_free_keyblock_contents(kparam.context, &creds->session); #else creds->keyblock.enctype = 0; if (creds->keyblock.contents) krb5_free_keyblock_contents(kparam.context, &creds->keyblock); #endif code = krb5_get_credentials(kparam.context, 0, kparam.cc[ccindex], creds, &tgt_creds); if (code) { k5_error("Error while getting tgt", code); goto loop_end; } else { debug((char *) "%s| %s: DEBUG: Found trusted principal name: %s\n", LogTime(), PROGRAM, principal_name); if (tgt_creds) krb5_free_creds(kparam.context, tgt_creds); tgt_creds = NULL; break; } loop_end: safe_free(principal_name); if (tgt_creds) krb5_free_creds(kparam.context, tgt_creds); tgt_creds = NULL; if (creds) krb5_free_creds(kparam.context, creds); creds = NULL; } if (creds) krb5_free_creds(kparam.context, creds); creds = NULL; } } else { debug((char *) "%s| %s: DEBUG: Got principal from ccache\n", LogTime(), PROGRAM); /* * get credentials */ code = krb5_unparse_name(kparam.context, principal, &principal_name); if (code) { k5_error("Error while unparsing principal name", code); retval = 1; goto cleanup; } debug((char *) "%s| %s: DEBUG: ccache has principal: %s\n", LogTime(), PROGRAM, principal_name); } if (!principal_name) { debug((char *) "%s| %s: DEBUG: Got no principal name\n", LogTime(), PROGRAM); retval = 1; } cleanup: if (keytab) krb5_kt_close(kparam.context, keytab); xfree(keytab_name); xfree(principal_name); xfree(mem_cache); if (principal) krb5_free_principal(kparam.context, principal); for (j = 0; j < nprinc; ++j) { if (principal_list[j]) krb5_free_principal(kparam.context, principal_list[j]); } xfree(principal_list); if (creds) krb5_free_creds(kparam.context, creds); return (retval); } #endif