--- /dev/null
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2007, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ * Adapted in part from MIT Kerberos 5-1.2.1 slave/kprop.c and from
+ * http://docs.sun.com/?p=/doc/816-1331/6m7oo9sms&a=view
+ *
+ * Copyright (c) 2002-2004 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@umich.edu>
+ * J. Bruce Fields <bfields@umich.edu>
+ * Marius Aamodt Eriksen <marius@umich.edu>
+ * Kevin Coffman <kwc@umich.edu>
+ */
+
+/*
+ * slave/kprop.c
+ *
+ * Copyright 1990,1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * Copyright 1994 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ krb5_util.c
+
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include "config.h"
+#include <sys/param.h>
+//#include <rpc/rpc.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <time.h>
+#include <gssapi/gssapi.h>
+#ifdef USE_PRIVATE_KRB5_FUNCTIONS
+#include <gssapi/gssapi_krb5.h>
+#endif
+#include <krb5.h>
+
+#include "lgss_utils.h"
+#include "lgss_krb5_utils.h"
+
+static void lgss_krb5_mutex_lock(void)
+{
+ if (lgss_mutex_lock(LGSS_MUTEX_KRB5)) {
+ logmsg(LL_ERR, "can't lock process, abort!\n");
+ exit(-1);
+ }
+}
+
+static void lgss_krb5_mutex_unlock(void)
+{
+ if (lgss_mutex_unlock(LGSS_MUTEX_KRB5)) {
+ logmsg(LL_WARN, "can't unlock process, other processes "
+ "might need to wait long time\n");
+ }
+}
+
+/*
+ * NOTE
+ * - currently we only support "normal" cache types: "FILE" and "MEMORY".
+ */
+
+#define krb5_err_msg(code) error_message(code)
+
+const char *krb5_cc_type_mem = "MEMORY:";
+const char *krb5_cc_type_file = "FILE:";
+
+char *krb5_this_realm = NULL;
+char *krb5_keytab_file = "/etc/krb5.keytab";
+char *krb5_cc_type = "FILE:";
+char *krb5_cc_dir = "/tmp";
+char *krb5_cred_prefix = "krb5cc_";
+char *krb5_cred_root_suffix = "lustre_root";
+
+struct lgss_krb5_cred {
+ char kc_ccname[128];
+ int kc_remove; /* remove cache upon release */
+};
+
+static
+int lgss_krb5_set_ccache_name(const char *ccname)
+{
+#ifdef USE_GSS_KRB5_CCACHE_NAME
+ unsigned int maj_stat, min_stat;
+
+ maj_stat = gss_krb5_ccache_name(&min_stat, ccname, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ logmsg(LL_ERR, "failed to set ccache name\n");
+ return -1;
+ }
+#else
+ /*
+ * Set the KRB5CCNAME environment variable to tell the krb5 code
+ * which credentials cache to use. (Instead of using the private
+ * function above for which there is no generic gssapi equivalent)
+ */
+ if (setenv("KRB5CCNAME", ccname, 1)) {
+ logmsg(LL_ERR, "set env of krb5 ccname: %s\n",
+ strerror(errno));
+ return -1;
+ }
+#endif
+ logmsg(LL_DEBUG, "set cc: %s\n", ccname);
+ return 0;
+}
+
+static
+int lgss_krb5_get_local_realm(void)
+{
+ krb5_context context = NULL;
+ krb5_error_code code;
+ int retval = -1;
+
+ if (krb5_this_realm != NULL)
+ return 0;
+
+ code = krb5_init_context(&context);
+ if (code) {
+ logmsg(LL_ERR, "init ctx: %s\n", krb5_err_msg(code));
+ return -1;
+ }
+
+ code = krb5_get_default_realm(context, &krb5_this_realm);
+ if (code) {
+ logmsg(LL_ERR, "get default realm: %s\n", krb5_err_msg(code));
+ goto out;
+ }
+
+ logmsg(LL_DEBUG, "Local realm: %s\n", krb5_this_realm);
+ retval = 0;
+out:
+ krb5_free_context(context);
+ return retval;
+}
+
+static
+int princ_is_local_realm(krb5_context ctx, krb5_principal princ)
+{
+ return (lgss_krb5_strcasecmp(krb5_princ_realm(ctx, princ),
+ krb5_this_realm) == 0);
+}
+
+static
+int svc_princ_is_local_host(krb5_context ctx,
+ krb5_principal princ,
+ loglevel_t loglevel)
+{
+ struct utsname utsbuf;
+ struct hostent *host;
+
+ if (krb5_princ_component(ctx, princ, 1) == NULL) {
+ logmsg(loglevel, "service principal has no host part\n");
+ return -1;
+ }
+
+ if (uname(&utsbuf)) {
+ logmsg(loglevel, "get UTS name: %s\n", strerror(errno));
+ return -1;
+ }
+
+ host = gethostbyname(utsbuf.nodename);
+ if (host == NULL) {
+ logmsg(loglevel, "failed to get local hostname\n");
+ return -1;
+ }
+
+ if (lgss_krb5_strcasecmp(krb5_princ_component(ctx, princ, 1),
+ host->h_name)) {
+ logmsg(loglevel, "service principal: hostname %.*s "
+ "doesn't match localhost %s\n",
+ krb5_princ_component(ctx, princ, 1)->length,
+ krb5_princ_component(ctx, princ, 1)->data,
+ host->h_name);
+ return -1;
+ }
+
+ return 0;
+}
+
+static
+int lkrb5_cc_check_tgt_princ(krb5_context ctx,
+ krb5_ccache ccache,
+ krb5_principal princ)
+{
+ logmsg(LL_TRACE, "principal: realm %.*s, type %d, size %d, name %.*s\n",
+ krb5_princ_realm(ctx, princ)->length,
+ krb5_princ_realm(ctx, princ)->data,
+ krb5_princ_type(ctx, princ),
+ krb5_princ_size(ctx, princ),
+ krb5_princ_name(ctx, princ)->length,
+ krb5_princ_name(ctx, princ)->data);
+
+ /* check type */
+ if (krb5_princ_type(ctx, princ) != KRB5_NT_PRINCIPAL) {
+ logmsg(LL_WARN, "principal type %d is not I want\n",
+ krb5_princ_type(ctx, princ));
+ return -1;
+ }
+
+ /* check local realm */
+ if (!princ_is_local_realm(ctx, princ)) {
+ logmsg(LL_WARN, "principal realm %.*s not local: %s\n",
+ krb5_princ_realm(ctx, princ)->length,
+ krb5_princ_realm(ctx, princ)->data,
+ krb5_this_realm);
+ return -1;
+ }
+
+ /* if it's mds service principal, check hostname */
+ if (lgss_krb5_strcmp(krb5_princ_name(ctx, princ),
+ LGSS_SVC_MDS_STR) == 0) {
+ if (svc_princ_is_local_host(ctx, princ, LL_WARN)) {
+ logmsg(LL_WARN, "mds service principal not belongs "
+ "to this node\n");
+ return -1;
+ }
+ } else if (lgss_krb5_strcmp(krb5_princ_name(ctx, princ),
+ LGSS_USR_ROOT_STR)) {
+ /* do nothing */
+ } else {
+ logmsg(LL_WARN, "unexpected krb5 cc principal name %.*s\n",
+ krb5_princ_name(ctx, princ)->length,
+ krb5_princ_name(ctx, princ)->data);
+ return -1;
+ }
+
+ logmsg(LL_TRACE, "principal is OK\n");
+ return 0;
+}
+
+/*
+ * find out whether current TGT cache is valid or not
+ */
+static
+int lkrb5_check_root_tgt_cc(krb5_context ctx,
+ krb5_ccache ccache,
+ char *ccname)
+{
+ struct stat statbuf;
+ krb5_ccache tgt_ccache;
+ krb5_creds cred;
+ krb5_principal princ;
+ krb5_cc_cursor cursor;
+ krb5_error_code code;
+ char *ccfile;
+ time_t now;
+ int rc = -1, found = 0;
+
+ if (strncmp(ccname, krb5_cc_type, strlen(krb5_cc_type_file))) {
+ logmsg(LL_ERR, "unexpected cc type\n");
+ return -1;
+ }
+
+ ccfile = ccname + strlen(krb5_cc_type_file);
+ logmsg(LL_TRACE, "cc file name: %s\n", ccfile);
+
+ /* firstly make sure the cache file is there */
+ if (stat(ccfile, &statbuf)) {
+ logmsg(LL_DEBUG, "krb5 cc %s: %s\n", ccname, strerror(errno));
+ return -1;
+ }
+
+ /* prepare parsing the cache file */
+ code = krb5_cc_resolve(ctx, ccname, &tgt_ccache);
+ if (code) {
+ logmsg(LL_ERR, "resolve krb5 cc %s: %s\n",
+ ccname, krb5_err_msg(code));
+ return -1;
+ }
+
+ /* checks the principal */
+ code = krb5_cc_get_principal(ctx, tgt_ccache, &princ);
+ if (code) {
+ logmsg(LL_ERR, "get cc principal: %s\n", krb5_err_msg(code));
+ goto out_cc;
+ }
+
+ if (lkrb5_cc_check_tgt_princ(ctx, tgt_ccache, princ)) {
+ logmsg(LL_WARN, "cc principal is not valid\n");
+ goto out_princ;
+ }
+
+ /*
+ * find a valid entry
+ */
+ code = krb5_cc_start_seq_get(ctx, tgt_ccache, &cursor);
+ if (code) {
+ logmsg(LL_ERR, "start cc iteration: %s\n", krb5_err_msg(code));
+ goto out_princ;
+ }
+
+ now = time(0);
+ do {
+ krb5_timestamp duration, delta;
+
+ code = krb5_cc_next_cred(ctx, tgt_ccache, &cursor, &cred);
+ if (code != 0)
+ break;
+
+ logmsg(LL_DEBUG, "cred: server realm %.*s, type %d, name %.*s; "
+ "time (%d-%d, renew till %d), valid %d\n",
+ krb5_princ_realm(ctx, cred.server)->length,
+ krb5_princ_realm(ctx, cred.server)->data,
+ krb5_princ_type(ctx, cred.server),
+ krb5_princ_name(ctx, cred.server)->length,
+ krb5_princ_name(ctx, cred.server)->data,
+ cred.times.starttime, cred.times.endtime,
+ cred.times.renew_till, cred.times.endtime - now);
+
+ /* FIXME
+ * we found the princ type is always 0 (KRB5_NT_UNKNOWN), why???
+ */
+
+ /* FIXME how about inter-realm TGT??? FIXME */
+ if (lgss_krb5_strcasecmp(krb5_princ_name(ctx, cred.server),
+ "krbtgt"))
+ continue;
+
+ if (lgss_krb5_strcasecmp(krb5_princ_realm(ctx, cred.server),
+ krb5_this_realm))
+ continue;
+
+ /* check validity of time */
+ delta = 60 * 30; /* half an hour */
+ duration = cred.times.endtime - cred.times.starttime;
+ if (duration / 4 < delta)
+ delta = duration / 4;
+
+ if (cred.times.starttime <= now &&
+ cred.times.endtime >= now + delta) {
+ found = 1;
+ break;
+ }
+ } while (1);
+
+ if (!found) {
+ logmsg(LL_DEBUG, "doesn't find good TGT cache\n");
+ goto out_seq;
+ }
+
+ /* found a good cred, store it into @ccache */
+ logmsg(LL_DEBUG, "found good TGT cache\n");
+
+ code = krb5_cc_initialize(ctx, ccache, princ);
+ if (code) {
+ logmsg(LL_ERR, "init private cc: %s\n", krb5_err_msg(code));
+ goto out_seq;
+ }
+
+ code = krb5_cc_store_cred(ctx, ccache, &cred);
+ if (code) {
+ logmsg(LL_ERR, "store private cred: %s\n", krb5_err_msg(code));
+ goto out_seq;
+ }
+
+ logmsg(LL_DEBUG, "store private ccache OK\n");
+ rc = 0;
+
+out_seq:
+ krb5_cc_end_seq_get(ctx, tgt_ccache, &cursor);
+out_princ:
+ krb5_free_principal(ctx, princ);
+out_cc:
+ krb5_cc_close(ctx, tgt_ccache);
+
+ return rc;
+}
+
+static
+int lkrb5_get_root_tgt_keytab(krb5_context ctx,
+ krb5_ccache ccache,
+ krb5_keytab kt,
+ krb5_principal princ,
+ const char *ccname)
+{
+ krb5_get_init_creds_opt opts;
+ krb5_creds cred;
+ krb5_ccache tgt_ccache;
+ krb5_error_code code;
+ int rc = -1;
+
+ krb5_get_init_creds_opt_init(&opts);
+ krb5_get_init_creds_opt_set_address_list(&opts, NULL);
+ /*
+ * by default krb5 library obtain ticket with lifetime shorter
+ * than the max value. we can change it here if we want. but
+ * seems not necessary now.
+ *
+ krb5_get_init_creds_opt_set_tkt_life(&opts, very-long-time);
+ *
+ */
+
+ /*
+ * obtain TGT and store into global ccache
+ */
+ code = krb5_get_init_creds_keytab(ctx, &cred, princ, kt,
+ 0, NULL, &opts);
+ if (code) {
+ logmsg(LL_ERR, "failed to get root TGT for "
+ "principal %.*s: %s\n",
+ krb5_princ_name(ctx, princ)->length,
+ krb5_princ_name(ctx, princ)->data,
+ krb5_err_msg(code));
+ return -1;
+ }
+
+ code = krb5_cc_resolve(ctx, ccname, &tgt_ccache);
+ if (code) {
+ logmsg(LL_ERR, "resolve cc %s: %s\n",
+ ccname, krb5_err_msg(code));
+ goto out_cred;
+ }
+
+ code = krb5_cc_initialize(ctx, tgt_ccache, princ);
+ if (code) {
+ logmsg(LL_ERR, "initialize cc %s: %s\n",
+ ccname, krb5_err_msg(code));
+ goto out_cc;
+ }
+
+ code = krb5_cc_store_cred(ctx, tgt_ccache, &cred);
+ if (code) {
+ logmsg(LL_ERR, "store cred to cc %s: %s\n",
+ ccname, krb5_err_msg(code));
+ goto out_cc;
+ }
+
+ logmsg(LL_INFO, "installed TGT of %.*s in cc %s\n",
+ krb5_princ_name(ctx, princ)->length,
+ krb5_princ_name(ctx, princ)->data,
+ ccname);
+
+ /*
+ * now store the cred into my own cc too
+ */
+ code = krb5_cc_initialize(ctx, ccache, princ);
+ if (code) {
+ logmsg(LL_ERR, "init mem cc: %s\n", krb5_err_msg(code));
+ goto out_cc;
+ }
+
+ code = krb5_cc_store_cred(ctx, ccache, &cred);
+ if (code) {
+ logmsg(LL_ERR, "store mm cred: %s\n", krb5_err_msg(code));
+ goto out_cc;
+ }
+
+ logmsg(LL_DEBUG, "stored TGT into mem cc OK\n");
+ rc = 0;
+out_cc:
+ krb5_cc_close(ctx, tgt_ccache);
+out_cred:
+ krb5_free_cred_contents(ctx, &cred);
+ return rc;
+}
+
+/*
+ * obtain a new root TGT
+ */
+static
+int lkrb5_refresh_root_tgt_cc(krb5_context ctx,
+ krb5_ccache ccache,
+ const char *ccname)
+{
+ krb5_keytab kt;
+ krb5_keytab_entry kte;
+ krb5_kt_cursor cursor;
+ krb5_principal princ = NULL;
+ krb5_error_code code;
+ int rc = -1;
+
+ /* prepare parsing the keytab file */
+ code = krb5_kt_resolve(ctx, krb5_keytab_file, &kt);
+ if (code) {
+ logmsg(LL_ERR, "resolve keytab %s: %s\n",
+ krb5_keytab_file, krb5_err_msg(code));
+ return -1;
+ }
+
+ code = krb5_kt_start_seq_get(ctx, kt, &cursor);
+ if (code) {
+ logmsg(LL_ERR, "start kt iteration: %s\n", krb5_err_msg(code));
+ goto out_kt;
+ }
+
+ /* iterate keytab to find proper a entry */
+ do {
+ code = krb5_kt_next_entry(ctx, kt, &kte, &cursor);
+ if (code != 0)
+ break;
+
+ logmsg(LL_TRACE, "kt entry: realm %.*s, type %d, "
+ "size %d, name %.*s\n",
+ krb5_princ_realm(ctx, kte.principal)->length,
+ krb5_princ_realm(ctx, kte.principal)->data,
+ krb5_princ_type(ctx, kte.principal),
+ krb5_princ_size(ctx, kte.principal),
+ krb5_princ_name(ctx, kte.principal)->length,
+ krb5_princ_name(ctx, kte.principal)->data);
+
+ if (!princ_is_local_realm(ctx, kte.principal))
+ continue;
+
+ /* lustre_root@realm */
+ if (lgss_krb5_strcmp(krb5_princ_name(ctx, kte.principal),
+ LGSS_USR_ROOT_STR) == 0) {
+ if (princ != NULL) {
+ logmsg(LL_WARN, "already picked one? "
+ "how could it possible???\n");
+ continue;
+ }
+
+ code = krb5_copy_principal(ctx, kte.principal, &princ);
+ if (code)
+ logmsg(LL_ERR, "copy lustre_root princ: %s\n",
+ krb5_err_msg(code));
+
+ continue;
+ }
+
+ /* lustre_mds/host@realm */
+ if (lgss_krb5_strcmp(krb5_princ_name(ctx, kte.principal),
+ LGSS_SVC_MDS_STR) == 0) {
+ krb5_principal princ2;
+
+ if (svc_princ_is_local_host(ctx, kte.principal,
+ LL_TRACE)) {
+ logmsg(LL_TRACE, "mds service principal: "
+ "not belongs to this node\n");
+ continue;
+ }
+
+ /* select this one */
+ code = krb5_copy_principal(ctx, kte.principal, &princ2);
+ if (code) {
+ logmsg(LL_ERR, "copy lustre_mds princ: %s\n",
+ krb5_err_msg(code));
+ continue;
+ }
+
+ if (princ != NULL) {
+ logmsg(LL_TRACE, "release a lustre_root one\n");
+ krb5_free_principal(ctx, princ);
+ }
+ princ = princ2;
+ break;
+ }
+ } while (1);
+
+ krb5_kt_end_seq_get(ctx, kt, &cursor);
+
+ if (princ == NULL) {
+ logmsg(LL_ERR, "can't find proper keytab entry\n");
+ goto out_kt;
+ }
+
+ /* obtain root TGT */
+ rc = lkrb5_get_root_tgt_keytab(ctx, ccache, kt, princ, ccname);
+
+ krb5_free_principal(ctx, princ);
+out_kt:
+ krb5_kt_close(ctx, kt);
+ return rc;
+}
+
+static
+int lkrb5_prepare_root_cred(struct lgss_cred *cred)
+{
+ krb5_context ctx;
+ krb5_ccache ccache;
+ krb5_error_code code;
+ struct lgss_krb5_cred *kcred;
+ char tgtcc[1024];
+ int rc = -1;
+
+ lassert(krb5_this_realm != NULL);
+
+ kcred = (struct lgss_krb5_cred *) cred->lc_mech_cred;
+
+ /* compose the TGT cc name */
+ snprintf(tgtcc, sizeof(tgtcc), "%s%s/%s%s_%s",
+ krb5_cc_type, krb5_cc_dir, krb5_cred_prefix,
+ krb5_cred_root_suffix, krb5_this_realm);
+ logmsg(LL_DEBUG, "root krb5 TGT ccname: %s\n", tgtcc);
+
+ /* compose the memory cc name, since the only user is myself,
+ * the name could be fixed
+ */
+ snprintf(kcred->kc_ccname, sizeof(kcred->kc_ccname),
+ "%s/self", krb5_cc_type_mem);
+ logmsg(LL_TRACE, "private cc: %s\n", kcred->kc_ccname);
+
+ code = krb5_init_context(&ctx);
+ if (code) {
+ logmsg(LL_ERR, "initialize krb5 context: %s\n",
+ krb5_err_msg(code));
+ return -1;
+ }
+
+ code = krb5_cc_resolve(ctx, kcred->kc_ccname, &ccache);
+ if (code) {
+ logmsg(LL_ERR, "resolve krb5 cc %s: %s\n",
+ kcred->kc_ccname, krb5_err_msg(code));
+ goto out_ctx;
+ }
+
+ /*
+ * search and/or obtain root TGT credential.
+ * it touched global (on-disk) tgt cache, do it inside mutex locking
+ */
+ lgss_krb5_mutex_lock();
+
+ rc = lkrb5_check_root_tgt_cc(ctx, ccache, tgtcc);
+ if (rc != 0)
+ rc = lkrb5_refresh_root_tgt_cc(ctx, ccache, tgtcc);
+
+ if (rc == 0)
+ rc = lgss_krb5_set_ccache_name(kcred->kc_ccname);
+
+ lgss_krb5_mutex_unlock();
+
+ krb5_cc_close(ctx, ccache);
+out_ctx:
+ krb5_free_context(ctx);
+
+ logmsg(LL_DEBUG, "prepare root credentail %s\n", rc ? "failed" : "OK");
+ return rc;
+}
+
+static
+int lkrb5_prepare_user_cred(struct lgss_cred *cred)
+{
+ struct lgss_krb5_cred *kcred;
+ int rc;
+
+ lassert(krb5_this_realm == NULL);
+
+ kcred = (struct lgss_krb5_cred *) cred->lc_mech_cred;
+
+ /*
+ * here we just specified a fix ccname, instead of searching
+ * entire cc dir. is this OK??
+ */
+ snprintf(kcred->kc_ccname, sizeof(kcred->kc_ccname),
+ "%s%s/%s%u",
+ krb5_cc_type, krb5_cc_dir, krb5_cred_prefix, cred->lc_uid);
+ logmsg(LL_DEBUG, "using krb5 cache name: %s\n", kcred->kc_ccname);
+
+ rc = lgss_krb5_set_ccache_name(kcred->kc_ccname);
+ if (rc)
+ logmsg(LL_ERR, "can't set krb5 ccache name: %s\n",
+ kcred->kc_ccname);
+
+ return rc;
+}
+
+static
+int lgss_krb5_prepare_cred(struct lgss_cred *cred)
+{
+ struct lgss_krb5_cred *kcred;
+ int rc;
+
+ kcred = malloc(sizeof(*kcred));
+ if (kcred == NULL) {
+ logmsg(LL_ERR, "can't allocate krb5 cred\n");
+ return -1;
+ }
+
+ kcred->kc_ccname[0] = '\0';
+ kcred->kc_remove = 0;
+ cred->lc_mech_cred = kcred;
+
+ if (cred->lc_fl_root || cred->lc_fl_mds) {
+ if (lgss_krb5_get_local_realm())
+ return -1;
+
+ rc = lkrb5_prepare_root_cred(cred);
+ } else {
+ rc = lkrb5_prepare_user_cred(cred);
+ }
+
+ return rc;
+}
+
+static
+void lgss_krb5_release_cred(struct lgss_cred *cred)
+{
+ struct lgss_krb5_cred *kcred;
+
+ kcred = (struct lgss_krb5_cred *) cred->lc_mech_cred;
+
+ free(kcred);
+ cred->lc_mech_cred = NULL;
+}
+
+struct lgss_mech_type lgss_mech_krb5 =
+{
+ .lmt_name = "krb5",
+ .lmt_mech_n = LGSS_MECH_KRB5,
+ .lmt_prepare_cred = lgss_krb5_prepare_cred,
+ .lmt_release_cred = lgss_krb5_release_cred,
+};