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