X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=lustre%2Futils%2Fgss%2Flgss_keyring.c;h=c5e0d4adc7532ff11819bff357677b089b98c596;hb=cd4caef54f;hp=c77fea10496ad76a1bf4c0d869033632336de522;hpb=6869932b552ac705f411de3362f01bd50c1f6f7d;p=fs%2Flustre-release.git diff --git a/lustre/utils/gss/lgss_keyring.c b/lustre/utils/gss/lgss_keyring.c index c77fea1..c5e0d4a 100644 --- a/lustre/utils/gss/lgss_keyring.c +++ b/lustre/utils/gss/lgss_keyring.c @@ -1,6 +1,4 @@ -/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- - * vim:expandtab:shiftwidth=8:tabstop=8: - * +/* * GPL HEADER START * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -17,21 +15,18 @@ * * You should have received a copy of the GNU General Public License * version 2 along with this program; If not, see - * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. + * http://www.gnu.org/licenses/gpl-2.0.html * * GPL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved + * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Use is subject to license terms. + * + * Copyright (c) 2011, Intel Corporation. */ /* * This file is part of Lustre, http://www.lustre.org/ - * Lustre is a trademark of Sun Microsystems, Inc. * * lustre/utils/gss/lgss_keyring.c * @@ -40,16 +35,23 @@ * Author: Eric Mei */ +#include +#include +#include #include #include #include #include +#include #include #include #include #include #include +#include +#include +#include #include "lsupport.h" #include "lgss_utils.h" #include "write_bytes.h" @@ -64,262 +66,422 @@ static char *g_service = NULL; * all data about negotiation */ struct lgss_nego_data { - uint32_t lnd_established:1; - - int lnd_secid; - uint32_t lnd_uid; - uint32_t lnd_lsvc; - char *lnd_uuid; - - gss_OID lnd_mech; /* mech OID */ - gss_name_t lnd_svc_name; /* service name */ - u_int lnd_req_flags; /* request flags */ - gss_cred_id_t lnd_cred; /* credential */ - gss_ctx_id_t lnd_ctx; /* session context */ - gss_buffer_desc lnd_rmt_ctx; /* remote handle of context */ - uint32_t lnd_seq_win; /* sequence window */ - - int lnd_rpc_err; - int lnd_gss_err; + uint32_t lnd_established:1; + + int lnd_secid; + uint32_t lnd_uid; + uint32_t lnd_lsvc; + char *lnd_uuid; + + gss_OID lnd_mech; /* mech OID */ + gss_name_t lnd_svc_name; /* service name */ + unsigned int lnd_req_flags; /* request flags */ + gss_cred_id_t lnd_cred; /* credential */ + gss_ctx_id_t lnd_ctx; /* session context */ + gss_buffer_desc lnd_rmt_ctx; /* remote handle of context */ + gss_buffer_desc lnd_ctx_token; /* context token for kernel */ + uint32_t lnd_seq_win; /* sequence window */ + + int lnd_rpc_err; + int lnd_gss_err; }; /* * context creation response */ struct lgss_init_res { - gss_buffer_desc gr_ctx; /* context handle */ - u_int gr_major; /* major status */ - u_int gr_minor; /* minor status */ - u_int gr_win; /* sequence window */ - gss_buffer_desc gr_token; /* token */ + gss_buffer_desc gr_ctx; /* context handle */ + unsigned int gr_major; /* major status */ + unsigned int gr_minor; /* minor status */ + unsigned int gr_win; /* sequence window */ + gss_buffer_desc gr_token; /* token */ }; struct keyring_upcall_param { - uint32_t kup_ver; - uint32_t kup_secid; - uint32_t kup_uid; - uint32_t kup_fsuid; - uint32_t kup_gid; - uint32_t kup_fsgid; - uint32_t kup_svc; - uint64_t kup_nid; - char kup_tgt[64]; - char kup_mech[16]; - int kup_is_root; - int kup_is_mds; + uint32_t kup_ver; + uint32_t kup_secid; + uint32_t kup_uid; + uint32_t kup_fsuid; + uint32_t kup_gid; + uint32_t kup_fsgid; + uint32_t kup_svc; + uint64_t kup_nid; + uint64_t kup_selfnid; + char kup_svc_type; + char kup_tgt[64]; + char kup_mech[16]; + unsigned int kup_is_root:1, + kup_is_mdt:1, + kup_is_ost:1; + uint32_t kup_pid; }; /**************************************** * child process: gss negotiation * ****************************************/ -#define INIT_CHANNEL "/proc/fs/lustre/sptlrpc/gss/init_channel" - -int do_nego_rpc(struct lgss_nego_data *lnd, - gss_buffer_desc *gss_token, - struct lgss_init_res *gr) +static int send_to(int fd, const void *buf, size_t size) { - struct lgssd_ioctl_param param; - struct passwd *pw; - int fd, ret, res; - char outbuf[8192]; - unsigned int *p; - - logmsg(LL_TRACE, "start negotiation rpc\n"); - - pw = getpwuid(lnd->lnd_uid); - if (!pw) { - logmsg(LL_ERR, "no uid %u in local user database\n", - lnd->lnd_uid); - return -EACCES; - } - - param.version = GSSD_INTERFACE_VERSION; - param.secid = lnd->lnd_secid; - param.uuid = lnd->lnd_uuid; - param.lustre_svc = lnd->lnd_lsvc; - param.uid = lnd->lnd_uid; - param.gid = pw->pw_gid; - param.send_token_size = gss_token->length; - param.send_token = (char *) gss_token->value; - param.reply_buf_size = sizeof(outbuf); - param.reply_buf = outbuf; - - logmsg(LL_TRACE, "to open " INIT_CHANNEL "\n"); - - fd = open(INIT_CHANNEL, O_WRONLY); - if (fd < 0) { - logmsg(LL_ERR, "can't open " INIT_CHANNEL "\n"); - return -EACCES; - } - - logmsg(LL_TRACE, "to down-write\n"); - - ret = write(fd, ¶m, sizeof(param)); - if (ret != sizeof(param)) { - logmsg(LL_ERR, "lustre ioctl err: %d\n", strerror(errno)); - close(fd); - return -EACCES; - } - close(fd); - - logmsg(LL_TRACE, "do_nego_rpc: to parse reply\n"); - if (param.status) { - logmsg(LL_ERR, "status: %d (%s)\n", - param.status, strerror((int)param.status)); - - /* kernel return -ETIMEDOUT means the rpc timedout, we should - * notify the caller to reinitiate the gss negotiation, by - * returning -ERESTART - */ - if (param.status == -ETIMEDOUT) - return -ERESTART; - else - return param.status; - } + ssize_t sz; + + sz = write(fd, buf, size); + if (sz == -1) { + logmsg(LL_ERR, "cannot send to GSS process: %s\n", + strerror(errno)); + return -errno; + } + if (sz < size) { + logmsg(LL_ERR, "short write sending to GSS process: %d/%d\n", + (int)sz, (int)size); + return -EPROTO; + } + + return 0; +} - p = (unsigned int *)outbuf; - res = *p++; - gr->gr_major = *p++; - gr->gr_minor = *p++; - gr->gr_win = *p++; +static int receive_from(int fd, void *buf, size_t size) +{ + ssize_t sz; + + sz = read(fd, buf, size); + if (sz == -1) { + logmsg(LL_ERR, "cannot receive from GSS process: %s\n", + strerror(errno)); + return -errno; + } + if (sz < size) { + logmsg(LL_ERR, "short read receiving from GSS process: %d/%d\n", + (int)sz, (int)size); + return -EPROTO; + } + + return 0; +} - gr->gr_ctx.length = *p++; - gr->gr_ctx.value = malloc(gr->gr_ctx.length); - memcpy(gr->gr_ctx.value, p, gr->gr_ctx.length); - p += (((gr->gr_ctx.length + 3) & ~3) / 4); +static int gss_do_ioctl(struct lgssd_ioctl_param *param) +{ + int fd, ret; + glob_t path; + int rc; + + rc = cfs_get_param_paths(&path, "sptlrpc/gss/init_channel"); + if (rc != 0) + return rc; + + logmsg(LL_TRACE, "to open %s\n", path.gl_pathv[0]); + + fd = open(path.gl_pathv[0], O_WRONLY); + if (fd < 0) { + logmsg(LL_ERR, "can't open %s\n", path.gl_pathv[0]); + rc = -EACCES; + goto out_params; + } + + logmsg(LL_TRACE, "to down-write\n"); + + ret = write(fd, param, sizeof(*param)); + close(fd); + if (ret != sizeof(*param)) { + logmsg(LL_ERR, "lustre ioctl err: %s\n", strerror(errno)); + rc = -EACCES; + } + +out_params: + cfs_free_param_data(&path); + return rc; +} - gr->gr_token.length = *p++; - gr->gr_token.value = malloc(gr->gr_token.length); - memcpy(gr->gr_token.value, p, gr->gr_token.length); - p += (((gr->gr_token.length + 3) & ~3) / 4); +int do_nego_rpc(struct lgss_nego_data *lnd, + gss_buffer_desc *gss_token, + struct lgss_init_res *gr, + int req_fd[2], int reply_fd[2]) +{ + struct lgssd_ioctl_param param; + struct passwd *pw; + int res; + char outbuf[8192] = { 0 }; + unsigned int *p; + int rc = 0; + + logmsg(LL_TRACE, "start negotiation rpc\n"); + + pw = getpwuid(lnd->lnd_uid); + if (!pw) { + logmsg(LL_ERR, "no uid %u in local user database\n", + lnd->lnd_uid); + return -EACCES; + } + + param.version = GSSD_INTERFACE_VERSION; + param.secid = lnd->lnd_secid; + param.uuid = lnd->lnd_uuid; + param.lustre_svc = lnd->lnd_lsvc; + param.uid = lnd->lnd_uid; + param.gid = pw->pw_gid; + param.send_token_size = gss_token->length; + param.send_token = (char *) gss_token->value; + + if (req_fd[0] == -1 && reply_fd[0] == -1) { + /* we can do the ioctl directly */ + param.reply_buf_size = sizeof(outbuf); + param.reply_buf = outbuf; + + rc = gss_do_ioctl(¶m); + if (rc != 0) + return rc; + } else { + /* looks like we are running in a container, + * so we cannot do the ioctl ourselves: delegate to + * parent process running directly on host */ + + /* send ioctl buffer to parent */ + rc = send_to(req_fd[1], ¶m, sizeof(param)); + if (rc != 0) + return rc; + /* send gss token to parent */ + rc = send_to(req_fd[1], gss_token->value, gss_token->length); + if (rc != 0) + return rc; + + /* read ioctl status from parent */ + rc = receive_from(reply_fd[0], ¶m.status, + sizeof(param.status)); + if (rc != 0) + return rc; + + if (param.status == 0) { + /* read reply buffer from parent */ + rc = receive_from(reply_fd[0], outbuf, sizeof(outbuf)); + if (rc != 0) + return rc; + } + } + + logmsg(LL_TRACE, "do_nego_rpc: to parse reply\n"); + if (param.status) { + logmsg(LL_ERR, "status: %ld (%s)\n", + param.status, strerror((int)(-param.status))); + return param.status; + } + + p = (unsigned int *)outbuf; + res = *p++; + gr->gr_major = *p++; + gr->gr_minor = *p++; + gr->gr_win = *p++; + + gr->gr_ctx.length = *p++; + gr->gr_ctx.value = malloc(gr->gr_ctx.length); + if (gr->gr_ctx.value == NULL) + return -ENOMEM; + memcpy(gr->gr_ctx.value, p, gr->gr_ctx.length); + p += (((gr->gr_ctx.length + 3) & ~3) / 4); + + gr->gr_token.length = *p++; + gr->gr_token.value = malloc(gr->gr_token.length); + if (gr->gr_token.value == NULL) { + free(gr->gr_ctx.value); + return -ENOMEM; + } + memcpy(gr->gr_token.value, p, gr->gr_token.length); + p += (((gr->gr_token.length + 3) & ~3) / 4); + + logmsg(LL_DEBUG, "do_nego_rpc: receive handle len %zu, token len %zu, " + "res %d\n", gr->gr_ctx.length, gr->gr_token.length, res); + + return rc; +} - logmsg(LL_DEBUG, "do_nego_rpc: receive handle len %d, token len %d\n", - gr->gr_ctx.length, gr->gr_token.length); - return 0; +/* This is used by incomplete GSSAPI implementations that can't use + * gss_init_sec_context and will parse the token themselves (gssnull and sk). + * Callers should have cred->lc_mech_token pointing to a gss_buffer_desc + * token to send to the peer as part of the SEC_CTX_INIT operation. The return + * RPC's token with be in gr.gr_token which is validated using + * lgss_validate_cred. */ +static int lgssc_negotiation_manual(struct lgss_nego_data *lnd, + struct lgss_cred *cred, + int req_fd[2], int reply_fd[2]) +{ + struct lgss_init_res gr; + OM_uint32 min_stat; + int rc; + + logmsg(LL_TRACE, "starting gss negotation\n"); + memset(&gr, 0, sizeof(gr)); + + lnd->lnd_rpc_err = do_nego_rpc(lnd, &cred->lc_mech_token, &gr, + req_fd, reply_fd); + if (lnd->lnd_rpc_err) { + logmsg(LL_ERR, "negotiation rpc error %d\n", lnd->lnd_rpc_err); + rc = lnd->lnd_rpc_err; + goto out_error; + } + + if (gr.gr_major == GSS_S_CONTINUE_NEEDED) { + rc = -EAGAIN; + goto out_error; + + } else if (gr.gr_major != GSS_S_COMPLETE) { + lnd->lnd_gss_err = gr.gr_major; + logmsg(LL_ERR, "negotiation gss error %x\n", lnd->lnd_gss_err); + rc = -ENOTCONN; + goto out_error; + } + + if (gr.gr_ctx.length == 0 || gr.gr_token.length == 0) { + logmsg(LL_ERR, "zero length context or token received\n"); + rc = -EINVAL; + goto out_error; + } + + rc = lgss_validate_cred(cred, &gr.gr_token, &lnd->lnd_ctx_token); + if (rc) { + logmsg(LL_ERR, "peer token failed validation\n"); + goto out_error; + } + + lnd->lnd_established = 1; + lnd->lnd_seq_win = gr.gr_win; + lnd->lnd_rmt_ctx = gr.gr_ctx; + + if (gr.gr_token.length != 0) + gss_release_buffer(&min_stat, &gr.gr_token); + + logmsg(LL_DEBUG, "successfully negotiated a context\n"); + return 0; + +out_error: + if (gr.gr_ctx.length != 0) + gss_release_buffer(&min_stat, &gr.gr_ctx); + if (gr.gr_token.length != 0) + gss_release_buffer(&min_stat, &gr.gr_token); + + return rc; } /* * if return error, the lnd_rpc_err or lnd_gss_err is set. */ -static int lgssc_negotiation(struct lgss_nego_data *lnd) +static int lgssc_negotiation(struct lgss_nego_data *lnd, int req_fd[2], + int reply_fd[2]) { - struct lgss_init_res gr; - gss_buffer_desc *recv_tokenp, send_token; - OM_uint32 maj_stat, min_stat, ret_flags; - - logmsg(LL_TRACE, "start gss negotiation\n"); - - /* GSS context establishment loop. */ - memset(&gr, 0, sizeof(gr)); - recv_tokenp = GSS_C_NO_BUFFER; - - for (;;) { - maj_stat = gss_init_sec_context(&min_stat, - lnd->lnd_cred, - &lnd->lnd_ctx, - lnd->lnd_svc_name, - lnd->lnd_mech, - lnd->lnd_req_flags, - 0, /* time req */ - NULL, /* channel */ - recv_tokenp, - NULL, /* used mech */ - &send_token, - &ret_flags, - NULL); /* time rec */ - - if (recv_tokenp != GSS_C_NO_BUFFER) { - gss_release_buffer(&min_stat, &gr.gr_token); - recv_tokenp = GSS_C_NO_BUFFER; - } - - if (maj_stat != GSS_S_COMPLETE && - maj_stat != GSS_S_CONTINUE_NEEDED) { - lnd->lnd_gss_err = maj_stat; - - logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat, - "failed init context"); - break; - } - - if (send_token.length != 0) { - memset(&gr, 0, sizeof(gr)); - - lnd->lnd_rpc_err = do_nego_rpc(lnd, &send_token, &gr); - gss_release_buffer(&min_stat, &send_token); - - if (lnd->lnd_rpc_err) { - logmsg(LL_ERR, "negotiation rpc error: %d\n", - lnd->lnd_rpc_err); - return -1; - } - - if (gr.gr_major != GSS_S_COMPLETE && - gr.gr_major != GSS_S_CONTINUE_NEEDED) { - lnd->lnd_gss_err = gr.gr_major; - - logmsg(LL_ERR, "negotiation gss error %x\n", - lnd->lnd_gss_err); - return -1; - } - - if (gr.gr_ctx.length != 0) { - if (lnd->lnd_rmt_ctx.value) - gss_release_buffer(&min_stat, - &lnd->lnd_rmt_ctx); - lnd->lnd_rmt_ctx = gr.gr_ctx; - } - - if (gr.gr_token.length != 0) { - if (maj_stat != GSS_S_CONTINUE_NEEDED) - break; - recv_tokenp = &gr.gr_token; - } - } - - /* GSS_S_COMPLETE => check gss header verifier, - * usually checked in gss_validate - */ - if (maj_stat == GSS_S_COMPLETE) { - lnd->lnd_established = 1; - lnd->lnd_seq_win = gr.gr_win; - break; - } - } - - /* End context negotiation loop. */ - if (!lnd->lnd_established) { - if (gr.gr_token.length != 0) - gss_release_buffer(&min_stat, &gr.gr_token); - - if (lnd->lnd_gss_err == GSS_S_COMPLETE) - lnd->lnd_rpc_err = -EACCES; - - logmsg(LL_ERR, "context negotiation failed\n"); - return -1; - } - - logmsg(LL_DEBUG, "successfully negotiated a context\n"); - return 0; + struct lgss_init_res gr; + gss_buffer_desc *recv_tokenp, send_token; + OM_uint32 maj_stat, min_stat, ret_flags; + + logmsg(LL_TRACE, "start gss negotiation\n"); + + /* GSS context establishment loop. */ + memset(&gr, 0, sizeof(gr)); + recv_tokenp = GSS_C_NO_BUFFER; + + for (;;) { + maj_stat = gss_init_sec_context(&min_stat, + lnd->lnd_cred, + &lnd->lnd_ctx, + lnd->lnd_svc_name, + lnd->lnd_mech, + lnd->lnd_req_flags, + 0, /* time req */ + NULL, /* channel */ + recv_tokenp, + NULL, /* used mech */ + &send_token, + &ret_flags, + NULL); /* time rec */ + + if (recv_tokenp != GSS_C_NO_BUFFER) { + gss_release_buffer(&min_stat, &gr.gr_token); + recv_tokenp = GSS_C_NO_BUFFER; + } + + if (maj_stat != GSS_S_COMPLETE && + maj_stat != GSS_S_CONTINUE_NEEDED) { + lnd->lnd_gss_err = maj_stat; + + logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat, + "failed init context"); + break; + } + + if (send_token.length != 0) { + memset(&gr, 0, sizeof(gr)); + + lnd->lnd_rpc_err = do_nego_rpc(lnd, &send_token, &gr, + req_fd, reply_fd); + gss_release_buffer(&min_stat, &send_token); + + if (lnd->lnd_rpc_err) { + logmsg(LL_ERR, "negotiation rpc error: %d\n", + lnd->lnd_rpc_err); + return lnd->lnd_rpc_err; + } + + if (gr.gr_major != GSS_S_COMPLETE && + gr.gr_major != GSS_S_CONTINUE_NEEDED) { + lnd->lnd_gss_err = gr.gr_major; + + logmsg(LL_ERR, "negotiation gss error %x\n", + lnd->lnd_gss_err); + return -ENOTCONN; + } + + if (gr.gr_ctx.length != 0) { + if (lnd->lnd_rmt_ctx.value) + gss_release_buffer(&min_stat, + &lnd->lnd_rmt_ctx); + lnd->lnd_rmt_ctx = gr.gr_ctx; + } + + if (gr.gr_token.length != 0) { + if (maj_stat != GSS_S_CONTINUE_NEEDED) + break; + recv_tokenp = &gr.gr_token; + } + } + + /* GSS_S_COMPLETE => check gss header verifier, + * usually checked in gss_validate + */ + if (maj_stat == GSS_S_COMPLETE) { + lnd->lnd_established = 1; + lnd->lnd_seq_win = gr.gr_win; + break; + } + } + + /* End context negotiation loop. */ + if (!lnd->lnd_established) { + if (gr.gr_token.length != 0) + gss_release_buffer(&min_stat, &gr.gr_token); + + if (lnd->lnd_gss_err == GSS_S_COMPLETE) + lnd->lnd_rpc_err = -EACCES; + + logmsg(LL_ERR, "context negotiation failed\n"); + return -1; + } + + logmsg(LL_DEBUG, "successfully negotiated a context\n"); + return 0; } /* * if return error, the lnd_rpc_err or lnd_gss_err is set. */ static int lgssc_init_nego_data(struct lgss_nego_data *lnd, - struct keyring_upcall_param *kup, - lgss_mech_t mech) + struct keyring_upcall_param *kup, + enum lgss_mech mech) { gss_buffer_desc sname; OM_uint32 maj_stat, min_stat; memset(lnd, 0, sizeof(*lnd)); - lnd->lnd_secid = kup->kup_secid; - lnd->lnd_uid = kup->kup_uid; - lnd->lnd_lsvc = kup->kup_svc; - lnd->lnd_uuid = kup->kup_tgt; + lnd->lnd_secid = kup->kup_secid; + lnd->lnd_uid = kup->kup_uid; + lnd->lnd_lsvc = kup->kup_svc | mech << LUSTRE_GSS_MECH_SHIFT; + lnd->lnd_uuid = kup->kup_tgt; lnd->lnd_established = 0; lnd->lnd_svc_name = GSS_C_NO_NAME; @@ -329,14 +491,23 @@ static int lgssc_init_nego_data(struct lgss_nego_data *lnd, lnd->lnd_seq_win = 0; switch (mech) { - case LGSS_MECH_KRB5: - lnd->lnd_mech = (gss_OID) &krb5oid; - lnd->lnd_req_flags = GSS_C_MUTUAL_FLAG; - break; - default: - logmsg(LL_ERR, "invalid mech: %d\n", mech); - lnd->lnd_rpc_err = -EACCES; - return -1; + case LGSS_MECH_KRB5: + lnd->lnd_mech = (gss_OID)&krb5oid; + lnd->lnd_req_flags = GSS_C_MUTUAL_FLAG; + break; + case LGSS_MECH_NULL: + lnd->lnd_mech = (gss_OID)&nulloid; + break; +#ifdef HAVE_OPENSSL_SSK + case LGSS_MECH_SK: + lnd->lnd_mech = (gss_OID)&skoid; + lnd->lnd_req_flags = GSS_C_MUTUAL_FLAG; + break; +#endif + default: + logmsg(LL_ERR, "invalid mech: %d\n", mech); + lnd->lnd_rpc_err = -EACCES; + return -1; } sname.value = g_service; @@ -461,72 +632,168 @@ out: return rc; } -/* - * note we inherited assumed authority from parent process - */ -static int lgssc_kr_negotiate(key_serial_t keyid, struct lgss_cred *cred, - struct keyring_upcall_param *kup) +static int lgssc_kr_negotiate_krb(key_serial_t keyid, struct lgss_cred *cred, + struct keyring_upcall_param *kup, + int req_fd[2], int reply_fd[2]) { - struct lgss_nego_data lnd; - gss_buffer_desc token = GSS_C_EMPTY_BUFFER; - OM_uint32 min_stat; - int rc = -1; - - logmsg(LL_TRACE, "child start on behalf of key %08x: " - "cred %p, uid %u, svc %u, nid %Lx, uids: %u:%u/%u:%u\n", - keyid, cred, cred->lc_uid, cred->lc_tgt_svc, cred->lc_tgt_nid, - kup->kup_uid, kup->kup_gid, kup->kup_fsuid, kup->kup_fsgid); - - if (lgss_get_service_str(&g_service, kup->kup_svc, kup->kup_nid)) { - logmsg(LL_ERR, "key %08x: failed to construct service " - "string\n", keyid); - error_kernel_key(keyid, -EACCES, 0); - goto out_cred; - } - - if (lgss_using_cred(cred)) { - logmsg(LL_ERR, "key %08x: can't using cred\n", keyid); - error_kernel_key(keyid, -EACCES, 0); - goto out_cred; - } - - if (lgssc_init_nego_data(&lnd, kup, cred->lc_mech->lmt_mech_n)) { - logmsg(LL_ERR, "key %08x: failed to initialize " - "negotiation data\n", keyid); - error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err); - goto out_cred; - } - - rc = lgssc_negotiation(&lnd); - if (rc) { - logmsg(LL_ERR, "key %08x: failed to negotiation\n", keyid); - error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err); - goto out; - } + struct lgss_nego_data lnd; + OM_uint32 min_stat; + int rc = -1; + bool redo = true; + + if (lgss_get_service_str(&g_service, kup->kup_svc, kup->kup_nid)) { + logmsg(LL_ERR, "key %08x: failed to construct service " + "string\n", keyid); + error_kernel_key(keyid, -EACCES, 0); + goto out_cred; + } + + if (lgss_using_cred(cred)) { + logmsg(LL_ERR, "key %08x: can't using cred\n", keyid); + error_kernel_key(keyid, -EACCES, 0); + goto out_cred; + } + +retry_nego: + memset(&lnd, 0, sizeof(lnd)); + if (lgssc_init_nego_data(&lnd, kup, cred->lc_mech->lmt_mech_n)) { + logmsg(LL_ERR, "key %08x: failed to initialize " + "negotiation data\n", keyid); + error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err); + goto out_cred; + } + + rc = lgssc_negotiation(&lnd, req_fd, reply_fd); + if (rc == -EAGAIN || (rc == -ETIMEDOUT && redo)) { + logmsg(LL_ERR, "Failed negotiation must retry\n"); + redo = false; + goto retry_nego; + } else if (rc) { + logmsg(LL_ERR, "key %08x: failed to negotiation\n", keyid); + error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err); + goto out; + } + + rc = serialize_context_for_kernel(lnd.lnd_ctx, &lnd.lnd_ctx_token, + lnd.lnd_mech); + if (rc) { + logmsg(LL_ERR, "key %08x: failed to export context\n", keyid); + error_kernel_key(keyid, rc, lnd.lnd_gss_err); + goto out; + } + + rc = update_kernel_key(keyid, &lnd, &lnd.lnd_ctx_token); + if (rc) + goto out; + + rc = 0; + logmsg(LL_INFO, "key %08x for user %u is updated OK!\n", + keyid, kup->kup_uid); +out: + if (lnd.lnd_ctx_token.length != 0) + gss_release_buffer(&min_stat, &lnd.lnd_ctx_token); - rc = serialize_context_for_kernel(lnd.lnd_ctx, &token, lnd.lnd_mech); - if (rc) { - logmsg(LL_ERR, "key %08x: failed to export context\n", keyid); - error_kernel_key(keyid, rc, lnd.lnd_gss_err); - goto out; - } + lgssc_fini_nego_data(&lnd); - rc = update_kernel_key(keyid, &lnd, &token); - if (rc) - goto out; +out_cred: + lgss_release_cred(cred); + return rc; +} - rc = 0; - logmsg(LL_INFO, "key %08x for user %u is updated OK!\n", - keyid, kup->kup_uid); +static int lgssc_kr_negotiate_manual(key_serial_t keyid, struct lgss_cred *cred, + struct keyring_upcall_param *kup, + int req_fd[2], int reply_fd[2]) +{ + struct lgss_nego_data lnd; + OM_uint32 min_stat; + int rc; + bool redo = true; + + rc = lgss_get_service_str(&g_service, kup->kup_svc, kup->kup_nid); + if (rc) { + logmsg(LL_ERR, "key %08x: failed to construct service " + "string\n", keyid); + error_kernel_key(keyid, -EACCES, 0); + goto out_cred; + } + + rc = lgss_using_cred(cred); + if (rc) { + logmsg(LL_ERR, "key %08x: can't use cred\n", keyid); + error_kernel_key(keyid, -EACCES, 0); + goto out_cred; + } + +retry: + memset(&lnd, 0, sizeof(lnd)); + rc = lgssc_init_nego_data(&lnd, kup, cred->lc_mech->lmt_mech_n); + if (rc) { + logmsg(LL_ERR, "key %08x: failed to initialize " + "negotiation data\n", keyid); + error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err); + goto out_cred; + } + + /* + * Handles the negotiation but then calls lgss_validate to make sure + * the token is valid. It also populates the lnd_ctx_token for the + * update to the kernel key + */ + rc = lgssc_negotiation_manual(&lnd, cred, req_fd, reply_fd); + if (rc == -EAGAIN || (rc == -ETIMEDOUT && redo)) { + logmsg(LL_ERR, "Failed negotiation must retry\n"); + redo = false; + goto retry; + } else if (rc) { + logmsg(LL_ERR, "key %08x: failed to negotiate\n", keyid); + error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err); + goto out; + } + + rc = update_kernel_key(keyid, &lnd, &lnd.lnd_ctx_token); + if (rc) + goto out; + + logmsg(LL_INFO, "key %08x for user %u is updated OK!\n", + keyid, kup->kup_uid); out: - if (token.length != 0) - gss_release_buffer(&min_stat, &token); + if (lnd.lnd_ctx_token.length != 0) + gss_release_buffer(&min_stat, &lnd.lnd_ctx_token); - lgssc_fini_nego_data(&lnd); + lgssc_fini_nego_data(&lnd); out_cred: - lgss_release_cred(cred); - return rc; + lgss_release_cred(cred); + return rc; +} + +/* + * note we inherited assumed authority from parent process + */ +static int lgssc_kr_negotiate(key_serial_t keyid, struct lgss_cred *cred, + struct keyring_upcall_param *kup, + int req_fd[2], int reply_fd[2]) +{ + int rc; + + logmsg(LL_TRACE, "child start on behalf of key %08x: " + "cred %p, uid %u, svc %u, nid %"PRIx64", uids: %u:%u/%u:%u\n", + keyid, cred, cred->lc_uid, cred->lc_tgt_svc, cred->lc_tgt_nid, + kup->kup_uid, kup->kup_gid, kup->kup_fsuid, kup->kup_fsgid); + + switch (cred->lc_mech->lmt_mech_n) { + case LGSS_MECH_NULL: + case LGSS_MECH_SK: + rc = lgssc_kr_negotiate_manual(keyid, cred, kup, + req_fd, reply_fd); + break; + case LGSS_MECH_KRB5: + default: + rc = lgssc_kr_negotiate_krb(keyid, cred, kup, req_fd, reply_fd); + break; + } + + return rc; } /* @@ -535,20 +802,23 @@ out_cred: * [1]: mech_name (string) * [2]: uid (uint) * [3]: gid (uint) - * [4]: flags (chars) FMT: r-root; m-mds - * [5]: lustre_svc (uint) - * [6]: target_nid (uint64) - * [7]: target_uuid (string) + * [4]: flags (string) FMT: r-root; m-mdt; o-ost + * [5]: svc type (char) + * [6]: lustre_svc (int) + * [7]: target_nid (uint64) + * [8]: target_uuid (string) + * [9]: self_nid (uint64) + * [10]: pid (uint) */ static int parse_callout_info(const char *coinfo, struct keyring_upcall_param *uparam) { - const int nargs = 8; - char buf[1024]; - char *string = buf; - int length, i; - char *data[nargs]; - char *pos; + const int nargs = 11; + char buf[1024]; + char *string = buf; + int length, i; + char *data[nargs]; + char *pos; length = strlen(coinfo) + 1; if (length > 1024) { @@ -570,167 +840,417 @@ static int parse_callout_info(const char *coinfo, } data[i] = string; - logmsg(LL_TRACE, "components: %s,%s,%s,%s,%s,%s,%s,%s\n", - data[0], data[1], data[2], data[3], data[4], data[5], - data[6], data[7]); - - uparam->kup_secid = strtol(data[0], NULL, 0); - strncpy(uparam->kup_mech, data[1], sizeof(uparam->kup_mech)); - uparam->kup_uid = strtol(data[2], NULL, 0); - uparam->kup_gid = strtol(data[3], NULL, 0); - if (strchr(data[4], 'r')) - uparam->kup_is_root = 1; - if (strchr(data[4], 'm')) - uparam->kup_is_mds = 1; - uparam->kup_svc = strtol(data[5], NULL, 0); - uparam->kup_nid = strtoll(data[6], NULL, 0); - strncpy(uparam->kup_tgt, data[7], sizeof(uparam->kup_tgt)); - - logmsg(LL_DEBUG, "parse call out info: secid %d, mech %s, ugid %u:%u " - "is_root %d, is_mds %d, svc %d, nid 0x%Lx, tgt %s\n", - uparam->kup_secid, uparam->kup_mech, - uparam->kup_uid, uparam->kup_gid, - uparam->kup_is_root, uparam->kup_is_mds, uparam->kup_svc, - uparam->kup_nid, uparam->kup_tgt); - return 0; + logmsg(LL_TRACE, "components: %s,%s,%s,%s,%s,%c,%s,%s,%s,%s,%s\n", + data[0], data[1], data[2], data[3], data[4], data[5][0], + data[6], data[7], data[8], data[9], data[10]); + + uparam->kup_secid = strtol(data[0], NULL, 0); + snprintf(uparam->kup_mech, sizeof(uparam->kup_mech), "%s", data[1]); + uparam->kup_uid = strtol(data[2], NULL, 0); + uparam->kup_gid = strtol(data[3], NULL, 0); + if (strchr(data[4], 'r')) + uparam->kup_is_root = 1; + if (strchr(data[4], 'm')) + uparam->kup_is_mdt = 1; + if (strchr(data[4], 'o')) + uparam->kup_is_ost = 1; + uparam->kup_svc_type = data[5][0]; + uparam->kup_svc = strtol(data[6], NULL, 0); + uparam->kup_nid = strtoll(data[7], NULL, 0); + snprintf(uparam->kup_tgt, sizeof(uparam->kup_tgt), "%s", data[8]); + uparam->kup_selfnid = strtoll(data[9], NULL, 0); + uparam->kup_pid = strtol(data[10], NULL, 0); + + logmsg(LL_DEBUG, "parse call out info: secid %d, mech %s, ugid %u:%u, " + "is_root %d, is_mdt %d, is_ost %d, svc type %c, svc %d, " + "nid 0x%"PRIx64", tgt %s, self nid 0x%"PRIx64", pid %d\n", + uparam->kup_secid, uparam->kup_mech, + uparam->kup_uid, uparam->kup_gid, + uparam->kup_is_root, uparam->kup_is_mdt, uparam->kup_is_ost, + uparam->kup_svc_type, uparam->kup_svc, uparam->kup_nid, + uparam->kup_tgt, uparam->kup_selfnid, uparam->kup_pid); + return 0; } -/**************************************** - * main process * - ****************************************/ - -int main(int argc, char *argv[]) +static void set_log_level() { - struct keyring_upcall_param uparam; - key_serial_t keyid; - key_serial_t sring; - key_serial_t inst_keyring; - pid_t child; - struct lgss_mech_type *mech; - struct lgss_cred *cred; - - /* - * parse & sanity check upcall parameters - * expected to be called with: - * [1]: operation - * [2]: key ID - * [3]: key type - * [4]: key description - * [5]: call out info - * [6]: UID - * [7]: GID - * [8]: thread keyring - * [9]: process keyring - * [10]: session keyring - */ - if (argc != 10 + 1) { - logmsg(LL_ERR, "invalid parameter number %d\n", argc); - return 1; - } - - logmsg(LL_INFO, "key %s, desc %s, ugid %s:%s, sring %s, coinfo %s\n", - argv[2], argv[4], argv[6], argv[7], argv[10], argv[5]); - - memset(&uparam, 0, sizeof(uparam)); - - if (strcmp(argv[1], "create") != 0) { - logmsg(LL_ERR, "invalid OP %s\n", argv[1]); - return 1; - } - - if (sscanf(argv[2], "%d", &keyid) != 1) { - logmsg(LL_ERR, "can't extract KeyID: %s\n", argv[2]); - return 1; - } - - if (sscanf(argv[6], "%d", &uparam.kup_fsuid) != 1) { - logmsg(LL_ERR, "can't extract UID: %s\n", argv[6]); - return 1; - } - - if (sscanf(argv[7], "%d", &uparam.kup_fsgid) != 1) { - logmsg(LL_ERR, "can't extract GID: %s\n", argv[7]); - return 1; - } - - if (sscanf(argv[10], "%d", &sring) != 1) { - logmsg(LL_ERR, "can't extract session keyring: %s\n", argv[10]); - return 1; - } - - if (parse_callout_info(argv[5], &uparam)) { - logmsg(LL_ERR, "can't extract callout info: %s\n", argv[5]); - return 1; - } - - logmsg(LL_TRACE, "parsing parameters OK\n"); - - /* - * prepare a cred - */ - mech = lgss_name2mech(uparam.kup_mech); - if (mech == NULL) { - logmsg(LL_ERR, "key %08x: unsupported mech: %s\n", - keyid, uparam.kup_mech); - return 1; - } - - if (lgss_mech_initialize(mech)) { - logmsg(LL_ERR, "key %08x: can't initialize mech %s\n", - keyid, mech->lmt_name); - return 1; - } + unsigned int level; + glob_t path; + FILE *file; + + if (cfs_get_param_paths(&path, + "sptlrpc/gss/lgss_keyring/debug_level") != 0) + return; + file = fopen(path.gl_pathv[0], "r"); + if (file == NULL) { + cfs_free_param_data(&path); + return; + } + + if (fscanf(file, "%u", &level) != 1) + goto out; + + if (level >= LL_MAX) + goto out; + + lgss_set_loglevel(level); +out: + cfs_free_param_data(&path); + fclose(file); +} - cred = lgss_create_cred(mech); - if (cred == NULL) { - logmsg(LL_ERR, "key %08x: can't create a new %s cred\n", - keyid, mech->lmt_name); - return 1; - } +static int associate_with_ns(char *path) +{ + int fd, rc = -1; - cred->lc_uid = uparam.kup_uid; - cred->lc_fl_root = (uparam.kup_is_root != 0); - cred->lc_fl_mds = (uparam.kup_is_mds != 0); - cred->lc_tgt_nid = uparam.kup_nid; - cred->lc_tgt_svc = uparam.kup_svc; + fd = open(path, O_RDONLY); + if (fd != -1) { + rc = setns(fd, 0); + close(fd); + } - if (lgss_prepare_cred(cred)) { - logmsg(LL_ERR, "key %08x: failed to prepare credentials " - "for user %d\n", keyid, uparam.kup_uid); - return 1; - } - - /* pre initialize the key. note the keyring linked to is actually of the - * original requesting process, not _this_ upcall process. if it's for - * root user, don't link to any keyrings because we want fully control - * on it, and share it among all root sessions; otherswise link to - * session keyring. - */ - if (cred->lc_fl_root || cred->lc_fl_mds) - inst_keyring = 0; - else - inst_keyring = KEY_SPEC_SESSION_KEYRING; - - if (keyctl_instantiate(keyid, NULL, 0, inst_keyring)) { - logmsg(LL_ERR, "instantiate key %08x: %s\n", - keyid, strerror(errno)); - return 1; - } + return rc; +} - logmsg(LL_TRACE, "instantiated kernel key %08x\n", keyid); +static int prepare_and_instantiate(struct lgss_cred *cred, key_serial_t keyid, + uint32_t uid) +{ + key_serial_t inst_keyring; + + if (lgss_prepare_cred(cred)) { + logmsg(LL_ERR, "key %08x: failed to prepare credentials " + "for user %d\n", keyid, uid); + return 1; + } + + /* pre initialize the key. note the keyring linked to is actually of the + * original requesting process, not _this_ upcall process. if it's for + * root user, don't link to any keyrings because we want fully control + * on it, and share it among all root sessions; otherswise link to + * session keyring. + */ + if (cred->lc_root_flags != 0) + inst_keyring = 0; + else + inst_keyring = KEY_SPEC_SESSION_KEYRING; + + if (keyctl_instantiate(keyid, NULL, 0, inst_keyring)) { + logmsg(LL_ERR, "instantiate key %08x: %s\n", + keyid, strerror(errno)); + return 1; + } + + logmsg(LL_TRACE, "instantiated kernel key %08x\n", keyid); + + return 0; +} - /* - * fork a child to do the real gss negotiation - */ - child = fork(); - if (child == -1) { - logmsg(LL_ERR, "key %08x: can't create child: %s\n", - keyid, strerror(errno)); - return 1; - } else if (child == 0) { - return lgssc_kr_negotiate(keyid, cred, &uparam); - } +/**************************************** + * main process * + ****************************************/ - logmsg(LL_TRACE, "forked child %d\n", child); - return 0; +int main(int argc, char *argv[]) +{ + struct keyring_upcall_param uparam; + key_serial_t keyid; + key_serial_t sring; + pid_t child; + int req_fd[2] = { -1, -1 }; + int reply_fd[2] = { -1, -1 }; + struct lgss_mech_type *mech; + struct lgss_cred *cred; + char path[PATH_MAX] = ""; + int other_ns = 0; + int rc = 0; + struct stat parent_ns = { .st_ino = 0 }; + struct stat caller_ns = { .st_ino = 0 }; + + set_log_level(); + + logmsg(LL_TRACE, "start parsing parameters\n"); + /* + * parse & sanity check upcall parameters + * expected to be called with: + * [1]: operation + * [2]: key ID + * [3]: key type + * [4]: key description + * [5]: call out info + * [6]: UID + * [7]: GID + * [8]: thread keyring + * [9]: process keyring + * [10]: session keyring + */ + if (argc != 10 + 1) { + logmsg(LL_ERR, "invalid parameter number %d\n", argc); + return 1; + } + + logmsg(LL_INFO, "key %s, desc %s, ugid %s:%s, sring %s, coinfo %s\n", + argv[2], argv[4], argv[6], argv[7], argv[10], argv[5]); + + memset(&uparam, 0, sizeof(uparam)); + + if (strcmp(argv[1], "create") != 0) { + logmsg(LL_ERR, "invalid OP %s\n", argv[1]); + return 1; + } + + if (sscanf(argv[2], "%d", &keyid) != 1) { + logmsg(LL_ERR, "can't extract KeyID: %s\n", argv[2]); + return 1; + } + + if (sscanf(argv[6], "%d", &uparam.kup_fsuid) != 1) { + logmsg(LL_ERR, "can't extract UID: %s\n", argv[6]); + return 1; + } + + if (sscanf(argv[7], "%d", &uparam.kup_fsgid) != 1) { + logmsg(LL_ERR, "can't extract GID: %s\n", argv[7]); + return 1; + } + + if (sscanf(argv[10], "%d", &sring) != 1) { + logmsg(LL_ERR, "can't extract session keyring: %s\n", argv[10]); + return 1; + } + + if (parse_callout_info(argv[5], &uparam)) { + logmsg(LL_ERR, "can't extract callout info: %s\n", argv[5]); + return 1; + } + + logmsg(LL_TRACE, "parsing parameters OK\n"); + + /* + * prepare a cred + */ + mech = lgss_name2mech(uparam.kup_mech); + if (mech == NULL) { + logmsg(LL_ERR, "key %08x: unsupported mech: %s\n", + keyid, uparam.kup_mech); + return 1; + } + + if (lgss_mech_initialize(mech)) { + logmsg(LL_ERR, "key %08x: can't initialize mech %s\n", + keyid, mech->lmt_name); + return 1; + } + + cred = lgss_create_cred(mech); + if (cred == NULL) { + logmsg(LL_ERR, "key %08x: can't create a new %s cred\n", + keyid, mech->lmt_name); + return 1; + } + + cred->lc_uid = uparam.kup_uid; + cred->lc_root_flags |= uparam.kup_is_root ? LGSS_ROOT_CRED_ROOT : 0; + cred->lc_root_flags |= uparam.kup_is_mdt ? LGSS_ROOT_CRED_MDT : 0; + cred->lc_root_flags |= uparam.kup_is_ost ? LGSS_ROOT_CRED_OST : 0; + cred->lc_tgt_nid = uparam.kup_nid; + cred->lc_tgt_svc = uparam.kup_svc; + cred->lc_tgt_uuid = uparam.kup_tgt; + cred->lc_svc_type = uparam.kup_svc_type; + cred->lc_self_nid = uparam.kup_selfnid; + + /* Is caller in different namespace? */ + /* If passed caller's pid is 0, it means we have to stick + * with current namespace. + */ + if (uparam.kup_pid) { + snprintf(path, sizeof(path), "/proc/%d/ns/mnt", getpid()); + if (stat(path, &parent_ns)) { + logmsg(LL_DEBUG, "cannot stat %s: %s\n", + path, strerror(errno)); + } else { + snprintf(path, sizeof(path), "/proc/%d/ns/mnt", + uparam.kup_pid); + if (stat(path, &caller_ns)) + logmsg(LL_DEBUG, "cannot stat %s: %s\n", + path, strerror(errno)); + else if (caller_ns.st_ino != parent_ns.st_ino) + other_ns = 1; + } + } + + /* + * if caller's namespace is different, fork a child and associate it + * with caller's namespace to do credentials preparation + */ + if (other_ns) { + logmsg(LL_TRACE, "caller's namespace is different\n"); + + /* use pipes to pass info between child and parent processes */ + if (pipe(req_fd) == -1) { + logmsg(LL_ERR, "key %08x: pipe failed: %s\n", + keyid, strerror(errno)); + return 1; + } + if (pipe(reply_fd) == -1) { + logmsg(LL_ERR, "key %08x: pipe failed: %s\n", + keyid, strerror(errno)); + return 1; + } + + child = fork(); + if (child == -1) { + logmsg(LL_ERR, "key %08x: can't create child: %s\n", + keyid, strerror(errno)); + rc = 1; + goto out_pipe; + } else if (child == 0) { + int rc2; + /* child process: carry out credentials preparation + * in caller's namespace */ + + close(req_fd[0]); /* close unsed read end */ + req_fd[0] = -1; + close(reply_fd[1]); /* close unsed write end */ + reply_fd[1] = -1; + + if (associate_with_ns(path) != 0) { + logmsg(LL_ERR, + "failed to attach to pid %d namespace: " + "%s\n", uparam.kup_pid, strerror(errno)); + rc = 1; + goto out_pipe; + } + logmsg(LL_TRACE, "working in namespace of pid %d\n", + uparam.kup_pid); + + rc = prepare_and_instantiate(cred, keyid, + uparam.kup_uid); + + /* send to parent the status of credentials preparation + * and key instantiation */ + rc2 = send_to(req_fd[1], &rc, sizeof(rc)); + rc = (rc == 0 ? rc2 : rc); + if (rc != 0) + goto out_pipe; + + /* now do real gss negotiation + * parent main process will not wait for us, + * as it has to be done in the background */ + rc = lgssc_kr_negotiate(keyid, cred, &uparam, + req_fd, reply_fd); + goto out_pipe; + } else { + int rc2; + /* parent process: exchange info with child carrying out + * credentials preparation */ + + close(req_fd[1]); /* close unsed write end */ + req_fd[1] = -1; + close(reply_fd[0]); /* close unsed read end */ + reply_fd[0] = -1; + + /* get status of credentials preparation + * and key instantiation */ + rc2 = receive_from(req_fd[0], &rc, sizeof(rc)); + if (rc2 != 0 || rc != 0) { + logmsg(LL_ERR, "child failed preparing creds: " + "%s\n", + rc2 != 0 ? strerror(-rc2) + : strerror(rc)); + goto out_pipe; + } + + /* + * fork a child here to participate in gss negotiation, + * as it has to be done in the background + */ + child = fork(); + if (child == -1) { + logmsg(LL_ERR, + "key %08x: can't create child: %s\n", + keyid, strerror(errno)); + rc = 1; + goto out_pipe; + } else if (child == 0) { + struct lgssd_ioctl_param param; + char outbuf[8192] = { 0 }; + void *gss_token = NULL; + + /* get ioctl buffer from child */ + rc = receive_from(req_fd[0], ¶m, + sizeof(param)); + if (rc != 0) + goto out_pipe; + + gss_token = calloc(1, param.send_token_size); + if (gss_token == NULL) + goto out_pipe; + + /* get gss token from child */ + rc = receive_from(req_fd[0], gss_token, + param.send_token_size); + if (rc != 0) + goto out_token; + + param.send_token = (char *)gss_token; + param.reply_buf_size = sizeof(outbuf); + param.reply_buf = outbuf; + + /* do ioctl in place of child process carrying + * out credentials negotiation: as it runs in + * a container, it might not be able to + * perform ioctl */ + rc = gss_do_ioctl(¶m); + if (rc != 0) + goto out_token; + + /* send ioctl status to child */ + rc = send_to(reply_fd[1], ¶m.status, + sizeof(param.status)); + if (rc != 0) + goto out_token; + /* send reply buffer to child */ + rc = send_to(reply_fd[1], outbuf, + sizeof(outbuf)); + if (rc != 0) + goto out_token; + +out_token: + free(gss_token); + goto out_pipe; + } + + logmsg(LL_TRACE, "forked child %d\n", child); + } +out_pipe: + close(req_fd[0]); + close(req_fd[1]); + close(reply_fd[0]); + close(reply_fd[1]); + return rc; + } else { + if (uparam.kup_pid) + logmsg(LL_TRACE, "caller's namespace is the same\n"); + else + logmsg(LL_TRACE, "stick with current namespace\n"); + + rc = prepare_and_instantiate(cred, keyid, uparam.kup_uid); + if (rc != 0) + return rc; + + /* + * fork a child to do the real gss negotiation + */ + child = fork(); + if (child == -1) { + logmsg(LL_ERR, "key %08x: can't create child: %s\n", + keyid, strerror(errno)); + return 1; + } else if (child == 0) { + return lgssc_kr_negotiate(keyid, cred, &uparam, + req_fd, reply_fd); + } + + logmsg(LL_TRACE, "forked child %d\n", child); + return 0; + } }