*
* 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
*/
* Author: Eric Mei <ericm@clusterfs.com>
*/
+#include <sched.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
};
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;
- uint64_t kup_selfnid;
+ 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,
+ 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;
};
/****************************************
return rc;
}
+/* 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)
+{
+ 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);
+ 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.
*/
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;
return rc;
}
+static int lgssc_kr_negotiate_manual(key_serial_t keyid, struct lgss_cred *cred,
+ struct keyring_upcall_param *kup)
+{
+ struct lgss_nego_data lnd;
+ OM_uint32 min_stat;
+ int rc;
+
+retry:
+ memset(&lnd, 0, sizeof(lnd));
+
+ 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;
+ }
+
+ 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);
+ if (rc == -EAGAIN) {
+ logmsg(LL_ERR, "Failed negotiation must retry\n");
+ 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 (lnd.lnd_ctx_token.length != 0)
+ gss_release_buffer(&min_stat, &lnd.lnd_ctx_token);
+
+ lgssc_fini_nego_data(&lnd);
+
+out_cred:
+ lgss_release_cred(cred);
+ return rc;
+}
+
/*
* note we inherited assumed authority from parent process
*/
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);
+ break;
case LGSS_MECH_KRB5:
default:
rc = lgssc_kr_negotiate_krb(keyid, cred, kup);
* [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 = 10;
+ const int nargs = 11;
char buf[1024];
char *string = buf;
int length, i;
}
data[i] = string;
- logmsg(LL_TRACE, "components: %s,%s,%s,%s,%s,%c,%s,%s,%s,%s\n",
+ 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[6], data[7], data[8], data[9], data[10]);
uparam->kup_secid = strtol(data[0], NULL, 0);
strlcpy(uparam->kup_mech, data[1], sizeof(uparam->kup_mech));
uparam->kup_nid = strtoll(data[7], NULL, 0);
strlcpy(uparam->kup_tgt, data[8], sizeof(uparam->kup_tgt));
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"\n",
+ "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_tgt, uparam->kup_selfnid, uparam->kup_pid);
return 0;
}
fclose(file);
}
+#ifdef HAVE_SETNS
+static int associate_with_ns(char *path)
+{
+ int fd, rc = -1;
+
+ fd = open(path, O_RDONLY);
+ if (fd != -1) {
+ rc = setns(fd, 0);
+ close(fd);
+ }
+
+ return rc;
+}
+#endif
+
/****************************************
* main process *
****************************************/
pid_t child;
struct lgss_mech_type *mech;
struct lgss_cred *cred;
+#ifdef HAVE_SETNS
+ char path[PATH_MAX];
+ struct stat parent_ns = { .st_ino = 0 }, caller_ns = { .st_ino = 0 };
+#endif
set_log_level();
cred->lc_svc_type = uparam.kup_svc_type;
cred->lc_self_nid = uparam.kup_selfnid;
- if (lgss_prepare_cred(cred)) {
- logmsg(LL_ERR, "key %08x: failed to prepare credentials "
- "for user %d\n", keyid, uparam.kup_uid);
- return 1;
- }
+#ifdef HAVE_SETNS
+ /* Is caller in different namespace? */
+ snprintf(path, sizeof(path), "/proc/%d/ns/mnt", getpid());
+ if (stat(path, &parent_ns))
+ logmsg(LL_ERR, "cannot stat %s: %s\n", path, strerror(errno));
+ snprintf(path, sizeof(path), "/proc/%d/ns/mnt", uparam.kup_pid);
+ if (stat(path, &caller_ns))
+ logmsg(LL_ERR, "cannot stat %s: %s\n", path, strerror(errno));
+ if (caller_ns.st_ino != parent_ns.st_ino) {
+ /*
+ * do credentials preparation in caller's namespace
+ */
+ if (associate_with_ns(path) != 0) {
+ logmsg(LL_ERR, "failed to attach to pid %d namespace: "
+ "%s\n", uparam.kup_pid, strerror(errno));
+ return 1;
+ }
+ logmsg(LL_TRACE, "working in namespace of pid %d\n",
+ uparam.kup_pid);
+ } else {
+ logmsg(LL_TRACE, "caller's namespace is the same\n");
+ }
+#endif /* HAVE_SETNS */
+
+ 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