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