]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/acl/external/kerberos_ldap_group/support_krb5.cc
2 * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
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.
10 * ----------------------------------------------------------------------------
12 * Author: Markus Moeller (markus_moeller at compuserve.com)
14 * Copyright (C) 2007 Markus Moeller. All rights reserved.
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.
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.
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.
30 * -----------------------------------------------------------------------------
36 #if HAVE_LDAP && HAVE_KRB5
41 extern struct kstruct kparam
;
44 #define KT_PATH_MAX 256
50 for (int i
=0; i
<MAX_DOMAINS
; i
++) {
52 krb5_cc_destroy(kparam
.context
, kparam
.cc
[i
]);
53 safe_free(kparam
.mem_ccache
[i
]);
55 krb5_free_context(kparam
.context
);
59 k5_error2(const char* msg
, char* msg2
, krb5_error_code code
)
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
);
74 k5_debug(const char* msg
, krb5_error_code code
)
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
);
89 k5_error(const char* msg
, krb5_error_code code
)
91 k5_error2(msg
, (char *)"", code
);
95 * create Kerberos memory cache
98 krb5_create_cache(char *domain
, char *service_principal_name
)
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
;
109 char *keytab_name
= NULL
, *principal_name
= NULL
, *mem_cache
= NULL
;
110 char buf
[KT_PATH_MAX
], *p
;
113 krb5_error_code code
= 0;
116 if (!domain
|| !strcmp(domain
, ""))
120 * prepare memory credential cache
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());
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());
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
])) {
138 if ( ccindex
== -1 ) {
139 kparam
.mem_ccache
[kparam
.ncache
]=xstrdup(mem_cache
);
140 ccindex
=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
);
147 code
= krb5_cc_resolve(kparam
.context
, mem_cache
, &kparam
.cc
[ccindex
]);
149 k5_error("Error while resolving memory ccache", code
);
155 * getting default principal from cache
158 code
= krb5_cc_get_principal(kparam
.context
, kparam
.cc
[ccindex
], &principal
);
161 krb5_free_principal(kparam
.context
, principal
);
163 k5_debug("No default principal found in ccache", code
);
166 * Look for krbtgt and check if it is expired (or soon to be expired)
168 code
= krb5_cc_start_seq_get(kparam
.context
, kparam
.cc
[ccindex
], &ccursor
);
170 k5_error("Error while starting ccache scan", code
);
171 code
= krb5_cc_close (kparam
.context
, kparam
.cc
[ccindex
]);
173 k5_error("Error while closing ccache", code
);
175 if (kparam
.cc
[ccindex
]) {
176 code
= krb5_cc_destroy(kparam
.context
, kparam
.cc
[ccindex
]);
178 k5_error("Error while destroying ccache", code
);
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
);
187 k5_error("Error while unparsing principal", code2
);
188 code
= krb5_cc_destroy(kparam
.context
, kparam
.cc
[ccindex
]);
190 k5_error("Error while destroying ccache", code
);
192 assert(creds
!= NULL
);
193 krb5_free_creds(kparam
.context
, creds
);
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
]);
199 k5_error("Error while resolving memory ccache", code
);
206 if (!strncmp(KRB5_TGS_NAME
,principal_name
,KRB5_TGS_NAME_SIZE
)) {
208 static krb5_deltat skew
=MAX_SKEW
;
210 debug((char *) "%s| %s: DEBUG: Found %s in cache : %s\n", LogTime(), PROGRAM
,KRB5_TGS_NAME
,principal_name
);
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
));
219 krb5_free_principal(kparam
.context
, principal
);
221 code
= krb5_cc_destroy(kparam
.context
, kparam
.cc
[ccindex
]);
223 k5_error("Error while destroying ccache", code
);
225 assert(creds
!= NULL
);
226 krb5_free_creds(kparam
.context
, creds
);
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
]);
232 k5_error("Error while resolving ccache", code
);
238 safe_free(principal_name
);
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
);
248 krb5_free_creds(kparam
.context
, creds
);
250 code2
= krb5_cc_end_seq_get(kparam
.context
, kparam
.cc
[ccindex
], &ccursor
);
252 k5_error("Error while ending ccache scan", code2
);
260 * getting default keytab name
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:" */
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
);
271 code
= krb5_kt_resolve(kparam
.context
, keytab_name
, &keytab
);
273 k5_error2("Error while resolving keytab ", keytab_name
,code
);
277 code
= krb5_kt_start_seq_get(kparam
.context
, keytab
, &cursor
);
279 k5_error("Error while starting keytab scan", code
);
283 debug((char *) "%s| %s: DEBUG: Get principal name from keytab %s\n", LogTime(), PROGRAM
, keytab_name
);
286 while ((code
= krb5_kt_next_entry(kparam
.context
, keytab
, &entry
, &cursor
)) == 0) {
289 principal_list
= (krb5_principal
*) xrealloc(principal_list
, sizeof(krb5_principal
) * (nprinc
+ 1));
290 krb5_copy_principal(kparam
.context
, entry
.principal
, &principal_list
[nprinc
++]);
292 debug((char *) "%s| %s: DEBUG: Keytab entry has realm name: %s\n", LogTime(), PROGRAM
, entry
.principal
->realm
);
294 debug((char *) "%s| %s: DEBUG: Keytab entry has realm name: %s\n", LogTime(), PROGRAM
, krb5_princ_realm(kparam
.context
, entry
.principal
)->data
);
297 if (!strcasecmp(domain
, entry
.principal
->realm
))
299 if (!strcasecmp(domain
, krb5_princ_realm(kparam
.context
, entry
.principal
)->data
))
302 code
= krb5_unparse_name(kparam
.context
, entry
.principal
, &principal_name
);
304 k5_error("Error while unparsing principal name", code
);
306 debug((char *) "%s| %s: DEBUG: Found principal name: %s\n", LogTime(), PROGRAM
, principal_name
);
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
);
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
);
318 code
= krb5_free_keytab_entry_contents(kparam
.context
, &entry
);
321 k5_error("Error while freeing keytab entry", code
);
326 debug((char *) "%s| %s: DEBUG: Got principal name %s\n", LogTime(), PROGRAM
, principal_name
);
330 code
= krb5_parse_name(kparam
.context
, principal_name
, &principal
);
332 k5_error2("Error while parsing name ", principal_name
,code
);
333 safe_free(principal_name
);
335 krb5_free_principal(kparam
.context
, principal
);
339 creds
= (krb5_creds
*) xcalloc(1,sizeof(*creds
));
344 #if HAVE_GET_INIT_CREDS_KEYTAB
345 code
= krb5_get_init_creds_keytab(kparam
.context
, creds
, principal
, keytab
, 0, NULL
, NULL
);
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
);
352 code
= krb5_get_in_tkt_with_keytab(kparam
.context
, 0, NULL
, NULL
, NULL
, keytab
, NULL
, creds
, 0);
356 k5_error("Error while initialising credentials from keytab", code
);
357 safe_free(principal_name
);
359 krb5_free_principal(kparam
.context
, principal
);
361 krb5_free_creds(kparam
.context
, creds
);
366 code
= krb5_cc_initialize(kparam
.context
, kparam
.cc
[ccindex
], principal
);
368 k5_error("Error while initialising cache", code
);
369 safe_free(principal_name
);
371 krb5_free_principal(kparam
.context
, principal
);
373 krb5_free_creds(kparam
.context
, creds
);
378 code
= krb5_cc_store_cred(kparam
.context
, kparam
.cc
[ccindex
], creds
);
380 k5_error("Error while storing credentials", code
);
382 krb5_free_principal(kparam
.context
, principal
);
383 safe_free(principal_name
);
385 krb5_free_creds(kparam
.context
, creds
);
390 debug((char *) "%s| %s: DEBUG: Stored credentials\n", LogTime(), PROGRAM
);
395 if (code
&& code
!= KRB5_KT_END
) {
396 k5_error("Error while scanning keytab", code
);
400 code
= krb5_kt_end_seq_get(kparam
.context
, keytab
, &cursor
);
402 k5_error("Error while ending keytab scan", code
);
408 * if no principal name found in keytab for domain use the prinipal name which can get a TGT
410 if (!principal_name
&& !service_principal_name
) {
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
);
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
));
422 code
= krb5_unparse_name(kparam
.context
, principal_list
[i
], &principal_name
);
424 k5_error("Error while unparsing principal name", code
);
427 debug((char *) "%s| %s: DEBUG: Keytab entry has principal: %s\n", LogTime(), PROGRAM
, principal_name
);
429 #if HAVE_GET_INIT_CREDS_KEYTAB
430 code
= krb5_get_init_creds_keytab(kparam
.context
, creds
, principal_list
[i
], keytab
, 0, NULL
, NULL
);
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
);
437 code
= krb5_get_in_tkt_with_keytab(kparam
.context
, 0, NULL
, NULL
, NULL
, keytab
, NULL
, creds
, 0);
440 k5_error("Error while initialising credentials from keytab", code
);
443 code
= krb5_cc_initialize(kparam
.context
, kparam
.cc
[ccindex
], principal_list
[i
]);
445 k5_error("Error while initialising memory caches", code
);
448 code
= krb5_cc_store_cred(kparam
.context
, kparam
.cc
[ccindex
], creds
);
450 k5_error("Error while storing credentials", code
);
454 krb5_free_principal(kparam
.context
, creds
->server
);
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
);
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
);
462 code
= krb5_parse_name(kparam
.context
, service
, &creds
->server
);
465 k5_error("Error while initialising TGT credentials", code
);
469 // overwrite limitation of enctypes
470 creds
->keyblock
.enctype
= 0;
471 if (creds
->keyblock
.contents
)
472 krb5_free_keyblock_contents(kparam
.context
, &creds
->keyblock
);
474 code
= krb5_get_credentials(kparam
.context
, 0, kparam
.cc
[ccindex
], creds
, &tgt_creds
);
476 k5_error("Error while getting tgt", code
);
479 debug((char *) "%s| %s: DEBUG: Found trusted principal name: %s\n", LogTime(), PROGRAM
, principal_name
);
481 krb5_free_creds(kparam
.context
, tgt_creds
);
487 safe_free(principal_name
);
489 krb5_free_creds(kparam
.context
, tgt_creds
);
492 krb5_free_creds(kparam
.context
, creds
);
498 krb5_free_creds(kparam
.context
, creds
);
502 debug((char *) "%s| %s: DEBUG: Got principal from ccache\n", LogTime(), PROGRAM
);
506 code
= krb5_unparse_name(kparam
.context
, principal
, &principal_name
);
508 k5_error("Error while unparsing principal name", code
);
512 debug((char *) "%s| %s: DEBUG: ccache has principal: %s\n", LogTime(), PROGRAM
, principal_name
);
515 if (!principal_name
) {
516 debug((char *) "%s| %s: DEBUG: Got no principal name\n", LogTime(), PROGRAM
);
521 krb5_kt_close(kparam
.context
, keytab
);
523 xfree(principal_name
);
526 krb5_free_principal(kparam
.context
, principal
);
527 for (j
= 0; j
< nprinc
; ++j
) {
528 if (principal_list
[j
])
529 krb5_free_principal(kparam
.context
, principal_list
[j
]);
531 xfree(principal_list
);
533 krb5_free_creds(kparam
.context
, creds
);