Whamcloud - gitweb
b=23728 gss: allow oss authenticate with mgs.
[fs/lustre-release.git] / lustre / utils / gss / lgss_krb5_utils.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * Modifications for Lustre
5  *
6  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
7  *
8  * Author: Eric Mei <ericm@clusterfs.com>
9  */
10
11 /*
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
14  *
15  *  Copyright (c) 2002-2004 The Regents of the University of Michigan.
16  *  All rights reserved.
17  *
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>
22  */
23
24 /*
25  * slave/kprop.c
26  *
27  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
28  * All Rights Reserved.
29  *
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.
34  *
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.
48  */
49
50 /*
51  * Copyright 1994 by OpenVision Technologies, Inc.
52  *
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.
62  *
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.
70  */
71 /*
72   krb5_util.c
73
74   Copyright (c) 2004 The Regents of the University of Michigan.
75   All rights reserved.
76
77   Redistribution and use in source and binary forms, with or without
78   modification, are permitted provided that the following conditions
79   are met:
80
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.
89
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.
101
102 */
103
104 #ifndef _GNU_SOURCE
105 #define _GNU_SOURCE
106 #endif
107 #include "config.h"
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>
115
116 #include <unistd.h>
117 #include <stdio.h>
118 #include <stdlib.h>
119 #include <string.h>
120 #include <netdb.h>
121 #include <dirent.h>
122 #include <fcntl.h>
123 #include <errno.h>
124 #include <time.h>
125 #include <gssapi/gssapi.h>
126 #ifdef USE_PRIVATE_KRB5_FUNCTIONS
127 #include <gssapi/gssapi_krb5.h>
128 #endif
129 #include <krb5.h>
130
131 #include "lgss_utils.h"
132 #include "lgss_krb5_utils.h"
133
134 static void lgss_krb5_mutex_lock(void)
135 {
136         if (lgss_mutex_lock(LGSS_MUTEX_KRB5)) {
137                 logmsg(LL_ERR, "can't lock process, abort!\n");
138                 exit(-1);
139         }
140 }
141
142 static void lgss_krb5_mutex_unlock(void)
143 {
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");
147         }
148 }
149
150 /*
151  * NOTE
152  *  - currently we only support "normal" cache types: "FILE" and "MEMORY".
153  */
154
155 #define krb5_err_msg(code)      error_message(code)
156
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";
162
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_";
168
169 struct lgss_krb5_cred {
170         char            kc_ccname[128];
171         int             kc_remove;        /* remove cache upon release */
172 };
173
174 static
175 int lgss_krb5_set_ccache_name(const char *ccname)
176 {
177 #ifdef USE_GSS_KRB5_CCACHE_NAME
178         unsigned int    maj_stat, min_stat;
179
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");
183                 return -1;
184         }
185 #else
186         /*
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)
190          */
191         if (setenv("KRB5CCNAME", ccname, 1)) {
192                 logmsg(LL_ERR, "set env of krb5 ccname: %s\n",
193                        strerror(errno));
194                 return -1;
195         }
196 #endif
197         logmsg(LL_DEBUG, "set cc: %s\n", ccname);
198         return 0;
199 }
200
201 static
202 int lgss_krb5_get_local_realm(void)
203 {
204         krb5_context    context = NULL;
205         krb5_error_code code;
206         int             retval = -1;
207
208         if (krb5_this_realm != NULL)
209                 return 0;
210
211         code = krb5_init_context(&context);
212         if (code) {
213                 logmsg(LL_ERR, "init ctx: %s\n", krb5_err_msg(code));
214                 return -1;
215         }
216
217         code = krb5_get_default_realm(context, &krb5_this_realm);
218         if (code) {
219                 logmsg(LL_ERR, "get default realm: %s\n", krb5_err_msg(code));
220                 goto out;
221         }
222
223         logmsg(LL_DEBUG, "Local realm: %s\n", krb5_this_realm);
224         retval = 0;
225 out:
226         krb5_free_context(context);
227         return retval;
228 }
229
230 static
231 int princ_is_local_realm(krb5_context ctx, krb5_principal princ)
232 {
233         return (lgss_krb5_strcasecmp(krb5_princ_realm(ctx, princ),
234                                      krb5_this_realm) == 0);
235 }
236
237 static
238 int svc_princ_verify_host(krb5_context ctx,
239                           krb5_principal princ,
240                           loglevel_t loglevel)
241 {
242         struct utsname utsbuf;
243         struct hostent *host;
244
245         if (krb5_princ_component(ctx, princ, 1) == NULL) {
246                 logmsg(loglevel, "service principal has no host part\n");
247                 return -1;
248         }
249
250         if (uname(&utsbuf)) {
251                 logmsg(loglevel, "get UTS name: %s\n", strerror(errno));
252                 return -1;
253         }
254
255         host = gethostbyname(utsbuf.nodename);
256         if (host == NULL) {
257                 logmsg(loglevel, "failed to get local hostname\n");
258                 return -1;
259         }
260
261         if (lgss_krb5_strcasecmp(krb5_princ_component(ctx, princ, 1),
262                                  host->h_name)) {
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,
267                        host->h_name);
268                 return -1;
269         }
270
271         return 0;
272 }
273
274 static
275 int lkrb5_cc_check_tgt_princ(krb5_context ctx,
276                              krb5_ccache ccache,
277                              krb5_principal princ,
278                              unsigned int flag)
279 {
280         const char     *princ_name;
281
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);
289
290         /* check type */
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));
294                 return -1;
295         }
296
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,
302                        krb5_this_realm);
303                 return -1;
304         }
305
306         /* check principal name */
307         switch (flag) {
308         case LGSS_ROOT_CRED_ROOT:
309                 princ_name = LGSS_USR_ROOT_STR;
310                 break;
311         case LGSS_ROOT_CRED_MDT:
312                 princ_name = LGSS_SVC_MDS_STR;
313                 break;
314         case LGSS_ROOT_CRED_OST:
315                 princ_name = LGSS_SVC_OSS_STR;
316                 break;
317         default:
318                 lassert(0);
319         }
320
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,
325                        princ_name);
326                 return -1;
327         }
328
329         /*
330          * verify the hostname part of the principal, except we do allow
331          * lustre_root without binding to a host.
332          */
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);
338                         return -1;
339                 }
340         } else {
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);
345                         return -1;
346                 }
347         }
348
349         logmsg(LL_TRACE, "principal is OK\n");
350         return 0;
351 }
352
353 /**
354  * compose the TGT cc name, according to the root flags.
355  */
356 static
357 void get_root_tgt_ccname(char *ccname, int size, unsigned int flag)
358 {
359         const char *suffix;
360
361         switch (flag) {
362         case LGSS_ROOT_CRED_ROOT:
363                 suffix = krb5_cred_root_suffix;
364                 break;
365         case LGSS_ROOT_CRED_MDT:
366                 suffix = krb5_cred_mds_suffix;
367                 break;
368         case LGSS_ROOT_CRED_OST:
369                 suffix = krb5_cred_oss_suffix;
370                 break;
371         default:
372                 lassert(0);
373         }
374
375         snprintf(ccname, size, "%s%s/%s%s_%s",
376                  krb5_cc_type, krb5_cc_dir, krb5_cred_prefix,
377                  suffix, krb5_this_realm);
378 }
379
380 static
381 int lkrb5_check_root_tgt_cc_base(krb5_context ctx,
382                                  krb5_ccache ccache,
383                                  char *ccname,
384                                  unsigned int flag)
385 {
386         krb5_ccache             tgt_ccache;
387         krb5_creds              cred;
388         krb5_principal          princ;
389         krb5_cc_cursor          cursor;
390         krb5_error_code         code;
391         time_t                  now;
392         int                     rc = -1, found = 0;
393
394         /* prepare parsing the cache file */
395         code = krb5_cc_resolve(ctx, ccname, &tgt_ccache);
396         if (code) {
397                 logmsg(LL_ERR, "resolve krb5 cc %s: %s\n",
398                        ccname, krb5_err_msg(code));
399                 return -1;
400         }
401
402         /* checks the principal */
403         code = krb5_cc_get_principal(ctx, tgt_ccache, &princ);
404         if (code) {
405                 logmsg(LL_ERR, "get cc principal: %s\n", krb5_err_msg(code));
406                 goto out_cc;
407         }
408
409         if (lkrb5_cc_check_tgt_princ(ctx, tgt_ccache, princ, flag))
410                 goto out_princ;
411
412         /*
413          * find a valid entry
414          */
415         code = krb5_cc_start_seq_get(ctx, tgt_ccache, &cursor);
416         if (code) {
417                 logmsg(LL_ERR, "start cc iteration: %s\n", krb5_err_msg(code));
418                 goto out_princ;
419         }
420
421         now = time(0);
422         do {
423                 krb5_timestamp  duration, delta;
424
425                 code = krb5_cc_next_cred(ctx, tgt_ccache, &cursor, &cred);
426                 if (code != 0)
427                         break;
428
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);
438
439                 /* FIXME
440                  * we found the princ type is always 0 (KRB5_NT_UNKNOWN), why???
441                  */
442
443                 /* FIXME how about inter-realm TGT??? FIXME */
444                 if (lgss_krb5_strcasecmp(krb5_princ_name(ctx, cred.server),
445                                          "krbtgt"))
446                         continue;
447
448                 if (lgss_krb5_strcasecmp(krb5_princ_realm(ctx, cred.server),
449                                          krb5_this_realm))
450                         continue;
451
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;
457
458                 if (cred.times.starttime <= now &&
459                     cred.times.endtime >= now + delta) {
460                         found = 1;
461                         break;
462                 }
463         } while (1);
464
465         if (!found) {
466                 logmsg(LL_DEBUG, "doesn't find good TGT cache\n");
467                 goto out_seq;
468         }
469
470         /* found a good cred, store it into @ccache */
471         logmsg(LL_DEBUG, "found good TGT cache\n");
472
473         code = krb5_cc_initialize(ctx, ccache, princ);
474         if (code) {
475                 logmsg(LL_ERR, "init private cc: %s\n", krb5_err_msg(code));
476                 goto out_seq;
477         }
478
479         code = krb5_cc_store_cred(ctx, ccache, &cred);
480         if (code) {
481                 logmsg(LL_ERR, "store private cred: %s\n", krb5_err_msg(code));
482                 goto out_seq;
483         }
484
485         logmsg(LL_DEBUG, "store private ccache OK\n");
486         rc = 0;
487
488 out_seq:
489         krb5_cc_end_seq_get(ctx, tgt_ccache, &cursor);
490 out_princ:
491         krb5_free_principal(ctx, princ);
492 out_cc:
493         krb5_cc_close(ctx, tgt_ccache);
494
495         return rc;
496 }
497
498 /**
499  * find out whether current TGT cache is valid or not
500  */
501 static
502 int lkrb5_check_root_tgt_cc(krb5_context ctx,
503                             krb5_ccache ccache,
504                             unsigned int root_flags)
505 {
506         struct stat             statbuf;
507         unsigned int            flag;
508         char                    ccname[1024];
509         char                   *ccfile;
510         int                     i, rc;
511
512         for (i = 0; i < LGSS_ROOT_CRED_NR; i++) {
513                 flag = 1 << i;
514
515                 if ((root_flags & flag) == 0)
516                         continue;
517
518                 get_root_tgt_ccname(ccname, sizeof(ccname), flag);
519                 logmsg(LL_DEBUG, "root krb5 TGT ccname: %s\n", ccname);
520
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));
527                         continue;
528                 }
529
530                 rc = lkrb5_check_root_tgt_cc_base(ctx, ccache, ccname, flag);
531                 if (rc == 0)
532                         return 0;
533         }
534
535         logmsg(LL_TRACE, "doesn't find a valid tgt cc\n");
536         return -1;
537 }
538
539 static
540 int lkrb5_get_root_tgt_keytab(krb5_context ctx,
541                               krb5_ccache ccache,
542                               krb5_keytab kt,
543                               krb5_principal princ,
544                               const char *ccname)
545 {
546         krb5_get_init_creds_opt opts;
547         krb5_creds              cred;
548         krb5_ccache             tgt_ccache;
549         krb5_error_code         code;
550         int                     rc = -1;
551
552         krb5_get_init_creds_opt_init(&opts);
553         krb5_get_init_creds_opt_set_address_list(&opts, NULL);
554         /*
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.
558          *
559         krb5_get_init_creds_opt_set_tkt_life(&opts, very-long-time);
560          *
561          */
562
563         /*
564          * obtain TGT and store into global ccache
565          */
566         code = krb5_get_init_creds_keytab(ctx, &cred, princ, kt,
567                                           0, NULL, &opts);
568         if (code) {
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,
573                        krb5_err_msg(code));
574                 return -1;
575         }
576
577         code = krb5_cc_resolve(ctx, ccname, &tgt_ccache);
578         if (code) {
579                 logmsg(LL_ERR, "resolve cc %s: %s\n",
580                        ccname, krb5_err_msg(code));
581                 goto out_cred;
582         }
583
584         code = krb5_cc_initialize(ctx, tgt_ccache, princ);
585         if (code) {
586                 logmsg(LL_ERR, "initialize cc %s: %s\n",
587                        ccname, krb5_err_msg(code));
588                 goto out_cc;
589         }
590
591         code = krb5_cc_store_cred(ctx, tgt_ccache, &cred);
592         if (code) {
593                 logmsg(LL_ERR, "store cred to cc %s: %s\n",
594                        ccname, krb5_err_msg(code));
595                 goto out_cc;
596         }
597
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,
601                ccname);
602
603         /*
604          * now store the cred into my own cc too
605          */
606         code = krb5_cc_initialize(ctx, ccache, princ);
607         if (code) {
608                 logmsg(LL_ERR, "init mem cc: %s\n", krb5_err_msg(code));
609                 goto out_cc;
610         }
611
612         code = krb5_cc_store_cred(ctx, ccache, &cred);
613         if (code) {
614                 logmsg(LL_ERR, "store mm cred: %s\n", krb5_err_msg(code));
615                 goto out_cc;
616         }
617
618         logmsg(LL_DEBUG, "stored TGT into mem cc OK\n");
619         rc = 0;
620 out_cc:
621         krb5_cc_close(ctx, tgt_ccache);
622 out_cred:
623         krb5_free_cred_contents(ctx, &cred);
624         return rc;
625 }
626
627 /*
628  * obtain a new root TGT
629  */
630 static
631 int lkrb5_refresh_root_tgt_cc(krb5_context ctx,
632                               krb5_ccache ccache,
633                               unsigned int root_flags)
634 {
635         krb5_keytab             kt;
636         krb5_keytab_entry       kte;
637         krb5_kt_cursor          cursor;
638         krb5_principal          princ = NULL;
639         krb5_error_code         code;
640         char                    ccname[1024];
641         unsigned int            flag = 0;
642         int                     rc = -1;
643
644         /* prepare parsing the keytab file */
645         code = krb5_kt_resolve(ctx, krb5_keytab_file, &kt);
646         if (code) {
647                 logmsg(LL_ERR, "resolve keytab %s: %s\n",
648                        krb5_keytab_file, krb5_err_msg(code));
649                 return -1;
650         }
651
652         code = krb5_kt_start_seq_get(ctx, kt, &cursor);
653         if (code) {
654                 logmsg(LL_ERR, "start kt iteration: %s\n", krb5_err_msg(code));
655                 goto out_kt;
656         }
657
658         /* iterate keytab to find proper a entry */
659         do {
660                 krb5_data      *princname;
661
662                 code = krb5_kt_next_entry(ctx, kt, &kte, &cursor);
663                 if (code != 0)
664                         break;
665
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);
674
675                 if (!princ_is_local_realm(ctx, kte.principal))
676                         continue;
677
678                 princname = krb5_princ_name(ctx, kte.principal);
679
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;
689                 } else {
690                         logmsg(LL_TRACE, "not what we want, skip\n");
691                         continue;
692                 }
693
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");
697                                 continue;
698                         }
699                 } else {
700                         if (svc_princ_verify_host(ctx, kte.principal,
701                                                   LL_TRACE)) {
702                                 logmsg(LL_TRACE, "doesn't belong to this "
703                                        "node, skip\n");
704                                 continue;
705                         }
706                 }
707
708                 code = krb5_copy_principal(ctx, kte.principal, &princ);
709                 if (code) {
710                         logmsg(LL_ERR, "copy princ: %s\n", krb5_err_msg(code));
711                         continue;
712                 }
713
714                 lassert(princ != NULL);
715                 break;
716         } while (1);
717
718         krb5_kt_end_seq_get(ctx, kt, &cursor);
719
720         if (princ == NULL) {
721                 logmsg(LL_ERR, "can't find proper keytab entry\n");
722                 goto out_kt;
723         }
724
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);
728
729         krb5_free_principal(ctx, princ);
730 out_kt:
731         krb5_kt_close(ctx, kt);
732         return rc;
733 }
734
735 static
736 int lkrb5_prepare_root_cred(struct lgss_cred *cred)
737 {
738         krb5_context            ctx;
739         krb5_ccache             ccache;
740         krb5_error_code         code;
741         struct lgss_krb5_cred  *kcred;
742         int                     rc = -1;
743
744         lassert(krb5_this_realm != NULL);
745
746         kcred = (struct lgss_krb5_cred *) cred->lc_mech_cred;
747
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);
753
754         code = krb5_init_context(&ctx);
755         if (code) {
756                 logmsg(LL_ERR, "initialize krb5 context: %s\n",
757                        krb5_err_msg(code));
758                 return -1;
759         }
760
761         code = krb5_cc_resolve(ctx, kcred->kc_ccname, &ccache);
762         if (code) {
763                 logmsg(LL_ERR, "resolve krb5 cc %s: %s\n",
764                        kcred->kc_ccname, krb5_err_msg(code));
765                 goto out_ctx;
766         }
767
768         /*
769          * search and/or obtain root TGT credential.
770          * it touched global (on-disk) tgt cache, do it inside mutex locking
771          */
772         lgss_krb5_mutex_lock();
773
774         rc = lkrb5_check_root_tgt_cc(ctx, ccache, cred->lc_root_flags);
775         if (rc != 0)
776                 rc = lkrb5_refresh_root_tgt_cc(ctx, ccache,
777                                                cred->lc_root_flags);
778
779         if (rc == 0)
780                 rc = lgss_krb5_set_ccache_name(kcred->kc_ccname);
781
782         lgss_krb5_mutex_unlock();
783
784         krb5_cc_close(ctx, ccache);
785 out_ctx:
786         krb5_free_context(ctx);
787
788         logmsg(LL_DEBUG, "prepare root credentail %s\n", rc ? "failed" : "OK");
789         return rc;
790 }
791
792 static
793 int lkrb5_prepare_user_cred(struct lgss_cred *cred)
794 {
795         struct lgss_krb5_cred   *kcred;
796         int                      rc;
797
798         lassert(krb5_this_realm == NULL);
799
800         kcred = (struct lgss_krb5_cred *) cred->lc_mech_cred;
801
802         /*
803          * here we just specified a fix ccname, instead of searching
804          * entire cc dir. is this OK??
805          */
806         snprintf(kcred->kc_ccname, sizeof(kcred->kc_ccname),
807                  "%s%s/%s%u",
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);
810
811         rc = lgss_krb5_set_ccache_name(kcred->kc_ccname);
812         if (rc)
813                 logmsg(LL_ERR, "can't set krb5 ccache name: %s\n",
814                        kcred->kc_ccname);
815
816         return rc;
817 }
818
819 static
820 int lgss_krb5_prepare_cred(struct lgss_cred *cred)
821 {
822         struct lgss_krb5_cred  *kcred;
823         int                     rc;
824
825         kcred = malloc(sizeof(*kcred));
826         if (kcred == NULL) {
827                 logmsg(LL_ERR, "can't allocate krb5 cred\n");
828                 return -1;
829         }
830
831         kcred->kc_ccname[0] = '\0';
832         kcred->kc_remove = 0;
833         cred->lc_mech_cred = kcred;
834
835         if (cred->lc_root_flags != 0) {
836                 if (lgss_krb5_get_local_realm())
837                         return -1;
838
839                 rc = lkrb5_prepare_root_cred(cred);
840         } else {
841                 rc = lkrb5_prepare_user_cred(cred);
842         }
843
844         return rc;
845 }
846
847 static
848 void lgss_krb5_release_cred(struct lgss_cred *cred)
849 {
850         struct lgss_krb5_cred   *kcred;
851
852         kcred = (struct lgss_krb5_cred *) cred->lc_mech_cred;
853
854         free(kcred);
855         cred->lc_mech_cred = NULL;
856 }
857
858 struct lgss_mech_type lgss_mech_krb5 = 
859 {
860         .lmt_name               = "krb5",
861         .lmt_mech_n             = LGSS_MECH_KRB5,
862         .lmt_prepare_cred       = lgss_krb5_prepare_cred,
863         .lmt_release_cred       = lgss_krb5_release_cred,
864 };