1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2 * vim:expandtab:shiftwidth=8:tabstop=8:
4 * Modifications for Lustre
6 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
8 * Author: Eric Mei <ericm@clusterfs.com>
12 * Adapted in part from MIT Kerberos 5-1.2.1 slave/kprop.c and from
13 * http://docs.sun.com/?p=/doc/816-1331/6m7oo9sms&a=view
15 * Copyright (c) 2002-2004 The Regents of the University of Michigan.
16 * All rights reserved.
18 * Andy Adamson <andros@umich.edu>
19 * J. Bruce Fields <bfields@umich.edu>
20 * Marius Aamodt Eriksen <marius@umich.edu>
21 * Kevin Coffman <kwc@umich.edu>
27 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
28 * All Rights Reserved.
30 * Export of this software from the United States of America may
31 * require a specific license from the United States Government.
32 * It is the responsibility of any person or organization contemplating
33 * export to obtain such a license before exporting.
35 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
36 * distribute this software and its documentation for any purpose and
37 * without fee is hereby granted, provided that the above copyright
38 * notice appear in all copies and that both that copyright notice and
39 * this permission notice appear in supporting documentation, and that
40 * the name of M.I.T. not be used in advertising or publicity pertaining
41 * to distribution of the software without specific, written prior
42 * permission. Furthermore if you modify this software you must label
43 * your software as modified software and not distribute it in such a
44 * fashion that it might be confused with the original M.I.T. software.
45 * M.I.T. makes no representations about the suitability of
46 * this software for any purpose. It is provided "as is" without express
47 * or implied warranty.
51 * Copyright 1994 by OpenVision Technologies, Inc.
53 * Permission to use, copy, modify, distribute, and sell this software
54 * and its documentation for any purpose is hereby granted without fee,
55 * provided that the above copyright notice appears in all copies and
56 * that both that copyright notice and this permission notice appear in
57 * supporting documentation, and that the name of OpenVision not be used
58 * in advertising or publicity pertaining to distribution of the software
59 * without specific, written prior permission. OpenVision makes no
60 * representations about the suitability of this software for any
61 * purpose. It is provided "as is" without express or implied warranty.
63 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
64 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
65 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
66 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
67 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
68 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
69 * PERFORMANCE OF THIS SOFTWARE.
74 Copyright (c) 2004 The Regents of the University of Michigan.
77 Redistribution and use in source and binary forms, with or without
78 modification, are permitted provided that the following conditions
81 1. Redistributions of source code must retain the above copyright
82 notice, this list of conditions and the following disclaimer.
83 2. Redistributions in binary form must reproduce the above copyright
84 notice, this list of conditions and the following disclaimer in the
85 documentation and/or other materials provided with the distribution.
86 3. Neither the name of the University nor the names of its
87 contributors may be used to endorse or promote products derived
88 from this software without specific prior written permission.
90 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
91 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
92 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
93 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
94 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
95 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
96 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
97 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
98 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
99 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
100 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
108 #include <sys/param.h>
109 //#include <rpc/rpc.h>
110 #include <sys/types.h>
111 #include <sys/stat.h>
112 #include <sys/utsname.h>
113 #include <sys/socket.h>
114 #include <arpa/inet.h>
125 #include <gssapi/gssapi.h>
126 #ifdef USE_PRIVATE_KRB5_FUNCTIONS
127 #include <gssapi/gssapi_krb5.h>
131 #include "lgss_utils.h"
132 #include "lgss_krb5_utils.h"
134 static void lgss_krb5_mutex_lock(void)
136 if (lgss_mutex_lock(LGSS_MUTEX_KRB5)) {
137 logmsg(LL_ERR, "can't lock process, abort!\n");
142 static void lgss_krb5_mutex_unlock(void)
144 if (lgss_mutex_unlock(LGSS_MUTEX_KRB5)) {
145 logmsg(LL_WARN, "can't unlock process, other processes "
146 "might need to wait long time\n");
152 * - currently we only support "normal" cache types: "FILE" and "MEMORY".
155 #define krb5_err_msg(code) error_message(code)
157 const char *krb5_cc_type_mem = "MEMORY:";
158 const char *krb5_cc_type_file = "FILE:";
159 const char *krb5_cred_root_suffix = "lustre_root";
160 const char *krb5_cred_mds_suffix = "lustre_mds";
161 const char *krb5_cred_oss_suffix = "lustre_oss";
163 char *krb5_this_realm = NULL;
164 char *krb5_keytab_file = "/etc/krb5.keytab";
165 char *krb5_cc_type = "FILE:";
166 char *krb5_cc_dir = "/tmp";
167 char *krb5_cred_prefix = "krb5cc_";
169 struct lgss_krb5_cred {
171 int kc_remove; /* remove cache upon release */
175 int lgss_krb5_set_ccache_name(const char *ccname)
177 #ifdef USE_GSS_KRB5_CCACHE_NAME
178 unsigned int maj_stat, min_stat;
180 maj_stat = gss_krb5_ccache_name(&min_stat, ccname, NULL);
181 if (maj_stat != GSS_S_COMPLETE) {
182 logmsg(LL_ERR, "failed to set ccache name\n");
187 * Set the KRB5CCNAME environment variable to tell the krb5 code
188 * which credentials cache to use. (Instead of using the private
189 * function above for which there is no generic gssapi equivalent)
191 if (setenv("KRB5CCNAME", ccname, 1)) {
192 logmsg(LL_ERR, "set env of krb5 ccname: %s\n",
197 logmsg(LL_DEBUG, "set cc: %s\n", ccname);
202 int lgss_krb5_get_local_realm(void)
204 krb5_context context = NULL;
205 krb5_error_code code;
208 if (krb5_this_realm != NULL)
211 code = krb5_init_context(&context);
213 logmsg(LL_ERR, "init ctx: %s\n", krb5_err_msg(code));
217 code = krb5_get_default_realm(context, &krb5_this_realm);
219 logmsg(LL_ERR, "get default realm: %s\n", krb5_err_msg(code));
223 logmsg(LL_DEBUG, "Local realm: %s\n", krb5_this_realm);
226 krb5_free_context(context);
231 int princ_is_local_realm(krb5_context ctx, krb5_principal princ)
233 return (lgss_krb5_strcasecmp(krb5_princ_realm(ctx, princ),
234 krb5_this_realm) == 0);
238 int svc_princ_verify_host(krb5_context ctx,
239 krb5_principal princ,
242 struct utsname utsbuf;
243 struct hostent *host;
245 if (krb5_princ_component(ctx, princ, 1) == NULL) {
246 logmsg(loglevel, "service principal has no host part\n");
250 if (uname(&utsbuf)) {
251 logmsg(loglevel, "get UTS name: %s\n", strerror(errno));
255 host = gethostbyname(utsbuf.nodename);
257 logmsg(loglevel, "failed to get local hostname\n");
261 if (lgss_krb5_strcasecmp(krb5_princ_component(ctx, princ, 1),
263 logmsg(loglevel, "service principal: hostname %.*s "
264 "doesn't match localhost %s\n",
265 krb5_princ_component(ctx, princ, 1)->length,
266 krb5_princ_component(ctx, princ, 1)->data,
275 int lkrb5_cc_check_tgt_princ(krb5_context ctx,
277 krb5_principal princ,
280 const char *princ_name;
282 logmsg(LL_DEBUG, "principal: realm %.*s, type %d, size %d, name %.*s\n",
283 krb5_princ_realm(ctx, princ)->length,
284 krb5_princ_realm(ctx, princ)->data,
285 krb5_princ_type(ctx, princ),
286 krb5_princ_size(ctx, princ),
287 krb5_princ_name(ctx, princ)->length,
288 krb5_princ_name(ctx, princ)->data);
291 if (krb5_princ_type(ctx, princ) != KRB5_NT_PRINCIPAL) {
292 logmsg(LL_WARN, "principal type %d is not I want\n",
293 krb5_princ_type(ctx, princ));
297 /* check local realm */
298 if (!princ_is_local_realm(ctx, princ)) {
299 logmsg(LL_WARN, "principal realm %.*s not local: %s\n",
300 krb5_princ_realm(ctx, princ)->length,
301 krb5_princ_realm(ctx, princ)->data,
306 /* check principal name */
308 case LGSS_ROOT_CRED_ROOT:
309 princ_name = LGSS_USR_ROOT_STR;
311 case LGSS_ROOT_CRED_MDT:
312 princ_name = LGSS_SVC_MDS_STR;
314 case LGSS_ROOT_CRED_OST:
315 princ_name = LGSS_SVC_OSS_STR;
321 if (lgss_krb5_strcmp(krb5_princ_name(ctx, princ), princ_name)) {
322 logmsg(LL_WARN, "%.*s: we expect %s instead\n",
323 krb5_princ_name(ctx, princ)->length,
324 krb5_princ_name(ctx, princ)->data,
330 * verify the hostname part of the principal, except we do allow
331 * lustre_root without binding to a host.
333 if (krb5_princ_component(ctx, princ, 1) == NULL) {
334 if (flag != LGSS_ROOT_CRED_ROOT) {
335 logmsg(LL_WARN, "%.*s: missing hostname\n",
336 krb5_princ_name(ctx, princ)->length,
337 krb5_princ_name(ctx, princ)->data);
341 if (svc_princ_verify_host(ctx, princ, LL_WARN)) {
342 logmsg(LL_DEBUG, "%.*s: doesn't belong to this node\n",
343 krb5_princ_name(ctx, princ)->length,
344 krb5_princ_name(ctx, princ)->data);
349 logmsg(LL_TRACE, "principal is OK\n");
354 * compose the TGT cc name, according to the root flags.
357 void get_root_tgt_ccname(char *ccname, int size, unsigned int flag)
362 case LGSS_ROOT_CRED_ROOT:
363 suffix = krb5_cred_root_suffix;
365 case LGSS_ROOT_CRED_MDT:
366 suffix = krb5_cred_mds_suffix;
368 case LGSS_ROOT_CRED_OST:
369 suffix = krb5_cred_oss_suffix;
375 snprintf(ccname, size, "%s%s/%s%s_%s",
376 krb5_cc_type, krb5_cc_dir, krb5_cred_prefix,
377 suffix, krb5_this_realm);
381 int lkrb5_check_root_tgt_cc_base(krb5_context ctx,
386 krb5_ccache tgt_ccache;
388 krb5_principal princ;
389 krb5_cc_cursor cursor;
390 krb5_error_code code;
392 int rc = -1, found = 0;
394 /* prepare parsing the cache file */
395 code = krb5_cc_resolve(ctx, ccname, &tgt_ccache);
397 logmsg(LL_ERR, "resolve krb5 cc %s: %s\n",
398 ccname, krb5_err_msg(code));
402 /* checks the principal */
403 code = krb5_cc_get_principal(ctx, tgt_ccache, &princ);
405 logmsg(LL_ERR, "get cc principal: %s\n", krb5_err_msg(code));
409 if (lkrb5_cc_check_tgt_princ(ctx, tgt_ccache, princ, flag))
415 code = krb5_cc_start_seq_get(ctx, tgt_ccache, &cursor);
417 logmsg(LL_ERR, "start cc iteration: %s\n", krb5_err_msg(code));
423 krb5_timestamp duration, delta;
425 code = krb5_cc_next_cred(ctx, tgt_ccache, &cursor, &cred);
429 logmsg(LL_DEBUG, "cred: server realm %.*s, type %d, name %.*s; "
430 "time (%d-%d, renew till %d), valid %d\n",
431 krb5_princ_realm(ctx, cred.server)->length,
432 krb5_princ_realm(ctx, cred.server)->data,
433 krb5_princ_type(ctx, cred.server),
434 krb5_princ_name(ctx, cred.server)->length,
435 krb5_princ_name(ctx, cred.server)->data,
436 cred.times.starttime, cred.times.endtime,
437 cred.times.renew_till, cred.times.endtime - now);
440 * we found the princ type is always 0 (KRB5_NT_UNKNOWN), why???
443 /* FIXME how about inter-realm TGT??? FIXME */
444 if (lgss_krb5_strcasecmp(krb5_princ_name(ctx, cred.server),
448 if (lgss_krb5_strcasecmp(krb5_princ_realm(ctx, cred.server),
452 /* check validity of time */
453 delta = 60 * 30; /* half an hour */
454 duration = cred.times.endtime - cred.times.starttime;
455 if (duration / 4 < delta)
456 delta = duration / 4;
458 if (cred.times.starttime <= now &&
459 cred.times.endtime >= now + delta) {
466 logmsg(LL_DEBUG, "doesn't find good TGT cache\n");
470 /* found a good cred, store it into @ccache */
471 logmsg(LL_DEBUG, "found good TGT cache\n");
473 code = krb5_cc_initialize(ctx, ccache, princ);
475 logmsg(LL_ERR, "init private cc: %s\n", krb5_err_msg(code));
479 code = krb5_cc_store_cred(ctx, ccache, &cred);
481 logmsg(LL_ERR, "store private cred: %s\n", krb5_err_msg(code));
485 logmsg(LL_DEBUG, "store private ccache OK\n");
489 krb5_cc_end_seq_get(ctx, tgt_ccache, &cursor);
491 krb5_free_principal(ctx, princ);
493 krb5_cc_close(ctx, tgt_ccache);
499 * find out whether current TGT cache is valid or not
502 int lkrb5_check_root_tgt_cc(krb5_context ctx,
504 unsigned int root_flags)
512 for (i = 0; i < LGSS_ROOT_CRED_NR; i++) {
515 if ((root_flags & flag) == 0)
518 get_root_tgt_ccname(ccname, sizeof(ccname), flag);
519 logmsg(LL_DEBUG, "root krb5 TGT ccname: %s\n", ccname);
521 /* currently we only support type "FILE", firstly make sure
522 * the cache file is there */
523 ccfile = ccname + strlen(krb5_cc_type);
524 if (stat(ccfile, &statbuf)) {
525 logmsg(LL_DEBUG, "krb5 cc %s: %s\n",
526 ccname, strerror(errno));
530 rc = lkrb5_check_root_tgt_cc_base(ctx, ccache, ccname, flag);
535 logmsg(LL_TRACE, "doesn't find a valid tgt cc\n");
540 int lkrb5_get_root_tgt_keytab(krb5_context ctx,
543 krb5_principal princ,
546 krb5_get_init_creds_opt opts;
548 krb5_ccache tgt_ccache;
549 krb5_error_code code;
552 krb5_get_init_creds_opt_init(&opts);
553 krb5_get_init_creds_opt_set_address_list(&opts, NULL);
555 * by default krb5 library obtain ticket with lifetime shorter
556 * than the max value. we can change it here if we want. but
557 * seems not necessary now.
559 krb5_get_init_creds_opt_set_tkt_life(&opts, very-long-time);
564 * obtain TGT and store into global ccache
566 code = krb5_get_init_creds_keytab(ctx, &cred, princ, kt,
569 logmsg(LL_ERR, "failed to get root TGT for "
570 "principal %.*s: %s\n",
571 krb5_princ_name(ctx, princ)->length,
572 krb5_princ_name(ctx, princ)->data,
577 code = krb5_cc_resolve(ctx, ccname, &tgt_ccache);
579 logmsg(LL_ERR, "resolve cc %s: %s\n",
580 ccname, krb5_err_msg(code));
584 code = krb5_cc_initialize(ctx, tgt_ccache, princ);
586 logmsg(LL_ERR, "initialize cc %s: %s\n",
587 ccname, krb5_err_msg(code));
591 code = krb5_cc_store_cred(ctx, tgt_ccache, &cred);
593 logmsg(LL_ERR, "store cred to cc %s: %s\n",
594 ccname, krb5_err_msg(code));
598 logmsg(LL_INFO, "installed TGT of %.*s in cc %s\n",
599 krb5_princ_name(ctx, princ)->length,
600 krb5_princ_name(ctx, princ)->data,
604 * now store the cred into my own cc too
606 code = krb5_cc_initialize(ctx, ccache, princ);
608 logmsg(LL_ERR, "init mem cc: %s\n", krb5_err_msg(code));
612 code = krb5_cc_store_cred(ctx, ccache, &cred);
614 logmsg(LL_ERR, "store mm cred: %s\n", krb5_err_msg(code));
618 logmsg(LL_DEBUG, "stored TGT into mem cc OK\n");
621 krb5_cc_close(ctx, tgt_ccache);
623 krb5_free_cred_contents(ctx, &cred);
628 * obtain a new root TGT
631 int lkrb5_refresh_root_tgt_cc(krb5_context ctx,
633 unsigned int root_flags)
636 krb5_keytab_entry kte;
637 krb5_kt_cursor cursor;
638 krb5_principal princ = NULL;
639 krb5_error_code code;
641 unsigned int flag = 0;
644 /* prepare parsing the keytab file */
645 code = krb5_kt_resolve(ctx, krb5_keytab_file, &kt);
647 logmsg(LL_ERR, "resolve keytab %s: %s\n",
648 krb5_keytab_file, krb5_err_msg(code));
652 code = krb5_kt_start_seq_get(ctx, kt, &cursor);
654 logmsg(LL_ERR, "start kt iteration: %s\n", krb5_err_msg(code));
658 /* iterate keytab to find proper a entry */
660 krb5_data *princname;
662 code = krb5_kt_next_entry(ctx, kt, &kte, &cursor);
666 logmsg(LL_TRACE, "kt entry: realm %.*s, type %d, "
667 "size %d, name %.*s\n",
668 krb5_princ_realm(ctx, kte.principal)->length,
669 krb5_princ_realm(ctx, kte.principal)->data,
670 krb5_princ_type(ctx, kte.principal),
671 krb5_princ_size(ctx, kte.principal),
672 krb5_princ_name(ctx, kte.principal)->length,
673 krb5_princ_name(ctx, kte.principal)->data);
675 if (!princ_is_local_realm(ctx, kte.principal))
678 princname = krb5_princ_name(ctx, kte.principal);
680 if ((root_flags & LGSS_ROOT_CRED_ROOT) != 0 &&
681 lgss_krb5_strcmp(princname, LGSS_USR_ROOT_STR) == 0) {
682 flag = LGSS_ROOT_CRED_ROOT;
683 } else if ((root_flags & LGSS_ROOT_CRED_MDT) != 0 &&
684 lgss_krb5_strcmp(princname, LGSS_SVC_MDS_STR) == 0) {
685 flag = LGSS_ROOT_CRED_MDT;
686 } else if ((root_flags & LGSS_ROOT_CRED_OST) != 0 &&
687 lgss_krb5_strcmp(princname, LGSS_SVC_OSS_STR) == 0) {
688 flag = LGSS_ROOT_CRED_OST;
690 logmsg(LL_TRACE, "not what we want, skip\n");
694 if (krb5_princ_component(ctx, kte.principal, 1) == NULL) {
695 if (flag != LGSS_ROOT_CRED_ROOT) {
696 logmsg(LL_TRACE, "no hostname, skip\n");
700 if (svc_princ_verify_host(ctx, kte.principal,
702 logmsg(LL_TRACE, "doesn't belong to this "
708 code = krb5_copy_principal(ctx, kte.principal, &princ);
710 logmsg(LL_ERR, "copy princ: %s\n", krb5_err_msg(code));
714 lassert(princ != NULL);
718 krb5_kt_end_seq_get(ctx, kt, &cursor);
721 logmsg(LL_ERR, "can't find proper keytab entry\n");
725 /* obtain root TGT */
726 get_root_tgt_ccname(ccname, sizeof(ccname), flag);
727 rc = lkrb5_get_root_tgt_keytab(ctx, ccache, kt, princ, ccname);
729 krb5_free_principal(ctx, princ);
731 krb5_kt_close(ctx, kt);
736 int lkrb5_prepare_root_cred(struct lgss_cred *cred)
740 krb5_error_code code;
741 struct lgss_krb5_cred *kcred;
744 lassert(krb5_this_realm != NULL);
746 kcred = (struct lgss_krb5_cred *) cred->lc_mech_cred;
748 /* compose the memory cc name, since the only user is myself,
749 * the name could be fixed */
750 snprintf(kcred->kc_ccname, sizeof(kcred->kc_ccname),
751 "%s/self", krb5_cc_type_mem);
752 logmsg(LL_TRACE, "private cc: %s\n", kcred->kc_ccname);
754 code = krb5_init_context(&ctx);
756 logmsg(LL_ERR, "initialize krb5 context: %s\n",
761 code = krb5_cc_resolve(ctx, kcred->kc_ccname, &ccache);
763 logmsg(LL_ERR, "resolve krb5 cc %s: %s\n",
764 kcred->kc_ccname, krb5_err_msg(code));
769 * search and/or obtain root TGT credential.
770 * it touched global (on-disk) tgt cache, do it inside mutex locking
772 lgss_krb5_mutex_lock();
774 rc = lkrb5_check_root_tgt_cc(ctx, ccache, cred->lc_root_flags);
776 rc = lkrb5_refresh_root_tgt_cc(ctx, ccache,
777 cred->lc_root_flags);
780 rc = lgss_krb5_set_ccache_name(kcred->kc_ccname);
782 lgss_krb5_mutex_unlock();
784 krb5_cc_close(ctx, ccache);
786 krb5_free_context(ctx);
788 logmsg(LL_DEBUG, "prepare root credentail %s\n", rc ? "failed" : "OK");
793 int lkrb5_prepare_user_cred(struct lgss_cred *cred)
795 struct lgss_krb5_cred *kcred;
798 lassert(krb5_this_realm == NULL);
800 kcred = (struct lgss_krb5_cred *) cred->lc_mech_cred;
803 * here we just specified a fix ccname, instead of searching
804 * entire cc dir. is this OK??
806 snprintf(kcred->kc_ccname, sizeof(kcred->kc_ccname),
808 krb5_cc_type, krb5_cc_dir, krb5_cred_prefix, cred->lc_uid);
809 logmsg(LL_DEBUG, "using krb5 cache name: %s\n", kcred->kc_ccname);
811 rc = lgss_krb5_set_ccache_name(kcred->kc_ccname);
813 logmsg(LL_ERR, "can't set krb5 ccache name: %s\n",
820 int lgss_krb5_prepare_cred(struct lgss_cred *cred)
822 struct lgss_krb5_cred *kcred;
825 kcred = malloc(sizeof(*kcred));
827 logmsg(LL_ERR, "can't allocate krb5 cred\n");
831 kcred->kc_ccname[0] = '\0';
832 kcred->kc_remove = 0;
833 cred->lc_mech_cred = kcred;
835 if (cred->lc_root_flags != 0) {
836 if (lgss_krb5_get_local_realm())
839 rc = lkrb5_prepare_root_cred(cred);
841 rc = lkrb5_prepare_user_cred(cred);
848 void lgss_krb5_release_cred(struct lgss_cred *cred)
850 struct lgss_krb5_cred *kcred;
852 kcred = (struct lgss_krb5_cred *) cred->lc_mech_cred;
855 cred->lc_mech_cred = NULL;
858 struct lgss_mech_type lgss_mech_krb5 =
861 .lmt_mech_n = LGSS_MECH_KRB5,
862 .lmt_prepare_cred = lgss_krb5_prepare_cred,
863 .lmt_release_cred = lgss_krb5_release_cred,