Whamcloud - gitweb
b=16098
[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  2008 Sun Microsystems, Inc. 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
160 char    *krb5_this_realm        = NULL;
161 char    *krb5_keytab_file       = "/etc/krb5.keytab";
162 char    *krb5_cc_type           = "FILE:";
163 char    *krb5_cc_dir            = "/tmp";
164 char    *krb5_cred_prefix       = "krb5cc_";
165 char    *krb5_cred_root_suffix  = "lustre_root";
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 {
277         logmsg(LL_TRACE, "principal: realm %.*s, type %d, size %d, name %.*s\n",
278                krb5_princ_realm(ctx, princ)->length,
279                krb5_princ_realm(ctx, princ)->data,
280                krb5_princ_type(ctx, princ),
281                krb5_princ_size(ctx, princ),
282                krb5_princ_name(ctx, princ)->length,
283                krb5_princ_name(ctx, princ)->data);
284
285         /* check type */
286         if (krb5_princ_type(ctx, princ) != KRB5_NT_PRINCIPAL) {
287                 logmsg(LL_WARN, "principal type %d is not I want\n",
288                        krb5_princ_type(ctx, princ));
289                 return -1;
290         }
291
292         /* check local realm */
293         if (!princ_is_local_realm(ctx, princ)) {
294                 logmsg(LL_WARN, "principal realm %.*s not local: %s\n",
295                        krb5_princ_realm(ctx, princ)->length,
296                        krb5_princ_realm(ctx, princ)->data,
297                        krb5_this_realm);
298                 return -1;
299         }
300
301         /* if it's mds service principal, or lustre_root principal
302          * with host part, verify the hostname.
303          * note we allow lustre_root without host part */
304         if (lgss_krb5_strcmp(krb5_princ_name(ctx, princ),
305                              LGSS_SVC_MDS_STR) == 0) {
306                 if (svc_princ_verify_host(ctx, princ, LL_WARN)) {
307                         logmsg(LL_WARN, "mds service principal doesn't belong "
308                                "to this node\n");
309                         return -1;
310                 }
311         } else if (lgss_krb5_strcmp(krb5_princ_name(ctx, princ),
312                                     LGSS_USR_ROOT_STR) == 0) {
313                 if (krb5_princ_component(ctx, princ, 1) != NULL &&
314                     svc_princ_verify_host(ctx, princ, LL_WARN)) {
315                         logmsg(LL_WARN, "lustre_root principal doesn't belong "
316                                "to this node\n");
317                         return -1;
318                 }
319         } else {
320                 logmsg(LL_WARN, "unexpected krb5 cc principal name %.*s\n",
321                        krb5_princ_name(ctx, princ)->length,
322                        krb5_princ_name(ctx, princ)->data);
323                 return -1;
324         }
325
326         logmsg(LL_TRACE, "principal is OK\n");
327         return 0;
328 }
329
330 /*
331  * find out whether current TGT cache is valid or not
332  */
333 static
334 int lkrb5_check_root_tgt_cc(krb5_context ctx,
335                             krb5_ccache ccache,
336                             char *ccname)
337 {
338         struct stat             statbuf;
339         krb5_ccache             tgt_ccache;
340         krb5_creds              cred;
341         krb5_principal          princ;
342         krb5_cc_cursor          cursor;
343         krb5_error_code         code;
344         char                   *ccfile;
345         time_t                  now;
346         int                     rc = -1, found = 0;
347
348         if (strncmp(ccname, krb5_cc_type, strlen(krb5_cc_type_file))) {
349                 logmsg(LL_ERR, "unexpected cc type\n");
350                 return -1;
351         }
352
353         ccfile = ccname + strlen(krb5_cc_type_file);
354         logmsg(LL_TRACE, "cc file name: %s\n", ccfile);
355
356         /* firstly make sure the cache file is there */
357         if (stat(ccfile, &statbuf)) {
358                 logmsg(LL_DEBUG, "krb5 cc %s: %s\n", ccname, strerror(errno));
359                 return -1;
360         }
361
362         /* prepare parsing the cache file */
363         code = krb5_cc_resolve(ctx, ccname, &tgt_ccache);
364         if (code) {
365                 logmsg(LL_ERR, "resolve krb5 cc %s: %s\n",
366                        ccname, krb5_err_msg(code));
367                 return -1;
368         }
369
370         /* checks the principal */
371         code = krb5_cc_get_principal(ctx, tgt_ccache, &princ);
372         if (code) {
373                 logmsg(LL_ERR, "get cc principal: %s\n", krb5_err_msg(code));
374                 goto out_cc;
375         }
376
377         if (lkrb5_cc_check_tgt_princ(ctx, tgt_ccache, princ)) {
378                 logmsg(LL_WARN, "cc principal is not valid\n");
379                 goto out_princ;
380         }
381
382         /*
383          * find a valid entry
384          */
385         code = krb5_cc_start_seq_get(ctx, tgt_ccache, &cursor);
386         if (code) {
387                 logmsg(LL_ERR, "start cc iteration: %s\n", krb5_err_msg(code));
388                 goto out_princ;
389         }
390
391         now = time(0);
392         do {
393                 krb5_timestamp  duration, delta;
394
395                 code = krb5_cc_next_cred(ctx, tgt_ccache, &cursor, &cred);
396                 if (code != 0)
397                         break;
398
399                 logmsg(LL_DEBUG, "cred: server realm %.*s, type %d, name %.*s; "
400                        "time (%d-%d, renew till %d), valid %d\n",
401                        krb5_princ_realm(ctx, cred.server)->length,
402                        krb5_princ_realm(ctx, cred.server)->data,
403                        krb5_princ_type(ctx, cred.server),
404                        krb5_princ_name(ctx, cred.server)->length,
405                        krb5_princ_name(ctx, cred.server)->data,
406                        cred.times.starttime, cred.times.endtime,
407                        cred.times.renew_till, cred.times.endtime - now);
408
409                 /* FIXME
410                  * we found the princ type is always 0 (KRB5_NT_UNKNOWN), why???
411                  */
412
413                 /* FIXME how about inter-realm TGT??? FIXME */
414                 if (lgss_krb5_strcasecmp(krb5_princ_name(ctx, cred.server),
415                                          "krbtgt"))
416                         continue;
417
418                 if (lgss_krb5_strcasecmp(krb5_princ_realm(ctx, cred.server),
419                                          krb5_this_realm))
420                         continue;
421
422                 /* check validity of time */
423                 delta = 60 * 30; /* half an hour */
424                 duration = cred.times.endtime - cred.times.starttime;
425                 if (duration / 4 < delta)
426                         delta = duration / 4;
427
428                 if (cred.times.starttime <= now &&
429                     cred.times.endtime >= now + delta) {
430                         found = 1;
431                         break;
432                 }
433         } while (1);
434
435         if (!found) {
436                 logmsg(LL_DEBUG, "doesn't find good TGT cache\n");
437                 goto out_seq;
438         }
439
440         /* found a good cred, store it into @ccache */
441         logmsg(LL_DEBUG, "found good TGT cache\n");
442
443         code = krb5_cc_initialize(ctx, ccache, princ);
444         if (code) {
445                 logmsg(LL_ERR, "init private cc: %s\n", krb5_err_msg(code));
446                 goto out_seq;
447         }
448
449         code = krb5_cc_store_cred(ctx, ccache, &cred);
450         if (code) {
451                 logmsg(LL_ERR, "store private cred: %s\n", krb5_err_msg(code));
452                 goto out_seq;
453         }
454
455         logmsg(LL_DEBUG, "store private ccache OK\n");
456         rc = 0;
457
458 out_seq:
459         krb5_cc_end_seq_get(ctx, tgt_ccache, &cursor);
460 out_princ:
461         krb5_free_principal(ctx, princ);
462 out_cc:
463         krb5_cc_close(ctx, tgt_ccache);
464
465         return rc;
466 }
467
468 static
469 int lkrb5_get_root_tgt_keytab(krb5_context ctx,
470                               krb5_ccache ccache,
471                               krb5_keytab kt,
472                               krb5_principal princ,
473                               const char *ccname)
474 {
475         krb5_get_init_creds_opt opts;
476         krb5_creds              cred;
477         krb5_ccache             tgt_ccache;
478         krb5_error_code         code;
479         int                     rc = -1;
480
481         krb5_get_init_creds_opt_init(&opts);
482         krb5_get_init_creds_opt_set_address_list(&opts, NULL);
483         /*
484          * by default krb5 library obtain ticket with lifetime shorter
485          * than the max value. we can change it here if we want. but
486          * seems not necessary now.
487          *
488         krb5_get_init_creds_opt_set_tkt_life(&opts, very-long-time);
489          *
490          */
491
492         /*
493          * obtain TGT and store into global ccache
494          */
495         code = krb5_get_init_creds_keytab(ctx, &cred, princ, kt,
496                                           0, NULL, &opts);
497         if (code) {
498                 logmsg(LL_ERR, "failed to get root TGT for "
499                        "principal %.*s: %s\n",
500                        krb5_princ_name(ctx, princ)->length,
501                        krb5_princ_name(ctx, princ)->data,
502                        krb5_err_msg(code));
503                 return -1;
504         }
505
506         code = krb5_cc_resolve(ctx, ccname, &tgt_ccache);
507         if (code) {
508                 logmsg(LL_ERR, "resolve cc %s: %s\n",
509                        ccname, krb5_err_msg(code));
510                 goto out_cred;
511         }
512
513         code = krb5_cc_initialize(ctx, tgt_ccache, princ);
514         if (code) {
515                 logmsg(LL_ERR, "initialize cc %s: %s\n",
516                        ccname, krb5_err_msg(code));
517                 goto out_cc;
518         }
519
520         code = krb5_cc_store_cred(ctx, tgt_ccache, &cred);
521         if (code) {
522                 logmsg(LL_ERR, "store cred to cc %s: %s\n",
523                        ccname, krb5_err_msg(code));
524                 goto out_cc;
525         }
526
527         logmsg(LL_INFO, "installed TGT of %.*s in cc %s\n",
528                krb5_princ_name(ctx, princ)->length,
529                krb5_princ_name(ctx, princ)->data,
530                ccname);
531
532         /*
533          * now store the cred into my own cc too
534          */
535         code = krb5_cc_initialize(ctx, ccache, princ);
536         if (code) {
537                 logmsg(LL_ERR, "init mem cc: %s\n", krb5_err_msg(code));
538                 goto out_cc;
539         }
540
541         code = krb5_cc_store_cred(ctx, ccache, &cred);
542         if (code) {
543                 logmsg(LL_ERR, "store mm cred: %s\n", krb5_err_msg(code));
544                 goto out_cc;
545         }
546
547         logmsg(LL_DEBUG, "stored TGT into mem cc OK\n");
548         rc = 0;
549 out_cc:
550         krb5_cc_close(ctx, tgt_ccache);
551 out_cred:
552         krb5_free_cred_contents(ctx, &cred);
553         return rc;
554 }
555
556 /*
557  * obtain a new root TGT
558  */
559 static
560 int lkrb5_refresh_root_tgt_cc(krb5_context ctx,
561                               krb5_ccache ccache,
562                               const char *ccname)
563 {
564         krb5_keytab             kt;
565         krb5_keytab_entry       kte;
566         krb5_kt_cursor          cursor;
567         krb5_principal          princ = NULL, princ2;
568         krb5_error_code         code;
569         int                     general_root = 0;
570         int                     rc = -1;
571
572         /* prepare parsing the keytab file */
573         code = krb5_kt_resolve(ctx, krb5_keytab_file, &kt);
574         if (code) {
575                 logmsg(LL_ERR, "resolve keytab %s: %s\n",
576                        krb5_keytab_file, krb5_err_msg(code));
577                 return -1;
578         }
579
580         code = krb5_kt_start_seq_get(ctx, kt, &cursor);
581         if (code) {
582                 logmsg(LL_ERR, "start kt iteration: %s\n", krb5_err_msg(code));
583                 goto out_kt;
584         }
585
586         /* iterate keytab to find proper a entry */
587         do {
588                 code = krb5_kt_next_entry(ctx, kt, &kte, &cursor);
589                 if (code != 0)
590                         break;
591
592                 logmsg(LL_TRACE, "kt entry: realm %.*s, type %d, "
593                        "size %d, name %.*s\n",
594                        krb5_princ_realm(ctx, kte.principal)->length,
595                        krb5_princ_realm(ctx, kte.principal)->data,
596                        krb5_princ_type(ctx, kte.principal),
597                        krb5_princ_size(ctx, kte.principal),
598                        krb5_princ_name(ctx, kte.principal)->length,
599                        krb5_princ_name(ctx, kte.principal)->data);
600
601                 if (!princ_is_local_realm(ctx, kte.principal))
602                         continue;
603
604                 /* lustre_root[/host]@realm */
605                 if (lgss_krb5_strcmp(krb5_princ_name(ctx, kte.principal),
606                                      LGSS_USR_ROOT_STR) == 0) {
607                         int tmp_general_root = 0;
608
609                         if (krb5_princ_component(ctx, kte.principal,1) == NULL){
610                                 if (princ != NULL) {
611                                         logmsg(LL_TRACE, "lustre_root: "
612                                                "already picked one, skip\n");
613                                         continue;
614                                 }
615
616                                 tmp_general_root = 1;
617                         } else {
618                                 if (svc_princ_verify_host(ctx, kte.principal,
619                                                           LL_TRACE)) {
620                                         logmsg(LL_TRACE, "lustre_root: "
621                                                "doesn't belong to this node\n");
622                                         continue;
623                                 }
624
625                                 if (princ != NULL && !general_root) {
626                                         logmsg(LL_TRACE, "lustre_root: already "
627                                                "have a host-specific one, "
628                                                "skip\n");
629                                         continue;
630                                 }
631                         }
632
633                         code = krb5_copy_principal(ctx, kte.principal, &princ2);
634                         if (code) {
635                                 logmsg(LL_ERR, "copy lustre_root princ: %s\n",
636                                        krb5_err_msg(code));
637                                 continue;
638                         }
639
640                         if (princ != NULL) {
641                                 logmsg(LL_TRACE, "release a lustre_root one\n");
642                                 krb5_free_principal(ctx, princ);
643                         }
644                         princ = princ2;
645
646                         general_root = tmp_general_root;
647                         continue;
648                 }
649
650                 /* lustre_mds/host@realm */
651                 if (lgss_krb5_strcmp(krb5_princ_name(ctx, kte.principal),
652                                      LGSS_SVC_MDS_STR) == 0) {
653                         if (svc_princ_verify_host(ctx, kte.principal,
654                                                   LL_TRACE)) {
655                                 logmsg(LL_TRACE, "mds service principal: "
656                                        "doesn't belong to this node\n");
657                                 continue;
658                         }
659
660                         /* select this one */
661                         code = krb5_copy_principal(ctx, kte.principal, &princ2);
662                         if (code) {
663                                 logmsg(LL_ERR, "copy lustre_mds princ: %s\n",
664                                        krb5_err_msg(code));
665                                 continue;
666                         }
667
668                         if (princ != NULL) {
669                                 logmsg(LL_TRACE, "release a lustre_root one\n");
670                                 krb5_free_principal(ctx, princ);
671                         }
672                         princ = princ2;
673                         break;
674                 }
675         } while (1);
676
677         krb5_kt_end_seq_get(ctx, kt, &cursor);
678
679         if (princ == NULL) {
680                 logmsg(LL_ERR, "can't find proper keytab entry\n");
681                 goto out_kt;
682         }
683
684         /* obtain root TGT */
685         rc = lkrb5_get_root_tgt_keytab(ctx, ccache, kt, princ, ccname);
686
687         krb5_free_principal(ctx, princ);
688 out_kt:
689         krb5_kt_close(ctx, kt);
690         return rc;
691 }
692
693 static
694 int lkrb5_prepare_root_cred(struct lgss_cred *cred)
695 {
696         krb5_context            ctx;
697         krb5_ccache             ccache;
698         krb5_error_code         code;
699         struct lgss_krb5_cred  *kcred;
700         char                    tgtcc[1024];
701         int                     rc = -1;
702
703         lassert(krb5_this_realm != NULL);
704
705         kcred = (struct lgss_krb5_cred *) cred->lc_mech_cred;
706
707         /* compose the TGT cc name */
708         snprintf(tgtcc, sizeof(tgtcc), "%s%s/%s%s_%s",
709                  krb5_cc_type, krb5_cc_dir, krb5_cred_prefix,
710                  krb5_cred_root_suffix, krb5_this_realm);
711         logmsg(LL_DEBUG, "root krb5 TGT ccname: %s\n", tgtcc);
712
713         /* compose the memory cc name, since the only user is myself,
714          * the name could be fixed
715          */
716         snprintf(kcred->kc_ccname, sizeof(kcred->kc_ccname),
717                  "%s/self", krb5_cc_type_mem);
718         logmsg(LL_TRACE, "private cc: %s\n", kcred->kc_ccname);
719
720         code = krb5_init_context(&ctx);
721         if (code) {
722                 logmsg(LL_ERR, "initialize krb5 context: %s\n",
723                        krb5_err_msg(code));
724                 return -1;
725         }
726
727         code = krb5_cc_resolve(ctx, kcred->kc_ccname, &ccache);
728         if (code) {
729                 logmsg(LL_ERR, "resolve krb5 cc %s: %s\n",
730                        kcred->kc_ccname, krb5_err_msg(code));
731                 goto out_ctx;
732         }
733
734         /*
735          * search and/or obtain root TGT credential.
736          * it touched global (on-disk) tgt cache, do it inside mutex locking
737          */
738         lgss_krb5_mutex_lock();
739
740         rc = lkrb5_check_root_tgt_cc(ctx, ccache, tgtcc);
741         if (rc != 0)
742                 rc = lkrb5_refresh_root_tgt_cc(ctx, ccache, tgtcc);
743
744         if (rc == 0)
745                 rc = lgss_krb5_set_ccache_name(kcred->kc_ccname);
746
747         lgss_krb5_mutex_unlock();
748
749         krb5_cc_close(ctx, ccache);
750 out_ctx:
751         krb5_free_context(ctx);
752
753         logmsg(LL_DEBUG, "prepare root credentail %s\n", rc ? "failed" : "OK");
754         return rc;
755 }
756
757 static
758 int lkrb5_prepare_user_cred(struct lgss_cred *cred)
759 {
760         struct lgss_krb5_cred   *kcred;
761         int                      rc;
762
763         lassert(krb5_this_realm == NULL);
764
765         kcred = (struct lgss_krb5_cred *) cred->lc_mech_cred;
766
767         /*
768          * here we just specified a fix ccname, instead of searching
769          * entire cc dir. is this OK??
770          */
771         snprintf(kcred->kc_ccname, sizeof(kcred->kc_ccname),
772                  "%s%s/%s%u",
773                  krb5_cc_type, krb5_cc_dir, krb5_cred_prefix, cred->lc_uid);
774         logmsg(LL_DEBUG, "using krb5 cache name: %s\n", kcred->kc_ccname);
775
776         rc = lgss_krb5_set_ccache_name(kcred->kc_ccname);
777         if (rc)
778                 logmsg(LL_ERR, "can't set krb5 ccache name: %s\n",
779                        kcred->kc_ccname);
780
781         return rc;
782 }
783
784 static
785 int lgss_krb5_prepare_cred(struct lgss_cred *cred)
786 {
787         struct lgss_krb5_cred  *kcred;
788         int                     rc;
789
790         kcred = malloc(sizeof(*kcred));
791         if (kcred == NULL) {
792                 logmsg(LL_ERR, "can't allocate krb5 cred\n");
793                 return -1;
794         }
795
796         kcred->kc_ccname[0] = '\0';
797         kcred->kc_remove = 0;
798         cred->lc_mech_cred = kcred;
799
800         if (cred->lc_fl_root || cred->lc_fl_mds) {
801                 if (lgss_krb5_get_local_realm())
802                         return -1;
803
804                 rc = lkrb5_prepare_root_cred(cred);
805         } else {
806                 rc = lkrb5_prepare_user_cred(cred);
807         }
808
809         return rc;
810 }
811
812 static
813 void lgss_krb5_release_cred(struct lgss_cred *cred)
814 {
815         struct lgss_krb5_cred   *kcred;
816
817         kcred = (struct lgss_krb5_cred *) cred->lc_mech_cred;
818
819         free(kcred);
820         cred->lc_mech_cred = NULL;
821 }
822
823 struct lgss_mech_type lgss_mech_krb5 = 
824 {
825         .lmt_name               = "krb5",
826         .lmt_mech_n             = LGSS_MECH_KRB5,
827         .lmt_prepare_cred       = lgss_krb5_prepare_cred,
828         .lmt_release_cred       = lgss_krb5_release_cred,
829 };