2 * Modifications for Lustre
4 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
6 * Author: Eric Mei <ericm@clusterfs.com>
10 * Adapted in part from MIT Kerberos 5-1.2.1 slave/kprop.c and from
11 * http://docs.sun.com/?p=/doc/816-1331/6m7oo9sms&a=view
13 * Copyright (c) 2002-2004 The Regents of the University of Michigan.
14 * All rights reserved.
16 * Andy Adamson <andros@umich.edu>
17 * J. Bruce Fields <bfields@umich.edu>
18 * Marius Aamodt Eriksen <marius@umich.edu>
19 * Kevin Coffman <kwc@umich.edu>
25 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
26 * All Rights Reserved.
28 * Export of this software from the United States of America may
29 * require a specific license from the United States Government.
30 * It is the responsibility of any person or organization contemplating
31 * export to obtain such a license before exporting.
33 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
34 * distribute this software and its documentation for any purpose and
35 * without fee is hereby granted, provided that the above copyright
36 * notice appear in all copies and that both that copyright notice and
37 * this permission notice appear in supporting documentation, and that
38 * the name of M.I.T. not be used in advertising or publicity pertaining
39 * to distribution of the software without specific, written prior
40 * permission. Furthermore if you modify this software you must label
41 * your software as modified software and not distribute it in such a
42 * fashion that it might be confused with the original M.I.T. software.
43 * M.I.T. makes no representations about the suitability of
44 * this software for any purpose. It is provided "as is" without express
45 * or implied warranty.
49 * Copyright 1994 by OpenVision Technologies, Inc.
51 * Permission to use, copy, modify, distribute, and sell this software
52 * and its documentation for any purpose is hereby granted without fee,
53 * provided that the above copyright notice appears in all copies and
54 * that both that copyright notice and this permission notice appear in
55 * supporting documentation, and that the name of OpenVision not be used
56 * in advertising or publicity pertaining to distribution of the software
57 * without specific, written prior permission. OpenVision makes no
58 * representations about the suitability of this software for any
59 * purpose. It is provided "as is" without express or implied warranty.
61 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
62 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
63 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
64 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
65 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
66 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
67 * PERFORMANCE OF THIS SOFTWARE.
72 Copyright (c) 2004 The Regents of the University of Michigan.
75 Redistribution and use in source and binary forms, with or without
76 modification, are permitted provided that the following conditions
79 1. Redistributions of source code must retain the above copyright
80 notice, this list of conditions and the following disclaimer.
81 2. Redistributions in binary form must reproduce the above copyright
82 notice, this list of conditions and the following disclaimer in the
83 documentation and/or other materials provided with the distribution.
84 3. Neither the name of the University nor the names of its
85 contributors may be used to endorse or promote products derived
86 from this software without specific prior written permission.
88 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
89 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
90 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
91 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
92 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
93 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
94 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
95 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
96 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
97 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
98 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
106 #include <sys/param.h>
107 #include <sys/types.h>
108 #include <sys/stat.h>
111 #include <sys/mman.h>
112 #include <sys/wait.h>
113 #include <sys/utsname.h>
114 #include <sys/socket.h>
115 #include <arpa/inet.h>
128 #include <gssapi/gssapi.h>
129 #include <gssapi/gssapi_krb5.h>
132 #include "lsupport.h"
133 #include "lgss_utils.h"
134 #include "lgss_krb5_utils.h"
136 static void lgss_krb5_mutex_lock(void)
138 if (lgss_mutex_lock(LGSS_MUTEX_KRB5)) {
139 logmsg(LL_ERR, "can't lock process, abort!\n");
144 static void lgss_krb5_mutex_unlock(void)
146 if (lgss_mutex_unlock(LGSS_MUTEX_KRB5)) {
147 logmsg(LL_WARN, "can't unlock process, other processes "
148 "might need to wait long time\n");
152 #define krb5_err_msg(code) error_message(code)
154 const char *krb5_cred_root_suffix = "lustre_root";
155 const char *krb5_cred_mds_suffix = "lustre_mds";
156 const char *krb5_cred_oss_suffix = "lustre_oss";
158 char *krb5_this_realm = NULL;
159 char *krb5_keytab_file = "/etc/krb5.keytab";
161 static int lgss_krb5_set_ccache_name(const char *ccname)
163 unsigned int maj_stat, min_stat;
165 maj_stat = gss_krb5_ccache_name(&min_stat, ccname, NULL);
166 if (maj_stat != GSS_S_COMPLETE) {
167 logmsg(LL_ERR, "failed to set ccache name\n");
171 logmsg(LL_DEBUG, "set cc: %s\n", ccname);
176 int lgss_krb5_get_local_realm(void)
178 krb5_context context = NULL;
179 krb5_error_code code;
182 if (krb5_this_realm != NULL)
185 code = krb5_init_context(&context);
187 logmsg(LL_ERR, "init ctx: %s\n", krb5_err_msg(code));
191 code = krb5_get_default_realm(context, &krb5_this_realm);
193 logmsg(LL_ERR, "get default realm: %s\n", krb5_err_msg(code));
197 logmsg(LL_DEBUG, "Local realm: %s\n", krb5_this_realm);
200 krb5_free_context(context);
205 int princ_is_local_realm(krb5_context ctx, krb5_principal princ)
207 return (lgss_krb5_strcasecmp(krb5_princ_realm(ctx, princ),
208 krb5_this_realm) == 0);
212 int svc_princ_verify_host(krb5_context ctx,
213 krb5_principal princ,
217 struct utsname utsbuf;
218 struct hostent *host;
219 const int max_namelen = 512;
220 char namebuf[max_namelen];
223 if (krb5_princ_component(ctx, princ, 1) == NULL) {
224 logmsg(loglevel, "service principal has no host part\n");
229 if (lnet_nid2hostname(self_nid, namebuf, max_namelen)) {
231 "can't resolve hostname from nid %"PRIx64"\n",
237 if (uname(&utsbuf)) {
238 logmsg(loglevel, "get UTS name: %s\n", strerror(errno));
242 host = gethostbyname(utsbuf.nodename);
244 logmsg(loglevel, "failed to get local hostname\n");
247 h_name = host->h_name;
250 if (lgss_krb5_strcasecmp(krb5_princ_component(ctx, princ, 1),
252 logmsg(loglevel, "service principal: hostname %.*s "
253 "doesn't match localhost %s\n",
254 krb5_princ_component(ctx, princ, 1)->length,
255 krb5_princ_component(ctx, princ, 1)->data,
263 static int lkrb5_cc_check_tgt_princ(krb5_context ctx,
265 krb5_principal princ,
269 const char *princ_name;
271 logmsg(LL_DEBUG, "principal: realm %.*s, type %d, size %d, name %.*s\n",
272 krb5_princ_realm(ctx, princ)->length,
273 krb5_princ_realm(ctx, princ)->data,
274 krb5_princ_type(ctx, princ),
275 krb5_princ_size(ctx, princ),
276 krb5_princ_name(ctx, princ)->length,
277 krb5_princ_name(ctx, princ)->data);
280 if (krb5_princ_type(ctx, princ) != KRB5_NT_PRINCIPAL) {
281 logmsg(LL_WARN, "principal type %d is not I want\n",
282 krb5_princ_type(ctx, princ));
286 /* check local realm */
287 if (!princ_is_local_realm(ctx, princ)) {
288 logmsg(LL_WARN, "principal realm %.*s not local: %s\n",
289 krb5_princ_realm(ctx, princ)->length,
290 krb5_princ_realm(ctx, princ)->data,
295 /* check principal name, give priority to MDT/OST cred over ROOT */
296 if (flag & LGSS_ROOT_CRED_MDT)
297 princ_name = LGSS_SVC_MDS_STR;
298 else if (flag & LGSS_ROOT_CRED_OST)
299 princ_name = LGSS_SVC_OSS_STR;
300 else if (flag & LGSS_ROOT_CRED_ROOT)
301 princ_name = LGSS_USR_ROOT_STR;
305 if (lgss_krb5_strcmp(krb5_princ_name(ctx, princ), princ_name) &&
306 (strcmp(princ_name, LGSS_USR_ROOT_STR) ||
307 lgss_krb5_strcmp(krb5_princ_name(ctx, princ), LGSS_SVC_HOST_STR))) {
308 logmsg(LL_WARN, "%.*s: we expect %s instead\n",
309 krb5_princ_name(ctx, princ)->length,
310 krb5_princ_name(ctx, princ)->data,
316 * verify the hostname part of the principal, except we do allow
317 * lustre_root without binding to a host.
319 if (krb5_princ_component(ctx, princ, 1) == NULL) {
320 if (flag != LGSS_ROOT_CRED_ROOT) {
321 logmsg(LL_WARN, "%.*s: missing hostname\n",
322 krb5_princ_name(ctx, princ)->length,
323 krb5_princ_name(ctx, princ)->data);
327 if (svc_princ_verify_host(ctx, princ, self_nid, LL_WARN)) {
328 logmsg(LL_DEBUG, "%.*s: doesn't belong to this node\n",
329 krb5_princ_name(ctx, princ)->length,
330 krb5_princ_name(ctx, princ)->data);
335 logmsg(LL_TRACE, "principal is OK\n");
339 static inline int lgss_krb5_get_default_ccache_name(krb5_context ctx,
340 char *ccname, int size)
342 if (snprintf(ccname, size, "%s", krb5_cc_default_name(ctx)) >= size)
343 return -ENAMETOOLONG;
349 * compose the TGT cc name, abiding to system configuration.
351 static int get_root_tgt_ccname(krb5_context ctx, char *ccname, int size)
353 return lgss_krb5_get_default_ccache_name(ctx, ccname, size);
356 static int switch_identity(uid_t uid)
361 /* drop list of supp groups */
362 rc = setgroups(0, NULL);
364 logmsg(LL_ERR, "cannot drop list of supp groups: %s\n",
372 logmsg(LL_ERR, "cannot get pw entry for %u: %s\n",
373 uid, strerror(errno));
378 rc = setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid);
380 logmsg(LL_ERR, "cannot set real gid to %u: %s\n",
381 pw->pw_gid, strerror(errno));
386 rc = setresuid(uid, uid, uid);
388 logmsg(LL_ERR, "cannot set real uid to %u: %s\n",
389 uid, strerror(errno));
398 static int acquire_user_cred_and_check(char *ccname)
400 gss_OID mech = (gss_OID)&krb5oid;
401 gss_OID_set_desc desired_mechs = { 1, mech };
402 gss_cred_id_t gss_cred;
403 OM_uint32 maj_stat, min_stat, lifetime;
406 if (lgss_krb5_set_ccache_name(ccname)) {
407 logmsg(LL_ERR, "cannot set ccache name: %s\n", ccname);
411 maj_stat = gss_acquire_cred(&min_stat, GSS_C_NO_NAME, GSS_C_INDEFINITE,
412 &desired_mechs, GSS_C_INITIATE,
413 &gss_cred, NULL, NULL);
414 if (maj_stat != GSS_S_COMPLETE) {
415 logmsg_gss(LL_INFO, mech, maj_stat, min_stat,
416 "failed gss_acquire_cred");
420 /* force validation of cred to check for expiry */
421 maj_stat = gss_inquire_cred(&min_stat, gss_cred,
422 NULL, &lifetime, NULL, NULL);
423 if (maj_stat != GSS_S_COMPLETE) {
424 logmsg_gss(LL_INFO, mech, maj_stat, min_stat,
425 "failed gss_inquire_cred");
429 if (gss_cred != GSS_C_NO_CREDENTIAL)
430 gss_release_cred(&min_stat, &gss_cred);
435 static int filter_krb5_ccache(const struct dirent *d)
437 if (strstr(d->d_name, LGSS_DEFAULT_CRED_PREFIX))
444 * Look in dirname for a possibly valid ccache for uid.
446 * Returns 0 if a potential entry is found.
447 * Otherwise, a negative errno is returned.
449 static int find_existing_krb5_ccache(uid_t uid, char *dir,
450 char *ccname, int size)
452 struct dirent **namelist;
454 struct stat tmp_stat;
455 char dirname[PATH_MAX], buf[PATH_MAX];
456 int num_ents, i, j = 0, rc = -1;
458 /* provided dir can be a pattern */
459 for (i = 0; dir[i] != '\0'; i++) {
462 switch (dir[i + 1]) {
464 j += sprintf(dirname + j, "%lu",
471 dirname[j++] = dir[i];
477 num_ents = scandir(dirname, &namelist, filter_krb5_ccache, 0);
479 logmsg(LL_INFO, "scandir %s failed: %s\n",
480 dirname, strerror(errno));
484 for (i = 0; i < num_ents; i++) {
488 if (snprintf(buf, sizeof(buf), "%s/%s",
489 dirname, namelist[i]->d_name) >= sizeof(buf)) {
490 logmsg(LL_INFO, "%s/%s name too long\n",
491 dirname, namelist[i]->d_name);
495 if (lstat(buf, &tmp_stat)) {
496 logmsg(LL_INFO, "lstat %s failed: %s\n",
497 buf, strerror(errno));
501 /* we only look for files as credentials caches */
502 if (!S_ISREG(tmp_stat.st_mode))
505 /* make sure it is owned by uid */
506 if (tmp_stat.st_uid != uid) {
507 logmsg(LL_INFO, "%s not owned by %u\n",
512 /* check user has rw perms */
513 if (!(tmp_stat.st_mode & S_IRUSR &&
514 tmp_stat.st_mode & S_IWUSR)) {
515 logmsg(LL_INFO, "%s does not have rw perms for %u\n",
520 if (snprintf(ccname, size, "FILE:%s", buf) >= size) {
521 logmsg(LL_INFO, "FILE:%s name too long\n", buf);
525 rc = acquire_user_cred_and_check(ccname);
539 * Compose the TGT cc name for user, needs to fork process and switch identity.
540 * For that reason, ccname buffer passed in must be mmapped with MAP_SHARED.
542 static int get_user_tgt_ccname(struct lgss_cred *cred, krb5_context ctx,
543 char *ccname, int size)
548 /* fork to not change identity in main process, it needs to stay root
549 * in order to proceed to ioctls
553 logmsg(LL_ERR, "cannot fork child for user %u: %s\n",
554 cred->lc_uid, strerror(errno));
556 } else if (child == 0) {
557 /* switch identity */
558 rc = switch_identity(cred->lc_uid);
562 /* getting default ccname requires impersonating user */
563 rc = lgss_krb5_get_default_ccache_name(ctx, ccname, size);
566 "cannot get default ccname for user %u\n",
571 /* job done for child */
574 logmsg(LL_TRACE, "forked child %d\n", child);
575 if (wait(&status) < 0) {
576 logmsg(LL_ERR, "wait child %d failed: %s\n",
577 child, strerror(errno));
580 if (!WIFEXITED(status)) {
581 logmsg(LL_ERR, "child %d terminated with %d\n",
587 /* try ccname as fetched by child */
588 rc = acquire_user_cred_and_check(ccname);
590 /* user's creds found in default ccache */
593 /* fallback: look at every file matching
595 * - /run/user/<uid>/ *krb5cc*
597 rc = find_existing_krb5_ccache(cred->lc_uid, LGSS_DEFAULT_CRED_DIR,
600 /* user's creds found in LGSS_DEFAULT_CRED_DIR */
603 rc = find_existing_krb5_ccache(cred->lc_uid, LGSS_USER_CRED_DIR,
606 /* user's creds found in LGSS_USER_CRED_DIR */
616 * find out whether current TGT cache is valid or not
618 static int lkrb5_check_root_tgt_cc(krb5_context ctx, unsigned int flag,
621 krb5_ccache tgt_ccache;
622 krb5_principal princ;
623 krb5_cc_cursor cursor;
624 krb5_error_code code;
625 char ccname[PATH_MAX];
630 found = get_root_tgt_ccname(ctx, ccname, sizeof(ccname));
633 logmsg(LL_DEBUG, "root krb5 TGT ccname: %s\n", ccname);
635 /* prepare parsing the cache file */
636 code = krb5_cc_resolve(ctx, ccname, &tgt_ccache);
638 logmsg(LL_ERR, "resolve krb5 cc %s: %s\n",
639 ccname, krb5_err_msg(code));
643 /* checks the principal */
644 code = krb5_cc_get_principal(ctx, tgt_ccache, &princ);
646 logmsg(LL_ERR, "get cc principal: %s\n", krb5_err_msg(code));
650 if (lkrb5_cc_check_tgt_princ(ctx, tgt_ccache, princ, flag, self_nid))
656 code = krb5_cc_start_seq_get(ctx, tgt_ccache, &cursor);
658 logmsg(LL_ERR, "start cc iteration: %s\n", krb5_err_msg(code));
664 krb5_timestamp duration, delta;
666 code = krb5_cc_next_cred(ctx, tgt_ccache, &cursor, &cred);
671 "cred: server realm %.*s, type %d, name %.*s; time (%lld-%lld, renew till %lld), valid %lld\n",
672 krb5_princ_realm(ctx, cred.server)->length,
673 krb5_princ_realm(ctx, cred.server)->data,
674 krb5_princ_type(ctx, cred.server),
675 krb5_princ_name(ctx, cred.server)->length,
676 krb5_princ_name(ctx, cred.server)->data,
677 (long long)cred.times.starttime,
678 (long long)cred.times.endtime,
679 (long long)cred.times.renew_till,
680 (long long)(cred.times.endtime - now));
683 * we found the princ type is always 0 (KRB5_NT_UNKNOWN), why???
686 /* FIXME how about inter-realm TGT??? FIXME */
687 if (lgss_krb5_strcasecmp(krb5_princ_name(ctx, cred.server),
691 if (lgss_krb5_strcasecmp(krb5_princ_realm(ctx, cred.server),
695 /* check validity of time */
696 delta = 60 * 30; /* half an hour */
697 duration = cred.times.endtime - cred.times.starttime;
698 if (duration / 4 < delta)
699 delta = duration / 4;
701 if (cred.times.starttime <= now &&
702 cred.times.endtime >= now + delta) {
708 krb5_cc_end_seq_get(ctx, tgt_ccache, &cursor);
710 krb5_free_principal(ctx, princ);
712 krb5_cc_close(ctx, tgt_ccache);
715 logmsg(LL_TRACE, "found good TGT cache\n");
716 return lgss_krb5_set_ccache_name(ccname);
720 logmsg(LL_TRACE, "did not find good TGT cache\n");
724 static int lkrb5_get_root_tgt_keytab(krb5_context ctx, krb5_keytab kt,
725 krb5_principal princ, const char *ccname)
727 krb5_get_init_creds_opt opts;
729 krb5_ccache tgt_ccache;
730 krb5_error_code code;
733 krb5_get_init_creds_opt_init(&opts);
734 krb5_get_init_creds_opt_set_address_list(&opts, NULL);
736 * by default krb5 library obtain ticket with lifetime shorter
737 * than the max value. we can change it here if we want. but
738 * seems not necessary now.
740 * krb5_get_init_creds_opt_set_tkt_life(&opts, very-long-time);
745 * obtain TGT and store into cache
747 code = krb5_get_init_creds_keytab(ctx, &cred, princ, kt,
751 "failed to get root TGT for principal %.*s: %s\n",
752 krb5_princ_name(ctx, princ)->length,
753 krb5_princ_name(ctx, princ)->data,
758 code = krb5_cc_resolve(ctx, ccname, &tgt_ccache);
760 logmsg(LL_ERR, "resolve cc %s: %s\n",
761 ccname, krb5_err_msg(code));
765 code = krb5_cc_initialize(ctx, tgt_ccache, princ);
767 logmsg(LL_ERR, "initialize cc %s: %s\n",
768 ccname, krb5_err_msg(code));
772 code = krb5_cc_store_cred(ctx, tgt_ccache, &cred);
774 logmsg(LL_ERR, "store cred to cc %s: %s\n",
775 ccname, krb5_err_msg(code));
779 logmsg(LL_INFO, "installed TGT of %.*s in cc %s\n",
780 krb5_princ_name(ctx, princ)->length,
781 krb5_princ_name(ctx, princ)->data,
784 rc = lgss_krb5_set_ccache_name(ccname);
787 krb5_cc_close(ctx, tgt_ccache);
789 krb5_free_cred_contents(ctx, &cred);
794 * obtain a new root TGT
796 static int lkrb5_refresh_root_tgt_cc(krb5_context ctx, unsigned int root_flags,
800 krb5_keytab_entry kte;
801 krb5_kt_cursor cursor;
802 krb5_principal princ = NULL;
803 krb5_error_code code;
804 char ccname[PATH_MAX];
805 unsigned int flag = 0;
808 /* prepare parsing the keytab file */
809 code = krb5_kt_resolve(ctx, krb5_keytab_file, &kt);
811 logmsg(LL_ERR, "resolve keytab %s: %s\n",
812 krb5_keytab_file, krb5_err_msg(code));
816 code = krb5_kt_start_seq_get(ctx, kt, &cursor);
818 logmsg(LL_ERR, "start kt iteration: %s\n", krb5_err_msg(code));
822 /* iterate keytab to find proper an entry */
824 krb5_data *princname;
826 code = krb5_kt_next_entry(ctx, kt, &kte, &cursor);
831 "kt entry: realm %.*s, type %d, size %d, name %.*s\n",
832 krb5_princ_realm(ctx, kte.principal)->length,
833 krb5_princ_realm(ctx, kte.principal)->data,
834 krb5_princ_type(ctx, kte.principal),
835 krb5_princ_size(ctx, kte.principal),
836 krb5_princ_name(ctx, kte.principal)->length,
837 krb5_princ_name(ctx, kte.principal)->data);
839 if (!princ_is_local_realm(ctx, kte.principal))
842 princname = krb5_princ_name(ctx, kte.principal);
844 if ((root_flags & LGSS_ROOT_CRED_ROOT) != 0 &&
845 (!lgss_krb5_strcmp(princname, LGSS_USR_ROOT_STR) ||
846 !lgss_krb5_strcmp(princname, LGSS_SVC_HOST_STR))) {
847 flag = LGSS_ROOT_CRED_ROOT;
848 } else if ((root_flags & LGSS_ROOT_CRED_MDT) != 0 &&
849 !lgss_krb5_strcmp(princname, LGSS_SVC_MDS_STR)) {
850 flag = LGSS_ROOT_CRED_MDT;
851 } else if ((root_flags & LGSS_ROOT_CRED_OST) != 0 &&
852 !lgss_krb5_strcmp(princname, LGSS_SVC_OSS_STR)) {
853 flag = LGSS_ROOT_CRED_OST;
855 logmsg(LL_TRACE, "not what we want, skip\n");
859 if (krb5_princ_component(ctx, kte.principal, 1) == NULL) {
860 if (flag != LGSS_ROOT_CRED_ROOT) {
861 logmsg(LL_TRACE, "no hostname, skip\n");
865 if (svc_princ_verify_host(ctx, kte.principal, self_nid,
867 logmsg(LL_TRACE, "doesn't belong to this "
873 code = krb5_copy_principal(ctx, kte.principal, &princ);
875 logmsg(LL_ERR, "copy princ: %s\n", krb5_err_msg(code));
879 lassert(princ != NULL);
883 krb5_kt_end_seq_get(ctx, kt, &cursor);
886 logmsg(LL_ERR, "can't find proper keytab entry\n");
890 /* obtain root TGT */
891 rc = get_root_tgt_ccname(ctx, ccname, sizeof(ccname));
893 rc = lkrb5_get_root_tgt_keytab(ctx, kt, princ, ccname);
895 krb5_free_principal(ctx, princ);
897 krb5_kt_close(ctx, kt);
901 static int lkrb5_prepare_root_cred(struct lgss_cred *cred)
904 krb5_error_code code;
907 lassert(krb5_this_realm != NULL);
909 code = krb5_init_context(&ctx);
911 logmsg(LL_ERR, "initialize krb5 context: %s\n",
917 * search and/or obtain root TGT credential.
918 * it touched global (on-disk) tgt cache, do it inside mutex locking
920 lgss_krb5_mutex_lock();
921 rc = lkrb5_check_root_tgt_cc(ctx, cred->lc_root_flags,
924 rc = lkrb5_refresh_root_tgt_cc(ctx, cred->lc_root_flags,
927 lgss_krb5_mutex_unlock();
928 krb5_free_context(ctx);
930 logmsg(LL_DEBUG, "prepare root credentail %s\n", rc ? "failed" : "OK");
934 static int lkrb5_prepare_user_cred(struct lgss_cred *cred)
937 krb5_error_code code;
942 lassert(krb5_this_realm == NULL);
944 code = krb5_init_context(&ctx);
946 logmsg(LL_ERR, "initialize krb5 context: %s\n",
951 /* buffer passed to get_user_tgt_ccname() must be mmapped with
952 * MAP_SHARED because it is accessed read/write from a child process
954 ccname = mmap(NULL, size, PROT_READ | PROT_WRITE,
955 MAP_SHARED | MAP_ANONYMOUS, -1, 0);
956 if (ccname == MAP_FAILED) {
957 logmsg(LL_ERR, "cannot mmap memory for user %u: %s\n",
958 cred->lc_uid, strerror(errno));
963 rc = get_user_tgt_ccname(cred, ctx, ccname, size);
965 logmsg(LL_ERR, "cannot get user %u ccname: %s\n",
966 cred->lc_uid, strerror(-rc));
968 logmsg(LL_INFO, "using krb5 cache name: %s\n", (char *)ccname);
970 if (munmap(ccname, size) == -1) {
971 logmsg(LL_ERR, "cannot munmap memory for user %u: %s\n",
972 cred->lc_uid, strerror(errno));
973 rc = rc ? rc : -errno;
977 krb5_free_context(ctx);
981 static int lgss_krb5_prepare_cred(struct lgss_cred *cred)
985 cred->lc_mech_cred = NULL;
987 if (cred->lc_root_flags != 0) {
988 if (lgss_krb5_get_local_realm())
991 rc = lkrb5_prepare_root_cred(cred);
993 rc = lkrb5_prepare_user_cred(cred);
1000 void lgss_krb5_release_cred(struct lgss_cred *cred)
1002 cred->lc_mech_cred = NULL;
1005 struct lgss_mech_type lgss_mech_krb5 =
1008 .lmt_mech_n = LGSS_MECH_KRB5,
1009 .lmt_prepare_cred = lgss_krb5_prepare_cred,
1010 .lmt_release_cred = lgss_krb5_release_cred,