Line data Source code
1 : /*
2 : * GSSAPI Security Extensions
3 : * Krb5 helpers
4 : * Copyright (C) Simo Sorce 2010.
5 : *
6 : * This program is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU General Public License as published by
8 : * the Free Software Foundation; either version 3 of the License, or
9 : * (at your option) any later version.
10 : *
11 : * This program is distributed in the hope that it will be useful,
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : * GNU General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU General Public License
17 : * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include "includes.h"
21 : #include "smb_krb5.h"
22 : #include "secrets.h"
23 : #include "librpc/gen_ndr/secrets.h"
24 : #include "gse_krb5.h"
25 : #include "lib/param/loadparm.h"
26 : #include "libads/kerberos_proto.h"
27 : #include "lib/util/string_wrappers.h"
28 :
29 : #ifdef HAVE_KRB5
30 :
31 0 : static krb5_error_code flush_keytab(krb5_context krbctx, krb5_keytab keytab)
32 : {
33 0 : krb5_error_code ret;
34 0 : krb5_kt_cursor kt_cursor;
35 0 : krb5_keytab_entry kt_entry;
36 :
37 0 : ZERO_STRUCT(kt_entry);
38 :
39 0 : ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
40 0 : if (ret != 0) {
41 0 : return ret;
42 : }
43 :
44 0 : ret = krb5_kt_next_entry(krbctx, keytab, &kt_entry, &kt_cursor);
45 0 : while (ret == 0) {
46 :
47 : /* we need to close and reopen enumeration because we modify
48 : * the keytab */
49 0 : ret = krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
50 0 : if (ret != 0) {
51 0 : DEBUG(1, (__location__ ": krb5_kt_end_seq_get() "
52 : "failed (%s)\n", error_message(ret)));
53 0 : goto out;
54 : }
55 :
56 : /* remove the entry */
57 0 : ret = krb5_kt_remove_entry(krbctx, keytab, &kt_entry);
58 0 : if (ret != 0) {
59 0 : DEBUG(1, (__location__ ": krb5_kt_remove_entry() "
60 : "failed (%s)\n", error_message(ret)));
61 0 : goto out;
62 : }
63 0 : smb_krb5_kt_free_entry(krbctx, &kt_entry);
64 0 : ZERO_STRUCT(kt_entry);
65 :
66 : /* now reopen */
67 0 : ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
68 0 : if (ret != 0) {
69 0 : DEBUG(1, (__location__ ": krb5_kt_start_seq() failed "
70 : "(%s)\n", error_message(ret)));
71 0 : goto out;
72 : }
73 :
74 0 : ret = krb5_kt_next_entry(krbctx, keytab,
75 : &kt_entry, &kt_cursor);
76 : }
77 :
78 0 : if (ret != KRB5_KT_END && ret != ENOENT) {
79 0 : DEBUG(1, (__location__ ": flushing keytab we got [%s]!\n",
80 : error_message(ret)));
81 : }
82 :
83 0 : ret = krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
84 0 : if (ret != 0) {
85 0 : DEBUG(1, (__location__ ": krb5_kt_end_seq_get() "
86 : "failed (%s)\n", error_message(ret)));
87 0 : goto out;
88 : }
89 0 : ret = 0;
90 :
91 0 : out:
92 0 : return ret;
93 : }
94 :
95 9322 : static krb5_error_code fill_keytab_from_password(krb5_context krbctx,
96 : krb5_keytab keytab,
97 : krb5_principal princ,
98 : krb5_kvno vno,
99 : struct secrets_domain_info1_password *pw)
100 : {
101 0 : krb5_error_code ret;
102 0 : krb5_enctype *enctypes;
103 0 : uint16_t i;
104 :
105 9322 : ret = smb_krb5_get_allowed_etypes(krbctx, &enctypes);
106 9322 : if (ret) {
107 0 : DEBUG(1, (__location__
108 : ": Can't determine permitted enctypes!\n"));
109 0 : return ret;
110 : }
111 :
112 37288 : for (i = 0; i < pw->num_keys; i++) {
113 : krb5_keytab_entry kt_entry;
114 27966 : krb5_keyblock *key = NULL;
115 : unsigned int ei;
116 27966 : bool found_etype = false;
117 :
118 79358 : for (ei=0; enctypes[ei] != 0; ei++) {
119 79358 : if ((uint32_t)enctypes[ei] != pw->keys[i].keytype) {
120 51392 : continue;
121 : }
122 :
123 27966 : found_etype = true;
124 27966 : break;
125 : }
126 :
127 27966 : if (!found_etype) {
128 0 : continue;
129 : }
130 :
131 27966 : ZERO_STRUCT(kt_entry);
132 27966 : kt_entry.principal = princ;
133 27966 : kt_entry.vno = vno;
134 :
135 27966 : key = KRB5_KT_KEY(&kt_entry);
136 27966 : KRB5_KEY_TYPE(key) = pw->keys[i].keytype;
137 27966 : KRB5_KEY_DATA(key) = pw->keys[i].value.data;
138 27966 : KRB5_KEY_LENGTH(key) = pw->keys[i].value.length;
139 :
140 27966 : ret = krb5_kt_add_entry(krbctx, keytab, &kt_entry);
141 27966 : if (ret) {
142 0 : DEBUG(1, (__location__ ": Failed to add entry to "
143 : "keytab for enctype %d (error: %s)\n",
144 : (unsigned)pw->keys[i].keytype,
145 : error_message(ret)));
146 0 : goto out;
147 : }
148 : }
149 :
150 9322 : ret = 0;
151 :
152 9322 : out:
153 9322 : krb5_free_enctypes(krbctx, enctypes);
154 9322 : return ret;
155 : }
156 :
157 : #define SRV_MEM_KEYTAB_NAME "MEMORY:cifs_srv_keytab"
158 : #define CLEARTEXT_PRIV_ENCTYPE -99
159 :
160 4441 : static krb5_error_code fill_mem_keytab_from_secrets(krb5_context krbctx,
161 : krb5_keytab *keytab)
162 : {
163 4441 : TALLOC_CTX *frame = talloc_stackframe();
164 0 : krb5_error_code ret, ret2;
165 4441 : const char *domain = lp_workgroup();
166 4441 : struct secrets_domain_info1 *info = NULL;
167 4441 : const char *realm = NULL;
168 4441 : const DATA_BLOB *ct = NULL;
169 0 : krb5_kt_cursor kt_cursor;
170 0 : krb5_keytab_entry kt_entry;
171 4441 : krb5_principal princ = NULL;
172 4441 : krb5_kvno kvno = 0; /* FIXME: fetch current vno from KDC ? */
173 0 : NTSTATUS status;
174 :
175 4441 : if (!secrets_init()) {
176 0 : DEBUG(1, (__location__ ": secrets_init failed\n"));
177 0 : TALLOC_FREE(frame);
178 0 : return KRB5_CONFIG_CANTOPEN;
179 : }
180 :
181 4441 : status = secrets_fetch_or_upgrade_domain_info(domain,
182 : frame,
183 : &info);
184 4441 : if (!NT_STATUS_IS_OK(status)) {
185 4 : DBG_WARNING("secrets_fetch_or_upgrade_domain_info(%s) - %s\n",
186 : domain, nt_errstr(status));
187 4 : TALLOC_FREE(frame);
188 4 : return KRB5_LIBOS_CANTREADPWD;
189 : }
190 4437 : ct = &info->password->cleartext_blob;
191 :
192 4437 : if (info->domain_info.dns_domain.string != NULL) {
193 4437 : realm = strupper_talloc(frame,
194 4437 : info->domain_info.dns_domain.string);
195 4437 : if (realm == NULL) {
196 0 : TALLOC_FREE(frame);
197 0 : return ENOMEM;
198 : }
199 : }
200 :
201 4437 : ZERO_STRUCT(kt_entry);
202 4437 : ZERO_STRUCT(kt_cursor);
203 :
204 : /* check if the keytab already has any entry */
205 4437 : ret = krb5_kt_start_seq_get(krbctx, *keytab, &kt_cursor);
206 4437 : if (ret != 0) {
207 0 : goto out;
208 : }
209 :
210 : /* check if we have our special enctype used to hold
211 : * the clear text password. If so, check it out so that
212 : * we can verify if the keytab needs to be upgraded */
213 4533 : while ((ret = krb5_kt_next_entry(krbctx, *keytab,
214 4533 : &kt_entry, &kt_cursor)) == 0) {
215 118 : if (smb_krb5_kt_get_enctype_from_entry(&kt_entry) ==
216 : CLEARTEXT_PRIV_ENCTYPE) {
217 22 : break;
218 : }
219 96 : smb_krb5_kt_free_entry(krbctx, &kt_entry);
220 96 : ZERO_STRUCT(kt_entry);
221 : }
222 :
223 4437 : ret2 = krb5_kt_end_seq_get(krbctx, *keytab, &kt_cursor);
224 4437 : if (ret2 != 0) {
225 0 : ret = ret2;
226 0 : DEBUG(1, (__location__ ": krb5_kt_end_seq_get() "
227 : "failed (%s)\n", error_message(ret)));
228 0 : goto out;
229 : }
230 :
231 4437 : if (ret != 0 && ret != KRB5_KT_END && ret != ENOENT ) {
232 : /* Error parsing keytab */
233 0 : DEBUG(1, (__location__ ": Failed to parse memory "
234 : "keytab!\n"));
235 0 : goto out;
236 : }
237 :
238 4437 : if (ret == 0) {
239 : /* found private entry,
240 : * check if keytab is up to date */
241 :
242 44 : if ((ct->length == KRB5_KEY_LENGTH(KRB5_KT_KEY(&kt_entry))) &&
243 22 : (mem_equal_const_time(KRB5_KEY_DATA(KRB5_KT_KEY(&kt_entry)),
244 22 : ct->data, ct->length))) {
245 : /* keytab is already up to date, return */
246 22 : smb_krb5_kt_free_entry(krbctx, &kt_entry);
247 22 : goto out;
248 : }
249 :
250 0 : smb_krb5_kt_free_entry(krbctx, &kt_entry);
251 0 : ZERO_STRUCT(kt_entry);
252 :
253 :
254 : /* flush keytab, we need to regen it */
255 0 : ret = flush_keytab(krbctx, *keytab);
256 0 : if (ret) {
257 0 : DEBUG(1, (__location__ ": Failed to flush "
258 : "memory keytab!\n"));
259 0 : goto out;
260 : }
261 : }
262 :
263 : /* keytab is not up to date, fill it up */
264 :
265 4415 : ret = smb_krb5_make_principal(krbctx, &princ, realm,
266 4415 : info->account_name, NULL);
267 4415 : if (ret) {
268 0 : DEBUG(1, (__location__ ": Failed to get host principal!\n"));
269 0 : goto out;
270 : }
271 :
272 4415 : ret = fill_keytab_from_password(krbctx, *keytab,
273 : princ, kvno,
274 4415 : info->password);
275 4415 : if (ret) {
276 0 : DBG_WARNING("fill_keytab_from_password() failed for "
277 : "info->password.\n.");
278 0 : goto out;
279 : }
280 :
281 4415 : if (info->old_password != NULL) {
282 2627 : ret = fill_keytab_from_password(krbctx, *keytab,
283 : princ, kvno - 1,
284 2627 : info->old_password);
285 2627 : if (ret) {
286 0 : DBG_WARNING("fill_keytab_from_password() failed for "
287 : "info->old_password.\n.");
288 0 : goto out;
289 : }
290 : }
291 :
292 4415 : if (info->older_password != NULL) {
293 2280 : ret = fill_keytab_from_password(krbctx, *keytab,
294 : princ, kvno - 2,
295 2280 : info->older_password);
296 2280 : if (ret) {
297 0 : DBG_WARNING("fill_keytab_from_password() failed for "
298 : "info->older_password.\n.");
299 0 : goto out;
300 : }
301 : }
302 :
303 4415 : if (info->next_change != NULL) {
304 0 : ret = fill_keytab_from_password(krbctx, *keytab,
305 : princ, kvno - 3,
306 0 : info->next_change->password);
307 0 : if (ret) {
308 0 : DBG_WARNING("fill_keytab_from_password() failed for "
309 : "info->next_change->password.\n.");
310 0 : goto out;
311 : }
312 : }
313 :
314 : /* add our private enctype + cleartext password so that we can
315 : * update the keytab if secrets change later on */
316 4415 : ZERO_STRUCT(kt_entry);
317 4415 : kt_entry.principal = princ;
318 4415 : kt_entry.vno = 0;
319 :
320 4415 : KRB5_KEY_TYPE(KRB5_KT_KEY(&kt_entry)) = CLEARTEXT_PRIV_ENCTYPE;
321 4415 : KRB5_KEY_LENGTH(KRB5_KT_KEY(&kt_entry)) = ct->length;
322 4415 : KRB5_KEY_DATA(KRB5_KT_KEY(&kt_entry)) = ct->data;
323 :
324 4415 : ret = krb5_kt_add_entry(krbctx, *keytab, &kt_entry);
325 4415 : if (ret) {
326 0 : DEBUG(1, (__location__ ": Failed to add entry to "
327 : "keytab for private enctype (%d) (error: %s)\n",
328 : CLEARTEXT_PRIV_ENCTYPE, error_message(ret)));
329 0 : goto out;
330 : }
331 :
332 4415 : ret = 0;
333 :
334 4437 : out:
335 :
336 4437 : if (princ) {
337 4415 : krb5_free_principal(krbctx, princ);
338 : }
339 :
340 4437 : TALLOC_FREE(frame);
341 4437 : return ret;
342 : }
343 :
344 0 : static krb5_error_code fill_mem_keytab_from_system_keytab(krb5_context krbctx,
345 : krb5_keytab *mkeytab)
346 : {
347 0 : krb5_error_code ret = 0;
348 0 : krb5_keytab keytab = NULL;
349 0 : krb5_kt_cursor kt_cursor = { 0, };
350 0 : krb5_keytab_entry kt_entry = { 0, };
351 0 : char *valid_princ_formats[7] = { NULL, NULL, NULL,
352 : NULL, NULL, NULL, NULL };
353 0 : char *entry_princ_s = NULL;
354 0 : fstring my_name, my_fqdn;
355 0 : unsigned i;
356 0 : int err;
357 0 : const char *dns_hostname = NULL;
358 :
359 : /* Generate the list of principal names which we expect
360 : * clients might want to use for authenticating to the file
361 : * service. We allow name$,{host,cifs}/{name,fqdn,name.REALM}. */
362 :
363 0 : fstrcpy(my_name, lp_netbios_name());
364 0 : dns_hostname = lp_dns_hostname();
365 0 : if (dns_hostname == NULL) {
366 0 : ret = ENOMEM;
367 0 : goto out;
368 : }
369 0 : fstrcpy(my_fqdn, dns_hostname);
370 :
371 0 : err = asprintf(&valid_princ_formats[0],
372 : "%s$@%s", my_name, lp_realm());
373 0 : if (err == -1) {
374 0 : ret = ENOMEM;
375 0 : goto out;
376 : }
377 0 : err = asprintf(&valid_princ_formats[1],
378 : "host/%s@%s", my_name, lp_realm());
379 0 : if (err == -1) {
380 0 : ret = ENOMEM;
381 0 : goto out;
382 : }
383 0 : err = asprintf(&valid_princ_formats[2],
384 : "host/%s@%s", my_fqdn, lp_realm());
385 0 : if (err == -1) {
386 0 : ret = ENOMEM;
387 0 : goto out;
388 : }
389 0 : err = asprintf(&valid_princ_formats[3],
390 : "host/%s.%s@%s", my_name, lp_realm(), lp_realm());
391 0 : if (err == -1) {
392 0 : ret = ENOMEM;
393 0 : goto out;
394 : }
395 0 : err = asprintf(&valid_princ_formats[4],
396 : "cifs/%s@%s", my_name, lp_realm());
397 0 : if (err == -1) {
398 0 : ret = ENOMEM;
399 0 : goto out;
400 : }
401 0 : err = asprintf(&valid_princ_formats[5],
402 : "cifs/%s@%s", my_fqdn, lp_realm());
403 0 : if (err == -1) {
404 0 : ret = ENOMEM;
405 0 : goto out;
406 : }
407 0 : err = asprintf(&valid_princ_formats[6],
408 : "cifs/%s.%s@%s", my_name, lp_realm(), lp_realm());
409 0 : if (err == -1) {
410 0 : ret = ENOMEM;
411 0 : goto out;
412 : }
413 :
414 0 : ret = smb_krb5_kt_open_relative(krbctx, NULL, false, &keytab);
415 0 : if (ret) {
416 0 : DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
417 : error_message(ret)));
418 0 : goto out;
419 : }
420 :
421 : /*
422 : * Iterate through the keytab. For each key, if the principal
423 : * name case-insensitively matches one of the allowed formats,
424 : * copy it to the memory keytab.
425 : */
426 :
427 0 : ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
428 0 : if (ret) {
429 0 : DEBUG(1, (__location__ ": krb5_kt_start_seq_get failed (%s)\n",
430 : error_message(ret)));
431 : /*
432 : * krb5_kt_start_seq_get() may leaves bogus data
433 : * in kt_cursor. And we want to use the all_zero()
434 : * logic below.
435 : *
436 : * See bug #10490
437 : */
438 0 : ZERO_STRUCT(kt_cursor);
439 0 : goto out;
440 : }
441 :
442 0 : while ((krb5_kt_next_entry(krbctx, keytab,
443 0 : &kt_entry, &kt_cursor) == 0)) {
444 0 : ret = smb_krb5_unparse_name(talloc_tos(), krbctx,
445 0 : kt_entry.principal,
446 : &entry_princ_s);
447 0 : if (ret) {
448 0 : DEBUG(1, (__location__ ": smb_krb5_unparse_name "
449 : "failed (%s)\n", error_message(ret)));
450 0 : goto out;
451 : }
452 :
453 0 : for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) {
454 :
455 0 : if (!strequal(entry_princ_s, valid_princ_formats[i])) {
456 0 : continue;
457 : }
458 :
459 0 : ret = krb5_kt_add_entry(krbctx, *mkeytab, &kt_entry);
460 0 : if (ret) {
461 0 : DEBUG(1, (__location__ ": smb_krb5_unparse_name "
462 : "failed (%s)\n", error_message(ret)));
463 0 : goto out;
464 : }
465 : }
466 :
467 : /* Free the name we parsed. */
468 0 : TALLOC_FREE(entry_princ_s);
469 :
470 : /* Free the entry we just read. */
471 0 : smb_krb5_kt_free_entry(krbctx, &kt_entry);
472 0 : ZERO_STRUCT(kt_entry);
473 : }
474 0 : krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
475 :
476 0 : ZERO_STRUCT(kt_cursor);
477 :
478 0 : out:
479 :
480 0 : for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) {
481 0 : SAFE_FREE(valid_princ_formats[i]);
482 : }
483 :
484 0 : TALLOC_FREE(entry_princ_s);
485 :
486 0 : if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
487 0 : smb_krb5_kt_free_entry(krbctx, &kt_entry);
488 : }
489 :
490 0 : if (!all_zero((uint8_t *)&kt_cursor, sizeof(kt_cursor)) && keytab) {
491 0 : krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
492 : }
493 :
494 0 : if (keytab) {
495 0 : krb5_kt_close(krbctx, keytab);
496 : }
497 :
498 0 : return ret;
499 : }
500 :
501 6 : static krb5_error_code fill_mem_keytab_from_dedicated_keytab(krb5_context krbctx,
502 : krb5_keytab *mkeytab)
503 : {
504 6 : krb5_error_code ret = 0;
505 6 : krb5_keytab keytab = NULL;
506 0 : krb5_kt_cursor kt_cursor;
507 0 : krb5_keytab_entry kt_entry;
508 :
509 6 : ret = smb_krb5_kt_open(krbctx, lp_dedicated_keytab_file(),
510 : false, &keytab);
511 6 : if (ret) {
512 0 : DEBUG(1, ("smb_krb5_kt_open of %s failed (%s)\n",
513 : lp_dedicated_keytab_file(),
514 : error_message(ret)));
515 0 : return ret;
516 : }
517 :
518 : /*
519 : * Copy the dedicated keyab to our in-memory keytab.
520 : */
521 :
522 6 : ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
523 6 : if (ret) {
524 6 : DEBUG(1, (__location__ ": krb5_kt_start_seq_get on %s "
525 : "failed (%s)\n",
526 : lp_dedicated_keytab_file(),
527 : error_message(ret)));
528 6 : goto out;
529 : }
530 :
531 0 : while ((krb5_kt_next_entry(krbctx, keytab,
532 0 : &kt_entry, &kt_cursor) == 0)) {
533 :
534 0 : ret = krb5_kt_add_entry(krbctx, *mkeytab, &kt_entry);
535 :
536 : /* Free the entry we just read. */
537 0 : smb_krb5_kt_free_entry(krbctx, &kt_entry);
538 :
539 0 : if (ret) {
540 0 : DEBUG(1, (__location__ ": smb_krb5_unparse_name "
541 : "failed (%s)\n", error_message(ret)));
542 0 : break;
543 : }
544 : }
545 0 : krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
546 :
547 6 : out:
548 :
549 6 : krb5_kt_close(krbctx, keytab);
550 :
551 6 : return ret;
552 : }
553 :
554 4447 : krb5_error_code gse_krb5_get_server_keytab(krb5_context krbctx,
555 : krb5_keytab *keytab)
556 : {
557 4447 : krb5_error_code ret = 0;
558 4447 : krb5_error_code ret1 = 0;
559 4447 : krb5_error_code ret2 = 0;
560 :
561 4447 : *keytab = NULL;
562 :
563 : /* create memory keytab */
564 4447 : ret = krb5_kt_resolve(krbctx, SRV_MEM_KEYTAB_NAME, keytab);
565 4447 : if (ret) {
566 0 : DEBUG(1, (__location__ ": Failed to get memory "
567 : "keytab!\n"));
568 0 : return ret;
569 : }
570 :
571 4447 : switch (lp_kerberos_method()) {
572 4441 : default:
573 : case KERBEROS_VERIFY_SECRETS:
574 4441 : ret = fill_mem_keytab_from_secrets(krbctx, keytab);
575 4441 : break;
576 0 : case KERBEROS_VERIFY_SYSTEM_KEYTAB:
577 0 : ret = fill_mem_keytab_from_system_keytab(krbctx, keytab);
578 0 : break;
579 6 : case KERBEROS_VERIFY_DEDICATED_KEYTAB:
580 : /* just use whatever keytab is configured */
581 6 : ret = fill_mem_keytab_from_dedicated_keytab(krbctx, keytab);
582 6 : break;
583 0 : case KERBEROS_VERIFY_SECRETS_AND_KEYTAB:
584 0 : ret1 = fill_mem_keytab_from_secrets(krbctx, keytab);
585 0 : if (ret1) {
586 0 : DEBUG(3, (__location__ ": Warning! Unable to set mem "
587 : "keytab from secrets!\n"));
588 : }
589 : /* Now append system keytab keys too */
590 0 : ret2 = fill_mem_keytab_from_system_keytab(krbctx, keytab);
591 0 : if (ret2) {
592 0 : DEBUG(3, (__location__ ": Warning! Unable to set mem "
593 : "keytab from system keytab!\n"));
594 : }
595 0 : if (ret1 == 0 || ret2 == 0) {
596 0 : ret = 0;
597 : } else {
598 0 : ret = ret1;
599 : }
600 0 : break;
601 : }
602 :
603 4447 : if (ret) {
604 10 : krb5_kt_close(krbctx, *keytab);
605 10 : *keytab = NULL;
606 10 : DEBUG(1,("%s: Error! Unable to set mem keytab - %d\n",
607 : __location__, ret));
608 : }
609 :
610 4447 : return ret;
611 : }
612 :
613 : #endif /* HAVE_KRB5 */
|