]> git.ipfire.org Git - thirdparty/squid.git/blame - src/acl/external/kerberos_ldap_group/support_krb5.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / acl / external / kerberos_ldap_group / support_krb5.cc
CommitLineData
ca02e0ec 1/*
4ac4a490 2 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
ca02e0ec
AJ
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
b1218840
AJ
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
f7f3304a 33#include "squid.h"
b1218840
AJ
34#include "util.h"
35
1a22a39e 36#if HAVE_LDAP && HAVE_KRB5
b1218840
AJ
37
38#include "support.h"
39
7451e5ad
MM
40#if HAVE_KRB5
41extern struct kstruct kparam;
42#endif
b1218840
AJ
43
44#define KT_PATH_MAX 256
45
46void
47krb5_cleanup()
48{
7451e5ad
MM
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);
b1218840 56}
f95eb8d8
MM
57
58static void
59k5_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
73static void
74k5_error(const char* msg, krb5_error_code code)
75{
76 k5_error2(msg, (char *)"", code);
77}
78
b1218840
AJ
79/*
80 * create Kerberos memory cache
81 */
82int
4ebcf1ce 83krb5_create_cache(char *domain)
b1218840
AJ
84{
85
7451e5ad 86 krb5_keytab keytab = NULL;
b1218840
AJ
87 krb5_keytab_entry entry;
88 krb5_kt_cursor cursor;
7451e5ad 89 krb5_cc_cursor ccursor;
b1218840 90 krb5_creds *creds = NULL;
b1218840
AJ
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;
4ebcf1ce 96 size_t j,nprinc = 0;
b1218840 97 int retval = 0;
b1218840 98 krb5_error_code code = 0;
7451e5ad 99 int ccindex=-1;
b1218840
AJ
100
101 if (!domain || !strcmp(domain, ""))
2e881a6f 102 return (1);
b1218840 103
b2bdde72
MM
104 /*
105 * prepare memory credential cache
106 */
107#if !HAVE_KRB5_MEMORY_CACHE || HAVE_SUN_LDAP_SDK
7451e5ad
MM
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());
b2bdde72 110#else
7451e5ad
MM
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());
b2bdde72
MM
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);
7451e5ad
MM
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 ) {
581d42c4 124 kparam.mem_ccache[kparam.ncache]=xstrdup(mem_cache);
7451e5ad
MM
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) {
f95eb8d8 134 k5_error("Error while resolving memory ccache",code);
7451e5ad
MM
135 retval = 1;
136 goto cleanup;
137 }
b2bdde72 138 }
b1218840 139 /*
7451e5ad 140 * getting default principal from cache
b1218840
AJ
141 */
142
7451e5ad 143 code = krb5_cc_get_principal(kparam.context, kparam.cc[ccindex], &principal);
b1218840 144 if (code) {
7451e5ad
MM
145 if (principal)
146 krb5_free_principal(kparam.context, principal);
147 principal = NULL;
f95eb8d8 148 k5_error("No default principal found in ccache",code);
7451e5ad
MM
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) {
f95eb8d8 155 k5_error("Error while starting ccache scan",code);
7451e5ad
MM
156 code = krb5_cc_close (kparam.context, kparam.cc[ccindex]);
157 if (code) {
f95eb8d8 158 k5_error("Error while closing ccache",code);
7451e5ad
MM
159 }
160 if (kparam.cc[ccindex]) {
161 code = krb5_cc_destroy(kparam.context, kparam.cc[ccindex]);
162 if (code) {
f95eb8d8 163 k5_error("Error while destroying ccache",code);
7451e5ad
MM
164 }
165 }
166 } else {
167 krb5_error_code code2 = 0;
4dccc594 168 creds = static_cast<krb5_creds *>(xcalloc(1,sizeof(*creds)));
7451e5ad
MM
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) {
f95eb8d8 172 k5_error("Error while unparsing principal",code2);
7451e5ad
MM
173 code = krb5_cc_destroy(kparam.context, kparam.cc[ccindex]);
174 if (code) {
f95eb8d8 175 k5_error("Error while destroying ccache",code);
7451e5ad 176 }
4dccc594
AJ
177 assert(creds != NULL);
178 krb5_free_creds(kparam.context, creds);
581d42c4 179 creds = NULL;
7451e5ad
MM
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) {
f95eb8d8 184 k5_error("Error while resolving memory ccache",code);
7451e5ad
MM
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) {
f95eb8d8 208 k5_error("Error while destroying ccache",code);
7451e5ad 209 }
4dccc594
AJ
210 assert(creds != NULL);
211 krb5_free_creds(kparam.context, creds);
7451e5ad
MM
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) {
f95eb8d8 217 k5_error("Error while resolving ccache",code);
7451e5ad
MM
218 retval = 1;
219 goto cleanup;
220 }
221 code = 1;
222 } else {
223 safe_free(principal_name);
224 }
225 break;
226 }
4dccc594
AJ
227 assert(creds != NULL);
228 krb5_free_creds(kparam.context, creds);
229 creds = static_cast<krb5_creds *>(xcalloc(1, sizeof(*creds)));
7451e5ad
MM
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) {
f95eb8d8 237 k5_error("Error while ending ccache scan",code2);
7451e5ad
MM
238 retval = 1;
239 goto cleanup;
240 }
241 }
b1218840 242 }
b1218840 243 if (code) {
7451e5ad
MM
244 /*
245 * getting default keytab name
246 */
b1218840 247
7451e5ad
MM
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);
f53969cc 250 p = strchr(buf, ':'); /* Find the end if "FILE:" */
7451e5ad 251 if (p)
f53969cc 252 ++p; /* step past : */
7451e5ad
MM
253 keytab_name = xstrdup(p ? p : buf);
254 debug((char *) "%s| %s: DEBUG: Got default keytab file name %s\n", LogTime(), PROGRAM, keytab_name);
b1218840 255
7451e5ad
MM
256 code = krb5_kt_resolve(kparam.context, keytab_name, &keytab);
257 if (code) {
f95eb8d8 258 k5_error2("Error while resolving keytab ",keytab_name,code);
7451e5ad
MM
259 retval = 1;
260 goto cleanup;
261 }
262 code = krb5_kt_start_seq_get(kparam.context, keytab, &cursor);
263 if (code) {
f95eb8d8 264 k5_error("Error while starting keytab scan",code);
7451e5ad
MM
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++]);
1a22a39e 276#if USE_HEIMDAL_KRB5
7451e5ad 277 debug((char *) "%s| %s: DEBUG: Keytab entry has realm name: %s\n", LogTime(), PROGRAM, entry.principal->realm);
b1218840 278#else
7451e5ad 279 debug((char *) "%s| %s: DEBUG: Keytab entry has realm name: %s\n", LogTime(), PROGRAM, krb5_princ_realm(kparam.context, entry.principal)->data);
b1218840 280#endif
1a22a39e 281#if USE_HEIMDAL_KRB5
7451e5ad 282 if (!strcasecmp(domain, entry.principal->realm))
b1218840 283#else
7451e5ad 284 if (!strcasecmp(domain, krb5_princ_realm(kparam.context, entry.principal)->data))
b1218840 285#endif
7451e5ad
MM
286 {
287 code = krb5_unparse_name(kparam.context, entry.principal, &principal_name);
288 if (code) {
f95eb8d8 289 k5_error("Error while unparsing principal name",code);
7451e5ad
MM
290 } else {
291 debug((char *) "%s| %s: DEBUG: Found principal name: %s\n", LogTime(), PROGRAM, principal_name);
292 found = 1;
293 }
2e881a6f 294 }
1a22a39e 295#if USE_HEIMDAL_KRB5 || ( HAVE_KRB5_KT_FREE_ENTRY && HAVE_DECL_KRB5_KT_FREE_ENTRY )
7451e5ad 296 code = krb5_kt_free_entry(kparam.context, &entry);
b1218840 297#else
7451e5ad 298 code = krb5_free_keytab_entry_contents(kparam.context, &entry);
b1218840 299#endif
b2bdde72 300 if (code) {
f95eb8d8 301 k5_error("Error while freeing keytab entry",code);
7451e5ad
MM
302 retval = 1;
303 break;
b2bdde72 304 }
7451e5ad
MM
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) {
f95eb8d8 312 k5_error2("Error while parsing name ", principal_name,code);
7451e5ad
MM
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));
b2bdde72 320
7451e5ad
MM
321 /*
322 * get credentials
323 */
b2bdde72 324#if HAVE_GET_INIT_CREDS_KEYTAB
7451e5ad 325 code = krb5_get_init_creds_keytab(kparam.context, creds, principal, keytab, 0, NULL, NULL);
b2bdde72 326#else
7451e5ad
MM
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);
b2bdde72
MM
333#endif
334
7451e5ad 335 if (code) {
f95eb8d8 336 k5_error("Error while initialising credentials from keytab" ,code);
7451e5ad
MM
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) {
f95eb8d8 348 k5_error("Error while initialising memory caches" ,code);
7451e5ad
MM
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) {
f95eb8d8 360 k5_error("Error while storing credentials" ,code);
7451e5ad
MM
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;
b2bdde72 372 }
b2bdde72 373 }
b1218840 374
7451e5ad 375 if (code && code != KRB5_KT_END) {
f95eb8d8 376 k5_error("Error while scanning keytab" ,code);
7451e5ad
MM
377 retval = 1;
378 goto cleanup;
379 }
380 code = krb5_kt_end_seq_get(kparam.context, keytab, &cursor);
381 if (code) {
f95eb8d8 382 k5_error("Error while ending keytab scan" ,code);
7451e5ad
MM
383 retval = 1;
384 goto cleanup;
385 }
b1218840 386
7451e5ad
MM
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);
b1218840 394
7451e5ad
MM
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) {
f95eb8d8 404 k5_error("Error while unparsing principal name" ,code);
7451e5ad
MM
405 goto loop_end;
406 }
407 debug((char *) "%s| %s: DEBUG: Keytab entry has principal: %s\n", LogTime(), PROGRAM, principal_name);
b1218840
AJ
408
409#if HAVE_GET_INIT_CREDS_KEYTAB
7451e5ad 410 code = krb5_get_init_creds_keytab(kparam.context, creds, principal_list[i], keytab, 0, NULL, NULL);
b1218840 411#else
7451e5ad
MM
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);
b1218840 418#endif
7451e5ad 419 if (code) {
f95eb8d8 420 k5_error("Error while initialising credentials from keytab" ,code);
7451e5ad
MM
421 goto loop_end;
422 }
423 code = krb5_cc_initialize(kparam.context, kparam.cc[ccindex], principal_list[i]);
424 if (code) {
f95eb8d8 425 k5_error("Error while initialising memory caches" ,code);
7451e5ad
MM
426 goto loop_end;
427 }
428 code = krb5_cc_store_cred(kparam.context, kparam.cc[ccindex], creds);
429 if (code) {
f95eb8d8 430 k5_error("Error while storing credentials" ,code);
7451e5ad
MM
431 goto loop_end;
432 }
433 if (creds->server)
434 krb5_free_principal(kparam.context, creds->server);
1a22a39e 435#if USE_HEIMDAL_KRB5
7451e5ad
MM
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);
b1218840 438#else
7451e5ad
MM
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);
b1218840 441#endif
7451e5ad
MM
442 code = krb5_parse_name(kparam.context, service, &creds->server);
443 xfree(service);
444 if (code) {
f95eb8d8 445 k5_error("Error while initialising TGT credentials" ,code);
7451e5ad
MM
446 goto loop_end;
447 }
448 code = krb5_get_credentials(kparam.context, 0, kparam.cc[ccindex], creds, &tgt_creds);
449 if (code) {
f95eb8d8 450 k5_error("Error while getting tgt" ,code);
7451e5ad
MM
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
460loop_end:
461 safe_free(principal_name);
b2bdde72
MM
462 if (tgt_creds)
463 krb5_free_creds(kparam.context, tgt_creds);
464 tgt_creds = NULL;
7451e5ad
MM
465 if (creds)
466 krb5_free_creds(kparam.context, creds);
467 creds = NULL;
468
2e881a6f 469 }
b1218840 470
4ebcf1ce
MM
471 if (creds)
472 krb5_free_creds(kparam.context, creds);
473 creds = NULL;
2e881a6f 474 }
7451e5ad
MM
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) {
f95eb8d8 482 k5_error("Error while unparsing principal name" ,code);
7451e5ad
MM
483 retval = 1;
484 goto cleanup;
485 }
486 debug((char *) "%s| %s: DEBUG: ccache has principal: %s\n", LogTime(), PROGRAM, principal_name);
b1218840 487 }
7451e5ad 488
b2bdde72 489 if (!principal_name) {
2e881a6f
A
490 debug((char *) "%s| %s: DEBUG: Got no principal name\n", LogTime(), PROGRAM);
491 retval = 1;
b1218840 492 }
2e881a6f 493cleanup:
b1218840 494 if (keytab)
2e881a6f 495 krb5_kt_close(kparam.context, keytab);
4ebcf1ce
MM
496 xfree(keytab_name);
497 xfree(principal_name);
498 xfree(mem_cache);
b1218840 499 if (principal)
2e881a6f 500 krb5_free_principal(kparam.context, principal);
4ebcf1ce
MM
501 for (j = 0; j < nprinc; ++j) {
502 if (principal_list[j])
503 krb5_free_principal(kparam.context, principal_list[j]);
b1218840 504 }
4ebcf1ce 505 xfree(principal_list);
b1218840 506 if (creds)
2e881a6f 507 krb5_free_creds(kparam.context, creds);
b1218840
AJ
508 return (retval);
509}
510#endif
f53969cc 511