Whamcloud - gitweb
2b2d9d8449bcc23cf5463d97de56e75e3acac2c3
[fs/lustre-release.git] / lustre / utils / gss / lgss_krb5_utils.c
1 /*
2  * Modifications for Lustre
3  *
4  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
5  *
6  * Author: Eric Mei <ericm@clusterfs.com>
7  */
8
9 /*
10  *  Adapted in part from MIT Kerberos 5-1.2.1 slave/kprop.c and from
11  *  http://docs.sun.com/?p=/doc/816-1331/6m7oo9sms&a=view
12  *
13  *  Copyright (c) 2002-2004 The Regents of the University of Michigan.
14  *  All rights reserved.
15  *
16  *  Andy Adamson <andros@umich.edu>
17  *  J. Bruce Fields <bfields@umich.edu>
18  *  Marius Aamodt Eriksen <marius@umich.edu>
19  *  Kevin Coffman <kwc@umich.edu>
20  */
21
22 /*
23  * slave/kprop.c
24  *
25  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
26  * All Rights Reserved.
27  *
28  * Export of this software from the United States of America may
29  *   require a specific license from the United States Government.
30  *   It is the responsibility of any person or organization contemplating
31  *   export to obtain such a license before exporting.
32  *
33  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
34  * distribute this software and its documentation for any purpose and
35  * without fee is hereby granted, provided that the above copyright
36  * notice appear in all copies and that both that copyright notice and
37  * this permission notice appear in supporting documentation, and that
38  * the name of M.I.T. not be used in advertising or publicity pertaining
39  * to distribution of the software without specific, written prior
40  * permission.  Furthermore if you modify this software you must label
41  * your software as modified software and not distribute it in such a
42  * fashion that it might be confused with the original M.I.T. software.
43  * M.I.T. makes no representations about the suitability of
44  * this software for any purpose.  It is provided "as is" without express
45  * or implied warranty.
46  */
47
48 /*
49  * Copyright 1994 by OpenVision Technologies, Inc.
50  *
51  * Permission to use, copy, modify, distribute, and sell this software
52  * and its documentation for any purpose is hereby granted without fee,
53  * provided that the above copyright notice appears in all copies and
54  * that both that copyright notice and this permission notice appear in
55  * supporting documentation, and that the name of OpenVision not be used
56  * in advertising or publicity pertaining to distribution of the software
57  * without specific, written prior permission. OpenVision makes no
58  * representations about the suitability of this software for any
59  * purpose.  It is provided "as is" without express or implied warranty.
60  *
61  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
62  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
63  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
64  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
65  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
66  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
67  * PERFORMANCE OF THIS SOFTWARE.
68  */
69 /*
70   krb5_util.c
71
72   Copyright (c) 2004 The Regents of the University of Michigan.
73   All rights reserved.
74
75   Redistribution and use in source and binary forms, with or without
76   modification, are permitted provided that the following conditions
77   are met:
78
79   1. Redistributions of source code must retain the above copyright
80      notice, this list of conditions and the following disclaimer.
81   2. Redistributions in binary form must reproduce the above copyright
82      notice, this list of conditions and the following disclaimer in the
83      documentation and/or other materials provided with the distribution.
84   3. Neither the name of the University nor the names of its
85      contributors may be used to endorse or promote products derived
86      from this software without specific prior written permission.
87
88   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
89   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
90   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
91   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
92   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
93   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
94   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
95   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
96   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
97   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
98   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
99
100 */
101
102 #ifndef _GNU_SOURCE
103 #define _GNU_SOURCE
104 #endif
105 #include "config.h"
106 #include <sys/param.h>
107 //#include <rpc/rpc.h>
108 #include <sys/types.h>
109 #include <sys/stat.h>
110 #include <sys/utsname.h>
111 #include <sys/socket.h>
112 #include <arpa/inet.h>
113
114 #include <unistd.h>
115 #include <stdio.h>
116 #include <stdlib.h>
117 #include <string.h>
118 #include <netdb.h>
119 #include <dirent.h>
120 #include <fcntl.h>
121 #include <errno.h>
122 #include <time.h>
123 #include <gssapi/gssapi.h>
124 #ifdef USE_PRIVATE_KRB5_FUNCTIONS
125 #include <gssapi/gssapi_krb5.h>
126 #endif
127 #include <krb5.h>
128
129 #include "lgss_utils.h"
130 #include "lgss_krb5_utils.h"
131
132 static void lgss_krb5_mutex_lock(void)
133 {
134         if (lgss_mutex_lock(LGSS_MUTEX_KRB5)) {
135                 logmsg(LL_ERR, "can't lock process, abort!\n");
136                 exit(-1);
137         }
138 }
139
140 static void lgss_krb5_mutex_unlock(void)
141 {
142         if (lgss_mutex_unlock(LGSS_MUTEX_KRB5)) {
143                 logmsg(LL_WARN, "can't unlock process, other processes "
144                        "might need to wait long time\n");
145         }
146 }
147
148 /*
149  * NOTE
150  *  - currently we only support "normal" cache types: "FILE" and "MEMORY".
151  */
152
153 #define krb5_err_msg(code)      error_message(code)
154
155 const char *krb5_cc_type_mem    = "MEMORY:";
156 const char *krb5_cc_type_file   = "FILE:";
157 const char *krb5_cred_root_suffix  = "lustre_root";
158 const char *krb5_cred_mds_suffix   = "lustre_mds";
159 const char *krb5_cred_oss_suffix   = "lustre_oss";
160
161 char    *krb5_this_realm        = NULL;
162 char    *krb5_keytab_file       = "/etc/krb5.keytab";
163 char    *krb5_cc_type           = "FILE:";
164 char    *krb5_cc_dir            = "/tmp";
165 char    *krb5_cred_prefix       = "krb5cc_";
166
167 struct lgss_krb5_cred {
168         char            kc_ccname[128];
169         int             kc_remove;        /* remove cache upon release */
170 };
171
172 static
173 int lgss_krb5_set_ccache_name(const char *ccname)
174 {
175 #ifdef USE_GSS_KRB5_CCACHE_NAME
176         unsigned int    maj_stat, min_stat;
177
178         maj_stat = gss_krb5_ccache_name(&min_stat, ccname, NULL);
179         if (maj_stat != GSS_S_COMPLETE) {
180                 logmsg(LL_ERR, "failed to set ccache name\n");
181                 return -1;
182         }
183 #else
184         /*
185          * Set the KRB5CCNAME environment variable to tell the krb5 code
186          * which credentials cache to use.  (Instead of using the private
187          * function above for which there is no generic gssapi equivalent)
188          */
189         if (setenv("KRB5CCNAME", ccname, 1)) {
190                 logmsg(LL_ERR, "set env of krb5 ccname: %s\n",
191                        strerror(errno));
192                 return -1;
193         }
194 #endif
195         logmsg(LL_DEBUG, "set cc: %s\n", ccname);
196         return 0;
197 }
198
199 static
200 int lgss_krb5_get_local_realm(void)
201 {
202         krb5_context    context = NULL;
203         krb5_error_code code;
204         int             retval = -1;
205
206         if (krb5_this_realm != NULL)
207                 return 0;
208
209         code = krb5_init_context(&context);
210         if (code) {
211                 logmsg(LL_ERR, "init ctx: %s\n", krb5_err_msg(code));
212                 return -1;
213         }
214
215         code = krb5_get_default_realm(context, &krb5_this_realm);
216         if (code) {
217                 logmsg(LL_ERR, "get default realm: %s\n", krb5_err_msg(code));
218                 goto out;
219         }
220
221         logmsg(LL_DEBUG, "Local realm: %s\n", krb5_this_realm);
222         retval = 0;
223 out:
224         krb5_free_context(context);
225         return retval;
226 }
227
228 static
229 int princ_is_local_realm(krb5_context ctx, krb5_principal princ)
230 {
231         return (lgss_krb5_strcasecmp(krb5_princ_realm(ctx, princ),
232                                      krb5_this_realm) == 0);
233 }
234
235 static
236 int svc_princ_verify_host(krb5_context ctx,
237                           krb5_principal princ,
238                           loglevel_t loglevel)
239 {
240         struct utsname utsbuf;
241         struct hostent *host;
242
243         if (krb5_princ_component(ctx, princ, 1) == NULL) {
244                 logmsg(loglevel, "service principal has no host part\n");
245                 return -1;
246         }
247
248         if (uname(&utsbuf)) {
249                 logmsg(loglevel, "get UTS name: %s\n", strerror(errno));
250                 return -1;
251         }
252
253         host = gethostbyname(utsbuf.nodename);
254         if (host == NULL) {
255                 logmsg(loglevel, "failed to get local hostname\n");
256                 return -1;
257         }
258
259         if (lgss_krb5_strcasecmp(krb5_princ_component(ctx, princ, 1),
260                                  host->h_name)) {
261                 logmsg(loglevel, "service principal: hostname %.*s "
262                        "doesn't match localhost %s\n",
263                        krb5_princ_component(ctx, princ, 1)->length,
264                        krb5_princ_component(ctx, princ, 1)->data,
265                        host->h_name);
266                 return -1;
267         }
268
269         return 0;
270 }
271
272 static
273 int lkrb5_cc_check_tgt_princ(krb5_context ctx,
274                              krb5_ccache ccache,
275                              krb5_principal princ,
276                              unsigned int flag)
277 {
278         const char     *princ_name;
279
280         logmsg(LL_DEBUG, "principal: realm %.*s, type %d, size %d, name %.*s\n",
281                krb5_princ_realm(ctx, princ)->length,
282                krb5_princ_realm(ctx, princ)->data,
283                krb5_princ_type(ctx, princ),
284                krb5_princ_size(ctx, princ),
285                krb5_princ_name(ctx, princ)->length,
286                krb5_princ_name(ctx, princ)->data);
287
288         /* check type */
289         if (krb5_princ_type(ctx, princ) != KRB5_NT_PRINCIPAL) {
290                 logmsg(LL_WARN, "principal type %d is not I want\n",
291                        krb5_princ_type(ctx, princ));
292                 return -1;
293         }
294
295         /* check local realm */
296         if (!princ_is_local_realm(ctx, princ)) {
297                 logmsg(LL_WARN, "principal realm %.*s not local: %s\n",
298                        krb5_princ_realm(ctx, princ)->length,
299                        krb5_princ_realm(ctx, princ)->data,
300                        krb5_this_realm);
301                 return -1;
302         }
303
304         /* check principal name */
305         switch (flag) {
306         case LGSS_ROOT_CRED_ROOT:
307                 princ_name = LGSS_USR_ROOT_STR;
308                 break;
309         case LGSS_ROOT_CRED_MDT:
310                 princ_name = LGSS_SVC_MDS_STR;
311                 break;
312         case LGSS_ROOT_CRED_OST:
313                 princ_name = LGSS_SVC_OSS_STR;
314                 break;
315         default:
316                 lassert(0);
317         }
318
319         if (lgss_krb5_strcmp(krb5_princ_name(ctx, princ), princ_name)) {
320                 logmsg(LL_WARN, "%.*s: we expect %s instead\n",
321                        krb5_princ_name(ctx, princ)->length,
322                        krb5_princ_name(ctx, princ)->data,
323                        princ_name);
324                 return -1;
325         }
326
327         /*
328          * verify the hostname part of the principal, except we do allow
329          * lustre_root without binding to a host.
330          */
331         if (krb5_princ_component(ctx, princ, 1) == NULL) {
332                 if (flag != LGSS_ROOT_CRED_ROOT) {
333                         logmsg(LL_WARN, "%.*s: missing hostname\n",
334                                krb5_princ_name(ctx, princ)->length,
335                                krb5_princ_name(ctx, princ)->data);
336                         return -1;
337                 }
338         } else {
339                 if (svc_princ_verify_host(ctx, princ, LL_WARN)) {
340                         logmsg(LL_DEBUG, "%.*s: doesn't belong to this node\n",
341                                krb5_princ_name(ctx, princ)->length,
342                                krb5_princ_name(ctx, princ)->data);
343                         return -1;
344                 }
345         }
346
347         logmsg(LL_TRACE, "principal is OK\n");
348         return 0;
349 }
350
351 /**
352  * compose the TGT cc name, according to the root flags.
353  */
354 static
355 void get_root_tgt_ccname(char *ccname, int size, unsigned int flag)
356 {
357         const char *suffix;
358
359         switch (flag) {
360         case LGSS_ROOT_CRED_ROOT:
361                 suffix = krb5_cred_root_suffix;
362                 break;
363         case LGSS_ROOT_CRED_MDT:
364                 suffix = krb5_cred_mds_suffix;
365                 break;
366         case LGSS_ROOT_CRED_OST:
367                 suffix = krb5_cred_oss_suffix;
368                 break;
369         default:
370                 lassert(0);
371         }
372
373         snprintf(ccname, size, "%s%s/%s%s_%s",
374                  krb5_cc_type, krb5_cc_dir, krb5_cred_prefix,
375                  suffix, krb5_this_realm);
376 }
377
378 static
379 int lkrb5_check_root_tgt_cc_base(krb5_context ctx,
380                                  krb5_ccache ccache,
381                                  char *ccname,
382                                  unsigned int flag)
383 {
384         krb5_ccache             tgt_ccache;
385         krb5_creds              cred;
386         krb5_principal          princ;
387         krb5_cc_cursor          cursor;
388         krb5_error_code         code;
389         time_t                  now;
390         int                     rc = -1, found = 0;
391
392         /* prepare parsing the cache file */
393         code = krb5_cc_resolve(ctx, ccname, &tgt_ccache);
394         if (code) {
395                 logmsg(LL_ERR, "resolve krb5 cc %s: %s\n",
396                        ccname, krb5_err_msg(code));
397                 return -1;
398         }
399
400         /* checks the principal */
401         code = krb5_cc_get_principal(ctx, tgt_ccache, &princ);
402         if (code) {
403                 logmsg(LL_ERR, "get cc principal: %s\n", krb5_err_msg(code));
404                 goto out_cc;
405         }
406
407         if (lkrb5_cc_check_tgt_princ(ctx, tgt_ccache, princ, flag))
408                 goto out_princ;
409
410         /*
411          * find a valid entry
412          */
413         code = krb5_cc_start_seq_get(ctx, tgt_ccache, &cursor);
414         if (code) {
415                 logmsg(LL_ERR, "start cc iteration: %s\n", krb5_err_msg(code));
416                 goto out_princ;
417         }
418
419         now = time(0);
420         do {
421                 krb5_timestamp  duration, delta;
422
423                 code = krb5_cc_next_cred(ctx, tgt_ccache, &cursor, &cred);
424                 if (code != 0)
425                         break;
426
427                 logmsg(LL_DEBUG, "cred: server realm %.*s, type %d, name %.*s; "
428                        "time (%d-%d, renew till %d), valid %d\n",
429                        krb5_princ_realm(ctx, cred.server)->length,
430                        krb5_princ_realm(ctx, cred.server)->data,
431                        krb5_princ_type(ctx, cred.server),
432                        krb5_princ_name(ctx, cred.server)->length,
433                        krb5_princ_name(ctx, cred.server)->data,
434                        cred.times.starttime, cred.times.endtime,
435                        cred.times.renew_till, cred.times.endtime - now);
436
437                 /* FIXME
438                  * we found the princ type is always 0 (KRB5_NT_UNKNOWN), why???
439                  */
440
441                 /* FIXME how about inter-realm TGT??? FIXME */
442                 if (lgss_krb5_strcasecmp(krb5_princ_name(ctx, cred.server),
443                                          "krbtgt"))
444                         continue;
445
446                 if (lgss_krb5_strcasecmp(krb5_princ_realm(ctx, cred.server),
447                                          krb5_this_realm))
448                         continue;
449
450                 /* check validity of time */
451                 delta = 60 * 30; /* half an hour */
452                 duration = cred.times.endtime - cred.times.starttime;
453                 if (duration / 4 < delta)
454                         delta = duration / 4;
455
456                 if (cred.times.starttime <= now &&
457                     cred.times.endtime >= now + delta) {
458                         found = 1;
459                         break;
460                 }
461         } while (1);
462
463         if (!found) {
464                 logmsg(LL_DEBUG, "doesn't find good TGT cache\n");
465                 goto out_seq;
466         }
467
468         /* found a good cred, store it into @ccache */
469         logmsg(LL_DEBUG, "found good TGT cache\n");
470
471         code = krb5_cc_initialize(ctx, ccache, princ);
472         if (code) {
473                 logmsg(LL_ERR, "init private cc: %s\n", krb5_err_msg(code));
474                 goto out_seq;
475         }
476
477         code = krb5_cc_store_cred(ctx, ccache, &cred);
478         if (code) {
479                 logmsg(LL_ERR, "store private cred: %s\n", krb5_err_msg(code));
480                 goto out_seq;
481         }
482
483         logmsg(LL_DEBUG, "store private ccache OK\n");
484         rc = 0;
485
486 out_seq:
487         krb5_cc_end_seq_get(ctx, tgt_ccache, &cursor);
488 out_princ:
489         krb5_free_principal(ctx, princ);
490 out_cc:
491         krb5_cc_close(ctx, tgt_ccache);
492
493         return rc;
494 }
495
496 /**
497  * find out whether current TGT cache is valid or not
498  */
499 static
500 int lkrb5_check_root_tgt_cc(krb5_context ctx,
501                             krb5_ccache ccache,
502                             unsigned int root_flags)
503 {
504         struct stat             statbuf;
505         unsigned int            flag;
506         char                    ccname[1024];
507         char                   *ccfile;
508         int                     i, rc;
509
510         for (i = 0; i < LGSS_ROOT_CRED_NR; i++) {
511                 flag = 1 << i;
512
513                 if ((root_flags & flag) == 0)
514                         continue;
515
516                 get_root_tgt_ccname(ccname, sizeof(ccname), flag);
517                 logmsg(LL_DEBUG, "root krb5 TGT ccname: %s\n", ccname);
518
519                 /* currently we only support type "FILE", firstly make sure
520                  * the cache file is there */
521                 ccfile = ccname + strlen(krb5_cc_type);
522                 if (stat(ccfile, &statbuf)) {
523                         logmsg(LL_DEBUG, "krb5 cc %s: %s\n",
524                                ccname, strerror(errno));
525                         continue;
526                 }
527
528                 rc = lkrb5_check_root_tgt_cc_base(ctx, ccache, ccname, flag);
529                 if (rc == 0)
530                         return 0;
531         }
532
533         logmsg(LL_TRACE, "doesn't find a valid tgt cc\n");
534         return -1;
535 }
536
537 static
538 int lkrb5_get_root_tgt_keytab(krb5_context ctx,
539                               krb5_ccache ccache,
540                               krb5_keytab kt,
541                               krb5_principal princ,
542                               const char *ccname)
543 {
544         krb5_get_init_creds_opt opts;
545         krb5_creds              cred;
546         krb5_ccache             tgt_ccache;
547         krb5_error_code         code;
548         int                     rc = -1;
549
550         krb5_get_init_creds_opt_init(&opts);
551         krb5_get_init_creds_opt_set_address_list(&opts, NULL);
552         /*
553          * by default krb5 library obtain ticket with lifetime shorter
554          * than the max value. we can change it here if we want. but
555          * seems not necessary now.
556          *
557         krb5_get_init_creds_opt_set_tkt_life(&opts, very-long-time);
558          *
559          */
560
561         /*
562          * obtain TGT and store into global ccache
563          */
564         code = krb5_get_init_creds_keytab(ctx, &cred, princ, kt,
565                                           0, NULL, &opts);
566         if (code) {
567                 logmsg(LL_ERR, "failed to get root TGT for "
568                        "principal %.*s: %s\n",
569                        krb5_princ_name(ctx, princ)->length,
570                        krb5_princ_name(ctx, princ)->data,
571                        krb5_err_msg(code));
572                 return -1;
573         }
574
575         code = krb5_cc_resolve(ctx, ccname, &tgt_ccache);
576         if (code) {
577                 logmsg(LL_ERR, "resolve cc %s: %s\n",
578                        ccname, krb5_err_msg(code));
579                 goto out_cred;
580         }
581
582         code = krb5_cc_initialize(ctx, tgt_ccache, princ);
583         if (code) {
584                 logmsg(LL_ERR, "initialize cc %s: %s\n",
585                        ccname, krb5_err_msg(code));
586                 goto out_cc;
587         }
588
589         code = krb5_cc_store_cred(ctx, tgt_ccache, &cred);
590         if (code) {
591                 logmsg(LL_ERR, "store cred to cc %s: %s\n",
592                        ccname, krb5_err_msg(code));
593                 goto out_cc;
594         }
595
596         logmsg(LL_INFO, "installed TGT of %.*s in cc %s\n",
597                krb5_princ_name(ctx, princ)->length,
598                krb5_princ_name(ctx, princ)->data,
599                ccname);
600
601         /*
602          * now store the cred into my own cc too
603          */
604         code = krb5_cc_initialize(ctx, ccache, princ);
605         if (code) {
606                 logmsg(LL_ERR, "init mem cc: %s\n", krb5_err_msg(code));
607                 goto out_cc;
608         }
609
610         code = krb5_cc_store_cred(ctx, ccache, &cred);
611         if (code) {
612                 logmsg(LL_ERR, "store mm cred: %s\n", krb5_err_msg(code));
613                 goto out_cc;
614         }
615
616         logmsg(LL_DEBUG, "stored TGT into mem cc OK\n");
617         rc = 0;
618 out_cc:
619         krb5_cc_close(ctx, tgt_ccache);
620 out_cred:
621         krb5_free_cred_contents(ctx, &cred);
622         return rc;
623 }
624
625 /*
626  * obtain a new root TGT
627  */
628 static
629 int lkrb5_refresh_root_tgt_cc(krb5_context ctx,
630                               krb5_ccache ccache,
631                               unsigned int root_flags)
632 {
633         krb5_keytab             kt;
634         krb5_keytab_entry       kte;
635         krb5_kt_cursor          cursor;
636         krb5_principal          princ = NULL;
637         krb5_error_code         code;
638         char                    ccname[1024];
639         unsigned int            flag = 0;
640         int                     rc = -1;
641
642         /* prepare parsing the keytab file */
643         code = krb5_kt_resolve(ctx, krb5_keytab_file, &kt);
644         if (code) {
645                 logmsg(LL_ERR, "resolve keytab %s: %s\n",
646                        krb5_keytab_file, krb5_err_msg(code));
647                 return -1;
648         }
649
650         code = krb5_kt_start_seq_get(ctx, kt, &cursor);
651         if (code) {
652                 logmsg(LL_ERR, "start kt iteration: %s\n", krb5_err_msg(code));
653                 goto out_kt;
654         }
655
656         /* iterate keytab to find proper an entry */
657         do {
658                 krb5_data      *princname;
659
660                 code = krb5_kt_next_entry(ctx, kt, &kte, &cursor);
661                 if (code != 0)
662                         break;
663
664                 logmsg(LL_TRACE, "kt entry: realm %.*s, type %d, "
665                        "size %d, name %.*s\n",
666                        krb5_princ_realm(ctx, kte.principal)->length,
667                        krb5_princ_realm(ctx, kte.principal)->data,
668                        krb5_princ_type(ctx, kte.principal),
669                        krb5_princ_size(ctx, kte.principal),
670                        krb5_princ_name(ctx, kte.principal)->length,
671                        krb5_princ_name(ctx, kte.principal)->data);
672
673                 if (!princ_is_local_realm(ctx, kte.principal))
674                         continue;
675
676                 princname = krb5_princ_name(ctx, kte.principal);
677
678                 if ((root_flags & LGSS_ROOT_CRED_ROOT) != 0 &&
679                     lgss_krb5_strcmp(princname, LGSS_USR_ROOT_STR) == 0) {
680                         flag = LGSS_ROOT_CRED_ROOT;
681                 } else if ((root_flags & LGSS_ROOT_CRED_MDT) != 0 &&
682                            lgss_krb5_strcmp(princname, LGSS_SVC_MDS_STR) == 0) {
683                         flag = LGSS_ROOT_CRED_MDT;
684                 } else if ((root_flags & LGSS_ROOT_CRED_OST) != 0 &&
685                            lgss_krb5_strcmp(princname, LGSS_SVC_OSS_STR) == 0) {
686                         flag = LGSS_ROOT_CRED_OST;
687                 } else {
688                         logmsg(LL_TRACE, "not what we want, skip\n");
689                         continue;
690                 }
691
692                 if (krb5_princ_component(ctx, kte.principal, 1) == NULL) {
693                         if (flag != LGSS_ROOT_CRED_ROOT) {
694                                 logmsg(LL_TRACE, "no hostname, skip\n");
695                                 continue;
696                         }
697                 } else {
698                         if (svc_princ_verify_host(ctx, kte.principal,
699                                                   LL_TRACE)) {
700                                 logmsg(LL_TRACE, "doesn't belong to this "
701                                        "node, skip\n");
702                                 continue;
703                         }
704                 }
705
706                 code = krb5_copy_principal(ctx, kte.principal, &princ);
707                 if (code) {
708                         logmsg(LL_ERR, "copy princ: %s\n", krb5_err_msg(code));
709                         continue;
710                 }
711
712                 lassert(princ != NULL);
713                 break;
714         } while (1);
715
716         krb5_kt_end_seq_get(ctx, kt, &cursor);
717
718         if (princ == NULL) {
719                 logmsg(LL_ERR, "can't find proper keytab entry\n");
720                 goto out_kt;
721         }
722
723         /* obtain root TGT */
724         get_root_tgt_ccname(ccname, sizeof(ccname), flag);
725         rc = lkrb5_get_root_tgt_keytab(ctx, ccache, kt, princ, ccname);
726
727         krb5_free_principal(ctx, princ);
728 out_kt:
729         krb5_kt_close(ctx, kt);
730         return rc;
731 }
732
733 static
734 int lkrb5_prepare_root_cred(struct lgss_cred *cred)
735 {
736         krb5_context            ctx;
737         krb5_ccache             ccache;
738         krb5_error_code         code;
739         struct lgss_krb5_cred  *kcred;
740         int                     rc = -1;
741
742         lassert(krb5_this_realm != NULL);
743
744         kcred = (struct lgss_krb5_cred *) cred->lc_mech_cred;
745
746         /* compose the memory cc name, since the only user is myself,
747          * the name could be fixed */
748         snprintf(kcred->kc_ccname, sizeof(kcred->kc_ccname),
749                  "%s/self", krb5_cc_type_mem);
750         logmsg(LL_TRACE, "private cc: %s\n", kcred->kc_ccname);
751
752         code = krb5_init_context(&ctx);
753         if (code) {
754                 logmsg(LL_ERR, "initialize krb5 context: %s\n",
755                        krb5_err_msg(code));
756                 return -1;
757         }
758
759         code = krb5_cc_resolve(ctx, kcred->kc_ccname, &ccache);
760         if (code) {
761                 logmsg(LL_ERR, "resolve krb5 cc %s: %s\n",
762                        kcred->kc_ccname, krb5_err_msg(code));
763                 goto out_ctx;
764         }
765
766         /*
767          * search and/or obtain root TGT credential.
768          * it touched global (on-disk) tgt cache, do it inside mutex locking
769          */
770         lgss_krb5_mutex_lock();
771
772         rc = lkrb5_check_root_tgt_cc(ctx, ccache, cred->lc_root_flags);
773         if (rc != 0)
774                 rc = lkrb5_refresh_root_tgt_cc(ctx, ccache,
775                                                cred->lc_root_flags);
776
777         if (rc == 0)
778                 rc = lgss_krb5_set_ccache_name(kcred->kc_ccname);
779
780         lgss_krb5_mutex_unlock();
781
782         krb5_cc_close(ctx, ccache);
783 out_ctx:
784         krb5_free_context(ctx);
785
786         logmsg(LL_DEBUG, "prepare root credentail %s\n", rc ? "failed" : "OK");
787         return rc;
788 }
789
790 static
791 int lkrb5_prepare_user_cred(struct lgss_cred *cred)
792 {
793         struct lgss_krb5_cred   *kcred;
794         int                      rc;
795
796         lassert(krb5_this_realm == NULL);
797
798         kcred = (struct lgss_krb5_cred *) cred->lc_mech_cred;
799
800         /*
801          * here we just specified a fix ccname, instead of searching
802          * entire cc dir. is this OK??
803          */
804         snprintf(kcred->kc_ccname, sizeof(kcred->kc_ccname),
805                  "%s%s/%s%u",
806                  krb5_cc_type, krb5_cc_dir, krb5_cred_prefix, cred->lc_uid);
807         logmsg(LL_DEBUG, "using krb5 cache name: %s\n", kcred->kc_ccname);
808
809         rc = lgss_krb5_set_ccache_name(kcred->kc_ccname);
810         if (rc)
811                 logmsg(LL_ERR, "can't set krb5 ccache name: %s\n",
812                        kcred->kc_ccname);
813
814         return rc;
815 }
816
817 static
818 int lgss_krb5_prepare_cred(struct lgss_cred *cred)
819 {
820         struct lgss_krb5_cred  *kcred;
821         int                     rc;
822
823         kcred = malloc(sizeof(*kcred));
824         if (kcred == NULL) {
825                 logmsg(LL_ERR, "can't allocate krb5 cred\n");
826                 return -1;
827         }
828
829         kcred->kc_ccname[0] = '\0';
830         kcred->kc_remove = 0;
831         cred->lc_mech_cred = kcred;
832
833         if (cred->lc_root_flags != 0) {
834                 if (lgss_krb5_get_local_realm())
835                         return -1;
836
837                 rc = lkrb5_prepare_root_cred(cred);
838         } else {
839                 rc = lkrb5_prepare_user_cred(cred);
840         }
841
842         return rc;
843 }
844
845 static
846 void lgss_krb5_release_cred(struct lgss_cred *cred)
847 {
848         struct lgss_krb5_cred   *kcred;
849
850         kcred = (struct lgss_krb5_cred *) cred->lc_mech_cred;
851
852         free(kcred);
853         cred->lc_mech_cred = NULL;
854 }
855
856 struct lgss_mech_type lgss_mech_krb5 = 
857 {
858         .lmt_name               = "krb5",
859         .lmt_mech_n             = LGSS_MECH_KRB5,
860         .lmt_prepare_cred       = lgss_krb5_prepare_cred,
861         .lmt_release_cred       = lgss_krb5_release_cred,
862 };