X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=lustre%2Futils%2Fgss%2Fsvcgssd_proc.c;h=8b57336f7746b1dd38cd1a22240ca205605c7119;hb=7a1cb859d83891c2d7d1a50891cede6bcb1a1106;hp=f9a543c9ab34c5099d4752e7e3cb9dec7306d367;hpb=8a41cc258a4cf12f779b82c32d98e9696aa49833;p=fs%2Flustre-release.git diff --git a/lustre/utils/gss/svcgssd_proc.c b/lustre/utils/gss/svcgssd_proc.c index f9a543c..8b57336 100644 --- a/lustre/utils/gss/svcgssd_proc.c +++ b/lustre/utils/gss/svcgssd_proc.c @@ -1,41 +1,41 @@ /* - svc_in_gssd_proc.c - - Copyright (c) 2000 The Regents of the University of Michigan. - All rights reserved. - - Copyright (c) 2002 Bruce Fields - - 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. - -*/ + * svc_in_gssd_proc.c + * + * Copyright (c) 2000 The Regents of the University of Michigan. + * All rights reserved. + * + * Copyright (c) 2002 Bruce Fields + * + * 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. + */ #include #include +#include #include #include #include @@ -43,7 +43,12 @@ #include #include #include -#include +#ifdef HAVE_NETDB_H +# include +#endif + +#include +#include #include "svcgssd.h" #include "gss_util.h" @@ -51,10 +56,12 @@ #include "context.h" #include "cacheio.h" #include "lsupport.h" +#include "gss_oids.h" +#include "sk_utils.h" +#include -extern char * mech2file(gss_OID mech); -#define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.ptlrpcs.context/channel" -#define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.ptlrpcs.init/channel" +#define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.sptlrpc.context/channel" +#define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.sptlrpc.init/channel" #define TOKEN_BUF_SIZE 8192 @@ -62,21 +69,42 @@ struct svc_cred { uint32_t cr_remote; uint32_t cr_usr_root; uint32_t cr_usr_mds; + uint32_t cr_usr_oss; uid_t cr_uid; uid_t cr_mapped_uid; uid_t cr_gid; }; +struct svc_nego_data { + /* kernel data*/ + uint32_t lustre_svc; + lnet_nid_t nid; + uint64_t handle_seq; + char nm_name[LUSTRE_NODEMAP_NAME_LENGTH + 1]; + gss_buffer_desc in_tok; + gss_buffer_desc out_tok; + gss_buffer_desc in_handle; + gss_buffer_desc out_handle; + uint32_t maj_stat; + uint32_t min_stat; + + /* userspace data */ + gss_OID mech; + gss_ctx_id_t ctx; + gss_buffer_desc ctx_token; +}; + static int do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred, - gss_OID mech, gss_buffer_desc *context_token) + gss_OID mechoid, gss_buffer_desc *context_token) { FILE *f; - char *fname = NULL; + const char *mechname; int err; printerr(2, "doing downcall\n"); - if ((fname = mech2file(mech)) == NULL) + mechname = gss_OID_mech_name(mechoid); + if (mechname == NULL) goto out_err; f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w"); if (f == NULL) { @@ -87,14 +115,15 @@ do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred, } qword_printhex(f, out_handle->value, out_handle->length); /* XXX are types OK for the rest of this? */ - qword_printint(f, 0x7fffffff); /*XXX need a better timeout */ + qword_printint(f, time(NULL) + 3600); /* 1 hour should be ok */ qword_printint(f, cred->cr_remote); qword_printint(f, cred->cr_usr_root); qword_printint(f, cred->cr_usr_mds); + qword_printint(f, cred->cr_usr_oss); qword_printint(f, cred->cr_mapped_uid); qword_printint(f, cred->cr_uid); qword_printint(f, cred->cr_gid); - qword_print(f, fname); + qword_print(f, mechname); qword_printhex(f, context_token->value, context_token->length); err = qword_eol(f); fclose(f); @@ -122,18 +151,17 @@ send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token, /* XXXARG: */ int g; - printerr(2, "sending null reply\n"); - + printerr(2, "sending reply\n"); qword_addhex(&bp, &blen, in_handle->value, in_handle->length); qword_addhex(&bp, &blen, in_token->value, in_token->length); - qword_addint(&bp, &blen, 0x7fffffff); /*XXX need a better timeout */ + qword_addint(&bp, &blen, time(NULL) + 3600); /* 1 hour should be ok */ qword_adduint(&bp, &blen, maj_stat); qword_adduint(&bp, &blen, min_stat); qword_addhex(&bp, &blen, out_handle->value, out_handle->length); qword_addhex(&bp, &blen, out_token->value, out_token->length); qword_addeol(&bp, &blen); if (blen <= 0) { - printerr(0, "WARNING: send_respsonse: message too long\n"); + printerr(0, "WARNING: send_response: message too long\n"); return -1; } g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY); @@ -162,349 +190,482 @@ send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token, #define rpcsec_gsserr_credproblem 13 #define rpcsec_gsserr_ctxproblem 14 -#if 0 -static void -add_supplementary_groups(char *secname, char *name, struct svc_cred *cred) -{ - int ret; - static gid_t *groups = NULL; - - cred->cr_ngroups = NGROUPS; - ret = nfs4_gss_princ_to_grouplist(secname, name, - cred->cr_groups, &cred->cr_ngroups); - if (ret < 0) { - groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t)); - ret = nfs4_gss_princ_to_grouplist(secname, name, - groups, &cred->cr_ngroups); - if (ret < 0) - cred->cr_ngroups = 0; - else { - if (cred->cr_ngroups > NGROUPS) - cred->cr_ngroups = NGROUPS; - memcpy(cred->cr_groups, groups, - cred->cr_ngroups*sizeof(gid_t)); - } - } -} -#endif - -#if 0 static int -get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred) +get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred, + lnet_nid_t nid, uint32_t lustre_svc) { u_int32_t maj_stat, min_stat; gss_buffer_desc name; - char *sname; + char *sname, *host, *realm; + const int namebuf_size = 512; + char namebuf[namebuf_size]; int res = -1; - uid_t uid, gid; gss_OID name_type = GSS_C_NO_OID; - char *secname; + struct passwd *pw; + + cred->cr_remote = 0; + cred->cr_usr_root = cred->cr_usr_mds = cred->cr_usr_oss = 0; + cred->cr_uid = cred->cr_mapped_uid = cred->cr_gid = -1; maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type); if (maj_stat != GSS_S_COMPLETE) { pgsserr("get_ids: gss_display_name", maj_stat, min_stat, mech); - goto out; + return -1; } if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */ !(sname = calloc(name.length + 1, 1))) { - printerr(0, "WARNING: get_ids: error allocating %d bytes " + printerr(0, "WARNING: get_ids: error allocating %zu bytes " "for sname\n", name.length + 1); gss_release_buffer(&min_stat, &name); - goto out; + return -1; } memcpy(sname, name.value, name.length); - printerr(1, "sname = %s\n", sname); + sname[name.length] = '\0'; gss_release_buffer(&min_stat, &name); - res = -EINVAL; - if ((secname = mech2file(mech)) == NULL) { - printerr(0, "WARNING: get_ids: error mapping mech to " - "file for name '%s'\n", sname); + if (lustre_svc == LUSTRE_GSS_SVC_MDS) + lookup_mapping(sname, nid, &cred->cr_mapped_uid); + else + cred->cr_mapped_uid = -1; + + realm = strchr(sname, '@'); + if (realm) { + *realm++ = '\0'; + } else { + printerr(0, "ERROR: %s has no realm name\n", sname); + goto out_free; + } + + host = strchr(sname, '/'); + if (host) + *host++ = '\0'; + + if (strcmp(sname, GSSD_SERVICE_MGS) == 0) { + printerr(0, "forbid %s as a user name\n", sname); goto out_free; } - nfs4_init_name_mapping(NULL); /* XXX: should only do this once */ - res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid); - if (res < 0) { - /* - * -ENOENT means there was no mapping, any other error - * value means there was an error trying to do the - * mapping. - * If there was no mapping, we send down the value -1 - * to indicate that the anonuid/anongid for the export - * should be used. - */ - if (res == -ENOENT) { - cred->cr_uid = -1; - cred->cr_gid = -1; - cred->cr_ngroups = 0; - res = 0; + + /* 1. check host part */ + if (host) { + if (lnet_nid2hostname(nid, namebuf, namebuf_size)) { + printerr(0, "ERROR: failed to resolve hostname for " + "%s/%s@%s from %016llx\n", + sname, host, realm, nid); goto out_free; } - printerr(0, "WARNING: get_ids: failed to map name '%s' " - "to uid/gid: %s\n", sname, strerror(-res)); - goto out_free; + + if (strcasecmp(host, namebuf)) { + printerr(0, "ERROR: %s/%s@%s claimed hostname doesn't " + "match %s, nid %016llx\n", sname, host, realm, + namebuf, nid); + goto out_free; + } + } else { + if (!strcmp(sname, GSSD_SERVICE_MDS) || + !strcmp(sname, GSSD_SERVICE_OSS)) { + printerr(0, "ERROR: %s@%s from %016llx doesn't " + "bind with hostname\n", sname, realm, nid); + goto out_free; + } + } + + /* 2. check realm and user */ + switch (lustre_svc) { + case LUSTRE_GSS_SVC_MDS: + if (strcasecmp(mds_local_realm, realm)) { + cred->cr_remote = 1; + + /* only allow mapped user from remote realm */ + if (cred->cr_mapped_uid == -1) { + printerr(0, "ERROR: %s%s%s@%s from %016llx " + "is remote but without mapping\n", + sname, host ? "/" : "", + host ? host : "", realm, nid); + break; + } + } else { + if (!strcmp(sname, LUSTRE_ROOT_NAME)) { + cred->cr_uid = 0; + cred->cr_usr_root = 1; + } else if (!strcmp(sname, GSSD_SERVICE_MDS)) { + cred->cr_uid = 0; + cred->cr_usr_mds = 1; + } else if (!strcmp(sname, GSSD_SERVICE_OSS)) { + cred->cr_uid = 0; + cred->cr_usr_oss = 1; + } else { + pw = getpwnam(sname); + if (pw != NULL) { + cred->cr_uid = pw->pw_uid; + printerr(2, "%s resolve to uid %u\n", + sname, cred->cr_uid); + } else if (cred->cr_mapped_uid != -1) { + printerr(2, "user %s from %016llx is " + "mapped to %u\n", sname, nid, + cred->cr_mapped_uid); + } else { + printerr(0, "ERROR: invalid user, " + "%s/%s@%s from %016llx\n", + sname, host, realm, nid); + break; + } + } + } + + res = 0; + break; + case LUSTRE_GSS_SVC_MGS: + if (!strcmp(sname, GSSD_SERVICE_OSS)) { + cred->cr_uid = 0; + cred->cr_usr_oss = 1; + } + /* fall through */ + case LUSTRE_GSS_SVC_OSS: + if (!strcmp(sname, LUSTRE_ROOT_NAME)) { + cred->cr_uid = 0; + cred->cr_usr_root = 1; + } else if (!strcmp(sname, GSSD_SERVICE_MDS)) { + cred->cr_uid = 0; + cred->cr_usr_mds = 1; + } + if (cred->cr_uid == -1) { + printerr(0, "ERROR: svc %d doesn't accept user %s " + "from %016llx\n", lustre_svc, sname, nid); + break; + } + res = 0; + break; + default: + assert(0); } - cred->cr_uid = uid; - cred->cr_gid = gid; - add_supplementary_groups(secname, sname, cred); - res = 0; + out_free: + if (!res) + printerr(1, "%s: authenticated %s%s%s@%s from %016llx\n", + lustre_svc_name[lustre_svc], sname, + host ? "/" : "", host ? host : "", realm, nid); free(sname); -out: return res; } -#endif -#if 0 -void -print_hexl(int pri, unsigned char *cp, int length) +typedef struct gss_union_ctx_id_t { + gss_OID mech_type; + gss_ctx_id_t internal_ctx_id; +} gss_union_ctx_id_desc, *gss_union_ctx_id_t; + +int handle_sk(struct svc_nego_data *snd) { - int i, j, jm; - unsigned char c; - - printerr(pri, "length %d\n",length); - printerr(pri, "\n"); - - for (i = 0; i < length; i += 0x10) { - printerr(pri, " %04x: ", (u_int)i); - jm = length - i; - jm = jm > 16 ? 16 : jm; - - for (j = 0; j < jm; j++) { - if ((j % 2) == 1) - printerr(pri,"%02x ", (u_int)cp[i+j]); - else - printerr(pri,"%02x", (u_int)cp[i+j]); - } - for (; j < 16; j++) { - if ((j % 2) == 1) - printerr(pri," "); - else - printerr(pri," "); - } - printerr(pri," "); +#ifdef HAVE_OPENSSL_SSK + struct sk_cred *skc = NULL; + struct svc_cred cred; + gss_buffer_desc bufs[SK_INIT_BUFFERS]; + gss_buffer_desc remote_pub_key = GSS_C_EMPTY_BUFFER; + char *target; + uint32_t rc = GSS_S_DEFECTIVE_TOKEN; + uint32_t version; + uint32_t flags; + int i; + + printerr(3, "Handling sk request\n"); + memset(bufs, 0, sizeof(gss_buffer_desc) * SK_INIT_BUFFERS); + + /* See lgss_sk_using_cred() for client side token formation. + * Decoding initiator buffers */ + i = sk_decode_netstring(bufs, SK_INIT_BUFFERS, &snd->in_tok); + if (i < SK_INIT_BUFFERS) { + printerr(0, "Invalid netstring token received from peer\n"); + goto cleanup_buffers; + } - for (j = 0; j < jm; j++) { - c = cp[i+j]; - c = isprint(c) ? c : '.'; - printerr(pri,"%c", c); - } - printerr(pri,"\n"); + /* Allowing for a larger length first buffer in the future */ + if (bufs[SK_INIT_VERSION].length < sizeof(version)) { + printerr(0, "Invalid version received (wrong size)\n"); + goto cleanup_buffers; + } + memcpy(&version, bufs[SK_INIT_VERSION].value, sizeof(version)); + version = be32toh(version); + if (version != SK_MSG_VERSION) { + printerr(0, "Invalid version received: %d\n", version); + goto cleanup_buffers; } -} -#endif -static int -get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred, - lnet_nid_t nid, uint32_t lustre_svc) -{ - u_int32_t maj_stat, min_stat; - gss_buffer_desc name; - char *sname, *realm, *slash; - int res = -1; - gss_OID name_type = GSS_C_NO_OID; - struct passwd *pw; + rc = GSS_S_FAILURE; - cred->cr_remote = cred->cr_usr_root = cred->cr_usr_mds = 0; - cred->cr_uid = cred->cr_mapped_uid = cred->cr_gid = -1; + /* target must be a null terminated string */ + i = bufs[SK_INIT_TARGET].length - 1; + target = bufs[SK_INIT_TARGET].value; + if (i >= 0 && target[i] != '\0') { + printerr(0, "Invalid target from netstring\n"); + goto cleanup_buffers; + } - maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type); - if (maj_stat != GSS_S_COMPLETE) { - pgsserr("get_ids: gss_display_name", - maj_stat, min_stat, mech); - return -1; + if (bufs[SK_INIT_FLAGS].length != sizeof(flags)) { + printerr(0, "Invalid flags from netstring\n"); + goto cleanup_buffers; } - if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */ - !(sname = calloc(name.length + 1, 1))) { - printerr(0, "WARNING: get_ids: error allocating %d bytes " - "for sname\n", name.length + 1); - gss_release_buffer(&min_stat, &name); - return -1; + memcpy(&flags, bufs[SK_INIT_FLAGS].value, sizeof(flags)); + + skc = sk_create_cred(target, snd->nm_name, be32toh(flags)); + if (!skc) { + printerr(0, "Failed to create sk credentials\n"); + goto cleanup_buffers; } - memcpy(sname, name.value, name.length); - printerr(1, "authenticated %s from %016llx\n", sname, nid); - gss_release_buffer(&min_stat, &name); - if (lustre_svc == LUSTRE_GSS_SVC_MDS) - lookup_mapping(sname, nid, &cred->cr_mapped_uid); - else - cred->cr_mapped_uid = -1; + /* Verify that the peer has used a prime size greater or equal to + * the size specified in the key file which may contain only zero + * fill but the size specifies the mimimum supported size on + * servers */ + if (skc->sc_flags & LGSS_SVC_PRIV && + bufs[SK_INIT_P].length < skc->sc_p.length) { + printerr(0, "Peer DHKE prime does not meet the size required " + "by keyfile: %zd bits\n", skc->sc_p.length * 8); + goto cleanup_buffers; + } - realm = strchr(sname, '@'); - if (!realm) { - printerr(0, "WARNNING: principal %s contains no realm name\n", - sname); - cred->cr_remote = (mds_local_realm != NULL); - } else { - *realm++ = '\0'; - if (!mds_local_realm) - cred->cr_remote = 1; - else - cred->cr_remote = - (strcasecmp(mds_local_realm, realm) != 0); - } - - if (cred->cr_remote) { - if (cred->cr_mapped_uid != -1) - res = 0; - else if (lustre_svc == LUSTRE_GSS_SVC_OSS && - strcmp(sname, "lustre_root") == 0) - res = 0; - else - printerr(0, "principal %s is remote without mapping\n", - sname); - goto out_free; - } - - slash = strchr(sname, '/'); - if (slash) - *slash = '\0'; - - if (!(pw = getpwnam(sname))) { - /* If client use machine credential, we map it to root, which - * will subject to further mapping by root-squash in kernel. - * - * MDS service keytab is treated as special user, also mapped - * to root. OSS service keytab can't be used as a user. - */ - if (!strcmp(sname, LUSTRE_ROOT_NAME)) { - printerr(2, "lustre_root principal, resolve to uid 0\n"); - cred->cr_uid = 0; - cred->cr_usr_root = 1; - } else if (!strcmp(sname, GSSD_SERVICE_MDS)) { - printerr(2, "mds service principal, resolve to uid 0\n"); - cred->cr_uid = 0; - cred->cr_usr_mds = 1; - } else { - cred->cr_uid = -1; - if (cred->cr_mapped_uid == -1) { - printerr(0, "invalid user %s\n", sname); - goto out_free; - } - printerr(2, "user %s mapped to %u\n", - sname, cred->cr_mapped_uid); - } - } else { - cred->cr_uid = pw->pw_uid; - printerr(2, "%s resolve to uid %u\n", sname, cred->cr_uid); - } - - res = 0; -out_free: - free(sname); - return res; -} + /* Throw out the p from the server and use the wire data */ + free(skc->sc_p.value); + skc->sc_p.value = NULL; + skc->sc_p.length = 0; -typedef struct gss_union_ctx_id_t { - gss_OID mech_type; - gss_ctx_id_t internal_ctx_id; -} gss_union_ctx_id_desc, *gss_union_ctx_id_t; + /* Take control of all the allocated buffers from decoding */ + if (bufs[SK_INIT_RANDOM].length != + sizeof(skc->sc_kctx.skc_peer_random)) { + printerr(0, "Invalid size for client random\n"); + goto cleanup_buffers; + } -/* - * return -1 only if we detect error during reading from upcall channel, - * all other cases return 0. - */ -int -handle_nullreq(FILE *f) { - uint64_t handle_seq; - char in_tok_buf[TOKEN_BUF_SIZE]; - char in_handle_buf[15]; - char out_handle_buf[15]; - gss_buffer_desc in_tok = {.value = in_tok_buf}, - out_tok = {.value = NULL}, - in_handle = {.value = in_handle_buf}, - out_handle = {.value = out_handle_buf}, - ctx_token = {.value = NULL}, - ignore_out_tok = {.value = NULL}, - /* XXX isn't there a define for this?: */ - null_token = {.value = NULL}; - uint32_t lustre_svc; - lnet_nid_t nid; - u_int32_t ret_flags; - gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; - gss_name_t client_name; - gss_OID mech = GSS_C_NO_OID; - gss_cred_id_t svc_cred; - u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0; - u_int32_t ignore_min_stat; - struct svc_cred cred; - static char *lbuf = NULL; - static int lbuflen = 0; - static char *cp; + memcpy(&skc->sc_kctx.skc_peer_random, bufs[SK_INIT_RANDOM].value, + sizeof(skc->sc_kctx.skc_peer_random)); + skc->sc_p = bufs[SK_INIT_P]; + remote_pub_key = bufs[SK_INIT_PUB_KEY]; + skc->sc_nodemap_hash = bufs[SK_INIT_NODEMAP]; + skc->sc_hmac = bufs[SK_INIT_HMAC]; + + /* Verify HMAC from peer. Ideally this would happen before anything + * else but we don't have enough information to lookup key without the + * token (fsname and cluster_hash) so it's done after. */ + rc = sk_verify_hmac(skc, bufs, SK_INIT_BUFFERS - 1, EVP_sha256(), + &skc->sc_hmac); + if (rc != GSS_S_COMPLETE) { + printerr(0, "HMAC verification error: 0x%x from peer %s\n", + rc, libcfs_nid2str((lnet_nid_t)snd->nid)); + goto cleanup_partial; + } - printerr(2, "handling null request\n"); + /* Check that the cluster hash matches the hash of nodemap name */ + rc = sk_verify_hash(snd->nm_name, EVP_sha256(), &skc->sc_nodemap_hash); + if (rc != GSS_S_COMPLETE) { + printerr(0, "Cluster hash failed validation: 0x%x\n", rc); + goto cleanup_partial; + } - if (readline(fileno(f), &lbuf, &lbuflen) != 1) { - printerr(0, "WARNING: handle_nullreq: " - "failed reading request\n"); - return -1; + rc = sk_gen_params(skc); + if (rc != GSS_S_COMPLETE) { + printerr(0, "Failed to generate DH params for responder\n"); + goto cleanup_partial; + } + if (sk_compute_dh_key(skc, &remote_pub_key)) { + printerr(0, "Failed to compute session key from DH params\n"); + goto cleanup_partial; } - cp = lbuf; + /* Cleanup init buffers we have copied or don't need anymore */ + free(bufs[SK_INIT_VERSION].value); + free(bufs[SK_INIT_RANDOM].value); + free(bufs[SK_INIT_TARGET].value); + free(bufs[SK_INIT_FLAGS].value); + + /* Server reply contains the servers public key, random, and HMAC */ + version = htobe32(SK_MSG_VERSION); + bufs[SK_RESP_VERSION].value = &version; + bufs[SK_RESP_VERSION].length = sizeof(version); + bufs[SK_RESP_RANDOM].value = &skc->sc_kctx.skc_host_random; + bufs[SK_RESP_RANDOM].length = sizeof(skc->sc_kctx.skc_host_random); + bufs[SK_RESP_PUB_KEY] = skc->sc_pub_key; + if (sk_sign_bufs(&skc->sc_kctx.skc_shared_key, bufs, + SK_RESP_BUFFERS - 1, EVP_sha256(), + &skc->sc_hmac)) { + printerr(0, "Failed to sign parameters\n"); + goto out_err; + } + bufs[SK_RESP_HMAC] = skc->sc_hmac; + if (sk_encode_netstring(bufs, SK_RESP_BUFFERS, &snd->out_tok)) { + printerr(0, "Failed to encode netstring for token\n"); + goto out_err; + } + printerr(2, "Created netstring of %zd bytes\n", snd->out_tok.length); - qword_get(&cp, (char *) &lustre_svc, sizeof(lustre_svc)); - qword_get(&cp, (char *) &nid, sizeof(nid)); - qword_get(&cp, (char *) &handle_seq, sizeof(handle_seq)); - printerr(1, "handling req: svc %u, nid %016llx, idx %llx\n", - lustre_svc, nid, handle_seq); - - in_handle.length = (size_t) qword_get(&cp, in_handle.value, - sizeof(in_handle_buf)); - printerr(3, "in_handle: \n"); - print_hexl(3, in_handle.value, in_handle.length); - - in_tok.length = (size_t) qword_get(&cp, in_tok.value, - sizeof(in_tok_buf)); - printerr(3, "in_tok: \n"); - print_hexl(3, in_tok.value, in_tok.length); - - if (in_tok.length < 0) { - printerr(0, "WARNING: handle_nullreq: " - "failed parsing request\n"); + if (sk_session_kdf(skc, snd->nid, &snd->in_tok, &snd->out_tok)) { + printerr(0, "Failed to calulate derviced session key\n"); + goto out_err; + } + if (sk_compute_keys(skc)) { + printerr(0, "Failed to compute HMAC and encryption keys\n"); + goto out_err; + } + if (sk_serialize_kctx(skc, &snd->ctx_token)) { + printerr(0, "Failed to serialize context for kernel\n"); goto out_err; } - if (in_handle.length != 0) { /* CONTINUE_INIT case */ - if (in_handle.length != sizeof(ctx)) { - printerr(0, "WARNING: handle_nullreq: " - "input handle has unexpected length %d\n", - in_handle.length); - goto out_err; - } - /* in_handle is the context id stored in the out_handle - * for the GSS_S_CONTINUE_NEEDED case below. */ - memcpy(&ctx, in_handle.value, in_handle.length); + snd->out_handle.length = sizeof(snd->handle_seq); + memcpy(snd->out_handle.value, &snd->handle_seq, + sizeof(snd->handle_seq)); + snd->maj_stat = GSS_S_COMPLETE; + + /* fix credentials */ + memset(&cred, 0, sizeof(cred)); + cred.cr_mapped_uid = -1; + + if (skc->sc_flags & LGSS_ROOT_CRED_ROOT) + cred.cr_usr_root = 1; + if (skc->sc_flags & LGSS_ROOT_CRED_MDT) + cred.cr_usr_mds = 1; + if (skc->sc_flags & LGSS_ROOT_CRED_OST) + cred.cr_usr_oss = 1; + + do_svc_downcall(&snd->out_handle, &cred, snd->mech, &snd->ctx_token); + + /* cleanup ctx_token, out_tok is cleaned up in handle_channel_req */ + free(remote_pub_key.value); + free(snd->ctx_token.value); + snd->ctx_token.length = 0; + + printerr(3, "sk returning success\n"); + return 0; + +cleanup_buffers: + for (i = 0; i < SK_INIT_BUFFERS; i++) + free(bufs[i].value); + sk_free_cred(skc); + snd->maj_stat = rc; + return -1; + +cleanup_partial: + free(bufs[SK_INIT_VERSION].value); + free(bufs[SK_INIT_RANDOM].value); + free(bufs[SK_INIT_TARGET].value); + free(bufs[SK_INIT_FLAGS].value); + free(remote_pub_key.value); + sk_free_cred(skc); + snd->maj_stat = rc; + return -1; + +out_err: + snd->maj_stat = rc; + if (snd->ctx_token.value) { + free(snd->ctx_token.value); + snd->ctx_token.value = 0; + snd->ctx_token.length = 0; + } + free(remote_pub_key.value); + sk_free_cred(skc); + printerr(3, "sk returning failure\n"); +#else /* !HAVE_OPENSSL_SSK */ + printerr(0, "ERROR: shared key subflavour is not enabled\n"); +#endif /* HAVE_OPENSSL_SSK */ + return -1; +} + +int handle_null(struct svc_nego_data *snd) +{ + struct svc_cred cred; + uint64_t tmp; + uint32_t flags; + + /* null just uses the same token as the return token and for + * for sending to the kernel. It is a single uint64_t. */ + if (snd->in_tok.length != sizeof(uint64_t)) { + snd->maj_stat = GSS_S_DEFECTIVE_TOKEN; + printerr(0, "Invalid token size (%zd) received\n", + snd->in_tok.length); + return -1; + } + snd->out_tok.length = snd->in_tok.length; + snd->out_tok.value = malloc(snd->out_tok.length); + if (!snd->out_tok.value) { + snd->maj_stat = GSS_S_FAILURE; + printerr(0, "Failed to allocate out_tok\n"); + return -1; + } + + snd->ctx_token.length = snd->in_tok.length; + snd->ctx_token.value = malloc(snd->ctx_token.length); + if (!snd->ctx_token.value) { + snd->maj_stat = GSS_S_FAILURE; + printerr(0, "Failed to allocate ctx_token\n"); + return -1; } - svc_cred = gssd_select_svc_cred(lustre_svc); + snd->out_handle.length = sizeof(snd->handle_seq); + memcpy(snd->out_handle.value, &snd->handle_seq, + sizeof(snd->handle_seq)); + snd->maj_stat = GSS_S_COMPLETE; + + memcpy(&tmp, snd->in_tok.value, sizeof(tmp)); + tmp = be64toh(tmp); + flags = (uint32_t)(tmp & 0x00000000ffffffff); + memset(&cred, 0, sizeof(cred)); + cred.cr_mapped_uid = -1; + + if (flags & LGSS_ROOT_CRED_ROOT) + cred.cr_usr_root = 1; + if (flags & LGSS_ROOT_CRED_MDT) + cred.cr_usr_mds = 1; + if (flags & LGSS_ROOT_CRED_OST) + cred.cr_usr_oss = 1; + + do_svc_downcall(&snd->out_handle, &cred, snd->mech, &snd->ctx_token); + + /* cleanup ctx_token, out_tok is cleaned up in handle_channel_req */ + free(snd->ctx_token.value); + snd->ctx_token.length = 0; + + return 0; +} + +static int handle_krb(struct svc_nego_data *snd) +{ + u_int32_t ret_flags; + gss_name_t client_name; + gss_buffer_desc ignore_out_tok = {.value = NULL}; + gss_OID mech = GSS_C_NO_OID; + gss_cred_id_t svc_cred; + u_int32_t ignore_min_stat; + struct svc_cred cred; + + svc_cred = gssd_select_svc_cred(snd->lustre_svc); if (!svc_cred) { - printerr(0, "no service credential for svc %u\n", lustre_svc); + printerr(0, "no service credential for svc %u\n", + snd->lustre_svc); goto out_err; } - maj_stat = gss_accept_sec_context(&min_stat, &ctx, svc_cred, - &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name, - &mech, &out_tok, &ret_flags, NULL, NULL); + snd->maj_stat = gss_accept_sec_context(&snd->min_stat, &snd->ctx, + svc_cred, &snd->in_tok, + GSS_C_NO_CHANNEL_BINDINGS, + &client_name, &mech, + &snd->out_tok, &ret_flags, NULL, + NULL); - if (maj_stat == GSS_S_CONTINUE_NEEDED) { + if (snd->maj_stat == GSS_S_CONTINUE_NEEDED) { printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n"); /* Save the context handle for future calls */ - out_handle.length = sizeof(ctx); - memcpy(out_handle.value, &ctx, sizeof(ctx)); - goto continue_needed; - } - else if (maj_stat != GSS_S_COMPLETE) { + snd->out_handle.length = sizeof(snd->ctx); + memcpy(snd->out_handle.value, &snd->ctx, sizeof(snd->ctx)); + return 0; + } else if (snd->maj_stat != GSS_S_COMPLETE) { printerr(0, "WARNING: gss_accept_sec_context failed\n"); - pgsserr("handle_nullreq: gss_accept_sec_context", - maj_stat, min_stat, mech); + pgsserr("handle_krb: gss_accept_sec_context", + snd->maj_stat, snd->min_stat, mech); goto out_err; } - if (get_ids(client_name, mech, &cred, nid, lustre_svc)) { + if (get_ids(client_name, mech, &cred, snd->nid, snd->lustre_svc)) { /* get_ids() prints error msg */ - maj_stat = GSS_S_BAD_NAME; /* XXX ? */ + snd->maj_stat = GSS_S_BAD_NAME; /* XXX ? */ gss_release_name(&ignore_min_stat, &client_name); goto out_err; } @@ -512,35 +673,190 @@ handle_nullreq(FILE *f) { /* Context complete. Pass handle_seq in out_handle to use * for context lookup in the kernel. */ - out_handle.length = sizeof(handle_seq); - memcpy(out_handle.value, &handle_seq, sizeof(handle_seq)); + snd->out_handle.length = sizeof(snd->handle_seq); + memcpy(snd->out_handle.value, &snd->handle_seq, + sizeof(snd->handle_seq)); /* kernel needs ctx to calculate verifier on null response, so * must give it context before doing null call: */ - if (serialize_context_for_kernel(ctx, &ctx_token, mech)) { - printerr(0, "WARNING: handle_nullreq: " - "serialize_context_for_kernel failed\n"); - maj_stat = GSS_S_FAILURE; + if (serialize_context_for_kernel(snd->ctx, &snd->ctx_token, mech)) { + printerr(0, "WARNING: handle_krb: " + "serialize_context_for_kernel failed\n"); + snd->maj_stat = GSS_S_FAILURE; goto out_err; } /* We no longer need the gss context */ - gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok); - - do_svc_downcall(&out_handle, &cred, mech, &ctx_token); -continue_needed: - send_response(f, &in_handle, &in_tok, maj_stat, min_stat, - &out_handle, &out_tok); -out: - if (ctx_token.value != NULL) - free(ctx_token.value); - if (out_tok.value != NULL) - gss_release_buffer(&ignore_min_stat, &out_tok); + gss_delete_sec_context(&ignore_min_stat, &snd->ctx, &ignore_out_tok); + do_svc_downcall(&snd->out_handle, &cred, mech, &snd->ctx_token); + return 0; out_err: - if (ctx != GSS_C_NO_CONTEXT) - gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok); - send_response(f, &in_handle, &in_tok, maj_stat, min_stat, - &null_token, &null_token); - goto out; + if (snd->ctx != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&ignore_min_stat, &snd->ctx, + &ignore_out_tok); + + return 1; +} + +/* + * return -1 only if we detect error during reading from upcall channel, + * all other cases return 0. + */ +int handle_channel_request(FILE *f) +{ + char in_tok_buf[TOKEN_BUF_SIZE]; + char in_handle_buf[15]; + char out_handle_buf[15]; + gss_buffer_desc ctx_token = {.value = NULL}, + null_token = {.value = NULL}; + uint32_t lustre_mech; + static char *lbuf; + static int lbuflen; + static char *cp; + int get_len; + int rc = 1; + u_int32_t ignore_min_stat; + struct svc_nego_data snd = { + .in_tok.value = in_tok_buf, + .in_handle.value = in_handle_buf, + .out_handle.value = out_handle_buf, + .maj_stat = GSS_S_FAILURE, + .ctx = GSS_C_NO_CONTEXT, + }; + + printerr(2, "handling request\n"); + if (readline(fileno(f), &lbuf, &lbuflen) != 1) { + printerr(0, "WARNING: failed reading request\n"); + return -1; + } + + cp = lbuf; + + /* see rsi_request() for the format of data being input here */ + qword_get(&cp, (char *)&snd.lustre_svc, sizeof(snd.lustre_svc)); + + /* lustre_svc is the svc and gss subflavor */ + lustre_mech = (snd.lustre_svc & LUSTRE_GSS_MECH_MASK) >> + LUSTRE_GSS_MECH_SHIFT; + snd.lustre_svc = snd.lustre_svc & LUSTRE_GSS_SVC_MASK; + switch (lustre_mech) { + case LGSS_MECH_KRB5: + if (!krb_enabled) { + static time_t next_krb; + + if (time(NULL) > next_krb) { + printerr(1, "warning: Request for kerberos but " + "service support not enabled\n"); + next_krb = time(NULL) + 3600; + } + goto ignore; + } + snd.mech = &krb5oid; + break; + case LGSS_MECH_NULL: + if (!null_enabled) { + static time_t next_null; + + if (time(NULL) > next_null) { + printerr(1, "warning: Request for gssnull but " + "service support not enabled\n"); + next_null = time(NULL) + 3600; + } + goto ignore; + } + snd.mech = &nulloid; + break; + case LGSS_MECH_SK: + if (!sk_enabled) { + static time_t next_ssk; + + if (time(NULL) > next_ssk) { + printerr(1, "warning: Request for SSK but " + "service support not %s\n", +#ifdef HAVE_OPENSSL_SSK + "enabled" +#else + "included" +#endif + ); + next_ssk = time(NULL) + 3600; + } + + goto ignore; + } + snd.mech = &skoid; + break; + default: + printerr(0, "WARNING: invalid mechanism recevied: %d\n", + lustre_mech); + goto out_err; + break; + } + + qword_get(&cp, (char *)&snd.nid, sizeof(snd.nid)); + qword_get(&cp, (char *)&snd.handle_seq, sizeof(snd.handle_seq)); + qword_get(&cp, snd.nm_name, sizeof(snd.nm_name)); + printerr(2, "handling req: svc %u, nid %016llx, idx %"PRIx64" nodemap " + "%s\n", snd.lustre_svc, snd.nid, snd.handle_seq, snd.nm_name); + + get_len = qword_get(&cp, snd.in_handle.value, sizeof(in_handle_buf)); + if (get_len < 0) { + printerr(0, "WARNING: failed parsing request\n"); + goto out_err; + } + snd.in_handle.length = (size_t)get_len; + + printerr(3, "in_handle:\n"); + print_hexl(3, snd.in_handle.value, snd.in_handle.length); + + get_len = qword_get(&cp, snd.in_tok.value, sizeof(in_tok_buf)); + if (get_len < 0) { + printerr(0, "WARNING: failed parsing request\n"); + goto out_err; + } + snd.in_tok.length = (size_t)get_len; + + printerr(3, "in_tok:\n"); + print_hexl(3, snd.in_tok.value, snd.in_tok.length); + + if (snd.in_handle.length != 0) { /* CONTINUE_INIT case */ + if (snd.in_handle.length != sizeof(snd.ctx)) { + printerr(0, "WARNING: input handle has unexpected " + "length %zu\n", snd.in_handle.length); + goto out_err; + } + /* in_handle is the context id stored in the out_handle + * for the GSS_S_CONTINUE_NEEDED case below. */ + memcpy(&snd.ctx, snd.in_handle.value, snd.in_handle.length); + } + + if (lustre_mech == LGSS_MECH_KRB5) + rc = handle_krb(&snd); + else if (lustre_mech == LGSS_MECH_SK) + rc = handle_sk(&snd); + else if (lustre_mech == LGSS_MECH_NULL) + rc = handle_null(&snd); + else + printerr(0, "WARNING: Received or request for" + "subflavor that is not enabled: %d\n", lustre_mech); + +out_err: + /* Failures send a null token */ + if (rc == 0) + send_response(f, &snd.in_handle, &snd.in_tok, snd.maj_stat, + snd.min_stat, &snd.out_handle, &snd.out_tok); + else + send_response(f, &snd.in_handle, &snd.in_tok, snd.maj_stat, + snd.min_stat, &null_token, &null_token); + + /* cleanup buffers */ + if (snd.ctx_token.value != NULL) + free(ctx_token.value); + if (snd.out_tok.value != NULL) + gss_release_buffer(&ignore_min_stat, &snd.out_tok); + + /* For junk wire data just ignore */ +ignore: + return 0; }