* This base64 encoding is RFC 4648 compliant base64-url encoding.
*/
-static const char base64url_table[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
-
#define LLCRYPT_BASE64URL_CHARS(nbytes) DIV_ROUND_UP((nbytes) * 4, 3)
/**
#include <libcfs/crypto/llcrypt.h>
#include <crypto/hash.h>
#include <lustre_disk.h>
+#include <uapi/linux/lustre/lgss.h>
#ifndef CRYPTO_TFM_REQ_FORBID_WEAK_KEYS
#define CRYPTO_TFM_REQ_FORBID_WEAK_KEYS CRYPTO_TFM_REQ_WEAK_KEY
struct sptlrpc_rule_set *rset);
/*
- * reverse context
+ * context and reverse context
*/
+#define GSS_SEQ_WIN (2048)
+#define GSS_SEQ_WIN_MAIN GSS_SEQ_WIN
+#define GSS_SEQ_WIN_BACK (128)
+#define GSS_SEQ_REPACK_THRESHOLD (GSS_SEQ_WIN_MAIN / 2 + \
+ GSS_SEQ_WIN_MAIN / 4)
+
+struct gss_svc_seq_data {
+ spinlock_t ssd_lock;
+ /*
+ * highest sequence number seen so far, for main and back window
+ */
+ __u32 ssd_max_main;
+ __u32 ssd_max_back;
+ /*
+ * main and back window
+ * for i such that ssd_max - GSS_SEQ_WIN < i <= ssd_max, the i-th bit
+ * of ssd_win is nonzero iff sequence number i has been seen already.
+ */
+ unsigned long ssd_win_main[GSS_SEQ_WIN_MAIN/BITS_PER_LONG];
+ unsigned long ssd_win_back[GSS_SEQ_WIN_BACK/BITS_PER_LONG];
+};
+
+struct gss_svc_ctx {
+ struct gss_ctx *gsc_mechctx;
+ struct gss_svc_seq_data gsc_seqdata;
+ rawobj_t gsc_rvs_hdl;
+ __u32 gsc_rvs_seq;
+ uid_t gsc_uid;
+ gid_t gsc_gid;
+ uid_t gsc_mapped_uid;
+ unsigned int gsc_usr_root:1,
+ gsc_usr_mds:1,
+ gsc_usr_oss:1,
+ gsc_remote:1,
+ gsc_reverse:1;
+};
+
int sptlrpc_svc_install_rvs_ctx(struct obd_import *imp,
- struct ptlrpc_svc_ctx *ctx);
+ struct ptlrpc_svc_ctx *ctx);
int sptlrpc_cli_install_rvs_ctx(struct obd_import *imp,
- struct ptlrpc_cli_ctx *ctx);
+ struct ptlrpc_cli_ctx *ctx);
/* bulk security api */
int sptlrpc_enc_pool_add_user(void);
#ifndef _LGSS_H
#define _LGSS_H
+#ifndef __KERNEL__
+# define __USE_ISOC99 1
+# include <stdio.h> /* snprintf() */
+# include <stdlib.h> /* abs() */
+# include <inttypes.h> /* PRIu64 */
+# include <ctype.h> /* isascii() */
+# define __USE_GNU 1
+# define __USE_XOPEN2K8 1
+#else
+#include <linux/ctype.h>
+#define PRIu64 "llu"
+#define PRIx64 "llx"
+#endif /* !__KERNEL__ */
+
#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/unistd.h>
/*
* sparse kernel source annotations
__u64 reply_length;
};
+#define GSS_SOCKET_PATH "/tmp/svcgssd.socket"
+#define RSI_DOWNCALL_MAGIC 0x6d6dd62a
+#define RSI_DOWNCALL_PATH "sptlrpc/gss/rsi_info"
+#define RSI_CACHE_NAME "rsicache"
+
+struct rsi_downcall_data {
+ __u32 sid_magic;
+ __u32 sid_err;
+ __u32 sid_hash;
+ __u32 sid_maj_stat;
+ __u32 sid_min_stat;
+ __u32 sid_len;
+ __s64 sid_offset;
+ /* sid_val contains in_handle, in_token,
+ * out_handle, out_token
+ */
+ char sid_val[0];
+};
+
+/*
+ * gss_string_write() - write some string
+ *
+ * If string is empty, write single digit 0.
+ * Pad with a trailing space.
+ */
+static inline void gss_string_write(char **dst, int *dstlen, const char *src)
+{
+ char *cp = *dst;
+ int ret;
+
+ if (*dstlen < 0)
+ return;
+
+ if (!strlen(src))
+ ret = snprintf(cp, *dstlen, "0");
+ else
+ ret = snprintf(cp, *dstlen, "%s", src);
+ if (ret >= *dstlen) {
+ cp += *dstlen;
+ *dstlen = -1;
+ } else {
+ cp[ret] = ' ';
+ cp += ret + 1;
+ *dstlen -= ret + 1;
+ }
+ *dst = cp;
+}
+
+/*
+ * gss_u64_write() - write some u64
+ */
+static inline void gss_u64_write_string(char **dst, int *dstlen, uint64_t n)
+{
+ char *cp = *dst;
+ int ret;
+
+ if (*dstlen < 0)
+ return;
+
+ ret = snprintf(cp, *dstlen, "%"PRIu64, n);
+ if (ret >= *dstlen) {
+ cp += *dstlen;
+ *dstlen = -1;
+ } else {
+ cp[ret] = ' ';
+ cp += ret + 1;
+ *dstlen -= ret + 1;
+ }
+ *dst = cp;
+}
+
+/*
+ * gss_u64_write_hex() - write some u64 in hex
+ */
+static inline void gss_u64_write_hex_string(char **dst, int *dstlen, uint64_t n)
+{
+ char *cp = *dst;
+ int ret;
+
+ if (*dstlen < 0)
+ return;
+
+ ret = snprintf(cp, *dstlen, "0x%"PRIx64, n);
+ if (ret >= *dstlen) {
+ cp += *dstlen;
+ *dstlen = -1;
+ } else {
+ cp[ret] = ' ';
+ cp += ret + 1;
+ *dstlen -= ret + 1;
+ }
+ *dst = cp;
+}
+
+/*
+ * gss_buffer_write() - write some buffer
+ */
+static inline void gss_buffer_write(char **dst, int *dstlen,
+ const __u8 *src, int srclen)
+{
+ char *cp = *dst;
+ int len = *dstlen;
+ __u32 *p;
+
+ if (len < 0)
+ return;
+
+ if (len < sizeof(__u32)) {
+ len = -1;
+ goto out;
+ }
+
+ /* write size of data */
+ p = (__u32 *)cp;
+ *p = srclen;
+ cp += sizeof(__u32);
+ len -= sizeof(__u32);
+
+ if (!srclen)
+ goto out;
+
+ /* write data itself */
+ while (srclen && len) {
+ *cp++ = *src++;
+ len--;
+ srclen--;
+ }
+ if (!len && srclen)
+ len = -1;
+
+out:
+ *dst = cp;
+ *dstlen = len;
+}
+
+/*
+ * gss_u32_write() - write some u32
+ */
+static inline void gss_u32_write(char **dst, int *dstlen, __u32 val)
+{
+ char *cp = *dst;
+ int len = *dstlen;
+ __u32 *p;
+
+ if (len < 0)
+ return;
+
+ if (len < sizeof(__u32)) {
+ len = -1;
+ goto out;
+ }
+
+ p = (__u32 *)cp;
+ *p = val;
+ cp += sizeof(__u32);
+ len -= sizeof(__u32);
+
+out:
+ *dst = cp;
+ *dstlen = len;
+}
+
+static const char base64url_table[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+
+#define BASE64URL_CHARS(nbytes) ((((nbytes) * 4) + 3 - 1) / 3)
+
+/*
+ * gss_base64url_encode() - base64url-encode some binary data
+ *
+ * Encode data using base64url encoding, i.e. the "Base 64 Encoding with URL
+ * and Filename Safe Alphabet" specified by RFC 4648. '='-padding isn't used,
+ * as it's unneeded and not required by the RFC.
+ * Pad with a trailing space.
+ */
+static inline void gss_base64url_encode(char **dst, int *dstlen,
+ const __u8 *src, int srclen)
+{
+ char *cp = *dst;
+ int len = *dstlen;
+ __u32 ac = 0;
+ int bits = 0;
+ int i;
+
+ if (len < 0)
+ return;
+
+ if (!srclen)
+ return gss_string_write(dst, dstlen, "");
+
+ for (i = 0; i < srclen; i++) {
+ if (!len)
+ break;
+ ac = (ac << 8) | src[i];
+ bits += 8;
+ do {
+ bits -= 6;
+ *cp++ = base64url_table[(ac >> bits) & 0x3f];
+ len--;
+ } while (bits >= 6 && len > 0);
+ }
+ if (i < srclen) {
+ len = -1;
+ goto out;
+ }
+
+ if (bits) {
+ *cp++ = base64url_table[(ac << (6 - bits)) & 0x3f];
+ len--;
+ }
+
+ if (!len) {
+ len = -1;
+ goto out;
+ }
+ *cp++ = ' ';
+ len--;
+
+out:
+ *dst = cp;
+ *dstlen = len;
+}
+
+/*
+ * gss_base64url_decode() - base64url-decode a string
+ *
+ * Decode a string using base64url encoding, i.e. the "Base 64 Encoding with
+ * URL and Filename Safe Alphabet" specified by RFC 4648. '='-padding isn't
+ * accepted, nor are non-encoding characters such as whitespace.
+ * String end is marked with a trailing space or '\n' or '\0'.
+ */
+static inline int gss_base64url_decode(char **src, char *dst, int destsize)
+{
+ int bits = 0, len = 0;
+ char *cp = *src, *p;
+ char *bp = dst;
+ __u32 ac = 0;
+
+ while (*cp == ' ')
+ cp++;
+
+ /* the single digit 0 is inserted if field is empty */
+ if (*cp == '0' &&
+ (*(cp + 1) == ' ' || *(cp + 1) == '\n' || *(cp + 1) == '\0')) {
+ cp++;
+ goto fini;
+ }
+
+ while (isascii(*cp)) {
+ if (*cp == ' ' || *cp == '\n' || *cp == '\0')
+ break;
+
+ p = strchr(base64url_table, *cp);
+ if (len > destsize || p == NULL || *cp == '\0') {
+ len = -1;
+ goto out;
+ }
+
+ cp++;
+ ac = (ac << 6) | (p - base64url_table);
+ bits += 6;
+ if (bits >= 8) {
+ bits -= 8;
+ *bp++ = (__u8)(ac >> bits);
+ len++;
+ }
+ }
+
+ if (!isascii(*cp) || (ac & ((1 << bits) - 1))) {
+ len = -1;
+ goto out;
+ }
+
+fini:
+ *src = cp;
+out:
+ return len;
+}
+
+/*
+ * gss_string_read() - read some string
+ *
+ * An empty string is represented with the single digit 0.
+ * String end is marked with a trailing space or '\n' or '\0'.
+ */
+static inline int gss_string_read(char **src, char *dst, int destsize,
+ int allowzero)
+{
+ char *cp = *src;
+ char *bp = dst;
+ int len = 0;
+
+ while (*cp == ' ')
+ cp++;
+
+ /* the single digit 0 is inserted if field is empty */
+ if (!allowzero && *cp == '0' &&
+ (*(cp + 1) == ' ' || *(cp + 1) == '\n')) {
+ cp++;
+ goto out;
+ }
+
+ while (isascii(*cp)) {
+ if (*cp == ' ' || *cp == '\n')
+ break;
+
+ if (len >= destsize || *cp == '\0') {
+ len = -1;
+ goto out;
+ }
+
+ *(bp++) = *(cp++);
+ len++;
+ }
+
+ if (!isascii(*cp)) {
+ len = -1;
+ goto out;
+ }
+
+ *src = cp;
+
+out:
+ return len;
+}
+
+#ifndef __KERNEL__
+/*
+ * gss_u64_read() - read some u64
+ */
+static inline int gss_u64_read_string(char **src, __u64 *n)
+{
+ char buf[24];
+ char *ep;
+ int ret;
+
+ ret = gss_string_read(src, buf, sizeof(buf), 1);
+ if (ret < 0)
+ return ret;
+
+ buf[ret] = '\0';
+ *n = strtoull(buf, &ep, 0);
+ if (*ep)
+ return -1;
+
+ return 0;
+}
#endif
+
+/*
+ * gss_buffer_read() - read some buffer
+ */
+static inline int gss_buffer_read(char **src, char *dst, int destsize)
+{
+ char *cp = *src;
+ char *bp = dst;
+ __u32 *p;
+ int len, size;
+
+ /* read data size */
+ p = (__u32 *)cp;
+ len = *p;
+ cp += sizeof(__u32);
+
+ if (len > destsize) {
+ len = -1;
+ goto out;
+ }
+
+ if (!len)
+ goto fini;
+
+ /* read data itself */
+ size = len;
+ while (size && destsize) {
+ *(bp++) = *(cp++);
+ destsize--;
+ size--;
+ }
+ if (!destsize && size)
+ len = -1;
+
+fini:
+ *src = cp;
+out:
+ return len;
+}
+
+/*
+ * gss_buffer_get() - get reference to gss buffer
+ */
+static inline int gss_buffer_get(char **src, __u32 *len, __u8 **data)
+{
+ char *cp = *src;
+ __u32 *p;
+
+ /* read data size */
+ p = (__u32 *)cp;
+ *len = *p;
+ cp += sizeof(__u32);
+
+ /* point to data buf */
+ if (!*len)
+ *data = NULL;
+ else
+ *data = (__u8 *)cp;
+
+ /* move forward */
+ cp += *len;
+
+ *src = cp;
+ return *len;
+}
+
+/*
+ * gss_u32_read() - read some u32
+ */
+static inline int gss_u32_read(char **src, __u32 *val)
+{
+ char *cp = *src;
+ __u32 *p;
+
+ p = (__u32 *)cp;
+ *val = *p;
+ cp += sizeof(__u32);
+
+ *src = cp;
+ return 0;
+}
+
+#endif /* _LGSS_H */
NODEMAP_GLOBAL_IDX = 15, /* stores nodemap activation status */
};
-#define LUSTRE_NODEMAP_NAME_LENGTH 16
-
/* lu_nodemap flags */
enum nm_flag_bits {
NM_FL_ALLOW_ROOT_ACCESS = 0x1,
SELINUX_POLICY_VER_LEN + \
SELINUX_POLICY_HASH_LEN + 3)
+#define LUSTRE_NODEMAP_NAME_LENGTH 16
+
/** enums containing the types of ids contained in a nodemap
* kept so other modules (mgs, mdt, etc) can define the type
* of search easily
#include <libcfs/libcfs.h>
#include <uapi/linux/lnet/lnet-types.h>
+#include <obd.h>
+#include <lustre_sec.h>
/** \defgroup ucache ucache
*
struct md_perm *mi_perms;
};
+struct gss_rsi {
+ struct upcall_cache_entry *si_uc_entry;
+ lnet_nid_t si_nid4; /* FIXME Support larger NID */
+ char si_nm_name[LUSTRE_NODEMAP_NAME_LENGTH + 1];
+ __u32 si_lustre_svc;
+ rawobj_t si_in_handle;
+ rawobj_t si_in_token;
+ rawobj_t si_out_handle;
+ rawobj_t si_out_token;
+ int si_major_status;
+ int si_minor_status;
+};
+
struct upcall_cache_entry {
struct list_head ue_hash;
uint64_t ue_key;
time64_t ue_expire;
union {
struct md_identity identity;
+ struct gss_rsi rsi;
} u;
};
obdclass-all-objs += statfs_pack.o obdo.o obd_config.o obd_mount.o obd_sysfs.o
obdclass-all-objs += lu_object.o dt_object.o
obdclass-all-objs += cl_object.o cl_page.o cl_lock.o cl_io.o lu_ref.o
-obdclass-all-objs += linkea.o
+obdclass-all-objs += linkea.o upcall_cache.o
obdclass-all-objs += kernelcomm.o jobid.o
obdclass-all-objs += integrity.o obd_cksum.o
obdclass-all-objs += lu_tgt_descs.o lu_tgt_pool.o
obdclass-all-objs += range_lock.o interval_tree.o
@SERVER_TRUE@obdclass-all-objs += idmap.o
-@SERVER_TRUE@obdclass-all-objs += upcall_cache.o
@SERVER_TRUE@obdclass-all-objs += lprocfs_jobstats.o
@SERVER_TRUE@obdclass-all-objs += lprocfs_status_server.o
@SERVER_TRUE@obdclass-all-objs += lu_ucred.o
EXTRA_DIST += range_lock.c interval_tree.c
@SERVER_FALSE@EXTRA_DIST += idmap.c
-@SERVER_FALSE@EXTRA_DIST += upcall_cache.c
@SERVER_FALSE@EXTRA_DIST += lprocfs_jobstats.c
@SERVER_FALSE@EXTRA_DIST += lprocfs_status_server.c
@SERVER_FALSE@EXTRA_DIST += lu_ucred.c
if (rc)
GOTO(out, rc);
- entry->ue_expire = ktime_get_seconds() + cache->uc_entry_expire;
+ if (!entry->ue_expire)
+ entry->ue_expire = ktime_get_seconds() + cache->uc_entry_expire;
UC_CACHE_SET_VALID(entry);
CDEBUG(D_OTHER, "%s: created upcall cache entry %p for key %llu\n",
cache->uc_name, entry, entry->ue_key);
#ifndef __PTLRPC_GSS_GSS_API_H_
#define __PTLRPC_GSS_GSS_API_H_
+#include <uapi/linux/lustre/lgss.h>
+
struct gss_api_mech;
typedef int (*digest_hash)(
#include <crypto/hash.h>
#include <libcfs/libcfs_crypto.h>
#include <lustre_sec.h>
+#include <upcall_cache.h>
/*
* rawobj stuff
return *((__u64 *) handle->data);
}
-#define GSS_SEQ_WIN (2048)
-#define GSS_SEQ_WIN_MAIN GSS_SEQ_WIN
-#define GSS_SEQ_WIN_BACK (128)
-#define GSS_SEQ_REPACK_THRESHOLD (GSS_SEQ_WIN_MAIN / 2 + \
- GSS_SEQ_WIN_MAIN / 4)
-
-struct gss_svc_seq_data {
- spinlock_t ssd_lock;
- /*
- * highest sequence number seen so far, for main and back window
- */
- __u32 ssd_max_main;
- __u32 ssd_max_back;
- /*
- * main and back window
- * for i such that ssd_max - GSS_SEQ_WIN < i <= ssd_max, the i-th bit
- * of ssd_win is nonzero iff sequence number i has been seen already.
- */
- unsigned long ssd_win_main[GSS_SEQ_WIN_MAIN/BITS_PER_LONG];
- unsigned long ssd_win_back[GSS_SEQ_WIN_BACK/BITS_PER_LONG];
-};
-
-struct gss_svc_ctx {
- struct gss_ctx *gsc_mechctx;
- struct gss_svc_seq_data gsc_seqdata;
- rawobj_t gsc_rvs_hdl;
- __u32 gsc_rvs_seq;
- uid_t gsc_uid;
- gid_t gsc_gid;
- uid_t gsc_mapped_uid;
- unsigned int gsc_usr_root:1,
- gsc_usr_mds:1,
- gsc_usr_oss:1,
- gsc_remote:1,
- gsc_reverse:1;
-};
-
struct gss_svc_reqctx {
struct ptlrpc_svc_ctx src_base;
/*
#endif
}
+#define RSI_UPCALL_PATH "/usr/sbin/l_getauth"
+#define UC_RSICACHE_HASH_SIZE 64
+
+extern struct upcall_cache_ops rsi_upcall_cache_ops;
+extern struct upcall_cache *rsicache;
+struct gss_rsi *rsi_entry_get(struct upcall_cache *cache, struct gss_rsi *rsi);
+void rsi_entry_put(struct upcall_cache *cache, struct gss_rsi *rsi);
+void rsi_flush(struct upcall_cache *cache, int hash);
+
#endif /* __PTLRPC_GSS_GSS_INTERNAL_H_ */
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/sunrpc/cache.h>
+#include <linux/binfmts.h>
#include <net/sock.h>
+#include <linux/un.h>
#include <obd.h>
#include <obd_class.h>
return hash >> (BITS_PER_LONG - bits);
}
+/* This is a little bit of a concern but we need to make our own hash64 function
+ * as the one from the kernel seems to be buggy by returning a u32:
+ * static __always_inline u32 hash_64_generic(u64 val, unsigned int bits)
+ */
+#if BITS_PER_LONG == 64
+static __always_inline __u64 gss_hash_64(__u64 val, unsigned int bits)
+{
+ __u64 hash = val;
+ /* Sigh, gcc can't optimise this alone like it does for 32 bits. */
+ __u64 n = hash;
+
+ n <<= 18;
+ hash -= n;
+ n <<= 33;
+ hash -= n;
+ n <<= 3;
+ hash += n;
+ n <<= 3;
+ hash -= n;
+ n <<= 4;
+ hash += n;
+ n <<= 2;
+ hash += n;
+
+ /* High bits are more random, so use them. */
+ return hash >> (64 - bits);
+}
+
+static inline unsigned long hash_mem_64(char *buf, int length, int bits)
+{
+ unsigned long hash = 0;
+ unsigned long l = 0;
+ int len = 0;
+ unsigned char c;
+
+ do {
+ if (len == length) {
+ c = (char) len;
+ len = -1;
+ } else
+ c = *buf++;
+
+ l = (l << 8) | c;
+ len++;
+
+ if ((len & (BITS_PER_LONG/8-1)) == 0)
+ hash = gss_hash_64(hash^l, BITS_PER_LONG);
+ } while (len);
+
+ return hash >> (BITS_PER_LONG - bits);
+}
+#endif /* BITS_PER_LONG == 64 */
+
/****************************************
- * rpc sec init (rsi) cache *
+ * rpc sec init (rsi) cache *
****************************************/
#define RSI_HASHBITS (6)
#define RSI_HASHMAX (1 << RSI_HASHBITS)
#define RSI_HASHMASK (RSI_HASHMAX - 1)
+static void rsi_entry_init(struct upcall_cache_entry *entry,
+ void *args)
+{
+ struct gss_rsi *rsi = &entry->u.rsi;
+ struct gss_rsi *tmp = args;
+
+ rsi->si_uc_entry = entry;
+ rawobj_dup(&rsi->si_in_handle, &tmp->si_in_handle);
+ rawobj_dup(&rsi->si_in_token, &tmp->si_in_token);
+ rsi->si_out_handle = RAWOBJ_EMPTY;
+ rsi->si_out_token = RAWOBJ_EMPTY;
+
+ rsi->si_lustre_svc = tmp->si_lustre_svc;
+ rsi->si_nid4 = tmp->si_nid4;
+ memcpy(rsi->si_nm_name, tmp->si_nm_name, sizeof(tmp->si_nm_name));
+}
+
+static void __rsi_free(struct gss_rsi *rsi)
+{
+ rawobj_free(&rsi->si_in_handle);
+ rawobj_free(&rsi->si_in_token);
+ rawobj_free(&rsi->si_out_handle);
+ rawobj_free(&rsi->si_out_token);
+}
+
+static void rsi_entry_free(struct upcall_cache *cache,
+ struct upcall_cache_entry *entry)
+{
+ struct gss_rsi *rsi = &entry->u.rsi;
+
+ __rsi_free(rsi);
+}
+
+static inline int rsi_entry_hash(struct gss_rsi *rsi)
+{
+#if BITS_PER_LONG == 64
+ return hash_mem_64((char *)rsi->si_in_handle.data,
+ rsi->si_in_handle.len, RSI_HASHBITS) ^
+ hash_mem_64((char *)rsi->si_in_token.data,
+ rsi->si_in_token.len, RSI_HASHBITS);
+#else
+ return hash_mem((char *)rsi->si_in_handle.data, rsi->si_in_handle.len,
+ RSI_HASHBITS) ^
+ hash_mem((char *)rsi->si_in_token.data, rsi->si_in_token.len,
+ RSI_HASHBITS);
+#endif
+}
+
+static inline int __rsi_entry_match(rawobj_t *h1, rawobj_t *h2,
+ rawobj_t *t1, rawobj_t *t2)
+{
+ return !(rawobj_equal(h1, h2) && rawobj_equal(t1, t2));
+}
+
+static inline int rsi_entry_match(struct gss_rsi *rsi, struct gss_rsi *tmp)
+{
+ return __rsi_entry_match(&rsi->si_in_handle, &tmp->si_in_handle,
+ &rsi->si_in_token, &tmp->si_in_token);
+}
+
+/* Returns 0 to tell this is a match */
+static inline int rsi_upcall_compare(struct upcall_cache *cache,
+ struct upcall_cache_entry *entry,
+ __u64 key, void *args)
+{
+ struct gss_rsi *rsi1 = &entry->u.rsi;
+ struct gss_rsi *rsi2 = args;
+
+ return rsi_entry_match(rsi1, rsi2);
+}
+
+/* See handle_channel_request() userspace for where the upcall data is read */
+static int rsi_do_upcall(struct upcall_cache *cache,
+ struct upcall_cache_entry *entry)
+{
+ int size, len, *blen;
+ char *buffer, *bp, **bpp;
+ char *argv[] = {
+ [0] = cache->uc_upcall,
+ [1] = "-c",
+ [2] = cache->uc_name,
+ [3] = "-r",
+ [4] = NULL,
+ [5] = NULL
+ };
+ char *envp[] = {
+ [0] = "HOME=/",
+ [1] = "PATH=/sbin:/usr/sbin",
+ [2] = NULL
+ };
+ ktime_t start, end;
+ struct gss_rsi *rsi = &entry->u.rsi;
+ __u64 index = 0;
+ int rc;
+
+ ENTRY;
+ CDEBUG(D_SEC, "rsi upcall '%s' on '%s'\n",
+ cache->uc_upcall, cache->uc_name);
+
+ size = 24 + 1 + /* ue_key is uint64_t */
+ 12 + 1 + /* si_lustre_svc is __u32*/
+ 18 + 1 + /* si_nid4 is lnet_nid_t, hex with leading 0x */
+ 18 + 1 + /* index is __u64, hex with leading 0x */
+ strlen(rsi->si_nm_name) + 1 +
+ BASE64URL_CHARS(rsi->si_in_handle.len) + 1 +
+ BASE64URL_CHARS(rsi->si_in_token.len) + 1 +
+ 1 + 1; /* eol */
+ if (size > MAX_ARG_STRLEN)
+ RETURN(-E2BIG);
+ OBD_ALLOC_LARGE(buffer, size);
+ if (!buffer)
+ RETURN(-ENOMEM);
+
+ bp = buffer;
+ bpp = &bp;
+ len = size;
+ blen = &len;
+
+ /* if in_handle is null, provide kernel suggestion */
+ if (rsi->si_in_handle.len == 0)
+ index = gss_get_next_ctx_index();
+
+ /* entry->ue_key is put into args sent via upcall, so that it can be
+ * returned by userspace. This will help find cache entry at downcall,
+ * without unnecessary recomputation of the hash.
+ */
+ gss_u64_write_string(bpp, blen, entry->ue_key);
+ gss_u64_write_string(bpp, blen, rsi->si_lustre_svc);
+ gss_u64_write_hex_string(bpp, blen, rsi->si_nid4);
+ gss_u64_write_hex_string(bpp, blen, index);
+ gss_string_write(bpp, blen, (char *) rsi->si_nm_name);
+ gss_base64url_encode(bpp, blen, rsi->si_in_handle.data,
+ rsi->si_in_handle.len);
+ gss_base64url_encode(bpp, blen, rsi->si_in_token.data,
+ rsi->si_in_token.len);
+ (*bpp)[-1] = '\n';
+ (*bpp)[0] = '\0';
+
+ argv[4] = buffer;
+ down_read(&cache->uc_upcall_rwsem);
+ start = ktime_get();
+ rc = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
+ end = ktime_get();
+ up_read(&cache->uc_upcall_rwsem);
+ if (rc < 0) {
+ CERROR("%s: error invoking upcall %s %s (time %ldus): rc = %d\n",
+ cache->uc_name, argv[0], argv[2],
+ (long)ktime_us_delta(end, start), rc);
+ } else {
+ CDEBUG(D_SEC, "%s: invoked upcall %s %s (time %ldus)\n",
+ cache->uc_name, argv[0], argv[2],
+ (long)ktime_us_delta(end, start));
+ rc = 0;
+ }
+
+ OBD_FREE_LARGE(buffer, size);
+ RETURN(rc);
+}
+
+static inline int rsi_downcall_compare(struct upcall_cache *cache,
+ struct upcall_cache_entry *entry,
+ __u64 key, void *args)
+{
+ struct gss_rsi *rsi = &entry->u.rsi;
+ struct rsi_downcall_data *sid = args;
+ char *mesg = sid->sid_val;
+ rawobj_t handle, token;
+ char *p = mesg;
+ int len;
+
+ /* sid_val starts with handle and token */
+
+ /* First, handle */
+ len = gss_buffer_get(&mesg, &handle.len, &handle.data);
+ sid->sid_offset = mesg - p;
+ p = mesg;
+
+ /* Second, token */
+ len = gss_buffer_get(&mesg, &token.len, &token.data);
+ sid->sid_offset += mesg - p;
+
+ return __rsi_entry_match(&rsi->si_in_handle, &handle,
+ &rsi->si_in_token, &token);
+}
+
+static int rsi_parse_downcall(struct upcall_cache *cache,
+ struct upcall_cache_entry *entry,
+ void *args)
+{
+ struct gss_rsi *rsi = &entry->u.rsi;
+ struct rsi_downcall_data *sid = args;
+ int mlen = sid->sid_len;
+ char *mesg = sid->sid_val + sid->sid_offset;
+ char *buf = sid->sid_val;
+ int status = -EINVAL;
+ int len;
+
+ ENTRY;
+
+ if (mlen <= 0)
+ goto out;
+
+ rsi->si_major_status = sid->sid_maj_stat;
+ rsi->si_minor_status = sid->sid_min_stat;
+
+ /* in_handle and in_token have already been consumed in
+ * rsi_downcall_compare(). sid_offset gives next field.
+ */
+
+ /* out_handle */
+ len = gss_buffer_read(&mesg, buf, mlen);
+ if (len < 0)
+ goto out;
+ if (rawobj_alloc(&rsi->si_out_handle, buf, len)) {
+ status = -ENOMEM;
+ goto out;
+ }
+
+ /* out_token */
+ len = gss_buffer_read(&mesg, buf, mlen);
+ if (len < 0)
+ goto out;
+ if (rawobj_alloc(&rsi->si_out_token, buf, len)) {
+ status = -ENOMEM;
+ goto out;
+ }
+
+ entry->ue_expire = 0;
+ status = 0;
+
+out:
+ CDEBUG(D_OTHER, "rsi parse %p: %d\n", rsi, status);
+ RETURN(status);
+}
+
+struct gss_rsi *rsi_entry_get(struct upcall_cache *cache, struct gss_rsi *rsi)
+{
+ struct upcall_cache_entry *entry;
+ int hash = rsi_entry_hash(rsi);
+
+ if (!cache)
+ return ERR_PTR(-ENOENT);
+
+ entry = upcall_cache_get_entry(cache, (__u64)hash, rsi);
+ if (unlikely(!entry))
+ return ERR_PTR(-ENOENT);
+ if (IS_ERR(entry))
+ return ERR_CAST(entry);
+
+ return &entry->u.rsi;
+}
+
+void rsi_entry_put(struct upcall_cache *cache, struct gss_rsi *rsi)
+{
+ if (!cache || !rsi)
+ return;
+
+ upcall_cache_put_entry(cache, rsi->si_uc_entry);
+}
+
+void rsi_flush(struct upcall_cache *cache, int hash)
+{
+ if (hash < 0)
+ upcall_cache_flush_idle(cache);
+ else
+ upcall_cache_flush_one(cache, (__u64)hash, NULL);
+}
+
+struct upcall_cache_ops rsi_upcall_cache_ops = {
+ .init_entry = rsi_entry_init,
+ .free_entry = rsi_entry_free,
+ .upcall_compare = rsi_upcall_compare,
+ .downcall_compare = rsi_downcall_compare,
+ .do_upcall = rsi_do_upcall,
+ .parse_downcall = rsi_parse_downcall,
+};
+
+struct upcall_cache *rsicache;
+
struct rsi {
struct cache_head h;
__u32 lustre_svc;
return 0;
}
-static struct cache_deferred_req* cache_upcall_defer(struct cache_req *req)
-{
- return NULL;
-}
-static struct cache_req cache_upcall_chandle = { cache_upcall_defer };
-
int gss_svc_upcall_handle_init(struct ptlrpc_request *req,
struct gss_svc_reqctx *grctx,
struct gss_wire_ctx *gw,
rawobj_t *rvs_hdl,
rawobj_t *in_token)
{
+ struct gss_rsi rsi = { 0 }, *rsip = NULL;
struct ptlrpc_reply_state *rs;
- struct rsc *rsci = NULL;
- struct rsi *rsip = NULL, rsikey;
- wait_queue_entry_t wait;
- int replen = sizeof(struct ptlrpc_body);
- struct gss_rep_header *rephdr;
- int first_check = 1;
- int rc = SECSVC_DROP;
+ struct rsc *rsci = NULL;
+ int replen = sizeof(struct ptlrpc_body);
+ struct gss_rep_header *rephdr;
+ int rc = SECSVC_DROP, rc2;
ENTRY;
- memset(&rsikey, 0, sizeof(rsikey));
- rsikey.lustre_svc = lustre_svc;
+
+ rsi.si_lustre_svc = lustre_svc;
/* In case of MR, rq_peer is not the NID from which request is received,
* but primary NID of peer.
* So we need LNetPrimaryNID(rq_source) to match what the clients uses.
*/
LNetPrimaryNID(&req->rq_source.nid);
- rsikey.nid4 = lnet_nid_to_nid4(&req->rq_source.nid);
- nodemap_test_nid(lnet_nid_to_nid4(&req->rq_peer.nid), rsikey.nm_name,
- sizeof(rsikey.nm_name));
-
- /* duplicate context handle. for INIT it always 0 */
- if (rawobj_dup(&rsikey.in_handle, &gw->gw_handle)) {
- CERROR("fail to dup context handle\n");
- GOTO(out, rc);
- }
-
- if (rawobj_dup(&rsikey.in_token, in_token)) {
- CERROR("can't duplicate token\n");
- rawobj_free(&rsikey.in_handle);
- GOTO(out, rc);
- }
-
- rsip = rsi_lookup(&rsikey);
- rsi_free(&rsikey);
- if (!rsip) {
- CERROR("error in rsi_lookup.\n");
-
- if (!gss_pack_err_notify(req, GSS_S_FAILURE, 0))
- rc = SECSVC_COMPLETE;
-
- GOTO(out, rc);
- }
-
- cache_get(&rsip->h); /* take an extra ref */
- init_wait(&wait);
- add_wait_queue(&rsip->waitq, &wait);
-
-cache_check:
- /* Note each time cache_check() will drop a reference if return
- * non-zero. We hold an extra reference on initial rsip, but must
- * take care of following calls. */
- rc = cache_check(&rsi_cache, &rsip->h, &cache_upcall_chandle);
- switch (rc) {
- case -ETIMEDOUT:
- case -EAGAIN: {
- int valid;
-
- if (first_check) {
- first_check = 0;
-
- cache_read_lock(&rsi_cache);
- valid = test_bit(CACHE_VALID, &rsip->h.flags);
- if (valid == 0)
- set_current_state(TASK_INTERRUPTIBLE);
- cache_read_unlock(&rsi_cache);
-
- if (valid == 0) {
- unsigned long timeout;
-
- timeout = cfs_time_seconds(GSS_SVC_UPCALL_TIMEOUT);
- schedule_timeout(timeout);
- }
- cache_get(&rsip->h);
- goto cache_check;
- }
- CWARN("waited %ds timeout, drop\n", GSS_SVC_UPCALL_TIMEOUT);
- break;
+ rsi.si_nid4 = lnet_nid_to_nid4(&req->rq_source.nid);
+ nodemap_test_nid(lnet_nid_to_nid4(&req->rq_peer.nid), rsi.si_nm_name,
+ sizeof(rsi.si_nm_name));
+
+ /* Note that context handle is always 0 for for INIT. */
+ rc2 = rawobj_dup(&rsi.si_in_handle, &gw->gw_handle);
+ if (rc2) {
+ CERROR("%s: failed to duplicate context handle: rc = %d\n",
+ target->obd_name, rc2);
+ GOTO(out, rc);
}
- case -ENOENT:
- CDEBUG(D_SEC, "cache_check return ENOENT, drop\n");
- break;
- case 0:
- /* if not the first check, we have to release the extra
- * reference we just added on it. */
- if (!first_check)
- cache_put(&rsip->h, &rsi_cache);
- CDEBUG(D_SEC, "cache_check is good\n");
- break;
+
+ rc2 = rawobj_dup(&rsi.si_in_token, in_token);
+ if (rc2) {
+ CERROR("%s: failed to duplicate token: rc = %d\n",
+ target->obd_name, rc2);
+ rawobj_free(&rsi.si_in_handle);
+ GOTO(out, rc);
}
- remove_wait_queue(&rsip->waitq, &wait);
- cache_put(&rsip->h, &rsi_cache);
+ rsip = rsi_entry_get(rsicache, &rsi);
+ __rsi_free(&rsi);
+ if (IS_ERR(rsip)) {
+ CERROR("%s: failed to get entry from rsi cache (nid %s): rc = %ld\n",
+ target->obd_name,
+ libcfs_nid2str(lnet_nid_to_nid4(&req->rq_source.nid)),
+ PTR_ERR(rsip));
- if (rc)
- GOTO(out, rc = SECSVC_DROP);
+ if (!gss_pack_err_notify(req, GSS_S_FAILURE, 0))
+ rc = SECSVC_COMPLETE;
- rc = SECSVC_DROP;
- rsci = gss_svc_searchbyctx(&rsip->out_handle);
- if (!rsci) {
- CERROR("authentication failed\n");
+ GOTO(out, rc);
+ }
+ rc = SECSVC_DROP;
+ rsci = gss_svc_searchbyctx(&rsip->si_out_handle);
+ if (!rsci) {
/* gss mechanism returned major and minor code so we return
* those in error message */
- if (!gss_pack_err_notify(req, rsip->major_status,
- rsip->minor_status))
+ if (!gss_pack_err_notify(req, rsip->si_major_status,
+ rsip->si_minor_status))
rc = SECSVC_COMPLETE;
- GOTO(out, rc);
- } else {
- cache_get(&rsci->h);
- grctx->src_ctx = &rsci->ctx;
- }
+ CERROR("%s: authentication failed: rc = %d\n",
+ target->obd_name, rc);
+ GOTO(out, rc);
+ } else {
+ cache_get(&rsci->h);
+ grctx->src_ctx = &rsci->ctx;
+ }
if (gw->gw_flags & LUSTRE_GSS_PACK_KCSUM) {
grctx->src_ctx->gsc_mechctx->hash_func = gss_digest_hash;
gss_digest_hash_compat;
}
- if (rawobj_dup(&rsci->ctx.gsc_rvs_hdl, rvs_hdl)) {
- CERROR("failed duplicate reverse handle\n");
- GOTO(out, rc);
- }
+ if (rawobj_dup(&rsci->ctx.gsc_rvs_hdl, rvs_hdl)) {
+ CERROR("%s: failed duplicate reverse handle\n",
+ target->obd_name);
+ GOTO(out, rc);
+ }
- rsci->target = target;
+ rsci->target = target;
- CDEBUG(D_SEC, "server create rsc %p(%u->%s)\n",
- rsci, rsci->ctx.gsc_uid, libcfs_nidstr(&req->rq_peer.nid));
+ CDEBUG(D_SEC, "%s: server create rsc %p(%u->%s)\n",
+ target->obd_name, rsci, rsci->ctx.gsc_uid,
+ libcfs_nidstr(&req->rq_peer.nid));
- if (rsip->out_handle.len > PTLRPC_GSS_MAX_HANDLE_SIZE) {
- CERROR("handle size %u too large\n", rsip->out_handle.len);
- GOTO(out, rc = SECSVC_DROP);
- }
+ if (rsip->si_out_handle.len > PTLRPC_GSS_MAX_HANDLE_SIZE) {
+ CERROR("%s: handle size %u too large\n",
+ target->obd_name, rsip->si_out_handle.len);
+ GOTO(out, rc = SECSVC_DROP);
+ }
- grctx->src_init = 1;
- grctx->src_reserve_len = round_up(rsip->out_token.len, 4);
+ grctx->src_init = 1;
+ grctx->src_reserve_len = round_up(rsip->si_out_token.len, 4);
- rc = lustre_pack_reply_v2(req, 1, &replen, NULL, 0);
- if (rc) {
- CERROR("failed to pack reply: %d\n", rc);
- GOTO(out, rc = SECSVC_DROP);
- }
+ rc = lustre_pack_reply_v2(req, 1, &replen, NULL, 0);
+ if (rc) {
+ CERROR("%s: failed to pack reply: rc = %d\n",
+ target->obd_name, rc);
+ GOTO(out, rc = SECSVC_DROP);
+ }
- rs = req->rq_reply_state;
- LASSERT(rs->rs_repbuf->lm_bufcount == 3);
- LASSERT(rs->rs_repbuf->lm_buflens[0] >=
- sizeof(*rephdr) + rsip->out_handle.len);
- LASSERT(rs->rs_repbuf->lm_buflens[2] >= rsip->out_token.len);
+ rs = req->rq_reply_state;
+ LASSERT(rs->rs_repbuf->lm_bufcount == 3);
+ LASSERT(rs->rs_repbuf->lm_buflens[0] >=
+ sizeof(*rephdr) + rsip->si_out_handle.len);
+ LASSERT(rs->rs_repbuf->lm_buflens[2] >= rsip->si_out_token.len);
- rephdr = lustre_msg_buf(rs->rs_repbuf, 0, 0);
- rephdr->gh_version = PTLRPC_GSS_VERSION;
- rephdr->gh_flags = 0;
- rephdr->gh_proc = PTLRPC_GSS_PROC_ERR;
- rephdr->gh_major = rsip->major_status;
- rephdr->gh_minor = rsip->minor_status;
- rephdr->gh_seqwin = GSS_SEQ_WIN;
- rephdr->gh_handle.len = rsip->out_handle.len;
- memcpy(rephdr->gh_handle.data, rsip->out_handle.data,
- rsip->out_handle.len);
+ rephdr = lustre_msg_buf(rs->rs_repbuf, 0, 0);
+ rephdr->gh_version = PTLRPC_GSS_VERSION;
+ rephdr->gh_flags = 0;
+ rephdr->gh_proc = PTLRPC_GSS_PROC_ERR;
+ rephdr->gh_major = rsip->si_major_status;
+ rephdr->gh_minor = rsip->si_minor_status;
+ rephdr->gh_seqwin = GSS_SEQ_WIN;
+ rephdr->gh_handle.len = rsip->si_out_handle.len;
+ memcpy(rephdr->gh_handle.data, rsip->si_out_handle.data,
+ rsip->si_out_handle.len);
- memcpy(lustre_msg_buf(rs->rs_repbuf, 2, 0), rsip->out_token.data,
- rsip->out_token.len);
+ memcpy(lustre_msg_buf(rs->rs_repbuf, 2, 0), rsip->si_out_token.data,
+ rsip->si_out_token.len);
- rs->rs_repdata_len = lustre_shrink_msg(rs->rs_repbuf, 2,
- rsip->out_token.len, 0);
+ rs->rs_repdata_len = lustre_shrink_msg(rs->rs_repbuf, 2,
+ rsip->si_out_token.len, 0);
- rc = SECSVC_OK;
+ rc = SECSVC_OK;
out:
- /* it looks like here we should put rsip also, but this mess up
- * with NFS cache mgmt code... FIXME
- * something like:
- * if (rsip)
- * rsi_put(&rsip->h, &rsi_cache); */
-
+ if (!IS_ERR_OR_NULL(rsip))
+ rsi_entry_put(rsicache, rsip);
if (rsci) {
/* if anything went wrong, we don't keep the context too */
if (rc != SECSVC_OK)
set_bit(CACHE_NEGATIVE, &rsci->h.flags);
else
- CDEBUG(D_SEC, "create rsc with idx %#llx\n",
+ CDEBUG(D_SEC, "%s: create rsc with idx %#llx\n",
+ target->obd_name,
gss_handle_to_u64(&rsci->handle));
COMPAT_RSC_PUT(&rsci->h, &rsc_cache);
rsc->h.expiry_time = 1;
}
+/* Wait for userspace daemon to open socket, approx 1.5 s.
+ * If socket is not open, upcall requests might fail.
+ */
+static int check_gssd_socket(void)
+{
+ struct sockaddr_un *sun;
+ struct socket *sock;
+ int tries = 0;
+ int err;
+
+#ifdef HAVE_SOCK_CREATE_KERN_USE_NET
+ err = sock_create_kern(current->nsproxy->net_ns,
+ AF_UNIX, SOCK_STREAM, 0, &sock);
+#else
+ err = sock_create_kern(AF_UNIX, SOCK_STREAM, 0, &sock);
+#endif
+ if (err < 0) {
+ CDEBUG(D_SEC, "Failed to create socket: %d\n", err);
+ return err;
+ }
+
+ OBD_ALLOC(sun, sizeof(*sun));
+ if (!sun) {
+ sock_release(sock);
+ return -ENOMEM;
+ }
+ memset(sun, 0, sizeof(*sun));
+ sun->sun_family = AF_UNIX;
+ strncpy(sun->sun_path, GSS_SOCKET_PATH, sizeof(sun->sun_path));
+
+ /* Try to connect to the socket */
+ while (tries++ < 6) {
+ err = kernel_connect(sock, (struct sockaddr *)sun,
+ sizeof(*sun), 0);
+ if (!err)
+ break;
+ schedule_timeout_uninterruptible(cfs_time_seconds(1) / 4);
+ }
+ if (err < 0)
+ CDEBUG(D_SEC, "Failed to connect to socket: %d\n", err);
+ else
+ kernel_sock_shutdown(sock, SHUT_RDWR);
+
+ sock_release(sock);
+ OBD_FREE(sun, sizeof(*sun));
+ return err;
+}
+
int __init gss_init_svc_upcall(void)
{
- int i, rc;
+ int rc;
/*
* this helps reducing context index confliction. after server reboot,
get_random_bytes(&__ctx_index, sizeof(__ctx_index));
#ifdef HAVE_CACHE_HEAD_HLIST
- for (i = 0; i < rsi_cache.hash_size; i++)
- INIT_HLIST_HEAD(&rsi_cache.hash_table[i]);
+ for (rc = 0; rc < rsi_cache.hash_size; rc++)
+ INIT_HLIST_HEAD(&rsi_cache.hash_table[rc]);
#endif
rc = cache_register_net(&rsi_cache, &init_net);
if (rc != 0)
return rc;
#ifdef HAVE_CACHE_HEAD_HLIST
- for (i = 0; i < rsc_cache.hash_size; i++)
- INIT_HLIST_HEAD(&rsc_cache.hash_table[i]);
+ for (rc = 0; rc < rsc_cache.hash_size; rc++)
+ INIT_HLIST_HEAD(&rsc_cache.hash_table[rc]);
#endif
rc = cache_register_net(&rsc_cache, &init_net);
if (rc != 0) {
return rc;
}
- /* FIXME this looks stupid. we intend to give lsvcgssd a chance to open
- * the init upcall channel, otherwise there's big chance that the first
- * upcall issued before the channel be opened thus nfsv4 cache code will
- * drop the request directly, thus lead to unnecessary recovery time.
- * Here we wait at minimum 1.5 seconds.
- */
- for (i = 0; i < 6; i++) {
- if (channel_users(&rsi_cache) > 0)
- break;
- schedule_timeout_uninterruptible(cfs_time_seconds(1) / 4);
+ rsicache = upcall_cache_init(RSI_CACHE_NAME, RSI_UPCALL_PATH,
+ UC_RSICACHE_HASH_SIZE,
+ 3600, /* entry expire: 1 h */
+ 20, /* acquire expire: 20 s */
+ &rsi_upcall_cache_ops);
+ if (IS_ERR(rsicache)) {
+ rc = PTR_ERR(rsicache);
+ rsicache = NULL;
+ return rc;
}
- if (channel_users(&rsi_cache) == 0)
+ if (check_gssd_socket())
CDEBUG(D_SEC,
- "Init channel is not opened by lsvcgssd, following request might be dropped until lsvcgssd is active\n");
+ "Init channel not opened by lsvcgssd, GSS might not work on server side until daemon is active\n");
return 0;
}
cache_purge(&rsc_cache);
cache_unregister_net(&rsc_cache, &init_net);
+
+ upcall_cache_cleanup(rsicache);
}
LPROC_SEQ_FOPS(sptlrpc_gss_check_upcall_ns);
#endif /* HAVE_GSS_KEYRING */
+static int rsi_upcall_seq_show(struct seq_file *m,
+ void *data)
+{
+ down_read(&rsicache->uc_upcall_rwsem);
+ seq_printf(m, "%s\n", rsicache->uc_upcall);
+ up_read(&rsicache->uc_upcall_rwsem);
+
+ return 0;
+}
+
+static ssize_t rsi_upcall_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ int rc;
+
+ if (count >= UC_CACHE_UPCALL_MAXPATH) {
+ CERROR("%s: rsi upcall too long\n", rsicache->uc_name);
+ return -EINVAL;
+ }
+
+ /* Remove any extraneous bits from the upcall (e.g. linefeeds) */
+ down_write(&rsicache->uc_upcall_rwsem);
+ rc = sscanf(buffer, "%s", rsicache->uc_upcall);
+ up_write(&rsicache->uc_upcall_rwsem);
+
+ if (rc != 1) {
+ CERROR("%s: invalid rsi upcall provided\n", rsicache->uc_name);
+ return -EINVAL;
+ }
+
+ CDEBUG(D_CONFIG, "%s: rsi upcall set to %s\n", rsicache->uc_name,
+ rsicache->uc_upcall);
+
+ return count;
+}
+LPROC_SEQ_FOPS(rsi_upcall);
+
+static ssize_t lprocfs_rsi_flush_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, void *data)
+{
+ int hash, rc;
+
+ rc = kstrtoint_from_user(buffer, count, 0, &hash);
+ if (rc)
+ return rc;
+
+ rsi_flush(rsicache, hash);
+ return count;
+}
+LPROC_SEQ_FOPS_WR_ONLY(gss, rsi_flush);
+
+static ssize_t lprocfs_rsi_info_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, void *data)
+{
+ struct rsi_downcall_data *param;
+ int size = sizeof(*param), rc, checked = 0;
+
+again:
+ if (count < size) {
+ CERROR("%s: invalid data count = %lu, size = %d\n",
+ rsicache->uc_name, (unsigned long)count, size);
+ return -EINVAL;
+ }
+
+ OBD_ALLOC_LARGE(param, size);
+ if (param == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(param, buffer, size)) {
+ CERROR("%s: bad rsi data\n", rsicache->uc_name);
+ GOTO(out, rc = -EFAULT);
+ }
+
+ if (checked == 0) {
+ checked = 1;
+ if (param->sid_magic != RSI_DOWNCALL_MAGIC) {
+ CERROR("%s: rsi downcall bad params\n",
+ rsicache->uc_name);
+ GOTO(out, rc = -EINVAL);
+ }
+
+ rc = param->sid_len; /* save sid_len */
+ OBD_FREE_LARGE(param, size);
+ size = offsetof(struct rsi_downcall_data, sid_val[rc]);
+ goto again;
+ }
+
+ rc = upcall_cache_downcall(rsicache, param->sid_err,
+ param->sid_hash, param);
+
+out:
+ if (param != NULL)
+ OBD_FREE_LARGE(param, size);
+
+ return rc ? rc : count;
+}
+LPROC_SEQ_FOPS_WR_ONLY(gss, rsi_info);
+
+static int rsi_entry_expire_seq_show(struct seq_file *m,
+ void *data)
+{
+ seq_printf(m, "%lld\n", rsicache->uc_entry_expire);
+ return 0;
+}
+
+static ssize_t rsi_entry_expire_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ time64_t val;
+ int rc;
+
+ rc = kstrtoll_from_user(buffer, count, 10, &val);
+ if (rc)
+ return rc;
+
+ if (val < 0)
+ return -ERANGE;
+
+ rsicache->uc_entry_expire = val;
+
+ return count;
+}
+LPROC_SEQ_FOPS(rsi_entry_expire);
+
+static int rsi_acquire_expire_seq_show(struct seq_file *m,
+ void *data)
+{
+ seq_printf(m, "%lld\n", rsicache->uc_acquire_expire);
+ return 0;
+}
+
+static ssize_t rsi_acquire_expire_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ time64_t val;
+ int rc;
+
+ rc = kstrtoll_from_user(buffer, count, 10, &val);
+ if (rc)
+ return rc;
+
+ if (val < 0 || val > INT_MAX)
+ return -ERANGE;
+
+ rsicache->uc_acquire_expire = val;
+
+ return count;
+}
+LPROC_SEQ_FOPS(rsi_acquire_expire);
+
static struct ldebugfs_vars gss_debugfs_vars[] = {
{ .name = "replays",
.fops = &gss_proc_oos_fops },
{ .name = "gss_check_upcall_ns",
.fops = &sptlrpc_gss_check_upcall_ns_fops },
#endif
+ { .name = "rsi_upcall",
+ .fops = &rsi_upcall_fops },
+ { .name = "rsi_flush",
+ .fops = &gss_rsi_flush_fops },
+ { .name = "rsi_info",
+ .fops = &gss_rsi_info_fops },
+ { .name = "rsi_entry_expire",
+ .fops = &rsi_entry_expire_fops },
+ { .name = "rsi_acquire_expire",
+ .fops = &rsi_acquire_expire_fops },
{ NULL }
};
#include <uapi/linux/lustre/lustre_access_log.h>
#include <uapi/linux/lustre/lustre_lfsck_user.h>
#include <uapi/linux/lustre/lustre_cfg.h>
+#include <uapi/linux/lustre/lgss.h>
#include "ptlrpc_internal.h"
(long long)(int)sizeof(((struct llog_changelog_user_rec *)0)->cur_tail));
#endif /* HAVE_SERVER_SUPPORT */
+ /* Checks for struct rsi_downcall_data */
+ LASSERTF((int)sizeof(struct rsi_downcall_data) == 32, "found %lld\n",
+ (long long)(int)sizeof(struct rsi_downcall_data));
+ LASSERTF((int)offsetof(struct rsi_downcall_data, sid_magic) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct rsi_downcall_data, sid_magic));
+ LASSERTF((int)sizeof(((struct rsi_downcall_data *)0)->sid_magic) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct rsi_downcall_data *)0)->sid_magic));
+ LASSERTF((int)offsetof(struct rsi_downcall_data, sid_err) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct rsi_downcall_data, sid_err));
+ LASSERTF((int)sizeof(((struct rsi_downcall_data *)0)->sid_err) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct rsi_downcall_data *)0)->sid_err));
+ LASSERTF((int)offsetof(struct rsi_downcall_data, sid_hash) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct rsi_downcall_data, sid_hash));
+ LASSERTF((int)sizeof(((struct rsi_downcall_data *)0)->sid_hash) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct rsi_downcall_data *)0)->sid_hash));
+ LASSERTF((int)offsetof(struct rsi_downcall_data, sid_maj_stat) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct rsi_downcall_data, sid_maj_stat));
+ LASSERTF((int)sizeof(((struct rsi_downcall_data *)0)->sid_maj_stat) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct rsi_downcall_data *)0)->sid_maj_stat));
+ LASSERTF((int)offsetof(struct rsi_downcall_data, sid_min_stat) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct rsi_downcall_data, sid_min_stat));
+ LASSERTF((int)sizeof(((struct rsi_downcall_data *)0)->sid_min_stat) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct rsi_downcall_data *)0)->sid_min_stat));
+ LASSERTF((int)offsetof(struct rsi_downcall_data, sid_len) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct rsi_downcall_data, sid_len));
+ LASSERTF((int)sizeof(((struct rsi_downcall_data *)0)->sid_len) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct rsi_downcall_data *)0)->sid_len));
+ LASSERTF((int)offsetof(struct rsi_downcall_data, sid_offset) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct rsi_downcall_data, sid_offset));
+ LASSERTF((int)sizeof(((struct rsi_downcall_data *)0)->sid_offset) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct rsi_downcall_data *)0)->sid_offset));
+ LASSERTF((int)offsetof(struct rsi_downcall_data, sid_val) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct rsi_downcall_data, sid_val));
+ LASSERTF((int)sizeof(((struct rsi_downcall_data *)0)->sid_val) == 0, "found %lld\n",
+ (long long)(int)sizeof(((struct rsi_downcall_data *)0)->sid_val));
+
/* Checks for struct llog_gen */
LASSERTF((int)sizeof(struct llog_gen) == 16, "found %lld\n",
(long long)(int)sizeof(struct llog_gen));
[ -z "$LNETCTL" ] && skip "without lnetctl support." && return
local_mode && skip "in local mode."
+ if $SHARED_KEY; then
+ skip "Conflicting test with SSK"
+ fi
+
# save mds failover nids for restore at cleanup
failover_mds1=$(do_facet mds1 $TUNEFS --dryrun $(mdsdevname 1))
if [ -n "$failover_mds1" ]; then
set_rule _mgs any any null
# stop gss daemon on MGS
- if ! combined_mgs_mds ; then
- send_sigint $mgs_HOST lsvcgssd
+ send_sigint $mgs_HOST lsvcgssd
+
+ # re-start gss daemon on MDS if necessary
+ if combined_mgs_mds ; then
+ start_gss_daemons $mds_HOST "$LSVCGSSD -vvv -s -m -o -z"
fi
# re-mount client
umount_client $MOUNT || error "umount $MOUNT failed"
fi
+ # kill daemon on MGS to start afresh
+ send_sigint $mgs_HOST lsvcgssd
+
# start gss daemon on MGS
if combined_mgs_mds ; then
- send_sigint $mds_HOST lsvcgssd
+ start_gss_daemons $mgs_HOST "$LSVCGSSD -vvv -s -g -m -o -z"
+ else
+ start_gss_daemons $mgs_HOST "$LSVCGSSD -vvv -s -g"
fi
- start_gss_daemons $mgs_HOST "$LSVCGSSD -vvv -s -g"
# add mgs key type and MGS NIDs in key on MGS
do_nodes $mgs_HOST "$LGSS_SK -t mgs,server -g $MGSNID -m \
zconf_umount_clients ${clients_arr[0]} $MOUNT
# stop gss daemon on MGS
- if ! combined_mgs_mds ; then
- send_sigint $mgs_HOST lsvcgssd
+ send_sigint $mgs_HOST lsvcgssd
+
+ # re-start gss daemon on MDS if necessary
+ if combined_mgs_mds ; then
+ start_gss_daemons $mds_HOST "$LSVCGSSD -vvv -s -m -o -z"
fi
# re-mount client
umount_client $MOUNT || error "umount $MOUNT failed"
fi
+ # kill daemon on MGS to start afresh
+ send_sigint $mgs_HOST lsvcgssd
+
# start gss daemon on MGS
if combined_mgs_mds ; then
- send_sigint $mds_HOST lsvcgssd
+ start_gss_daemons $mgs_HOST "$LSVCGSSD -vvv -s -g -m -o -z"
+ else
+ start_gss_daemons $mgs_HOST "$LSVCGSSD -vvv -s -g"
fi
- start_gss_daemons $mgs_HOST "$LSVCGSSD -vvv -s -g"
# add mgs key type and MGS NIDs in key on MGS
do_nodes $mgs_HOST "$LGSS_SK -t mgs,server -g $MGSNID -m \
[ ! -f "$LSOM_SYNC" ] &&
export LSOM_SYNC=$(which llsom_sync 2> /dev/null)
[ -z "$LSOM_SYNC" ] && export LSOM_SYNC="/usr/sbin/llsom_sync"
+ export L_GETAUTH=${L_GETAUTH:-"$LUSTRE/utils/gss/l_getauth"}
+ [ ! -f "$L_GETAUTH" ] && export L_GETAUTH=$(which l_getauth 2> /dev/null)
export LSVCGSSD=${LSVCGSSD:-"$LUSTRE/utils/gss/lsvcgssd"}
[ ! -f "$LSVCGSSD" ] && export LSVCGSSD=$(which lsvcgssd 2> /dev/null)
export KRB5DIR=${KRB5DIR:-"/usr/kerberos"}
check_gss_daemon_nodes() {
local list=$1
local dname=$2
+ local loopmax=10
+ local loop
+ local node
+ local ret
do_nodesv $list "num=\\\$(ps -o cmd -C $dname | grep $dname | wc -l);
if [ \\\"\\\$num\\\" -ne 1 ]; then
echo \\\$num instance of $dname;
exit 1;
fi; "
+ ret=$?
+ (( $ret == 0 )) || return $ret
+
+ for node in ${list//,/ }; do
+ loop=0
+ while (( $loop < $loopmax )); do
+ do_nodesv $node "$L_GETAUTH -d"
+ ret=$?
+ (( $ret == 0 )) && break
+ loop=$((loop + 1))
+ sleep 5
+ done
+ (( $loop < $loopmax )) || return 1
+ done
+ return 0
}
check_gss_daemon_facet() {
if [ "$nodes" ] && [ "$daemon" ] ; then
echo "Starting gss daemon on nodes: $nodes"
do_nodes $nodes "$daemon" "$options" || return 8
+ check_gss_daemon_nodes $nodes lsvcgssd || return 9
return 0
fi
local clients=${CLIENTS:-$HOSTNAME}
- # wait daemons entering "stable" status
- sleep 5
-
#
# check daemons are running
#
lctl set_param -n \
sptlrpc.gss.lgss_keyring.debug_level=$LGSS_KEYRING_DEBUG"
fi
+
+ do_nodesv $(comma_list $(all_server_nodes)) \
+ "$LCTL set_param sptlrpc.gss.rsi_upcall=$L_GETAUTH"
}
cleanup_gss() {
/l_idmap
/lgss_keyring
/lgss_sk
+/l_getauth
sbin_PROGRAMS = l_idmap
if GSS_KEYRING
-sbin_PROGRAMS += lsvcgssd lgss_keyring
+sbin_PROGRAMS += lsvcgssd lgss_keyring l_getauth
if GSS_SSK
sbin_PROGRAMS += lgss_sk
endif
l_idmap_LDADD = $(top_builddir)/lustre/utils/liblustreapi.la $(KRBLIBS)
+l_getauth_SOURCES = \
+ l_getauth.c \
+ lsupport.c \
+ err_util.c \
+ lgss_utils.c \
+ lgss_krb5_utils.c \
+ lgss_null_utils.c \
+ lgss_utils.h \
+ lgss_krb5_utils.h \
+ lsupport.h
+
+l_getauth_CFLAGS = $(AM_CFLAGS) $(CFLAGS) $(KRBCFLAGS) -D _NEW_BUILD_
+l_getauth_LDADD = $(top_builddir)/lustre/utils/liblustreapi.la $(GSSAPI_LIBS) $(KRBLIBS) -lm -lkeyutils
+l_getauth_LDFLAGS = $(KRBLDFLAGS)
+
lgss_keyring_SOURCES = \
lgss_keyring.c \
context.c \
lgss_keyring_LDFLAGS = $(KRBLDFLAGS)
if GSS_SSK
+l_getauth_SOURCES += sk_utils.c sk_utils.h lgss_sk_utils.c
+l_getauth_LDADD += -lcrypto -lssl
+
lgss_keyring_SOURCES += sk_utils.c sk_utils.h lgss_sk_utils.c
lgss_keyring_LDADD += -lcrypto -lssl
--- /dev/null
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <glob.h>
+#include <getopt.h>
+
+#include "lsupport.h"
+#include "err_util.h"
+
+int main(int argc, char **argv)
+{
+ int local_socket;
+ struct sockaddr_un addr;
+ ssize_t bytes_sent;
+ char *auth_req = NULL, *cachename = NULL;
+ ssize_t req_len;
+ int opt, debug = 0, rc = 0;
+
+ /* Parameters received from kernel (see rsi_do_upcall()):
+ * -c <cache name> -r <auth request> -d
+ * -d checks connection to lsvcgssd daemon
+ */
+
+ static struct option long_opts[] = {
+ { .name = "cache", .has_arg = required_argument, .val = 'c'},
+ { .name = "debug", .has_arg = no_argument, .val = 'd'},
+ { .name = "authreq", .has_arg = required_argument, .val = 'r'},
+ { .name = NULL, } };
+
+ /* init gss logger for foreground (stderr) or background (syslog) */
+ initerr(NULL, LL_MAX, isatty(STDOUT_FILENO));
+
+ while ((opt = getopt_long(argc, argv, "c:dr:",
+ long_opts, NULL)) != EOF) {
+ switch (opt) {
+ case 'c':
+ cachename = optarg;
+ break;
+ case 'd':
+ debug = 1;
+ goto connect;
+ case 'r':
+ auth_req = optarg;
+ break;
+ default:
+ printerr(LL_ERR, "error: unknown option: '%c'\n", opt);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (optind != argc) {
+ printerr(LL_ERR,
+ "error: extraneous arguments provided, check usage\n");
+ return EXIT_FAILURE;
+ }
+
+ if (!cachename || !auth_req) {
+ printerr(LL_ERR, "error: missing arguments, check usage\n");
+ return EXIT_FAILURE;
+ }
+
+ if (strcmp(cachename, RSI_CACHE_NAME) != 0) {
+ printerr(LL_ERR, "invalid cache name %s\n", cachename);
+ return EXIT_FAILURE;
+ }
+
+ req_len = strlen(auth_req);
+
+connect:
+ /* Send auth request to lsvcgssd via a socket. */
+ local_socket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (local_socket == -1) {
+ rc = -errno;
+ printerr(LL_ERR, "cannot create socket: %d\n", rc);
+ return rc;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, GSS_SOCKET_PATH, sizeof(addr.sun_path) - 1);
+
+ if (connect(local_socket, (struct sockaddr *)&addr,
+ sizeof(addr)) == -1) {
+ rc = -errno;
+ printerr(LL_ERR, "cannot connect to socket: %d\n", rc);
+ goto out;
+ }
+
+ if (debug)
+ goto out;
+
+ bytes_sent = write(local_socket, auth_req, req_len);
+ if (bytes_sent < 0) {
+ rc = -errno;
+ printerr(LL_ERR, "write failed: %d\n", rc);
+ } else if (bytes_sent != req_len) {
+ printerr(LL_ERR, "partial write %zu vs. %zu\n",
+ bytes_sent, req_len);
+ rc = -EMSGSIZE;
+ }
+
+out:
+ close(local_socket);
+ return rc;
+}
#include <unistd.h>
#include <stdbool.h>
#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
#include <libcfs/util/list.h>
#include <linux/lnet/lnet-types.h>
#include <linux/lnet/nidstr.h>
+#include <uapi/linux/lustre/lgss.h>
#include <krb5.h>
int lookup_mapping(char *princ, lnet_nid_t nid, uid_t *uid);
int gss_get_realm(char *realm);
+/*
+ * gss_buffer_write() - write some buffer to stream
+ */
+static inline int gss_buffer_write_file(FILE *f, void *value, size_t length)
+{
+ int rc = 0;
+
+ /* write size of data */
+ if (fwrite(&length, sizeof(__u32), 1, f) != 1) {
+ rc = -errno;
+ goto out;
+ }
+
+ if (!length || !value)
+ goto out;
+
+ /* write data itself */
+ if (fwrite(value, length, 1, f) != 1) {
+ rc = -errno;
+ goto out;
+ }
+
+out:
+ return rc;
+}
+
#endif /* __LSUPPORT_H__ */
cleanup_mapping();
/* cleanup allocated strings for realms */
gssd_cleanup_realms();
+ /* remove socket */
+ unlink(GSS_SOCKET_PATH);
printerr(LL_WARN, "exiting on signal %d\n", signal);
exit(1);
}
fprintf(fp, "usage: %s [ -fnvmogkRsz ]\n",
progname);
fprintf(stderr, "-f - Run in foreground\n");
- fprintf(stderr, "-n - Don't establish kerberos credentials\n");
- fprintf(stderr, "-v - Verbosity\n");
- fprintf(stderr, "-m - Service MDS\n");
- fprintf(stderr, "-o - Service OSS\n");
fprintf(stderr, "-g - Service MGS\n");
fprintf(stderr, "-k - Enable kerberos support\n");
+ fprintf(stderr, "-m - Service MDS\n");
+ fprintf(stderr, "-n - Don't establish kerberos credentials\n");
+ fprintf(stderr, "-o - Service OSS\n");
fprintf(stderr, "-R REALM - Kerberos Realm to use, instead of default\n");
#ifdef HAVE_OPENSSL_SSK
fprintf(stderr, "-s - Enable shared secret key support\n");
#endif
+ fprintf(stderr, "-v - Verbosity\n");
fprintf(stderr, "-z - Enable gssnull support\n");
exit(fp == stderr);
#include <gssapi/gssapi.h>
#include "lsupport.h"
-int handle_channel_request(FILE *f);
+int handle_channel_request(int fd);
void svcgssd_run(void);
int gssd_prepare_creds(int must_srv_mgs, int must_srv_mds, int must_srv_oss);
gss_cred_id_t gssd_select_svc_cred(int lustre_svc);
#include <sys/param.h>
#include <sys/socket.h>
+#include <sys/un.h>
#include <sys/poll.h>
#include <sys/types.h>
#include <sys/stat.h>
/* For nanosleep() */
#include <time.h>
+#include "cacheio.h"
#include "svcgssd.h"
+#include "lsupport.h"
#include "err_util.h"
#include "sk_utils.h"
#define MAX_ALLOWED_TIME_FOR_PRIME 400000
int sk_dh_checks;
-/*
- * nfs4 in-kernel cache implementation make upcall failed directly
- * if there's no listener detected. so here we should keep the init
- * channel file open as possible as we can.
- *
- * unfortunately the proc doesn't support dir change notification.
- * and when an entry get unlinked, we only got POLLIN event once,
- * it's the only oppotunity we can close the file and startover.
- */
-void
-svcgssd_run()
+void svcgssd_run(void)
{
- static const char gss_rpc_channel_path[] =
- "/proc/net/rpc/auth.sptlrpc.init/channel";
- int ret;
- FILE *f = NULL;
- struct pollfd pollfd;
- struct timespec halfsec = { .tv_sec = 0, .tv_nsec = 500000000 };
+ int local_socket, remote_socket;
+ struct sockaddr_un addr;
+ bool retried = false;
+ int ret = 0;
if (sk_enabled) {
#if !defined(HAVE_OPENSSL_EVP_PKEY) && OPENSSL_VERSION_NUMBER >= 0x1010103fL
load_mapping();
}
- while (1) {
- int save_err;
-
- while (f == NULL) {
- f = fopen(gss_rpc_channel_path, "r+");
- if (f == NULL) {
- printerr(LL_TRACE, "failed to open %s: %s\n",
- gss_rpc_channel_path, strerror(errno));
- nanosleep(&halfsec, NULL);
- } else {
- printerr(LL_WARN, "successfully open %s\n",
- gss_rpc_channel_path);
- break;
- }
+again:
+ local_socket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (local_socket == -1) {
+ printerr(LL_ERR, "unable to create socket: %d\n", -errno);
+ ret = 1;
+ goto out_close;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, GSS_SOCKET_PATH, sizeof(addr.sun_path) - 1);
+
+ if (bind(local_socket, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ if (!retried) {
+ retried = true;
+ unlink(GSS_SOCKET_PATH);
+ close(local_socket);
+ goto again;
}
- pollfd.fd = fileno(f);
- pollfd.events = POLLIN;
-
- pollfd.revents = 0;
- ret = poll(&pollfd, 1, 1000);
- save_err = errno;
-
- if (ret < 0) {
- printerr(LL_ERR, "error return from poll: %s\n",
- strerror(save_err));
- fclose(f);
- f = NULL;
- } else if (ret == 0) {
- printerr(LL_TRACE, "poll timeout\n");
- } else {
- if (ret != 1) {
- printerr(LL_ERR,
- "bug: unexpected poll return %d\n",
- ret);
- exit(1);
- }
- if (pollfd.revents & POLLIN) {
- if (handle_channel_request(f) < 0) {
- fclose(f);
- f = NULL;
- }
- }
+ printerr(LL_ERR, "unable to bind socket: %d\n", -errno);
+ ret = 1;
+ goto out_close;
+ }
+
+ if (listen(local_socket, 10) == -1) {
+ printerr(LL_ERR, "unable to listen on socket: %d\n", -errno);
+ ret = 1;
+ goto out;
+ }
+
+ while (1) {
+ remote_socket = accept(local_socket, NULL, NULL);
+ if (remote_socket == -1) {
+ printerr(LL_TRACE, "accept on socket ret %d\n", -errno);
+ continue;
}
+
+ ret = handle_channel_request(remote_socket);
+ printerr(LL_DEBUG, "handle_channel_request ret %d\n", ret);
+ close(remote_socket);
}
+
+out:
+ unlink(GSS_SOCKET_PATH);
+out_close:
+ if (local_socket >= 0)
+ close(local_socket);
+ exit(ret);
}
#include "sk_utils.h"
#include <sys/time.h>
#include <gssapi/gssapi_krb5.h>
+#include <libcfs/util/param.h>
#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
-
struct svc_cred {
uint32_t cr_remote;
uint32_t cr_usr_root;
#define RPCSEC_GSS_SEQ_WIN 5
-static int
-send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
- u_int32_t maj_stat, u_int32_t min_stat,
- gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
+static int send_response(int auth_res, uint64_t hash,
+ gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
+ u_int32_t maj_stat, u_int32_t min_stat,
+ gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
{
- char buf[2 * TOKEN_BUF_SIZE];
- char *bp = buf;
- int blen = sizeof(buf);
- /* XXXARG: */
- int g;
+ struct rsi_downcall_data *rsi_dd;
+ int blen, fd, size, rc = 0;
+ glob_t path;
+ char *bp;
printerr(LL_INFO, "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, 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(LL_ERR, "ERROR: %s: message too long\n", __func__);
- return -1;
+
+ size = in_handle->length + sizeof(__u32) +
+ in_token->length + sizeof(__u32) +
+ sizeof(__u32) + sizeof(__u32);
+ if (!auth_res)
+ size += out_handle->length + out_token->length;
+ blen = size;
+
+ size += offsetof(struct rsi_downcall_data, sid_val[0]);
+ rsi_dd = calloc(1, size);
+ if (!rsi_dd) {
+ printerr(LL_ERR, "malloc downcall data (%d) failed\n", size);
+ return -ENOMEM;
+ }
+ rsi_dd->sid_magic = RSI_DOWNCALL_MAGIC;
+ rsi_dd->sid_hash = hash;
+ rsi_dd->sid_maj_stat = maj_stat;
+ rsi_dd->sid_min_stat = min_stat;
+
+ bp = rsi_dd->sid_val;
+ gss_buffer_write(&bp, &blen, in_handle->value, in_handle->length);
+ gss_buffer_write(&bp, &blen, in_token->value, in_token->length);
+ if (!auth_res) {
+ gss_buffer_write(&bp, &blen, out_handle->value,
+ out_handle->length);
+ gss_buffer_write(&bp, &blen, out_token->value,
+ out_token->length);
+ } else {
+ rsi_dd->sid_err = -EACCES;
+ gss_buffer_write(&bp, &blen, NULL, 0);
+ gss_buffer_write(&bp, &blen, NULL, 0);
}
- g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
- if (g == -1) {
+ if (blen < 0) {
+ printerr(LL_ERR, "ERROR: %s: message too long > %d\n",
+ __func__, size);
+ rc = -EMSGSIZE;
+ goto out;
+ }
+ rsi_dd->sid_len = bp - rsi_dd->sid_val;
+
+ rc = cfs_get_param_paths(&path, RSI_DOWNCALL_PATH);
+ if (rc != 0) {
+ rc = -errno;
+ printerr(LL_ERR, "ERROR: %s: cannot get param path %s: %s\n",
+ __func__, RSI_DOWNCALL_PATH, strerror(-rc));
+ goto out;
+ }
+
+ fd = open(path.gl_pathv[0], O_WRONLY);
+ if (fd == -1) {
+ rc = -errno;
printerr(LL_ERR, "ERROR: %s: open %s failed: %s\n",
- __func__, SVCGSSD_INIT_CHANNEL, strerror(errno));
- return -1;
+ __func__, RSI_DOWNCALL_PATH, strerror(-rc));
+ goto out_path;
}
- *bp = '\0';
- printerr(LL_DEBUG, "writing message: %s", buf);
- if (write(g, buf, bp - buf) == -1) {
- printerr(LL_ERR, "ERROR: %s: failed to write message\n",
- __func__);
- close(g);
- return -1;
+ size = offsetof(struct rsi_downcall_data,
+ sid_val[bp - rsi_dd->sid_val]);
+ printerr(LL_DEBUG, "writing response, size %d\n", size);
+ if (write(fd, rsi_dd, size) == -1) {
+ rc = -errno;
+ printerr(LL_ERR, "ERROR: %s: failed to write message: %s\n",
+ __func__, strerror(-rc));
}
- close(g);
- return 0;
+ printerr(LL_DEBUG, "response written ok\n");
+
+ close(fd);
+out_path:
+ cfs_free_param_data(&path);
+out:
+ free(rsi_dd);
+ return rc;
}
#define rpc_auth_ok 0
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;
+ /* cleanup ctx_token, out_tok is cleaned up in handle_channel_request */
+ if (remote_pub_key.length != 0) {
+ free(remote_pub_key.value);
+ remote_pub_key.value = NULL;
+ remote_pub_key.length = 0;
+ }
+ if (snd->ctx_token.value) {
+ free(snd->ctx_token.value);
+ snd->ctx_token.value = NULL;
+ snd->ctx_token.length = 0;
+ }
printerr(LL_DEBUG, "sk returning success\n");
return 0;
free(bufs[SK_INIT_RANDOM].value);
free(bufs[SK_INIT_TARGET].value);
free(bufs[SK_INIT_FLAGS].value);
- free(remote_pub_key.value);
+ if (remote_pub_key.length != 0) {
+ free(remote_pub_key.value);
+ remote_pub_key.value = NULL;
+ remote_pub_key.length = 0;
+ }
sk_free_cred(skc);
snd->maj_stat = rc;
return -1;
snd->maj_stat = rc;
if (snd->ctx_token.value) {
free(snd->ctx_token.value);
- snd->ctx_token.value = 0;
+ snd->ctx_token.value = NULL;
snd->ctx_token.length = 0;
}
- free(remote_pub_key.value);
+ if (remote_pub_key.length != 0) {
+ free(remote_pub_key.value);
+ remote_pub_key.value = NULL;
+ remote_pub_key.length = 0;
+ }
sk_free_cred(skc);
printerr(LL_DEBUG, "sk returning failure\n");
#else /* !HAVE_OPENSSL_SSK */
/* We no longer need the gss context */
gss_delete_sec_context(&ignore_min_stat, &snd->ctx, &ignore_out_tok);
do_svc_downcall(&snd->out_handle, &cred, mech, &snd->ctx_token);
-
+ /* We no longer need the context token */
+ if (snd->ctx_token.value) {
+ free(snd->ctx_token.value);
+ snd->ctx_token.value = NULL;
+ snd->ctx_token.length = 0;
+ }
return 0;
out_err:
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)
+int handle_channel_request(int fd)
{
- 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,
+ char in_handle_buf[15];
+ char out_handle_buf[15];
+ uint32_t lustre_mech;
+ static char *lbuf;
+ static int lbuflen;
+ static char *cp;
+ int get_len;
+ int rc;
+ u_int32_t ignore_min_stat;
+ struct svc_nego_data snd = {
+ .in_tok.value = NULL,
.in_handle.value = in_handle_buf,
.out_handle.value = out_handle_buf,
.maj_stat = GSS_S_FAILURE,
.ctx = GSS_C_NO_CONTEXT,
};
+ uint64_t hash = 0;
printerr(LL_INFO, "handling request\n");
- if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
+ if (readline(fd, &lbuf, &lbuflen) != 1) {
printerr(LL_ERR, "ERROR: 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));
-
+ /* see rsi_do_upcall() for the format of data being input here */
+ rc = gss_u64_read_string(&cp, (__u64 *)&hash);
+ if (rc < 0) {
+ printerr(LL_ERR, "ERROR: failed parsing request: hash\n");
+ goto out_err;
+ }
+ rc = gss_u64_read_string(&cp, (__u64 *)&snd.lustre_svc);
+ if (rc < 0) {
+ printerr(LL_ERR, "ERROR: failed parsing request: lustre svc\n");
+ goto out_err;
+ }
/* lustre_svc is the svc and gss subflavor */
lustre_mech = (snd.lustre_svc & LUSTRE_GSS_MECH_MASK) >>
- LUSTRE_GSS_MECH_SHIFT;
+ LUSTRE_GSS_MECH_SHIFT;
snd.lustre_svc = snd.lustre_svc & LUSTRE_GSS_SVC_MASK;
switch (lustre_mech) {
case LGSS_MECH_KRB5:
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));
+ rc = gss_u64_read_string(&cp, (__u64 *)&snd.nid);
+ if (rc < 0) {
+ printerr(LL_ERR, "ERROR: failed parsing request: source nid\n");
+ goto out_err;
+ }
+ rc = gss_u64_read_string(&cp, (__u64 *)&snd.handle_seq);
+ if (rc < 0) {
+ printerr(LL_ERR, "ERROR: failed parsing request: handle seq\n");
+ goto out_err;
+ }
+ get_len = gss_string_read(&cp, snd.nm_name, sizeof(snd.nm_name), 0);
+ if (get_len <= 0) {
+ printerr(LL_ERR,
+ "ERROR: failed parsing request: nodemap name\n");
+ goto out_err;
+ }
+ snd.nm_name[get_len] = '\0';
printerr(LL_INFO,
"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));
+ get_len = gss_base64url_decode(&cp, snd.in_handle.value,
+ sizeof(in_handle_buf));
if (get_len < 0) {
- printerr(LL_ERR, "ERROR: failed parsing request\n");
+ printerr(LL_ERR, "ERROR: failed parsing request: in handle\n");
goto out_err;
}
snd.in_handle.length = (size_t)get_len;
printerr(LL_DEBUG, "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));
+ snd.in_tok.value = malloc(strlen(cp));
+ if (!snd.in_tok.value) {
+ printerr(LL_ERR, "ERROR: failed alloc for in token\n");
+ goto out_err;
+ }
+ get_len = gss_base64url_decode(&cp, snd.in_tok.value, strlen(cp));
if (get_len < 0) {
- printerr(LL_ERR, "ERROR: failed parsing request\n");
+ printerr(LL_ERR, "ERROR: failed parsing request: in token\n");
goto out_err;
}
snd.in_tok.length = (size_t)get_len;
memcpy(&snd.ctx, snd.in_handle.value, snd.in_handle.length);
}
+ rc = -1;
if (lustre_mech == LGSS_MECH_KRB5)
rc = handle_krb(&snd);
else if (lustre_mech == LGSS_MECH_SK)
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);
+ rc = send_response(rc, hash, &snd.in_handle, &snd.in_tok,
+ snd.maj_stat, snd.min_stat,
+ &snd.out_handle, &snd.out_tok);
/* cleanup buffers */
- if (snd.ctx_token.value != NULL)
- free(ctx_token.value);
+ if (snd.in_tok.value)
+ free(snd.in_tok.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;
+ return rc;
}
#endif /* !HAVE_NATIVE_LINUX_CLIENT */
static void
+check_rsi_downcall_data(void)
+{
+ BLANK_LINE();
+ CHECK_STRUCT(rsi_downcall_data);
+ CHECK_MEMBER(rsi_downcall_data, sid_magic);
+ CHECK_MEMBER(rsi_downcall_data, sid_err);
+ CHECK_MEMBER(rsi_downcall_data, sid_hash);
+ CHECK_MEMBER(rsi_downcall_data, sid_maj_stat);
+ CHECK_MEMBER(rsi_downcall_data, sid_min_stat);
+ CHECK_MEMBER(rsi_downcall_data, sid_len);
+ CHECK_MEMBER(rsi_downcall_data, sid_offset);
+ CHECK_MEMBER(rsi_downcall_data, sid_val);
+}
+
+static void
check_llog_gen(void)
{
BLANK_LINE();
#ifndef HAVE_NATIVE_LINUX_CLIENT
check_llog_changelog_user_rec();
#endif /* !HAVE_NATIVE_LINUX_CLIENT */
+ check_rsi_downcall_data();
check_llog_gen();
check_llog_gen_rec();
check_llog_log_hdr();
#endif /* CONFIG_FS_POSIX_ACL */
#endif /* HAVE_SERVER_SUPPORT */
#include <linux/lustre/lustre_cfg.h>
+#include <linux/lustre/lgss.h>
#include <lustre/lustreapi.h>
#ifndef BUILD_BUG_ON
(long long)(int)sizeof(((struct llog_changelog_user_rec *)0)->cur_tail));
#endif /* HAVE_SERVER_SUPPORT */
+ /* Checks for struct rsi_downcall_data */
+ LASSERTF((int)sizeof(struct rsi_downcall_data) == 32, "found %lld\n",
+ (long long)(int)sizeof(struct rsi_downcall_data));
+ LASSERTF((int)offsetof(struct rsi_downcall_data, sid_magic) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct rsi_downcall_data, sid_magic));
+ LASSERTF((int)sizeof(((struct rsi_downcall_data *)0)->sid_magic) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct rsi_downcall_data *)0)->sid_magic));
+ LASSERTF((int)offsetof(struct rsi_downcall_data, sid_err) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct rsi_downcall_data, sid_err));
+ LASSERTF((int)sizeof(((struct rsi_downcall_data *)0)->sid_err) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct rsi_downcall_data *)0)->sid_err));
+ LASSERTF((int)offsetof(struct rsi_downcall_data, sid_hash) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct rsi_downcall_data, sid_hash));
+ LASSERTF((int)sizeof(((struct rsi_downcall_data *)0)->sid_hash) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct rsi_downcall_data *)0)->sid_hash));
+ LASSERTF((int)offsetof(struct rsi_downcall_data, sid_maj_stat) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct rsi_downcall_data, sid_maj_stat));
+ LASSERTF((int)sizeof(((struct rsi_downcall_data *)0)->sid_maj_stat) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct rsi_downcall_data *)0)->sid_maj_stat));
+ LASSERTF((int)offsetof(struct rsi_downcall_data, sid_min_stat) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct rsi_downcall_data, sid_min_stat));
+ LASSERTF((int)sizeof(((struct rsi_downcall_data *)0)->sid_min_stat) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct rsi_downcall_data *)0)->sid_min_stat));
+ LASSERTF((int)offsetof(struct rsi_downcall_data, sid_len) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct rsi_downcall_data, sid_len));
+ LASSERTF((int)sizeof(((struct rsi_downcall_data *)0)->sid_len) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct rsi_downcall_data *)0)->sid_len));
+ LASSERTF((int)offsetof(struct rsi_downcall_data, sid_offset) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct rsi_downcall_data, sid_offset));
+ LASSERTF((int)sizeof(((struct rsi_downcall_data *)0)->sid_offset) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct rsi_downcall_data *)0)->sid_offset));
+ LASSERTF((int)offsetof(struct rsi_downcall_data, sid_val) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct rsi_downcall_data, sid_val));
+ LASSERTF((int)sizeof(((struct rsi_downcall_data *)0)->sid_val) == 0, "found %lld\n",
+ (long long)(int)sizeof(((struct rsi_downcall_data *)0)->sid_val));
+
/* Checks for struct llog_gen */
LASSERTF((int)sizeof(struct llog_gen) == 16, "found %lld\n",
(long long)(int)sizeof(struct llog_gen));