12573a464SChuck Lever // SPDX-License-Identifier: BSD-3-Clause 21da177e4SLinus Torvalds /* 3f30c2269SUwe Zeisberger * linux/net/sunrpc/auth_gss/auth_gss.c 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * RPCSEC_GSS client authentication. 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * Copyright (c) 2000 The Regents of the University of Michigan. 81da177e4SLinus Torvalds * All rights reserved. 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * Dug Song <dugsong@monkey.org> 111da177e4SLinus Torvalds * Andy Adamson <andros@umich.edu> 121da177e4SLinus Torvalds */ 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds #include <linux/module.h> 151da177e4SLinus Torvalds #include <linux/init.h> 161da177e4SLinus Torvalds #include <linux/types.h> 171da177e4SLinus Torvalds #include <linux/slab.h> 181da177e4SLinus Torvalds #include <linux/sched.h> 192d2da60cSJ. Bruce Fields #include <linux/pagemap.h> 201da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h> 211da177e4SLinus Torvalds #include <linux/sunrpc/auth.h> 221da177e4SLinus Torvalds #include <linux/sunrpc/auth_gss.h> 23df513a77SOlga Kornievskaia #include <linux/sunrpc/gss_krb5.h> 241da177e4SLinus Torvalds #include <linux/sunrpc/svcauth_gss.h> 251da177e4SLinus Torvalds #include <linux/sunrpc/gss_err.h> 261da177e4SLinus Torvalds #include <linux/workqueue.h> 271da177e4SLinus Torvalds #include <linux/sunrpc/rpc_pipe_fs.h> 281da177e4SLinus Torvalds #include <linux/sunrpc/gss_api.h> 297c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 30eb6dc19dSTrond Myklebust #include <linux/hashtable.h> 311da177e4SLinus Torvalds 32ba6dfce4SDave Wysochanski #include "auth_gss_internal.h" 33abfdbd53STrond Myklebust #include "../netns.h" 34abfdbd53STrond Myklebust 350c77668dSChuck Lever #include <trace/events/rpcgss.h> 360c77668dSChuck Lever 37f1c0a861STrond Myklebust static const struct rpc_authops authgss_ops; 381da177e4SLinus Torvalds 39f1c0a861STrond Myklebust static const struct rpc_credops gss_credops; 400df7fb74STrond Myklebust static const struct rpc_credops gss_nullops; 411da177e4SLinus Torvalds 42126e216aSTrond Myklebust #define GSS_RETRY_EXPIRED 5 43126e216aSTrond Myklebust static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED; 44126e216aSTrond Myklebust 454de6caa2SAndy Adamson #define GSS_KEY_EXPIRE_TIMEO 240 464de6caa2SAndy Adamson static unsigned int gss_key_expire_timeo = GSS_KEY_EXPIRE_TIMEO; 474de6caa2SAndy Adamson 48f895b252SJeff Layton #if IS_ENABLED(CONFIG_SUNRPC_DEBUG) 491da177e4SLinus Torvalds # define RPCDBG_FACILITY RPCDBG_AUTH 501da177e4SLinus Torvalds #endif 511da177e4SLinus Torvalds 52*6e460c23SChuck Lever /* 53*6e460c23SChuck Lever * This compile-time check verifies that we will not exceed the 54*6e460c23SChuck Lever * slack space allotted by the client and server auth_gss code 55*6e460c23SChuck Lever * before they call gss_wrap(). 56*6e460c23SChuck Lever */ 57*6e460c23SChuck Lever #define GSS_KRB5_MAX_SLACK_NEEDED \ 58*6e460c23SChuck Lever (GSS_KRB5_TOK_HDR_LEN /* gss token header */ \ 59*6e460c23SChuck Lever + GSS_KRB5_MAX_CKSUM_LEN /* gss token checksum */ \ 60*6e460c23SChuck Lever + GSS_KRB5_MAX_BLOCKSIZE /* confounder */ \ 61*6e460c23SChuck Lever + GSS_KRB5_MAX_BLOCKSIZE /* possible padding */ \ 62*6e460c23SChuck Lever + GSS_KRB5_TOK_HDR_LEN /* encrypted hdr in v2 token */ \ 63*6e460c23SChuck Lever + GSS_KRB5_MAX_CKSUM_LEN /* encryption hmac */ \ 64*6e460c23SChuck Lever + XDR_UNIT * 2 /* RPC verifier */ \ 65*6e460c23SChuck Lever + GSS_KRB5_TOK_HDR_LEN \ 66*6e460c23SChuck Lever + GSS_KRB5_MAX_CKSUM_LEN) 67*6e460c23SChuck Lever 68725f2865SKevin Coffman #define GSS_CRED_SLACK (RPC_MAX_AUTH_SIZE * 2) 691da177e4SLinus Torvalds /* length of a krb5 verifier (48), plus data added before arguments when 701da177e4SLinus Torvalds * using integrity (two 4-byte integers): */ 71adeb8133SOlga Kornievskaia #define GSS_VERF_SLACK 100 721da177e4SLinus Torvalds 7323c323afSTrond Myklebust static DEFINE_HASHTABLE(gss_auth_hash_table, 4); 74eb6dc19dSTrond Myklebust static DEFINE_SPINLOCK(gss_auth_hash_lock); 75eb6dc19dSTrond Myklebust 7619172284STrond Myklebust struct gss_pipe { 7719172284STrond Myklebust struct rpc_pipe_dir_object pdo; 7819172284STrond Myklebust struct rpc_pipe *pipe; 7919172284STrond Myklebust struct rpc_clnt *clnt; 8019172284STrond Myklebust const char *name; 81414a6295STrond Myklebust struct kref kref; 8219172284STrond Myklebust }; 8319172284STrond Myklebust 841da177e4SLinus Torvalds struct gss_auth { 850285ed1fSTrond Myklebust struct kref kref; 86eb6dc19dSTrond Myklebust struct hlist_node hash; 871da177e4SLinus Torvalds struct rpc_auth rpc_auth; 881da177e4SLinus Torvalds struct gss_api_mech *mech; 891da177e4SLinus Torvalds enum rpc_gss_svc service; 901da177e4SLinus Torvalds struct rpc_clnt *client; 91e726340aSTrond Myklebust struct net *net; 929b1831e5SEric Dumazet netns_tracker ns_tracker; 9334769fc4S\"J. Bruce Fields\ /* 9434769fc4S\"J. Bruce Fields\ * There are two upcall pipes; dentry[1], named "gssd", is used 9534769fc4S\"J. Bruce Fields\ * for the new text-based upcall; dentry[0] is named after the 9634769fc4S\"J. Bruce Fields\ * mechanism (for example, "krb5") and exists for 9734769fc4S\"J. Bruce Fields\ * backwards-compatibility with older gssd's. 9834769fc4S\"J. Bruce Fields\ */ 9919172284STrond Myklebust struct gss_pipe *gss_pipe[2]; 100bd4a3eb1STrond Myklebust const char *target_name; 1011da177e4SLinus Torvalds }; 1021da177e4SLinus Torvalds 10379a3f20bS\"J. Bruce Fields\ /* pipe_version >= 0 if and only if someone has a pipe open. */ 10479a3f20bS\"J. Bruce Fields\ static DEFINE_SPINLOCK(pipe_version_lock); 10579a3f20bS\"J. Bruce Fields\ static struct rpc_wait_queue pipe_version_rpc_waitqueue; 10679a3f20bS\"J. Bruce Fields\ static DECLARE_WAIT_QUEUE_HEAD(pipe_version_waitqueue); 1079eb2ddb4STrond Myklebust static void gss_put_auth(struct gss_auth *gss_auth); 108cf81939dS\"J. Bruce Fields\ 1095d28dc82STrond Myklebust static void gss_free_ctx(struct gss_cl_ctx *); 110b693ba4aSTrond Myklebust static const struct rpc_pipe_ops gss_upcall_ops_v0; 111b693ba4aSTrond Myklebust static const struct rpc_pipe_ops gss_upcall_ops_v1; 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds static inline struct gss_cl_ctx * 1141da177e4SLinus Torvalds gss_get_ctx(struct gss_cl_ctx *ctx) 1151da177e4SLinus Torvalds { 1160fa10472SReshetova, Elena refcount_inc(&ctx->count); 1171da177e4SLinus Torvalds return ctx; 1181da177e4SLinus Torvalds } 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds static inline void 1211da177e4SLinus Torvalds gss_put_ctx(struct gss_cl_ctx *ctx) 1221da177e4SLinus Torvalds { 1230fa10472SReshetova, Elena if (refcount_dec_and_test(&ctx->count)) 1245d28dc82STrond Myklebust gss_free_ctx(ctx); 1251da177e4SLinus Torvalds } 1261da177e4SLinus Torvalds 1275d28dc82STrond Myklebust /* gss_cred_set_ctx: 1285d28dc82STrond Myklebust * called by gss_upcall_callback and gss_create_upcall in order 1295d28dc82STrond Myklebust * to set the gss context. The actual exchange of an old context 1309beae467SStanislav Kinsbursky * and a new one is protected by the pipe->lock. 1315d28dc82STrond Myklebust */ 1321da177e4SLinus Torvalds static void 1331da177e4SLinus Torvalds gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx) 1341da177e4SLinus Torvalds { 1351da177e4SLinus Torvalds struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 1365d28dc82STrond Myklebust 137cd019f75STrond Myklebust if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags)) 138cd019f75STrond Myklebust return; 1397b6962b0STrond Myklebust gss_get_ctx(ctx); 140cf778b00SEric Dumazet rcu_assign_pointer(gss_cred->gc_ctx, ctx); 141fc432dd9STrond Myklebust set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 1424e857c58SPeter Zijlstra smp_mb__before_atomic(); 143fc432dd9STrond Myklebust clear_bit(RPCAUTH_CRED_NEW, &cred->cr_flags); 1441da177e4SLinus Torvalds } 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds static struct gss_cl_ctx * 1471da177e4SLinus Torvalds gss_cred_get_ctx(struct rpc_cred *cred) 1481da177e4SLinus Torvalds { 1491da177e4SLinus Torvalds struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 1501da177e4SLinus Torvalds struct gss_cl_ctx *ctx = NULL; 1511da177e4SLinus Torvalds 1525d28dc82STrond Myklebust rcu_read_lock(); 153c5e6aecdSJeff Layton ctx = rcu_dereference(gss_cred->gc_ctx); 154c5e6aecdSJeff Layton if (ctx) 155c5e6aecdSJeff Layton gss_get_ctx(ctx); 1565d28dc82STrond Myklebust rcu_read_unlock(); 1571da177e4SLinus Torvalds return ctx; 1581da177e4SLinus Torvalds } 1591da177e4SLinus Torvalds 1601da177e4SLinus Torvalds static struct gss_cl_ctx * 1611da177e4SLinus Torvalds gss_alloc_context(void) 1621da177e4SLinus Torvalds { 1631da177e4SLinus Torvalds struct gss_cl_ctx *ctx; 1641da177e4SLinus Torvalds 1654c2883e7STrond Myklebust ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 1661da177e4SLinus Torvalds if (ctx != NULL) { 1671da177e4SLinus Torvalds ctx->gc_proc = RPC_GSS_PROC_DATA; 1681da177e4SLinus Torvalds ctx->gc_seq = 1; /* NetApp 6.4R1 doesn't accept seq. no. 0 */ 1691da177e4SLinus Torvalds spin_lock_init(&ctx->gc_seq_lock); 1700fa10472SReshetova, Elena refcount_set(&ctx->count,1); 1711da177e4SLinus Torvalds } 1721da177e4SLinus Torvalds return ctx; 1731da177e4SLinus Torvalds } 1741da177e4SLinus Torvalds 1751da177e4SLinus Torvalds #define GSSD_MIN_TIMEOUT (60 * 60) 1761da177e4SLinus Torvalds static const void * 1771da177e4SLinus Torvalds gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct gss_api_mech *gm) 1781da177e4SLinus Torvalds { 1791da177e4SLinus Torvalds const void *q; 1801da177e4SLinus Torvalds unsigned int seclen; 1811da177e4SLinus Torvalds unsigned int timeout; 182620038f6SAndy Adamson unsigned long now = jiffies; 1831da177e4SLinus Torvalds u32 window_size; 1841da177e4SLinus Torvalds int ret; 1851da177e4SLinus Torvalds 186620038f6SAndy Adamson /* First unsigned int gives the remaining lifetime in seconds of the 187620038f6SAndy Adamson * credential - e.g. the remaining TGT lifetime for Kerberos or 188620038f6SAndy Adamson * the -t value passed to GSSD. 189620038f6SAndy Adamson */ 1901da177e4SLinus Torvalds p = simple_get_bytes(p, end, &timeout, sizeof(timeout)); 1911da177e4SLinus Torvalds if (IS_ERR(p)) 1921da177e4SLinus Torvalds goto err; 1931da177e4SLinus Torvalds if (timeout == 0) 1941da177e4SLinus Torvalds timeout = GSSD_MIN_TIMEOUT; 195620038f6SAndy Adamson ctx->gc_expiry = now + ((unsigned long)timeout * HZ); 196620038f6SAndy Adamson /* Sequence number window. Determines the maximum number of 197620038f6SAndy Adamson * simultaneous requests 198620038f6SAndy Adamson */ 1991da177e4SLinus Torvalds p = simple_get_bytes(p, end, &window_size, sizeof(window_size)); 2001da177e4SLinus Torvalds if (IS_ERR(p)) 2011da177e4SLinus Torvalds goto err; 2021da177e4SLinus Torvalds ctx->gc_win = window_size; 2031da177e4SLinus Torvalds /* gssd signals an error by passing ctx->gc_win = 0: */ 2041da177e4SLinus Torvalds if (ctx->gc_win == 0) { 205dc5ddce9SJeff Layton /* 206dc5ddce9SJeff Layton * in which case, p points to an error code. Anything other 207dc5ddce9SJeff Layton * than -EKEYEXPIRED gets converted to -EACCES. 208dc5ddce9SJeff Layton */ 209dc5ddce9SJeff Layton p = simple_get_bytes(p, end, &ret, sizeof(ret)); 210dc5ddce9SJeff Layton if (!IS_ERR(p)) 211dc5ddce9SJeff Layton p = (ret == -EKEYEXPIRED) ? ERR_PTR(-EKEYEXPIRED) : 212dc5ddce9SJeff Layton ERR_PTR(-EACCES); 2131da177e4SLinus Torvalds goto err; 2141da177e4SLinus Torvalds } 2151da177e4SLinus Torvalds /* copy the opaque wire context */ 2161da177e4SLinus Torvalds p = simple_get_netobj(p, end, &ctx->gc_wire_ctx); 2171da177e4SLinus Torvalds if (IS_ERR(p)) 2181da177e4SLinus Torvalds goto err; 2191da177e4SLinus Torvalds /* import the opaque security context */ 2201da177e4SLinus Torvalds p = simple_get_bytes(p, end, &seclen, sizeof(seclen)); 2211da177e4SLinus Torvalds if (IS_ERR(p)) 2221da177e4SLinus Torvalds goto err; 2231da177e4SLinus Torvalds q = (const void *)((const char *)p + seclen); 2241da177e4SLinus Torvalds if (unlikely(q > end || q < p)) { 2251da177e4SLinus Torvalds p = ERR_PTR(-EFAULT); 2261da177e4SLinus Torvalds goto err; 2271da177e4SLinus Torvalds } 2284c2883e7STrond Myklebust ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx, NULL, GFP_KERNEL); 2291da177e4SLinus Torvalds if (ret < 0) { 2300c77668dSChuck Lever trace_rpcgss_import_ctx(ret); 2311da177e4SLinus Torvalds p = ERR_PTR(ret); 2321da177e4SLinus Torvalds goto err; 2331da177e4SLinus Torvalds } 2342004c726SJeff Layton 2352004c726SJeff Layton /* is there any trailing data? */ 2362004c726SJeff Layton if (q == end) { 2372004c726SJeff Layton p = q; 2382004c726SJeff Layton goto done; 2392004c726SJeff Layton } 2402004c726SJeff Layton 2412004c726SJeff Layton /* pull in acceptor name (if there is one) */ 2422004c726SJeff Layton p = simple_get_netobj(q, end, &ctx->gc_acceptor); 2432004c726SJeff Layton if (IS_ERR(p)) 2442004c726SJeff Layton goto err; 2452004c726SJeff Layton done: 24674fb8fecSChuck Lever trace_rpcgss_context(window_size, ctx->gc_expiry, now, timeout, 2470c77668dSChuck Lever ctx->gc_acceptor.len, ctx->gc_acceptor.data); 2481da177e4SLinus Torvalds err: 2491da177e4SLinus Torvalds return p; 2501da177e4SLinus Torvalds } 2511da177e4SLinus Torvalds 252a1a23777SChuck Lever /* XXX: Need some documentation about why UPCALL_BUF_LEN is so small. 253a1a23777SChuck Lever * Is user space expecting no more than UPCALL_BUF_LEN bytes? 254a1a23777SChuck Lever * Note that there are now _two_ NI_MAXHOST sized data items 255a1a23777SChuck Lever * being passed in this string. 256a1a23777SChuck Lever */ 257a1a23777SChuck Lever #define UPCALL_BUF_LEN 256 2581da177e4SLinus Torvalds 2591da177e4SLinus Torvalds struct gss_upcall_msg { 2607ff13969SReshetova, Elena refcount_t count; 2617eaf040bSEric W. Biederman kuid_t uid; 262ac83228aSTrond Myklebust const char *service_name; 2631da177e4SLinus Torvalds struct rpc_pipe_msg msg; 2641da177e4SLinus Torvalds struct list_head list; 2651da177e4SLinus Torvalds struct gss_auth *auth; 2669beae467SStanislav Kinsbursky struct rpc_pipe *pipe; 2671da177e4SLinus Torvalds struct rpc_wait_queue rpc_waitqueue; 2681da177e4SLinus Torvalds wait_queue_head_t waitqueue; 2691da177e4SLinus Torvalds struct gss_cl_ctx *ctx; 27034769fc4S\"J. Bruce Fields\ char databuf[UPCALL_BUF_LEN]; 2711da177e4SLinus Torvalds }; 2721da177e4SLinus Torvalds 2732aed8b47STrond Myklebust static int get_pipe_version(struct net *net) 27479a3f20bS\"J. Bruce Fields\ { 2752aed8b47STrond Myklebust struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 27679a3f20bS\"J. Bruce Fields\ int ret; 27779a3f20bS\"J. Bruce Fields\ 27879a3f20bS\"J. Bruce Fields\ spin_lock(&pipe_version_lock); 2792aed8b47STrond Myklebust if (sn->pipe_version >= 0) { 2802aed8b47STrond Myklebust atomic_inc(&sn->pipe_users); 2812aed8b47STrond Myklebust ret = sn->pipe_version; 28279a3f20bS\"J. Bruce Fields\ } else 28379a3f20bS\"J. Bruce Fields\ ret = -EAGAIN; 28479a3f20bS\"J. Bruce Fields\ spin_unlock(&pipe_version_lock); 28579a3f20bS\"J. Bruce Fields\ return ret; 28679a3f20bS\"J. Bruce Fields\ } 28779a3f20bS\"J. Bruce Fields\ 2882aed8b47STrond Myklebust static void put_pipe_version(struct net *net) 28979a3f20bS\"J. Bruce Fields\ { 2902aed8b47STrond Myklebust struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 2912aed8b47STrond Myklebust 2922aed8b47STrond Myklebust if (atomic_dec_and_lock(&sn->pipe_users, &pipe_version_lock)) { 2932aed8b47STrond Myklebust sn->pipe_version = -1; 29479a3f20bS\"J. Bruce Fields\ spin_unlock(&pipe_version_lock); 29579a3f20bS\"J. Bruce Fields\ } 29679a3f20bS\"J. Bruce Fields\ } 29779a3f20bS\"J. Bruce Fields\ 2981da177e4SLinus Torvalds static void 2991da177e4SLinus Torvalds gss_release_msg(struct gss_upcall_msg *gss_msg) 3001da177e4SLinus Torvalds { 301e726340aSTrond Myklebust struct net *net = gss_msg->auth->net; 3027ff13969SReshetova, Elena if (!refcount_dec_and_test(&gss_msg->count)) 3031da177e4SLinus Torvalds return; 3042aed8b47STrond Myklebust put_pipe_version(net); 3051da177e4SLinus Torvalds BUG_ON(!list_empty(&gss_msg->list)); 3061da177e4SLinus Torvalds if (gss_msg->ctx != NULL) 3071da177e4SLinus Torvalds gss_put_ctx(gss_msg->ctx); 308f6a1cc89STrond Myklebust rpc_destroy_wait_queue(&gss_msg->rpc_waitqueue); 3099eb2ddb4STrond Myklebust gss_put_auth(gss_msg->auth); 310ac83228aSTrond Myklebust kfree_const(gss_msg->service_name); 3111da177e4SLinus Torvalds kfree(gss_msg); 3121da177e4SLinus Torvalds } 3131da177e4SLinus Torvalds 3141da177e4SLinus Torvalds static struct gss_upcall_msg * 3159130b8dbSOlga Kornievskaia __gss_find_upcall(struct rpc_pipe *pipe, kuid_t uid, const struct gss_auth *auth) 3161da177e4SLinus Torvalds { 3171da177e4SLinus Torvalds struct gss_upcall_msg *pos; 3189beae467SStanislav Kinsbursky list_for_each_entry(pos, &pipe->in_downcall, list) { 3190b4d51b0SEric W. Biederman if (!uid_eq(pos->uid, uid)) 3201da177e4SLinus Torvalds continue; 321b18cba09Sminoura makoto if (pos->auth->service != auth->service) 3229130b8dbSOlga Kornievskaia continue; 3237ff13969SReshetova, Elena refcount_inc(&pos->count); 3241da177e4SLinus Torvalds return pos; 3251da177e4SLinus Torvalds } 3261da177e4SLinus Torvalds return NULL; 3271da177e4SLinus Torvalds } 3281da177e4SLinus Torvalds 329720b8f2dS\\\"J. Bruce Fields\\\ /* Try to add an upcall to the pipefs queue. 3301da177e4SLinus Torvalds * If an upcall owned by our uid already exists, then we return a reference 3311da177e4SLinus Torvalds * to that upcall instead of adding the new upcall. 3321da177e4SLinus Torvalds */ 3331da177e4SLinus Torvalds static inline struct gss_upcall_msg * 334053e324fSSuresh Jayaraman gss_add_msg(struct gss_upcall_msg *gss_msg) 3351da177e4SLinus Torvalds { 3369beae467SStanislav Kinsbursky struct rpc_pipe *pipe = gss_msg->pipe; 3371da177e4SLinus Torvalds struct gss_upcall_msg *old; 3381da177e4SLinus Torvalds 3399beae467SStanislav Kinsbursky spin_lock(&pipe->lock); 3409130b8dbSOlga Kornievskaia old = __gss_find_upcall(pipe, gss_msg->uid, gss_msg->auth); 3411da177e4SLinus Torvalds if (old == NULL) { 3427ff13969SReshetova, Elena refcount_inc(&gss_msg->count); 3439beae467SStanislav Kinsbursky list_add(&gss_msg->list, &pipe->in_downcall); 3441da177e4SLinus Torvalds } else 3451da177e4SLinus Torvalds gss_msg = old; 3469beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 3471da177e4SLinus Torvalds return gss_msg; 3481da177e4SLinus Torvalds } 3491da177e4SLinus Torvalds 3501da177e4SLinus Torvalds static void 3511da177e4SLinus Torvalds __gss_unhash_msg(struct gss_upcall_msg *gss_msg) 3521da177e4SLinus Torvalds { 3531da177e4SLinus Torvalds list_del_init(&gss_msg->list); 3541da177e4SLinus Torvalds rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); 3551da177e4SLinus Torvalds wake_up_all(&gss_msg->waitqueue); 3567ff13969SReshetova, Elena refcount_dec(&gss_msg->count); 3571da177e4SLinus Torvalds } 3581da177e4SLinus Torvalds 3591da177e4SLinus Torvalds static void 3601da177e4SLinus Torvalds gss_unhash_msg(struct gss_upcall_msg *gss_msg) 3611da177e4SLinus Torvalds { 3629beae467SStanislav Kinsbursky struct rpc_pipe *pipe = gss_msg->pipe; 3631da177e4SLinus Torvalds 3643b68aaeaSTrond Myklebust if (list_empty(&gss_msg->list)) 3653b68aaeaSTrond Myklebust return; 3669beae467SStanislav Kinsbursky spin_lock(&pipe->lock); 3673b68aaeaSTrond Myklebust if (!list_empty(&gss_msg->list)) 3681da177e4SLinus Torvalds __gss_unhash_msg(gss_msg); 3699beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 3701da177e4SLinus Torvalds } 3711da177e4SLinus Torvalds 3721da177e4SLinus Torvalds static void 373126e216aSTrond Myklebust gss_handle_downcall_result(struct gss_cred *gss_cred, struct gss_upcall_msg *gss_msg) 374126e216aSTrond Myklebust { 375126e216aSTrond Myklebust switch (gss_msg->msg.errno) { 376126e216aSTrond Myklebust case 0: 377126e216aSTrond Myklebust if (gss_msg->ctx == NULL) 378126e216aSTrond Myklebust break; 379126e216aSTrond Myklebust clear_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags); 380126e216aSTrond Myklebust gss_cred_set_ctx(&gss_cred->gc_base, gss_msg->ctx); 381126e216aSTrond Myklebust break; 382126e216aSTrond Myklebust case -EKEYEXPIRED: 383126e216aSTrond Myklebust set_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags); 384126e216aSTrond Myklebust } 385126e216aSTrond Myklebust gss_cred->gc_upcall_timestamp = jiffies; 386126e216aSTrond Myklebust gss_cred->gc_upcall = NULL; 387126e216aSTrond Myklebust rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); 388126e216aSTrond Myklebust } 389126e216aSTrond Myklebust 390126e216aSTrond Myklebust static void 3911da177e4SLinus Torvalds gss_upcall_callback(struct rpc_task *task) 3921da177e4SLinus Torvalds { 393a17c2153STrond Myklebust struct gss_cred *gss_cred = container_of(task->tk_rqstp->rq_cred, 3941da177e4SLinus Torvalds struct gss_cred, gc_base); 3951da177e4SLinus Torvalds struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall; 3969beae467SStanislav Kinsbursky struct rpc_pipe *pipe = gss_msg->pipe; 3971da177e4SLinus Torvalds 3989beae467SStanislav Kinsbursky spin_lock(&pipe->lock); 399126e216aSTrond Myklebust gss_handle_downcall_result(gss_cred, gss_msg); 4009beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 401126e216aSTrond Myklebust task->tk_status = gss_msg->msg.errno; 4021da177e4SLinus Torvalds gss_release_msg(gss_msg); 4031da177e4SLinus Torvalds } 4041da177e4SLinus Torvalds 405ac83228aSTrond Myklebust static void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg, 406ac83228aSTrond Myklebust const struct cred *cred) 40734769fc4S\"J. Bruce Fields\ { 408ac83228aSTrond Myklebust struct user_namespace *userns = cred->user_ns; 409283ebe3eSTrond Myklebust 410283ebe3eSTrond Myklebust uid_t uid = from_kuid_munged(userns, gss_msg->uid); 41190602c7bSEric W. Biederman memcpy(gss_msg->databuf, &uid, sizeof(uid)); 41290602c7bSEric W. Biederman gss_msg->msg.data = gss_msg->databuf; 41390602c7bSEric W. Biederman gss_msg->msg.len = sizeof(uid); 4149d3a2260STrond Myklebust 4159d3a2260STrond Myklebust BUILD_BUG_ON(sizeof(uid) > sizeof(gss_msg->databuf)); 41634769fc4S\"J. Bruce Fields\ } 41734769fc4S\"J. Bruce Fields\ 418ac83228aSTrond Myklebust static ssize_t 419ac83228aSTrond Myklebust gss_v0_upcall(struct file *file, struct rpc_pipe_msg *msg, 420ac83228aSTrond Myklebust char __user *buf, size_t buflen) 421ac83228aSTrond Myklebust { 422ac83228aSTrond Myklebust struct gss_upcall_msg *gss_msg = container_of(msg, 423ac83228aSTrond Myklebust struct gss_upcall_msg, 424ac83228aSTrond Myklebust msg); 425ac83228aSTrond Myklebust if (msg->copied == 0) 426ac83228aSTrond Myklebust gss_encode_v0_msg(gss_msg, file->f_cred); 427ac83228aSTrond Myklebust return rpc_pipe_generic_upcall(file, msg, buf, buflen); 428ac83228aSTrond Myklebust } 429ac83228aSTrond Myklebust 4309d3a2260STrond Myklebust static int gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, 431bd4a3eb1STrond Myklebust const char *service_name, 432ac83228aSTrond Myklebust const char *target_name, 433ac83228aSTrond Myklebust const struct cred *cred) 43434769fc4S\"J. Bruce Fields\ { 435ac83228aSTrond Myklebust struct user_namespace *userns = cred->user_ns; 436683ac665STrond Myklebust struct gss_api_mech *mech = gss_msg->auth->mech; 4378b1c7bf5SOlga Kornievskaia char *p = gss_msg->databuf; 4389d3a2260STrond Myklebust size_t buflen = sizeof(gss_msg->databuf); 4399d3a2260STrond Myklebust int len; 4408b1c7bf5SOlga Kornievskaia 4419d3a2260STrond Myklebust len = scnprintf(p, buflen, "mech=%s uid=%d", mech->gm_name, 442283ebe3eSTrond Myklebust from_kuid_munged(userns, gss_msg->uid)); 4439d3a2260STrond Myklebust buflen -= len; 4449d3a2260STrond Myklebust p += len; 4459d3a2260STrond Myklebust gss_msg->msg.len = len; 446108b833cSChuck Lever 447108b833cSChuck Lever /* 448108b833cSChuck Lever * target= is a full service principal that names the remote 449108b833cSChuck Lever * identity that we are authenticating to. 450108b833cSChuck Lever */ 451bd4a3eb1STrond Myklebust if (target_name) { 4529d3a2260STrond Myklebust len = scnprintf(p, buflen, " target=%s", target_name); 4539d3a2260STrond Myklebust buflen -= len; 4548b1c7bf5SOlga Kornievskaia p += len; 4558b1c7bf5SOlga Kornievskaia gss_msg->msg.len += len; 4568b1c7bf5SOlga Kornievskaia } 457108b833cSChuck Lever 458108b833cSChuck Lever /* 459108b833cSChuck Lever * gssd uses service= and srchost= to select a matching key from 460108b833cSChuck Lever * the system's keytab to use as the source principal. 461108b833cSChuck Lever * 462108b833cSChuck Lever * service= is the service name part of the source principal, 463108b833cSChuck Lever * or "*" (meaning choose any). 464108b833cSChuck Lever * 465108b833cSChuck Lever * srchost= is the hostname part of the source principal. When 466108b833cSChuck Lever * not provided, gssd uses the local hostname. 467108b833cSChuck Lever */ 468a1a23777SChuck Lever if (service_name) { 469a1a23777SChuck Lever char *c = strchr(service_name, '@'); 470a1a23777SChuck Lever 471a1a23777SChuck Lever if (!c) 472a1a23777SChuck Lever len = scnprintf(p, buflen, " service=%s", 473a1a23777SChuck Lever service_name); 474a1a23777SChuck Lever else 475a1a23777SChuck Lever len = scnprintf(p, buflen, 476a1a23777SChuck Lever " service=%.*s srchost=%s", 477a1a23777SChuck Lever (int)(c - service_name), 478a1a23777SChuck Lever service_name, c + 1); 4799d3a2260STrond Myklebust buflen -= len; 4802efef708SOlga Kornievskaia p += len; 4812efef708SOlga Kornievskaia gss_msg->msg.len += len; 4822efef708SOlga Kornievskaia } 483108b833cSChuck Lever 484683ac665STrond Myklebust if (mech->gm_upcall_enctypes) { 4859d3a2260STrond Myklebust len = scnprintf(p, buflen, " enctypes=%s", 4869d3a2260STrond Myklebust mech->gm_upcall_enctypes); 4879d3a2260STrond Myklebust buflen -= len; 488683ac665STrond Myklebust p += len; 489683ac665STrond Myklebust gss_msg->msg.len += len; 490683ac665STrond Myklebust } 4910c77668dSChuck Lever trace_rpcgss_upcall_msg(gss_msg->databuf); 4929d3a2260STrond Myklebust len = scnprintf(p, buflen, "\n"); 4939d3a2260STrond Myklebust if (len == 0) 4949d3a2260STrond Myklebust goto out_overflow; 4958b1c7bf5SOlga Kornievskaia gss_msg->msg.len += len; 49634769fc4S\"J. Bruce Fields\ gss_msg->msg.data = gss_msg->databuf; 4979d3a2260STrond Myklebust return 0; 4989d3a2260STrond Myklebust out_overflow: 4999d3a2260STrond Myklebust WARN_ON_ONCE(1); 5009d3a2260STrond Myklebust return -ENOMEM; 50134769fc4S\"J. Bruce Fields\ } 50234769fc4S\"J. Bruce Fields\ 503ac83228aSTrond Myklebust static ssize_t 504ac83228aSTrond Myklebust gss_v1_upcall(struct file *file, struct rpc_pipe_msg *msg, 505ac83228aSTrond Myklebust char __user *buf, size_t buflen) 506ac83228aSTrond Myklebust { 507ac83228aSTrond Myklebust struct gss_upcall_msg *gss_msg = container_of(msg, 508ac83228aSTrond Myklebust struct gss_upcall_msg, 509ac83228aSTrond Myklebust msg); 510ac83228aSTrond Myklebust int err; 511ac83228aSTrond Myklebust if (msg->copied == 0) { 512ac83228aSTrond Myklebust err = gss_encode_v1_msg(gss_msg, 513ac83228aSTrond Myklebust gss_msg->service_name, 514ac83228aSTrond Myklebust gss_msg->auth->target_name, 515ac83228aSTrond Myklebust file->f_cred); 516ac83228aSTrond Myklebust if (err) 517ac83228aSTrond Myklebust return err; 518ac83228aSTrond Myklebust } 519ac83228aSTrond Myklebust return rpc_pipe_generic_upcall(file, msg, buf, buflen); 520ac83228aSTrond Myklebust } 521ac83228aSTrond Myklebust 52268c97153STrond Myklebust static struct gss_upcall_msg * 523e726340aSTrond Myklebust gss_alloc_msg(struct gss_auth *gss_auth, 5247eaf040bSEric W. Biederman kuid_t uid, const char *service_name) 5251da177e4SLinus Torvalds { 5261da177e4SLinus Torvalds struct gss_upcall_msg *gss_msg; 52779a3f20bS\"J. Bruce Fields\ int vers; 5289d3a2260STrond Myklebust int err = -ENOMEM; 5291da177e4SLinus Torvalds 5304c2883e7STrond Myklebust gss_msg = kzalloc(sizeof(*gss_msg), GFP_KERNEL); 531db75b3d6S\"J. Bruce Fields\ if (gss_msg == NULL) 5329d3a2260STrond Myklebust goto err; 533e726340aSTrond Myklebust vers = get_pipe_version(gss_auth->net); 5349d3a2260STrond Myklebust err = vers; 5359d3a2260STrond Myklebust if (err < 0) 5369d3a2260STrond Myklebust goto err_free_msg; 53719172284STrond Myklebust gss_msg->pipe = gss_auth->gss_pipe[vers]->pipe; 5381da177e4SLinus Torvalds INIT_LIST_HEAD(&gss_msg->list); 5391da177e4SLinus Torvalds rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq"); 5401da177e4SLinus Torvalds init_waitqueue_head(&gss_msg->waitqueue); 5417ff13969SReshetova, Elena refcount_set(&gss_msg->count, 1); 5421da177e4SLinus Torvalds gss_msg->uid = uid; 5431da177e4SLinus Torvalds gss_msg->auth = gss_auth; 5445940d1cfSChuck Lever kref_get(&gss_auth->kref); 545ac83228aSTrond Myklebust if (service_name) { 5464c2883e7STrond Myklebust gss_msg->service_name = kstrdup_const(service_name, GFP_KERNEL); 547fe31ce83SDan Carpenter if (!gss_msg->service_name) { 548fe31ce83SDan Carpenter err = -ENOMEM; 549e9776d0fSTrond Myklebust goto err_put_pipe_version; 55007d53ae4Szhong jiang } 551fe31ce83SDan Carpenter } 5521da177e4SLinus Torvalds return gss_msg; 553e9776d0fSTrond Myklebust err_put_pipe_version: 554e9776d0fSTrond Myklebust put_pipe_version(gss_auth->net); 5559d3a2260STrond Myklebust err_free_msg: 5569d3a2260STrond Myklebust kfree(gss_msg); 5579d3a2260STrond Myklebust err: 5589d3a2260STrond Myklebust return ERR_PTR(err); 5591da177e4SLinus Torvalds } 5601da177e4SLinus Torvalds 5611da177e4SLinus Torvalds static struct gss_upcall_msg * 562e726340aSTrond Myklebust gss_setup_upcall(struct gss_auth *gss_auth, struct rpc_cred *cred) 5631da177e4SLinus Torvalds { 5647c67db3aSTrond Myklebust struct gss_cred *gss_cred = container_of(cred, 5657c67db3aSTrond Myklebust struct gss_cred, gc_base); 5661da177e4SLinus Torvalds struct gss_upcall_msg *gss_new, *gss_msg; 56704d1532bSNeilBrown kuid_t uid = cred->cr_cred->fsuid; 5681da177e4SLinus Torvalds 569e726340aSTrond Myklebust gss_new = gss_alloc_msg(gss_auth, uid, gss_cred->gc_principal); 570db75b3d6S\"J. Bruce Fields\ if (IS_ERR(gss_new)) 571db75b3d6S\"J. Bruce Fields\ return gss_new; 572053e324fSSuresh Jayaraman gss_msg = gss_add_msg(gss_new); 5731da177e4SLinus Torvalds if (gss_msg == gss_new) { 5741cded9d2SNeilBrown int res; 5757ff13969SReshetova, Elena refcount_inc(&gss_msg->count); 5761cded9d2SNeilBrown res = rpc_queue_upcall(gss_new->pipe, &gss_new->msg); 5771da177e4SLinus Torvalds if (res) { 5781da177e4SLinus Torvalds gss_unhash_msg(gss_new); 5797ff13969SReshetova, Elena refcount_dec(&gss_msg->count); 5801cded9d2SNeilBrown gss_release_msg(gss_new); 5811da177e4SLinus Torvalds gss_msg = ERR_PTR(res); 5821da177e4SLinus Torvalds } 5831da177e4SLinus Torvalds } else 5841da177e4SLinus Torvalds gss_release_msg(gss_new); 5851da177e4SLinus Torvalds return gss_msg; 5861da177e4SLinus Torvalds } 5871da177e4SLinus Torvalds 588b03568c3S\"J. Bruce Fields\ static void warn_gssd(void) 589b03568c3S\"J. Bruce Fields\ { 5900ea9de0eSJeff Layton dprintk("AUTH_GSS upcall failed. Please check user daemon is running.\n"); 591b03568c3S\"J. Bruce Fields\ } 592b03568c3S\"J. Bruce Fields\ 5931da177e4SLinus Torvalds static inline int 5941da177e4SLinus Torvalds gss_refresh_upcall(struct rpc_task *task) 5951da177e4SLinus Torvalds { 596a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 5974a8c1344STrond Myklebust struct gss_auth *gss_auth = container_of(cred->cr_auth, 5981da177e4SLinus Torvalds struct gss_auth, rpc_auth); 5991da177e4SLinus Torvalds struct gss_cred *gss_cred = container_of(cred, 6001da177e4SLinus Torvalds struct gss_cred, gc_base); 6011da177e4SLinus Torvalds struct gss_upcall_msg *gss_msg; 6029beae467SStanislav Kinsbursky struct rpc_pipe *pipe; 6031da177e4SLinus Torvalds int err = 0; 6041da177e4SLinus Torvalds 605e726340aSTrond Myklebust gss_msg = gss_setup_upcall(gss_auth, cred); 606480e3243SRoel Kluin if (PTR_ERR(gss_msg) == -EAGAIN) { 60779a3f20bS\"J. Bruce Fields\ /* XXX: warning on the first, under the assumption we 60879a3f20bS\"J. Bruce Fields\ * shouldn't normally hit this case on a refresh. */ 60979a3f20bS\"J. Bruce Fields\ warn_gssd(); 6106b2e6856STrond Myklebust rpc_sleep_on_timeout(&pipe_version_rpc_waitqueue, 6116b2e6856STrond Myklebust task, NULL, jiffies + (15 * HZ)); 6120c77668dSChuck Lever err = -EAGAIN; 6130c77668dSChuck Lever goto out; 61479a3f20bS\"J. Bruce Fields\ } 6151da177e4SLinus Torvalds if (IS_ERR(gss_msg)) { 6161da177e4SLinus Torvalds err = PTR_ERR(gss_msg); 6171da177e4SLinus Torvalds goto out; 6181da177e4SLinus Torvalds } 6199beae467SStanislav Kinsbursky pipe = gss_msg->pipe; 6209beae467SStanislav Kinsbursky spin_lock(&pipe->lock); 6211da177e4SLinus Torvalds if (gss_cred->gc_upcall != NULL) 6225d00837bSTrond Myklebust rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL); 623126e216aSTrond Myklebust else if (gss_msg->ctx == NULL && gss_msg->msg.errno >= 0) { 6241da177e4SLinus Torvalds gss_cred->gc_upcall = gss_msg; 6251da177e4SLinus Torvalds /* gss_upcall_callback will release the reference to gss_upcall_msg */ 6267ff13969SReshetova, Elena refcount_inc(&gss_msg->count); 6275d00837bSTrond Myklebust rpc_sleep_on(&gss_msg->rpc_waitqueue, task, gss_upcall_callback); 628126e216aSTrond Myklebust } else { 629126e216aSTrond Myklebust gss_handle_downcall_result(gss_cred, gss_msg); 6301da177e4SLinus Torvalds err = gss_msg->msg.errno; 631126e216aSTrond Myklebust } 6329beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 6331da177e4SLinus Torvalds gss_release_msg(gss_msg); 6341da177e4SLinus Torvalds out: 6350c77668dSChuck Lever trace_rpcgss_upcall_result(from_kuid(&init_user_ns, 6360c77668dSChuck Lever cred->cr_cred->fsuid), err); 6371da177e4SLinus Torvalds return err; 6381da177e4SLinus Torvalds } 6391da177e4SLinus Torvalds 6401da177e4SLinus Torvalds static inline int 6411da177e4SLinus Torvalds gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred) 6421da177e4SLinus Torvalds { 643e726340aSTrond Myklebust struct net *net = gss_auth->net; 644abfdbd53STrond Myklebust struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 6459beae467SStanislav Kinsbursky struct rpc_pipe *pipe; 6461da177e4SLinus Torvalds struct rpc_cred *cred = &gss_cred->gc_base; 6471da177e4SLinus Torvalds struct gss_upcall_msg *gss_msg; 6481da177e4SLinus Torvalds DEFINE_WAIT(wait); 649d36ccb9cSTrond Myklebust int err; 6501da177e4SLinus Torvalds 65179a3f20bS\"J. Bruce Fields\ retry: 652d36ccb9cSTrond Myklebust err = 0; 65389f84243SJeff Layton /* if gssd is down, just skip upcalling altogether */ 65489f84243SJeff Layton if (!gssd_running(net)) { 65589f84243SJeff Layton warn_gssd(); 6560c77668dSChuck Lever err = -EACCES; 6570c77668dSChuck Lever goto out; 65889f84243SJeff Layton } 659e726340aSTrond Myklebust gss_msg = gss_setup_upcall(gss_auth, cred); 66079a3f20bS\"J. Bruce Fields\ if (PTR_ERR(gss_msg) == -EAGAIN) { 66179a3f20bS\"J. Bruce Fields\ err = wait_event_interruptible_timeout(pipe_version_waitqueue, 66289f84243SJeff Layton sn->pipe_version >= 0, 15 * HZ); 6632aed8b47STrond Myklebust if (sn->pipe_version < 0) { 664d1a8016aSBryan Schumaker warn_gssd(); 665d1a8016aSBryan Schumaker err = -EACCES; 666d1a8016aSBryan Schumaker } 667d36ccb9cSTrond Myklebust if (err < 0) 66879a3f20bS\"J. Bruce Fields\ goto out; 66979a3f20bS\"J. Bruce Fields\ goto retry; 67079a3f20bS\"J. Bruce Fields\ } 6711da177e4SLinus Torvalds if (IS_ERR(gss_msg)) { 6721da177e4SLinus Torvalds err = PTR_ERR(gss_msg); 6731da177e4SLinus Torvalds goto out; 6741da177e4SLinus Torvalds } 6759beae467SStanislav Kinsbursky pipe = gss_msg->pipe; 6761da177e4SLinus Torvalds for (;;) { 6775afa9133STrond Myklebust prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_KILLABLE); 6789beae467SStanislav Kinsbursky spin_lock(&pipe->lock); 6791da177e4SLinus Torvalds if (gss_msg->ctx != NULL || gss_msg->msg.errno < 0) { 6801da177e4SLinus Torvalds break; 6811da177e4SLinus Torvalds } 6829beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 6835afa9133STrond Myklebust if (fatal_signal_pending(current)) { 6841da177e4SLinus Torvalds err = -ERESTARTSYS; 6851da177e4SLinus Torvalds goto out_intr; 6861da177e4SLinus Torvalds } 6871da177e4SLinus Torvalds schedule(); 6881da177e4SLinus Torvalds } 68974fb8fecSChuck Lever if (gss_msg->ctx) { 69074fb8fecSChuck Lever trace_rpcgss_ctx_init(gss_cred); 6917b6962b0STrond Myklebust gss_cred_set_ctx(cred, gss_msg->ctx); 69274fb8fecSChuck Lever } else { 6931da177e4SLinus Torvalds err = gss_msg->msg.errno; 69474fb8fecSChuck Lever } 6959beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 6961da177e4SLinus Torvalds out_intr: 6971da177e4SLinus Torvalds finish_wait(&gss_msg->waitqueue, &wait); 6981da177e4SLinus Torvalds gss_release_msg(gss_msg); 6991da177e4SLinus Torvalds out: 7000c77668dSChuck Lever trace_rpcgss_upcall_result(from_kuid(&init_user_ns, 7010c77668dSChuck Lever cred->cr_cred->fsuid), err); 7021da177e4SLinus Torvalds return err; 7031da177e4SLinus Torvalds } 7041da177e4SLinus Torvalds 705b18cba09Sminoura makoto static struct gss_upcall_msg * 706b18cba09Sminoura makoto gss_find_downcall(struct rpc_pipe *pipe, kuid_t uid) 707b18cba09Sminoura makoto { 708b18cba09Sminoura makoto struct gss_upcall_msg *pos; 709b18cba09Sminoura makoto list_for_each_entry(pos, &pipe->in_downcall, list) { 710b18cba09Sminoura makoto if (!uid_eq(pos->uid, uid)) 711b18cba09Sminoura makoto continue; 712b18cba09Sminoura makoto if (!rpc_msg_is_inflight(&pos->msg)) 713b18cba09Sminoura makoto continue; 714b18cba09Sminoura makoto refcount_inc(&pos->count); 715b18cba09Sminoura makoto return pos; 716b18cba09Sminoura makoto } 717b18cba09Sminoura makoto return NULL; 718b18cba09Sminoura makoto } 719b18cba09Sminoura makoto 7201da177e4SLinus Torvalds #define MSG_BUF_MAXSIZE 1024 7211da177e4SLinus Torvalds 7221da177e4SLinus Torvalds static ssize_t 7231da177e4SLinus Torvalds gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) 7241da177e4SLinus Torvalds { 7251da177e4SLinus Torvalds const void *p, *end; 7261da177e4SLinus Torvalds void *buf; 7271da177e4SLinus Torvalds struct gss_upcall_msg *gss_msg; 728496ad9aaSAl Viro struct rpc_pipe *pipe = RPC_I(file_inode(filp))->pipe; 7291da177e4SLinus Torvalds struct gss_cl_ctx *ctx; 73090602c7bSEric W. Biederman uid_t id; 73190602c7bSEric W. Biederman kuid_t uid; 7323b68aaeaSTrond Myklebust ssize_t err = -EFBIG; 7331da177e4SLinus Torvalds 7341da177e4SLinus Torvalds if (mlen > MSG_BUF_MAXSIZE) 7351da177e4SLinus Torvalds goto out; 7361da177e4SLinus Torvalds err = -ENOMEM; 7374c2883e7STrond Myklebust buf = kmalloc(mlen, GFP_KERNEL); 7381da177e4SLinus Torvalds if (!buf) 7391da177e4SLinus Torvalds goto out; 7401da177e4SLinus Torvalds 7411da177e4SLinus Torvalds err = -EFAULT; 7421da177e4SLinus Torvalds if (copy_from_user(buf, src, mlen)) 7431da177e4SLinus Torvalds goto err; 7441da177e4SLinus Torvalds 7451da177e4SLinus Torvalds end = (const void *)((char *)buf + mlen); 74690602c7bSEric W. Biederman p = simple_get_bytes(buf, end, &id, sizeof(id)); 7471da177e4SLinus Torvalds if (IS_ERR(p)) { 7481da177e4SLinus Torvalds err = PTR_ERR(p); 7491da177e4SLinus Torvalds goto err; 7501da177e4SLinus Torvalds } 7511da177e4SLinus Torvalds 752283ebe3eSTrond Myklebust uid = make_kuid(current_user_ns(), id); 75390602c7bSEric W. Biederman if (!uid_valid(uid)) { 75490602c7bSEric W. Biederman err = -EINVAL; 75590602c7bSEric W. Biederman goto err; 75690602c7bSEric W. Biederman } 75790602c7bSEric W. Biederman 7581da177e4SLinus Torvalds err = -ENOMEM; 7591da177e4SLinus Torvalds ctx = gss_alloc_context(); 7601da177e4SLinus Torvalds if (ctx == NULL) 7611da177e4SLinus Torvalds goto err; 7623b68aaeaSTrond Myklebust 7633b68aaeaSTrond Myklebust err = -ENOENT; 7643b68aaeaSTrond Myklebust /* Find a matching upcall */ 7659beae467SStanislav Kinsbursky spin_lock(&pipe->lock); 766b18cba09Sminoura makoto gss_msg = gss_find_downcall(pipe, uid); 7673b68aaeaSTrond Myklebust if (gss_msg == NULL) { 7689beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 7693b68aaeaSTrond Myklebust goto err_put_ctx; 7703b68aaeaSTrond Myklebust } 7713b68aaeaSTrond Myklebust list_del_init(&gss_msg->list); 7729beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 7733b68aaeaSTrond Myklebust 7746e84c7b6STrond Myklebust p = gss_fill_context(p, end, ctx, gss_msg->auth->mech); 7751da177e4SLinus Torvalds if (IS_ERR(p)) { 7761da177e4SLinus Torvalds err = PTR_ERR(p); 777486bad2eSJeff Layton switch (err) { 778486bad2eSJeff Layton case -EACCES: 779dc5ddce9SJeff Layton case -EKEYEXPIRED: 780486bad2eSJeff Layton gss_msg->msg.errno = err; 781486bad2eSJeff Layton err = mlen; 782486bad2eSJeff Layton break; 783486bad2eSJeff Layton case -EFAULT: 784486bad2eSJeff Layton case -ENOMEM: 785486bad2eSJeff Layton case -EINVAL: 786486bad2eSJeff Layton case -ENOSYS: 787486bad2eSJeff Layton gss_msg->msg.errno = -EAGAIN; 788486bad2eSJeff Layton break; 789486bad2eSJeff Layton default: 790486bad2eSJeff Layton printk(KERN_CRIT "%s: bad return from " 7916c853099SRandy Dunlap "gss_fill_context: %zd\n", __func__, err); 792437b300cSScott Mayhew gss_msg->msg.errno = -EIO; 793486bad2eSJeff Layton } 7943b68aaeaSTrond Myklebust goto err_release_msg; 7951da177e4SLinus Torvalds } 7961da177e4SLinus Torvalds gss_msg->ctx = gss_get_ctx(ctx); 7973b68aaeaSTrond Myklebust err = mlen; 7983b68aaeaSTrond Myklebust 7993b68aaeaSTrond Myklebust err_release_msg: 8009beae467SStanislav Kinsbursky spin_lock(&pipe->lock); 8011da177e4SLinus Torvalds __gss_unhash_msg(gss_msg); 8029beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 8031da177e4SLinus Torvalds gss_release_msg(gss_msg); 8041da177e4SLinus Torvalds err_put_ctx: 8051da177e4SLinus Torvalds gss_put_ctx(ctx); 8061da177e4SLinus Torvalds err: 8071da177e4SLinus Torvalds kfree(buf); 8081da177e4SLinus Torvalds out: 8091da177e4SLinus Torvalds return err; 8101da177e4SLinus Torvalds } 8111da177e4SLinus Torvalds 81234769fc4S\"J. Bruce Fields\ static int gss_pipe_open(struct inode *inode, int new_version) 813cf81939dS\"J. Bruce Fields\ { 8142aed8b47STrond Myklebust struct net *net = inode->i_sb->s_fs_info; 8152aed8b47STrond Myklebust struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 81634769fc4S\"J. Bruce Fields\ int ret = 0; 81734769fc4S\"J. Bruce Fields\ 81879a3f20bS\"J. Bruce Fields\ spin_lock(&pipe_version_lock); 8192aed8b47STrond Myklebust if (sn->pipe_version < 0) { 82034769fc4S\"J. Bruce Fields\ /* First open of any gss pipe determines the version: */ 8212aed8b47STrond Myklebust sn->pipe_version = new_version; 82279a3f20bS\"J. Bruce Fields\ rpc_wake_up(&pipe_version_rpc_waitqueue); 82379a3f20bS\"J. Bruce Fields\ wake_up(&pipe_version_waitqueue); 8242aed8b47STrond Myklebust } else if (sn->pipe_version != new_version) { 82534769fc4S\"J. Bruce Fields\ /* Trying to open a pipe of a different version */ 82634769fc4S\"J. Bruce Fields\ ret = -EBUSY; 82734769fc4S\"J. Bruce Fields\ goto out; 82879a3f20bS\"J. Bruce Fields\ } 8292aed8b47STrond Myklebust atomic_inc(&sn->pipe_users); 83034769fc4S\"J. Bruce Fields\ out: 83179a3f20bS\"J. Bruce Fields\ spin_unlock(&pipe_version_lock); 83234769fc4S\"J. Bruce Fields\ return ret; 83334769fc4S\"J. Bruce Fields\ 83434769fc4S\"J. Bruce Fields\ } 83534769fc4S\"J. Bruce Fields\ 83634769fc4S\"J. Bruce Fields\ static int gss_pipe_open_v0(struct inode *inode) 83734769fc4S\"J. Bruce Fields\ { 83834769fc4S\"J. Bruce Fields\ return gss_pipe_open(inode, 0); 83934769fc4S\"J. Bruce Fields\ } 84034769fc4S\"J. Bruce Fields\ 84134769fc4S\"J. Bruce Fields\ static int gss_pipe_open_v1(struct inode *inode) 84234769fc4S\"J. Bruce Fields\ { 84334769fc4S\"J. Bruce Fields\ return gss_pipe_open(inode, 1); 844cf81939dS\"J. Bruce Fields\ } 845cf81939dS\"J. Bruce Fields\ 8461da177e4SLinus Torvalds static void 8471da177e4SLinus Torvalds gss_pipe_release(struct inode *inode) 8481da177e4SLinus Torvalds { 8492aed8b47STrond Myklebust struct net *net = inode->i_sb->s_fs_info; 8509beae467SStanislav Kinsbursky struct rpc_pipe *pipe = RPC_I(inode)->pipe; 8511da177e4SLinus Torvalds struct gss_upcall_msg *gss_msg; 8521da177e4SLinus Torvalds 8535a67657aSTrond Myklebust restart: 8549beae467SStanislav Kinsbursky spin_lock(&pipe->lock); 8559beae467SStanislav Kinsbursky list_for_each_entry(gss_msg, &pipe->in_downcall, list) { 8566e84c7b6STrond Myklebust 8575a67657aSTrond Myklebust if (!list_empty(&gss_msg->msg.list)) 8585a67657aSTrond Myklebust continue; 8591da177e4SLinus Torvalds gss_msg->msg.errno = -EPIPE; 8607ff13969SReshetova, Elena refcount_inc(&gss_msg->count); 8611da177e4SLinus Torvalds __gss_unhash_msg(gss_msg); 8629beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 8631da177e4SLinus Torvalds gss_release_msg(gss_msg); 8645a67657aSTrond Myklebust goto restart; 8651da177e4SLinus Torvalds } 8669beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 867cf81939dS\"J. Bruce Fields\ 8682aed8b47STrond Myklebust put_pipe_version(net); 8691da177e4SLinus Torvalds } 8701da177e4SLinus Torvalds 8711da177e4SLinus Torvalds static void 8721da177e4SLinus Torvalds gss_pipe_destroy_msg(struct rpc_pipe_msg *msg) 8731da177e4SLinus Torvalds { 8741da177e4SLinus Torvalds struct gss_upcall_msg *gss_msg = container_of(msg, struct gss_upcall_msg, msg); 8751da177e4SLinus Torvalds 8761da177e4SLinus Torvalds if (msg->errno < 0) { 8777ff13969SReshetova, Elena refcount_inc(&gss_msg->count); 8781da177e4SLinus Torvalds gss_unhash_msg(gss_msg); 879b03568c3S\"J. Bruce Fields\ if (msg->errno == -ETIMEDOUT) 880b03568c3S\"J. Bruce Fields\ warn_gssd(); 8811da177e4SLinus Torvalds gss_release_msg(gss_msg); 8821da177e4SLinus Torvalds } 8831cded9d2SNeilBrown gss_release_msg(gss_msg); 8841da177e4SLinus Torvalds } 8851da177e4SLinus Torvalds 88619172284STrond Myklebust static void gss_pipe_dentry_destroy(struct dentry *dir, 88719172284STrond Myklebust struct rpc_pipe_dir_object *pdo) 888ccdc28f8SStanislav Kinsbursky { 88919172284STrond Myklebust struct gss_pipe *gss_pipe = pdo->pdo_data; 89019172284STrond Myklebust struct rpc_pipe *pipe = gss_pipe->pipe; 891ccdc28f8SStanislav Kinsbursky 89219172284STrond Myklebust if (pipe->dentry != NULL) { 89319172284STrond Myklebust rpc_unlink(pipe->dentry); 89419172284STrond Myklebust pipe->dentry = NULL; 8956b2fddd3STrond Myklebust } 896ccdc28f8SStanislav Kinsbursky } 897ccdc28f8SStanislav Kinsbursky 89819172284STrond Myklebust static int gss_pipe_dentry_create(struct dentry *dir, 89919172284STrond Myklebust struct rpc_pipe_dir_object *pdo) 900ccdc28f8SStanislav Kinsbursky { 90119172284STrond Myklebust struct gss_pipe *p = pdo->pdo_data; 9026b2fddd3STrond Myklebust struct dentry *dentry; 903ccdc28f8SStanislav Kinsbursky 90419172284STrond Myklebust dentry = rpc_mkpipe_dentry(dir, p->name, p->clnt, p->pipe); 90519172284STrond Myklebust if (IS_ERR(dentry)) 90619172284STrond Myklebust return PTR_ERR(dentry); 90719172284STrond Myklebust p->pipe->dentry = dentry; 908ccdc28f8SStanislav Kinsbursky return 0; 90919172284STrond Myklebust } 910ccdc28f8SStanislav Kinsbursky 91119172284STrond Myklebust static const struct rpc_pipe_dir_object_ops gss_pipe_dir_object_ops = { 91219172284STrond Myklebust .create = gss_pipe_dentry_create, 91319172284STrond Myklebust .destroy = gss_pipe_dentry_destroy, 91419172284STrond Myklebust }; 91519172284STrond Myklebust 91619172284STrond Myklebust static struct gss_pipe *gss_pipe_alloc(struct rpc_clnt *clnt, 91719172284STrond Myklebust const char *name, 91819172284STrond Myklebust const struct rpc_pipe_ops *upcall_ops) 91919172284STrond Myklebust { 92019172284STrond Myklebust struct gss_pipe *p; 92119172284STrond Myklebust int err = -ENOMEM; 92219172284STrond Myklebust 92319172284STrond Myklebust p = kmalloc(sizeof(*p), GFP_KERNEL); 92419172284STrond Myklebust if (p == NULL) 92519172284STrond Myklebust goto err; 92619172284STrond Myklebust p->pipe = rpc_mkpipe_data(upcall_ops, RPC_PIPE_WAIT_FOR_OPEN); 92719172284STrond Myklebust if (IS_ERR(p->pipe)) { 92819172284STrond Myklebust err = PTR_ERR(p->pipe); 92919172284STrond Myklebust goto err_free_gss_pipe; 93019172284STrond Myklebust } 93119172284STrond Myklebust p->name = name; 93219172284STrond Myklebust p->clnt = clnt; 933414a6295STrond Myklebust kref_init(&p->kref); 93419172284STrond Myklebust rpc_init_pipe_dir_object(&p->pdo, 93519172284STrond Myklebust &gss_pipe_dir_object_ops, 93619172284STrond Myklebust p); 93719172284STrond Myklebust return p; 93819172284STrond Myklebust err_free_gss_pipe: 93919172284STrond Myklebust kfree(p); 9406b2fddd3STrond Myklebust err: 94119172284STrond Myklebust return ERR_PTR(err); 942ccdc28f8SStanislav Kinsbursky } 943ccdc28f8SStanislav Kinsbursky 944414a6295STrond Myklebust struct gss_alloc_pdo { 945414a6295STrond Myklebust struct rpc_clnt *clnt; 946414a6295STrond Myklebust const char *name; 947414a6295STrond Myklebust const struct rpc_pipe_ops *upcall_ops; 948414a6295STrond Myklebust }; 949414a6295STrond Myklebust 950414a6295STrond Myklebust static int gss_pipe_match_pdo(struct rpc_pipe_dir_object *pdo, void *data) 951414a6295STrond Myklebust { 952414a6295STrond Myklebust struct gss_pipe *gss_pipe; 953414a6295STrond Myklebust struct gss_alloc_pdo *args = data; 954414a6295STrond Myklebust 955414a6295STrond Myklebust if (pdo->pdo_ops != &gss_pipe_dir_object_ops) 956414a6295STrond Myklebust return 0; 957414a6295STrond Myklebust gss_pipe = container_of(pdo, struct gss_pipe, pdo); 958414a6295STrond Myklebust if (strcmp(gss_pipe->name, args->name) != 0) 959414a6295STrond Myklebust return 0; 960414a6295STrond Myklebust if (!kref_get_unless_zero(&gss_pipe->kref)) 961414a6295STrond Myklebust return 0; 962414a6295STrond Myklebust return 1; 963414a6295STrond Myklebust } 964414a6295STrond Myklebust 965414a6295STrond Myklebust static struct rpc_pipe_dir_object *gss_pipe_alloc_pdo(void *data) 966414a6295STrond Myklebust { 967414a6295STrond Myklebust struct gss_pipe *gss_pipe; 968414a6295STrond Myklebust struct gss_alloc_pdo *args = data; 969414a6295STrond Myklebust 970414a6295STrond Myklebust gss_pipe = gss_pipe_alloc(args->clnt, args->name, args->upcall_ops); 971414a6295STrond Myklebust if (!IS_ERR(gss_pipe)) 972414a6295STrond Myklebust return &gss_pipe->pdo; 973414a6295STrond Myklebust return NULL; 974414a6295STrond Myklebust } 975414a6295STrond Myklebust 976414a6295STrond Myklebust static struct gss_pipe *gss_pipe_get(struct rpc_clnt *clnt, 977414a6295STrond Myklebust const char *name, 978414a6295STrond Myklebust const struct rpc_pipe_ops *upcall_ops) 979414a6295STrond Myklebust { 980414a6295STrond Myklebust struct net *net = rpc_net_ns(clnt); 981414a6295STrond Myklebust struct rpc_pipe_dir_object *pdo; 982414a6295STrond Myklebust struct gss_alloc_pdo args = { 983414a6295STrond Myklebust .clnt = clnt, 984414a6295STrond Myklebust .name = name, 985414a6295STrond Myklebust .upcall_ops = upcall_ops, 986414a6295STrond Myklebust }; 987414a6295STrond Myklebust 988414a6295STrond Myklebust pdo = rpc_find_or_alloc_pipe_dir_object(net, 989414a6295STrond Myklebust &clnt->cl_pipedir_objects, 990414a6295STrond Myklebust gss_pipe_match_pdo, 991414a6295STrond Myklebust gss_pipe_alloc_pdo, 992414a6295STrond Myklebust &args); 993414a6295STrond Myklebust if (pdo != NULL) 994414a6295STrond Myklebust return container_of(pdo, struct gss_pipe, pdo); 995414a6295STrond Myklebust return ERR_PTR(-ENOMEM); 996414a6295STrond Myklebust } 997414a6295STrond Myklebust 99819172284STrond Myklebust static void __gss_pipe_free(struct gss_pipe *p) 999ccdc28f8SStanislav Kinsbursky { 100019172284STrond Myklebust struct rpc_clnt *clnt = p->clnt; 100119172284STrond Myklebust struct net *net = rpc_net_ns(clnt); 1002ccdc28f8SStanislav Kinsbursky 100319172284STrond Myklebust rpc_remove_pipe_dir_object(net, 100419172284STrond Myklebust &clnt->cl_pipedir_objects, 100519172284STrond Myklebust &p->pdo); 100619172284STrond Myklebust rpc_destroy_pipe_data(p->pipe); 100719172284STrond Myklebust kfree(p); 1008ccdc28f8SStanislav Kinsbursky } 1009ccdc28f8SStanislav Kinsbursky 1010414a6295STrond Myklebust static void __gss_pipe_release(struct kref *kref) 1011414a6295STrond Myklebust { 1012414a6295STrond Myklebust struct gss_pipe *p = container_of(kref, struct gss_pipe, kref); 1013414a6295STrond Myklebust 1014414a6295STrond Myklebust __gss_pipe_free(p); 1015414a6295STrond Myklebust } 1016414a6295STrond Myklebust 101719172284STrond Myklebust static void gss_pipe_free(struct gss_pipe *p) 1018ccdc28f8SStanislav Kinsbursky { 101919172284STrond Myklebust if (p != NULL) 1020414a6295STrond Myklebust kref_put(&p->kref, __gss_pipe_release); 1021ccdc28f8SStanislav Kinsbursky } 1022ccdc28f8SStanislav Kinsbursky 10231da177e4SLinus Torvalds /* 10241da177e4SLinus Torvalds * NOTE: we have the opportunity to use different 10251da177e4SLinus Torvalds * parameters based on the input flavor (which must be a pseudoflavor) 10261da177e4SLinus Torvalds */ 1027eb6dc19dSTrond Myklebust static struct gss_auth * 102882b98ca5SSargun Dhillon gss_create_new(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) 10291da177e4SLinus Torvalds { 1030c2190661STrond Myklebust rpc_authflavor_t flavor = args->pseudoflavor; 10311da177e4SLinus Torvalds struct gss_auth *gss_auth; 103219172284STrond Myklebust struct gss_pipe *gss_pipe; 10331da177e4SLinus Torvalds struct rpc_auth * auth; 10346a19275aSJ. Bruce Fields int err = -ENOMEM; /* XXX? */ 10351da177e4SLinus Torvalds 10361da177e4SLinus Torvalds if (!try_module_get(THIS_MODULE)) 10376a19275aSJ. Bruce Fields return ERR_PTR(err); 10381da177e4SLinus Torvalds if (!(gss_auth = kmalloc(sizeof(*gss_auth), GFP_KERNEL))) 10391da177e4SLinus Torvalds goto out_dec; 1040eb6dc19dSTrond Myklebust INIT_HLIST_NODE(&gss_auth->hash); 1041bd4a3eb1STrond Myklebust gss_auth->target_name = NULL; 1042c2190661STrond Myklebust if (args->target_name) { 1043c2190661STrond Myklebust gss_auth->target_name = kstrdup(args->target_name, GFP_KERNEL); 1044bd4a3eb1STrond Myklebust if (gss_auth->target_name == NULL) 1045bd4a3eb1STrond Myklebust goto err_free; 1046bd4a3eb1STrond Myklebust } 10471da177e4SLinus Torvalds gss_auth->client = clnt; 10489b1831e5SEric Dumazet gss_auth->net = get_net_track(rpc_net_ns(clnt), &gss_auth->ns_tracker, 10499b1831e5SEric Dumazet GFP_KERNEL); 10506a19275aSJ. Bruce Fields err = -EINVAL; 10511da177e4SLinus Torvalds gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor); 10520c77668dSChuck Lever if (!gss_auth->mech) 1053e726340aSTrond Myklebust goto err_put_net; 10541da177e4SLinus Torvalds gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor); 1055438b6fdeSJ. Bruce Fields if (gss_auth->service == 0) 1056438b6fdeSJ. Bruce Fields goto err_put_mech; 1057a699d65eSTrond Myklebust if (!gssd_running(gss_auth->net)) 1058a699d65eSTrond Myklebust goto err_put_mech; 10591da177e4SLinus Torvalds auth = &gss_auth->rpc_auth; 10601da177e4SLinus Torvalds auth->au_cslack = GSS_CRED_SLACK >> 2; 1061*6e460c23SChuck Lever BUILD_BUG_ON(GSS_KRB5_MAX_SLACK_NEEDED > RPC_MAX_AUTH_SIZE); 1062df513a77SOlga Kornievskaia auth->au_rslack = GSS_KRB5_MAX_SLACK_NEEDED >> 2; 1063a00275baSChuck Lever auth->au_verfsize = GSS_VERF_SLACK >> 2; 106435e77d21SChuck Lever auth->au_ralign = GSS_VERF_SLACK >> 2; 106553bc19f1SChuck Lever __set_bit(RPCAUTH_AUTH_UPDATE_SLACK, &auth->au_flags); 10661da177e4SLinus Torvalds auth->au_ops = &authgss_ops; 10671da177e4SLinus Torvalds auth->au_flavor = flavor; 106865b80179SChuck Lever if (gss_pseudoflavor_to_datatouch(gss_auth->mech, flavor)) 106953bc19f1SChuck Lever __set_bit(RPCAUTH_AUTH_DATATOUCH, &auth->au_flags); 1070331bc71cSTrond Myklebust refcount_set(&auth->au_count, 1); 10710285ed1fSTrond Myklebust kref_init(&gss_auth->kref); 10721da177e4SLinus Torvalds 107319172284STrond Myklebust err = rpcauth_init_credcache(auth); 107419172284STrond Myklebust if (err) 107519172284STrond Myklebust goto err_put_mech; 107634769fc4S\"J. Bruce Fields\ /* 107734769fc4S\"J. Bruce Fields\ * Note: if we created the old pipe first, then someone who 107834769fc4S\"J. Bruce Fields\ * examined the directory at the right moment might conclude 107934769fc4S\"J. Bruce Fields\ * that we supported only the old pipe. So we instead create 108034769fc4S\"J. Bruce Fields\ * the new pipe first. 108134769fc4S\"J. Bruce Fields\ */ 1082414a6295STrond Myklebust gss_pipe = gss_pipe_get(clnt, "gssd", &gss_upcall_ops_v1); 108319172284STrond Myklebust if (IS_ERR(gss_pipe)) { 108419172284STrond Myklebust err = PTR_ERR(gss_pipe); 108519172284STrond Myklebust goto err_destroy_credcache; 10866a19275aSJ. Bruce Fields } 108719172284STrond Myklebust gss_auth->gss_pipe[1] = gss_pipe; 10881da177e4SLinus Torvalds 1089414a6295STrond Myklebust gss_pipe = gss_pipe_get(clnt, gss_auth->mech->gm_name, 109019172284STrond Myklebust &gss_upcall_ops_v0); 109119172284STrond Myklebust if (IS_ERR(gss_pipe)) { 109219172284STrond Myklebust err = PTR_ERR(gss_pipe); 1093c239d83bSStanislav Kinsbursky goto err_destroy_pipe_1; 1094c239d83bSStanislav Kinsbursky } 109519172284STrond Myklebust gss_auth->gss_pipe[0] = gss_pipe; 109607a2bf1dSTrond Myklebust 1097eb6dc19dSTrond Myklebust return gss_auth; 1098c239d83bSStanislav Kinsbursky err_destroy_pipe_1: 1099414a6295STrond Myklebust gss_pipe_free(gss_auth->gss_pipe[1]); 110019172284STrond Myklebust err_destroy_credcache: 110119172284STrond Myklebust rpcauth_destroy_credcache(auth); 11021da177e4SLinus Torvalds err_put_mech: 11031da177e4SLinus Torvalds gss_mech_put(gss_auth->mech); 1104e726340aSTrond Myklebust err_put_net: 11059b1831e5SEric Dumazet put_net_track(gss_auth->net, &gss_auth->ns_tracker); 11061da177e4SLinus Torvalds err_free: 1107bd4a3eb1STrond Myklebust kfree(gss_auth->target_name); 11081da177e4SLinus Torvalds kfree(gss_auth); 11091da177e4SLinus Torvalds out_dec: 11101da177e4SLinus Torvalds module_put(THIS_MODULE); 11110c77668dSChuck Lever trace_rpcgss_createauth(flavor, err); 11126a19275aSJ. Bruce Fields return ERR_PTR(err); 11131da177e4SLinus Torvalds } 11141da177e4SLinus Torvalds 11151da177e4SLinus Torvalds static void 11160285ed1fSTrond Myklebust gss_free(struct gss_auth *gss_auth) 11170285ed1fSTrond Myklebust { 111819172284STrond Myklebust gss_pipe_free(gss_auth->gss_pipe[0]); 111919172284STrond Myklebust gss_pipe_free(gss_auth->gss_pipe[1]); 11200285ed1fSTrond Myklebust gss_mech_put(gss_auth->mech); 11219b1831e5SEric Dumazet put_net_track(gss_auth->net, &gss_auth->ns_tracker); 1122bd4a3eb1STrond Myklebust kfree(gss_auth->target_name); 11230285ed1fSTrond Myklebust 11240285ed1fSTrond Myklebust kfree(gss_auth); 11250285ed1fSTrond Myklebust module_put(THIS_MODULE); 11260285ed1fSTrond Myklebust } 11270285ed1fSTrond Myklebust 11280285ed1fSTrond Myklebust static void 11290285ed1fSTrond Myklebust gss_free_callback(struct kref *kref) 11300285ed1fSTrond Myklebust { 11310285ed1fSTrond Myklebust struct gss_auth *gss_auth = container_of(kref, struct gss_auth, kref); 11320285ed1fSTrond Myklebust 11330285ed1fSTrond Myklebust gss_free(gss_auth); 11340285ed1fSTrond Myklebust } 11350285ed1fSTrond Myklebust 11360285ed1fSTrond Myklebust static void 11379eb2ddb4STrond Myklebust gss_put_auth(struct gss_auth *gss_auth) 11389eb2ddb4STrond Myklebust { 11399eb2ddb4STrond Myklebust kref_put(&gss_auth->kref, gss_free_callback); 11409eb2ddb4STrond Myklebust } 11419eb2ddb4STrond Myklebust 11429eb2ddb4STrond Myklebust static void 11431da177e4SLinus Torvalds gss_destroy(struct rpc_auth *auth) 11441da177e4SLinus Torvalds { 114519172284STrond Myklebust struct gss_auth *gss_auth = container_of(auth, 114619172284STrond Myklebust struct gss_auth, rpc_auth); 11471da177e4SLinus Torvalds 1148eb6dc19dSTrond Myklebust if (hash_hashed(&gss_auth->hash)) { 1149eb6dc19dSTrond Myklebust spin_lock(&gss_auth_hash_lock); 1150eb6dc19dSTrond Myklebust hash_del(&gss_auth->hash); 1151eb6dc19dSTrond Myklebust spin_unlock(&gss_auth_hash_lock); 1152eb6dc19dSTrond Myklebust } 1153eb6dc19dSTrond Myklebust 115419172284STrond Myklebust gss_pipe_free(gss_auth->gss_pipe[0]); 115519172284STrond Myklebust gss_auth->gss_pipe[0] = NULL; 115619172284STrond Myklebust gss_pipe_free(gss_auth->gss_pipe[1]); 115719172284STrond Myklebust gss_auth->gss_pipe[1] = NULL; 11583ab9bb72STrond Myklebust rpcauth_destroy_credcache(auth); 11593ab9bb72STrond Myklebust 11609eb2ddb4STrond Myklebust gss_put_auth(gss_auth); 11611da177e4SLinus Torvalds } 11621da177e4SLinus Torvalds 1163a0f6ed8eSJ. Bruce Fields /* 1164a0f6ed8eSJ. Bruce Fields * Auths may be shared between rpc clients that were cloned from a 1165a0f6ed8eSJ. Bruce Fields * common client with the same xprt, if they also share the flavor and 1166a0f6ed8eSJ. Bruce Fields * target_name. 1167a0f6ed8eSJ. Bruce Fields * 1168a0f6ed8eSJ. Bruce Fields * The auth is looked up from the oldest parent sharing the same 1169a0f6ed8eSJ. Bruce Fields * cl_xprt, and the auth itself references only that common parent 1170a0f6ed8eSJ. Bruce Fields * (which is guaranteed to last as long as any of its descendants). 1171a0f6ed8eSJ. Bruce Fields */ 1172eb6dc19dSTrond Myklebust static struct gss_auth * 117382b98ca5SSargun Dhillon gss_auth_find_or_add_hashed(const struct rpc_auth_create_args *args, 1174eb6dc19dSTrond Myklebust struct rpc_clnt *clnt, 1175eb6dc19dSTrond Myklebust struct gss_auth *new) 1176eb6dc19dSTrond Myklebust { 1177eb6dc19dSTrond Myklebust struct gss_auth *gss_auth; 1178eb6dc19dSTrond Myklebust unsigned long hashval = (unsigned long)clnt; 1179eb6dc19dSTrond Myklebust 1180eb6dc19dSTrond Myklebust spin_lock(&gss_auth_hash_lock); 1181eb6dc19dSTrond Myklebust hash_for_each_possible(gss_auth_hash_table, 1182eb6dc19dSTrond Myklebust gss_auth, 1183eb6dc19dSTrond Myklebust hash, 1184eb6dc19dSTrond Myklebust hashval) { 1185a0f6ed8eSJ. Bruce Fields if (gss_auth->client != clnt) 1186a0f6ed8eSJ. Bruce Fields continue; 1187eb6dc19dSTrond Myklebust if (gss_auth->rpc_auth.au_flavor != args->pseudoflavor) 1188eb6dc19dSTrond Myklebust continue; 1189eb6dc19dSTrond Myklebust if (gss_auth->target_name != args->target_name) { 1190eb6dc19dSTrond Myklebust if (gss_auth->target_name == NULL) 1191eb6dc19dSTrond Myklebust continue; 1192eb6dc19dSTrond Myklebust if (args->target_name == NULL) 1193eb6dc19dSTrond Myklebust continue; 1194eb6dc19dSTrond Myklebust if (strcmp(gss_auth->target_name, args->target_name)) 1195eb6dc19dSTrond Myklebust continue; 1196eb6dc19dSTrond Myklebust } 1197331bc71cSTrond Myklebust if (!refcount_inc_not_zero(&gss_auth->rpc_auth.au_count)) 1198eb6dc19dSTrond Myklebust continue; 1199eb6dc19dSTrond Myklebust goto out; 1200eb6dc19dSTrond Myklebust } 1201eb6dc19dSTrond Myklebust if (new) 1202eb6dc19dSTrond Myklebust hash_add(gss_auth_hash_table, &new->hash, hashval); 1203eb6dc19dSTrond Myklebust gss_auth = new; 1204eb6dc19dSTrond Myklebust out: 1205eb6dc19dSTrond Myklebust spin_unlock(&gss_auth_hash_lock); 1206eb6dc19dSTrond Myklebust return gss_auth; 1207eb6dc19dSTrond Myklebust } 1208eb6dc19dSTrond Myklebust 1209eb6dc19dSTrond Myklebust static struct gss_auth * 121082b98ca5SSargun Dhillon gss_create_hashed(const struct rpc_auth_create_args *args, 121182b98ca5SSargun Dhillon struct rpc_clnt *clnt) 1212eb6dc19dSTrond Myklebust { 1213eb6dc19dSTrond Myklebust struct gss_auth *gss_auth; 1214eb6dc19dSTrond Myklebust struct gss_auth *new; 1215eb6dc19dSTrond Myklebust 1216eb6dc19dSTrond Myklebust gss_auth = gss_auth_find_or_add_hashed(args, clnt, NULL); 1217eb6dc19dSTrond Myklebust if (gss_auth != NULL) 1218eb6dc19dSTrond Myklebust goto out; 1219eb6dc19dSTrond Myklebust new = gss_create_new(args, clnt); 1220eb6dc19dSTrond Myklebust if (IS_ERR(new)) 1221eb6dc19dSTrond Myklebust return new; 1222eb6dc19dSTrond Myklebust gss_auth = gss_auth_find_or_add_hashed(args, clnt, new); 1223eb6dc19dSTrond Myklebust if (gss_auth != new) 1224eb6dc19dSTrond Myklebust gss_destroy(&new->rpc_auth); 1225eb6dc19dSTrond Myklebust out: 1226eb6dc19dSTrond Myklebust return gss_auth; 1227eb6dc19dSTrond Myklebust } 1228eb6dc19dSTrond Myklebust 1229eb6dc19dSTrond Myklebust static struct rpc_auth * 123082b98ca5SSargun Dhillon gss_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) 1231eb6dc19dSTrond Myklebust { 1232eb6dc19dSTrond Myklebust struct gss_auth *gss_auth; 1233ad01b2c6STrond Myklebust struct rpc_xprt_switch *xps = rcu_access_pointer(clnt->cl_xpi.xpi_xpswitch); 1234eb6dc19dSTrond Myklebust 1235eb6dc19dSTrond Myklebust while (clnt != clnt->cl_parent) { 1236eb6dc19dSTrond Myklebust struct rpc_clnt *parent = clnt->cl_parent; 1237eb6dc19dSTrond Myklebust /* Find the original parent for this transport */ 1238ad01b2c6STrond Myklebust if (rcu_access_pointer(parent->cl_xpi.xpi_xpswitch) != xps) 1239eb6dc19dSTrond Myklebust break; 1240eb6dc19dSTrond Myklebust clnt = parent; 1241eb6dc19dSTrond Myklebust } 1242eb6dc19dSTrond Myklebust 1243eb6dc19dSTrond Myklebust gss_auth = gss_create_hashed(args, clnt); 1244eb6dc19dSTrond Myklebust if (IS_ERR(gss_auth)) 1245eb6dc19dSTrond Myklebust return ERR_CAST(gss_auth); 1246eb6dc19dSTrond Myklebust return &gss_auth->rpc_auth; 1247eb6dc19dSTrond Myklebust } 1248eb6dc19dSTrond Myklebust 1249a652a4bcSTrond Myklebust static struct gss_cred * 1250a652a4bcSTrond Myklebust gss_dup_cred(struct gss_auth *gss_auth, struct gss_cred *gss_cred) 1251a652a4bcSTrond Myklebust { 1252a652a4bcSTrond Myklebust struct gss_cred *new; 1253a652a4bcSTrond Myklebust 1254a652a4bcSTrond Myklebust /* Make a copy of the cred so that we can reference count it */ 12554c2883e7STrond Myklebust new = kzalloc(sizeof(*gss_cred), GFP_KERNEL); 1256a652a4bcSTrond Myklebust if (new) { 1257a652a4bcSTrond Myklebust struct auth_cred acred = { 12588276c902SNeilBrown .cred = gss_cred->gc_base.cr_cred, 1259a652a4bcSTrond Myklebust }; 1260a652a4bcSTrond Myklebust struct gss_cl_ctx *ctx = 1261a652a4bcSTrond Myklebust rcu_dereference_protected(gss_cred->gc_ctx, 1); 1262a652a4bcSTrond Myklebust 1263a652a4bcSTrond Myklebust rpcauth_init_cred(&new->gc_base, &acred, 1264a652a4bcSTrond Myklebust &gss_auth->rpc_auth, 1265a652a4bcSTrond Myklebust &gss_nullops); 1266a652a4bcSTrond Myklebust new->gc_base.cr_flags = 1UL << RPCAUTH_CRED_UPTODATE; 1267a652a4bcSTrond Myklebust new->gc_service = gss_cred->gc_service; 1268a652a4bcSTrond Myklebust new->gc_principal = gss_cred->gc_principal; 1269a652a4bcSTrond Myklebust kref_get(&gss_auth->kref); 1270a652a4bcSTrond Myklebust rcu_assign_pointer(new->gc_ctx, ctx); 1271a652a4bcSTrond Myklebust gss_get_ctx(ctx); 1272a652a4bcSTrond Myklebust } 1273a652a4bcSTrond Myklebust return new; 1274a652a4bcSTrond Myklebust } 1275a652a4bcSTrond Myklebust 12760df7fb74STrond Myklebust /* 1277a652a4bcSTrond Myklebust * gss_send_destroy_context will cause the RPCSEC_GSS to send a NULL RPC call 12780df7fb74STrond Myklebust * to the server with the GSS control procedure field set to 12790df7fb74STrond Myklebust * RPC_GSS_PROC_DESTROY. This should normally cause the server to release 12800df7fb74STrond Myklebust * all RPCSEC_GSS state associated with that context. 12810df7fb74STrond Myklebust */ 1282a652a4bcSTrond Myklebust static void 1283a652a4bcSTrond Myklebust gss_send_destroy_context(struct rpc_cred *cred) 12840df7fb74STrond Myklebust { 12850df7fb74STrond Myklebust struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 12860df7fb74STrond Myklebust struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth); 1287c5e6aecdSJeff Layton struct gss_cl_ctx *ctx = rcu_dereference_protected(gss_cred->gc_ctx, 1); 1288a652a4bcSTrond Myklebust struct gss_cred *new; 12890df7fb74STrond Myklebust struct rpc_task *task; 12900df7fb74STrond Myklebust 1291a652a4bcSTrond Myklebust new = gss_dup_cred(gss_auth, gss_cred); 1292a652a4bcSTrond Myklebust if (new) { 1293c5e6aecdSJeff Layton ctx->gc_proc = RPC_GSS_PROC_DESTROY; 12940df7fb74STrond Myklebust 129574fb8fecSChuck Lever trace_rpcgss_ctx_destroy(gss_cred); 1296a652a4bcSTrond Myklebust task = rpc_call_null(gss_auth->client, &new->gc_base, 12976fc3737aSChuck Lever RPC_TASK_ASYNC); 12980df7fb74STrond Myklebust if (!IS_ERR(task)) 12990df7fb74STrond Myklebust rpc_put_task(task); 13000df7fb74STrond Myklebust 1301a652a4bcSTrond Myklebust put_rpccred(&new->gc_base); 1302a652a4bcSTrond Myklebust } 13030df7fb74STrond Myklebust } 13040df7fb74STrond Myklebust 13050df7fb74STrond Myklebust /* gss_destroy_cred (and gss_free_ctx) are used to clean up after failure 13061da177e4SLinus Torvalds * to create a new cred or context, so they check that things have been 13071da177e4SLinus Torvalds * allocated before freeing them. */ 13081da177e4SLinus Torvalds static void 13095d28dc82STrond Myklebust gss_do_free_ctx(struct gss_cl_ctx *ctx) 13101da177e4SLinus Torvalds { 13110d8a3746STrond Myklebust gss_delete_sec_context(&ctx->gc_gss_ctx); 13121da177e4SLinus Torvalds kfree(ctx->gc_wire_ctx.data); 13132004c726SJeff Layton kfree(ctx->gc_acceptor.data); 13141da177e4SLinus Torvalds kfree(ctx); 13151da177e4SLinus Torvalds } 13161da177e4SLinus Torvalds 13171da177e4SLinus Torvalds static void 13185d28dc82STrond Myklebust gss_free_ctx_callback(struct rcu_head *head) 13195d28dc82STrond Myklebust { 13205d28dc82STrond Myklebust struct gss_cl_ctx *ctx = container_of(head, struct gss_cl_ctx, gc_rcu); 13215d28dc82STrond Myklebust gss_do_free_ctx(ctx); 13225d28dc82STrond Myklebust } 13235d28dc82STrond Myklebust 13245d28dc82STrond Myklebust static void 13255d28dc82STrond Myklebust gss_free_ctx(struct gss_cl_ctx *ctx) 13265d28dc82STrond Myklebust { 13275d28dc82STrond Myklebust call_rcu(&ctx->gc_rcu, gss_free_ctx_callback); 13285d28dc82STrond Myklebust } 13295d28dc82STrond Myklebust 13305d28dc82STrond Myklebust static void 133131be5bf1STrond Myklebust gss_free_cred(struct gss_cred *gss_cred) 13321da177e4SLinus Torvalds { 133331be5bf1STrond Myklebust kfree(gss_cred); 133431be5bf1STrond Myklebust } 13351da177e4SLinus Torvalds 133631be5bf1STrond Myklebust static void 133731be5bf1STrond Myklebust gss_free_cred_callback(struct rcu_head *head) 133831be5bf1STrond Myklebust { 133931be5bf1STrond Myklebust struct gss_cred *gss_cred = container_of(head, struct gss_cred, gc_base.cr_rcu); 134031be5bf1STrond Myklebust gss_free_cred(gss_cred); 134131be5bf1STrond Myklebust } 13421da177e4SLinus Torvalds 134331be5bf1STrond Myklebust static void 13446dcd3926SJeff Layton gss_destroy_nullcred(struct rpc_cred *cred) 134531be5bf1STrond Myklebust { 13465d28dc82STrond Myklebust struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 13470285ed1fSTrond Myklebust struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth); 1348c5e6aecdSJeff Layton struct gss_cl_ctx *ctx = rcu_dereference_protected(gss_cred->gc_ctx, 1); 13495d28dc82STrond Myklebust 1350a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(gss_cred->gc_ctx, NULL); 135197f68c6bSNeilBrown put_cred(cred->cr_cred); 135231be5bf1STrond Myklebust call_rcu(&cred->cr_rcu, gss_free_cred_callback); 13535d28dc82STrond Myklebust if (ctx) 13545d28dc82STrond Myklebust gss_put_ctx(ctx); 13559eb2ddb4STrond Myklebust gss_put_auth(gss_auth); 13561da177e4SLinus Torvalds } 13571da177e4SLinus Torvalds 13586dcd3926SJeff Layton static void 13596dcd3926SJeff Layton gss_destroy_cred(struct rpc_cred *cred) 13606dcd3926SJeff Layton { 1361a652a4bcSTrond Myklebust if (test_and_clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0) 1362a652a4bcSTrond Myklebust gss_send_destroy_context(cred); 13636dcd3926SJeff Layton gss_destroy_nullcred(cred); 13646dcd3926SJeff Layton } 13656dcd3926SJeff Layton 1366a960f8d6SFrank Sorenson static int 1367a960f8d6SFrank Sorenson gss_hash_cred(struct auth_cred *acred, unsigned int hashbits) 1368a960f8d6SFrank Sorenson { 13698276c902SNeilBrown return hash_64(from_kuid(&init_user_ns, acred->cred->fsuid), hashbits); 1370a960f8d6SFrank Sorenson } 1371a960f8d6SFrank Sorenson 13721da177e4SLinus Torvalds /* 13731da177e4SLinus Torvalds * Lookup RPCSEC_GSS cred for the current process 13741da177e4SLinus Torvalds */ 13754b8dbdfbSTrond Myklebust static struct rpc_cred *gss_lookup_cred(struct rpc_auth *auth, 13764b8dbdfbSTrond Myklebust struct auth_cred *acred, int flags) 13771da177e4SLinus Torvalds { 13784b8dbdfbSTrond Myklebust return rpcauth_lookup_credcache(auth, acred, flags, 13794b8dbdfbSTrond Myklebust rpc_task_gfp_mask()); 13801da177e4SLinus Torvalds } 13811da177e4SLinus Torvalds 13821da177e4SLinus Torvalds static struct rpc_cred * 13833c6e0bc8SJeff Layton gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags, gfp_t gfp) 13841da177e4SLinus Torvalds { 13851da177e4SLinus Torvalds struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth); 13861da177e4SLinus Torvalds struct gss_cred *cred = NULL; 13871da177e4SLinus Torvalds int err = -ENOMEM; 13881da177e4SLinus Torvalds 13893c6e0bc8SJeff Layton if (!(cred = kzalloc(sizeof(*cred), gfp))) 13901da177e4SLinus Torvalds goto out_err; 13911da177e4SLinus Torvalds 13925fe4755eSTrond Myklebust rpcauth_init_cred(&cred->gc_base, acred, auth, &gss_credops); 13931da177e4SLinus Torvalds /* 13941da177e4SLinus Torvalds * Note: in order to force a call to call_refresh(), we deliberately 13951da177e4SLinus Torvalds * fail to flag the credential as RPCAUTH_CRED_UPTODATE. 13961da177e4SLinus Torvalds */ 1397fc432dd9STrond Myklebust cred->gc_base.cr_flags = 1UL << RPCAUTH_CRED_NEW; 13981da177e4SLinus Torvalds cred->gc_service = gss_auth->service; 139968c97153STrond Myklebust cred->gc_principal = acred->principal; 14000285ed1fSTrond Myklebust kref_get(&gss_auth->kref); 14011da177e4SLinus Torvalds return &cred->gc_base; 14021da177e4SLinus Torvalds 14031da177e4SLinus Torvalds out_err: 14041da177e4SLinus Torvalds return ERR_PTR(err); 14051da177e4SLinus Torvalds } 14061da177e4SLinus Torvalds 14071da177e4SLinus Torvalds static int 1408fba3bad4STrond Myklebust gss_cred_init(struct rpc_auth *auth, struct rpc_cred *cred) 1409fba3bad4STrond Myklebust { 1410fba3bad4STrond Myklebust struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth); 1411fba3bad4STrond Myklebust struct gss_cred *gss_cred = container_of(cred,struct gss_cred, gc_base); 1412fba3bad4STrond Myklebust int err; 1413fba3bad4STrond Myklebust 1414fba3bad4STrond Myklebust do { 1415fba3bad4STrond Myklebust err = gss_create_upcall(gss_auth, gss_cred); 1416fba3bad4STrond Myklebust } while (err == -EAGAIN); 1417fba3bad4STrond Myklebust return err; 1418fba3bad4STrond Myklebust } 1419fba3bad4STrond Myklebust 1420a0337d1dSJeff Layton static char * 1421a0337d1dSJeff Layton gss_stringify_acceptor(struct rpc_cred *cred) 1422a0337d1dSJeff Layton { 1423c5e6aecdSJeff Layton char *string = NULL; 1424a0337d1dSJeff Layton struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 1425c5e6aecdSJeff Layton struct gss_cl_ctx *ctx; 1426b3ecba09SJeff Layton unsigned int len; 1427c5e6aecdSJeff Layton struct xdr_netobj *acceptor; 1428c5e6aecdSJeff Layton 1429c5e6aecdSJeff Layton rcu_read_lock(); 1430c5e6aecdSJeff Layton ctx = rcu_dereference(gss_cred->gc_ctx); 1431c5e6aecdSJeff Layton if (!ctx) 1432c5e6aecdSJeff Layton goto out; 1433c5e6aecdSJeff Layton 1434b3ecba09SJeff Layton len = ctx->gc_acceptor.len; 1435b3ecba09SJeff Layton rcu_read_unlock(); 1436a0337d1dSJeff Layton 1437a0337d1dSJeff Layton /* no point if there's no string */ 1438b3ecba09SJeff Layton if (!len) 1439b3ecba09SJeff Layton return NULL; 1440b3ecba09SJeff Layton realloc: 1441b3ecba09SJeff Layton string = kmalloc(len + 1, GFP_KERNEL); 1442a0337d1dSJeff Layton if (!string) 1443b3ecba09SJeff Layton return NULL; 1444b3ecba09SJeff Layton 1445b3ecba09SJeff Layton rcu_read_lock(); 1446b3ecba09SJeff Layton ctx = rcu_dereference(gss_cred->gc_ctx); 1447b3ecba09SJeff Layton 1448b3ecba09SJeff Layton /* did the ctx disappear or was it replaced by one with no acceptor? */ 1449b3ecba09SJeff Layton if (!ctx || !ctx->gc_acceptor.len) { 1450b3ecba09SJeff Layton kfree(string); 1451b3ecba09SJeff Layton string = NULL; 1452c5e6aecdSJeff Layton goto out; 1453b3ecba09SJeff Layton } 1454b3ecba09SJeff Layton 1455b3ecba09SJeff Layton acceptor = &ctx->gc_acceptor; 1456b3ecba09SJeff Layton 1457b3ecba09SJeff Layton /* 1458b3ecba09SJeff Layton * Did we find a new acceptor that's longer than the original? Allocate 1459b3ecba09SJeff Layton * a longer buffer and try again. 1460b3ecba09SJeff Layton */ 1461b3ecba09SJeff Layton if (len < acceptor->len) { 1462b3ecba09SJeff Layton len = acceptor->len; 1463b3ecba09SJeff Layton rcu_read_unlock(); 1464b3ecba09SJeff Layton kfree(string); 1465b3ecba09SJeff Layton goto realloc; 1466b3ecba09SJeff Layton } 1467a0337d1dSJeff Layton 1468a0337d1dSJeff Layton memcpy(string, acceptor->data, acceptor->len); 1469a0337d1dSJeff Layton string[acceptor->len] = '\0'; 1470c5e6aecdSJeff Layton out: 1471c5e6aecdSJeff Layton rcu_read_unlock(); 1472a0337d1dSJeff Layton return string; 1473a0337d1dSJeff Layton } 1474a0337d1dSJeff Layton 14754de6caa2SAndy Adamson /* 14764de6caa2SAndy Adamson * Returns -EACCES if GSS context is NULL or will expire within the 14774de6caa2SAndy Adamson * timeout (miliseconds) 14784de6caa2SAndy Adamson */ 14794de6caa2SAndy Adamson static int 14804de6caa2SAndy Adamson gss_key_timeout(struct rpc_cred *rc) 14814de6caa2SAndy Adamson { 14824de6caa2SAndy Adamson struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); 1483c5e6aecdSJeff Layton struct gss_cl_ctx *ctx; 1484cc6a7aabSArnd Bergmann unsigned long timeout = jiffies + (gss_key_expire_timeo * HZ); 1485cc6a7aabSArnd Bergmann int ret = 0; 14864de6caa2SAndy Adamson 1487c5e6aecdSJeff Layton rcu_read_lock(); 1488c5e6aecdSJeff Layton ctx = rcu_dereference(gss_cred->gc_ctx); 1489cc6a7aabSArnd Bergmann if (!ctx || time_after(timeout, ctx->gc_expiry)) 1490cc6a7aabSArnd Bergmann ret = -EACCES; 1491c5e6aecdSJeff Layton rcu_read_unlock(); 1492cc6a7aabSArnd Bergmann 1493cc6a7aabSArnd Bergmann return ret; 14944de6caa2SAndy Adamson } 14954de6caa2SAndy Adamson 1496fba3bad4STrond Myklebust static int 14978a317760STrond Myklebust gss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags) 14981da177e4SLinus Torvalds { 14991da177e4SLinus Torvalds struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); 1500c5e6aecdSJeff Layton struct gss_cl_ctx *ctx; 15014de6caa2SAndy Adamson int ret; 15021da177e4SLinus Torvalds 1503cd019f75STrond Myklebust if (test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags)) 15048a317760STrond Myklebust goto out; 15051da177e4SLinus Torvalds /* Don't match with creds that have expired. */ 1506c5e6aecdSJeff Layton rcu_read_lock(); 1507c5e6aecdSJeff Layton ctx = rcu_dereference(gss_cred->gc_ctx); 1508c5e6aecdSJeff Layton if (!ctx || time_after(jiffies, ctx->gc_expiry)) { 1509c5e6aecdSJeff Layton rcu_read_unlock(); 1510cd019f75STrond Myklebust return 0; 1511c5e6aecdSJeff Layton } 1512c5e6aecdSJeff Layton rcu_read_unlock(); 1513cd019f75STrond Myklebust if (!test_bit(RPCAUTH_CRED_UPTODATE, &rc->cr_flags)) 15141da177e4SLinus Torvalds return 0; 15158a317760STrond Myklebust out: 151668c97153STrond Myklebust if (acred->principal != NULL) { 151768c97153STrond Myklebust if (gss_cred->gc_principal == NULL) 151868c97153STrond Myklebust return 0; 15194de6caa2SAndy Adamson ret = strcmp(acred->principal, gss_cred->gc_principal) == 0; 1520ddf529eeSNeilBrown } else { 152168c97153STrond Myklebust if (gss_cred->gc_principal != NULL) 15227c67db3aSTrond Myklebust return 0; 152304d1532bSNeilBrown ret = uid_eq(rc->cr_cred->fsuid, acred->cred->fsuid); 15244de6caa2SAndy Adamson } 15254de6caa2SAndy Adamson return ret; 15261da177e4SLinus Torvalds } 15271da177e4SLinus Torvalds 15281da177e4SLinus Torvalds /* 15291da177e4SLinus Torvalds * Marshal credentials. 1530e8680a24SChuck Lever * 1531e8680a24SChuck Lever * The expensive part is computing the verifier. We can't cache a 1532e8680a24SChuck Lever * pre-computed version of the verifier because the seqno, which 1533e8680a24SChuck Lever * is different every time, is included in the MIC. 15341da177e4SLinus Torvalds */ 1535e8680a24SChuck Lever static int gss_marshal(struct rpc_task *task, struct xdr_stream *xdr) 15361da177e4SLinus Torvalds { 1537a17c2153STrond Myklebust struct rpc_rqst *req = task->tk_rqstp; 1538a17c2153STrond Myklebust struct rpc_cred *cred = req->rq_cred; 15391da177e4SLinus Torvalds struct gss_cred *gss_cred = container_of(cred, struct gss_cred, 15401da177e4SLinus Torvalds gc_base); 15411da177e4SLinus Torvalds struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 1542e8680a24SChuck Lever __be32 *p, *cred_len; 15431da177e4SLinus Torvalds u32 maj_stat = 0; 15441da177e4SLinus Torvalds struct xdr_netobj mic; 15451da177e4SLinus Torvalds struct kvec iov; 15461da177e4SLinus Torvalds struct xdr_buf verf_buf; 15470c77668dSChuck Lever int status; 15481da177e4SLinus Torvalds 1549e8680a24SChuck Lever /* Credential */ 1550e8680a24SChuck Lever 1551e8680a24SChuck Lever p = xdr_reserve_space(xdr, 7 * sizeof(*p) + 1552e8680a24SChuck Lever ctx->gc_wire_ctx.len); 1553e8680a24SChuck Lever if (!p) 15540c77668dSChuck Lever goto marshal_failed; 1555e8680a24SChuck Lever *p++ = rpc_auth_gss; 15561da177e4SLinus Torvalds cred_len = p++; 15571da177e4SLinus Torvalds 15581da177e4SLinus Torvalds spin_lock(&ctx->gc_seq_lock); 155997b78ae9STrond Myklebust req->rq_seqno = (ctx->gc_seq < MAXSEQ) ? ctx->gc_seq++ : MAXSEQ; 15601da177e4SLinus Torvalds spin_unlock(&ctx->gc_seq_lock); 156197b78ae9STrond Myklebust if (req->rq_seqno == MAXSEQ) 15620c77668dSChuck Lever goto expired; 15630c77668dSChuck Lever trace_rpcgss_seqno(task); 15641da177e4SLinus Torvalds 1565e8680a24SChuck Lever *p++ = cpu_to_be32(RPC_GSS_VERSION); 1566e8680a24SChuck Lever *p++ = cpu_to_be32(ctx->gc_proc); 1567e8680a24SChuck Lever *p++ = cpu_to_be32(req->rq_seqno); 1568e8680a24SChuck Lever *p++ = cpu_to_be32(gss_cred->gc_service); 15691da177e4SLinus Torvalds p = xdr_encode_netobj(p, &ctx->gc_wire_ctx); 1570e8680a24SChuck Lever *cred_len = cpu_to_be32((p - (cred_len + 1)) << 2); 1571e8680a24SChuck Lever 1572e8680a24SChuck Lever /* Verifier */ 15731da177e4SLinus Torvalds 15741da177e4SLinus Torvalds /* We compute the checksum for the verifier over the xdr-encoded bytes 15751da177e4SLinus Torvalds * starting with the xid and ending at the end of the credential: */ 1576067fb11bSChuck Lever iov.iov_base = req->rq_snd_buf.head[0].iov_base; 15771da177e4SLinus Torvalds iov.iov_len = (u8 *)p - (u8 *)iov.iov_base; 15781da177e4SLinus Torvalds xdr_buf_from_iov(&iov, &verf_buf); 15791da177e4SLinus Torvalds 1580e8680a24SChuck Lever p = xdr_reserve_space(xdr, sizeof(*p)); 1581e8680a24SChuck Lever if (!p) 15820c77668dSChuck Lever goto marshal_failed; 1583e8680a24SChuck Lever *p++ = rpc_auth_gss; 15841da177e4SLinus Torvalds mic.data = (u8 *)(p + 1); 158500fd6e14SJ. Bruce Fields maj_stat = gss_get_mic(ctx->gc_gss_ctx, &verf_buf, &mic); 1586e8680a24SChuck Lever if (maj_stat == GSS_S_CONTEXT_EXPIRED) 15870c77668dSChuck Lever goto expired; 1588e8680a24SChuck Lever else if (maj_stat != 0) 15890c77668dSChuck Lever goto bad_mic; 1590e8680a24SChuck Lever if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0) 15910c77668dSChuck Lever goto marshal_failed; 15920c77668dSChuck Lever status = 0; 15930c77668dSChuck Lever out: 15941da177e4SLinus Torvalds gss_put_ctx(ctx); 15950c77668dSChuck Lever return status; 15960c77668dSChuck Lever expired: 159797b78ae9STrond Myklebust clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 15980c77668dSChuck Lever status = -EKEYEXPIRED; 15990c77668dSChuck Lever goto out; 16000c77668dSChuck Lever marshal_failed: 16010c77668dSChuck Lever status = -EMSGSIZE; 16020c77668dSChuck Lever goto out; 16030c77668dSChuck Lever bad_mic: 16040c77668dSChuck Lever trace_rpcgss_get_mic(task, maj_stat); 16050c77668dSChuck Lever status = -EIO; 16060c77668dSChuck Lever goto out; 16071da177e4SLinus Torvalds } 16081da177e4SLinus Torvalds 1609cd019f75STrond Myklebust static int gss_renew_cred(struct rpc_task *task) 1610cd019f75STrond Myklebust { 1611a17c2153STrond Myklebust struct rpc_cred *oldcred = task->tk_rqstp->rq_cred; 1612cd019f75STrond Myklebust struct gss_cred *gss_cred = container_of(oldcred, 1613cd019f75STrond Myklebust struct gss_cred, 1614cd019f75STrond Myklebust gc_base); 1615cd019f75STrond Myklebust struct rpc_auth *auth = oldcred->cr_auth; 1616cd019f75STrond Myklebust struct auth_cred acred = { 161797f68c6bSNeilBrown .cred = oldcred->cr_cred, 161868c97153STrond Myklebust .principal = gss_cred->gc_principal, 1619cd019f75STrond Myklebust }; 1620cd019f75STrond Myklebust struct rpc_cred *new; 1621cd019f75STrond Myklebust 1622cd019f75STrond Myklebust new = gss_lookup_cred(auth, &acred, RPCAUTH_LOOKUP_NEW); 1623cd019f75STrond Myklebust if (IS_ERR(new)) 1624cd019f75STrond Myklebust return PTR_ERR(new); 162553bc19f1SChuck Lever 1626a17c2153STrond Myklebust task->tk_rqstp->rq_cred = new; 1627cd019f75STrond Myklebust put_rpccred(oldcred); 1628cd019f75STrond Myklebust return 0; 1629cd019f75STrond Myklebust } 1630cd019f75STrond Myklebust 1631126e216aSTrond Myklebust static int gss_cred_is_negative_entry(struct rpc_cred *cred) 1632126e216aSTrond Myklebust { 1633126e216aSTrond Myklebust if (test_bit(RPCAUTH_CRED_NEGATIVE, &cred->cr_flags)) { 1634126e216aSTrond Myklebust unsigned long now = jiffies; 1635126e216aSTrond Myklebust unsigned long begin, expire; 1636126e216aSTrond Myklebust struct gss_cred *gss_cred; 1637126e216aSTrond Myklebust 1638126e216aSTrond Myklebust gss_cred = container_of(cred, struct gss_cred, gc_base); 1639126e216aSTrond Myklebust begin = gss_cred->gc_upcall_timestamp; 1640126e216aSTrond Myklebust expire = begin + gss_expired_cred_retry_delay * HZ; 1641126e216aSTrond Myklebust 1642126e216aSTrond Myklebust if (time_in_range_open(now, begin, expire)) 1643126e216aSTrond Myklebust return 1; 1644126e216aSTrond Myklebust } 1645126e216aSTrond Myklebust return 0; 1646126e216aSTrond Myklebust } 1647126e216aSTrond Myklebust 16481da177e4SLinus Torvalds /* 16491da177e4SLinus Torvalds * Refresh credentials. XXX - finish 16501da177e4SLinus Torvalds */ 16511da177e4SLinus Torvalds static int 16521da177e4SLinus Torvalds gss_refresh(struct rpc_task *task) 16531da177e4SLinus Torvalds { 1654a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 1655cd019f75STrond Myklebust int ret = 0; 16561da177e4SLinus Torvalds 1657126e216aSTrond Myklebust if (gss_cred_is_negative_entry(cred)) 1658126e216aSTrond Myklebust return -EKEYEXPIRED; 1659126e216aSTrond Myklebust 1660cd019f75STrond Myklebust if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) && 1661cd019f75STrond Myklebust !test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags)) { 1662cd019f75STrond Myklebust ret = gss_renew_cred(task); 1663cd019f75STrond Myklebust if (ret < 0) 1664cd019f75STrond Myklebust goto out; 1665a17c2153STrond Myklebust cred = task->tk_rqstp->rq_cred; 1666cd019f75STrond Myklebust } 1667cd019f75STrond Myklebust 1668cd019f75STrond Myklebust if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags)) 1669cd019f75STrond Myklebust ret = gss_refresh_upcall(task); 1670cd019f75STrond Myklebust out: 1671cd019f75STrond Myklebust return ret; 16721da177e4SLinus Torvalds } 16731da177e4SLinus Torvalds 16740df7fb74STrond Myklebust /* Dummy refresh routine: used only when destroying the context */ 16750df7fb74STrond Myklebust static int 16760df7fb74STrond Myklebust gss_refresh_null(struct rpc_task *task) 16770df7fb74STrond Myklebust { 1678c297c8b9SAndy Adamson return 0; 16790df7fb74STrond Myklebust } 16800df7fb74STrond Myklebust 1681a0584ee9SChuck Lever static int 1682a0584ee9SChuck Lever gss_validate(struct rpc_task *task, struct xdr_stream *xdr) 16831da177e4SLinus Torvalds { 1684a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 16851da177e4SLinus Torvalds struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 1686a0584ee9SChuck Lever __be32 *p, *seq = NULL; 16871da177e4SLinus Torvalds struct kvec iov; 16881da177e4SLinus Torvalds struct xdr_buf verf_buf; 16891da177e4SLinus Torvalds struct xdr_netobj mic; 1690a0584ee9SChuck Lever u32 len, maj_stat; 1691a0584ee9SChuck Lever int status; 16921da177e4SLinus Torvalds 1693a0584ee9SChuck Lever p = xdr_inline_decode(xdr, 2 * sizeof(*p)); 1694a0584ee9SChuck Lever if (!p) 1695a0584ee9SChuck Lever goto validate_failed; 1696a0584ee9SChuck Lever if (*p++ != rpc_auth_gss) 1697a0584ee9SChuck Lever goto validate_failed; 1698a0584ee9SChuck Lever len = be32_to_cpup(p); 1699a0584ee9SChuck Lever if (len > RPC_MAX_AUTH_SIZE) 1700a0584ee9SChuck Lever goto validate_failed; 1701a0584ee9SChuck Lever p = xdr_inline_decode(xdr, len); 1702a0584ee9SChuck Lever if (!p) 1703a0584ee9SChuck Lever goto validate_failed; 17041da177e4SLinus Torvalds 17054c2883e7STrond Myklebust seq = kmalloc(4, GFP_KERNEL); 17062876a344SJ. Bruce Fields if (!seq) 1707a0584ee9SChuck Lever goto validate_failed; 1708a0584ee9SChuck Lever *seq = cpu_to_be32(task->tk_rqstp->rq_seqno); 17092876a344SJ. Bruce Fields iov.iov_base = seq; 17102876a344SJ. Bruce Fields iov.iov_len = 4; 17111da177e4SLinus Torvalds xdr_buf_from_iov(&iov, &verf_buf); 17121da177e4SLinus Torvalds mic.data = (u8 *)p; 17131da177e4SLinus Torvalds mic.len = len; 171400fd6e14SJ. Bruce Fields maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic); 17151da177e4SLinus Torvalds if (maj_stat == GSS_S_CONTEXT_EXPIRED) 1716fc432dd9STrond Myklebust clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 1717a0584ee9SChuck Lever if (maj_stat) 1718a0584ee9SChuck Lever goto bad_mic; 1719a0584ee9SChuck Lever 172024b2605bSJ. Bruce Fields /* We leave it to unwrap to calculate au_rslack. For now we just 172124b2605bSJ. Bruce Fields * calculate the length of the verifier: */ 172253bc19f1SChuck Lever if (test_bit(RPCAUTH_AUTH_UPDATE_SLACK, &cred->cr_auth->au_flags)) 17231be27f36STrond Myklebust cred->cr_auth->au_verfsize = XDR_QUADLEN(len) + 2; 1724a0584ee9SChuck Lever status = 0; 1725a0584ee9SChuck Lever out: 17261da177e4SLinus Torvalds gss_put_ctx(ctx); 17272876a344SJ. Bruce Fields kfree(seq); 1728a0584ee9SChuck Lever return status; 1729a0584ee9SChuck Lever 1730a0584ee9SChuck Lever validate_failed: 1731a0584ee9SChuck Lever status = -EIO; 1732a0584ee9SChuck Lever goto out; 1733a0584ee9SChuck Lever bad_mic: 17340c77668dSChuck Lever trace_rpcgss_verify_mic(task, maj_stat); 1735a0584ee9SChuck Lever status = -EACCES; 1736a0584ee9SChuck Lever goto out; 17371da177e4SLinus Torvalds } 17381da177e4SLinus Torvalds 1739d162372aSChuck Lever static noinline_for_stack int 1740d162372aSChuck Lever gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, 1741e8680a24SChuck Lever struct rpc_task *task, struct xdr_stream *xdr) 17429f06c719SChuck Lever { 1743e8680a24SChuck Lever struct rpc_rqst *rqstp = task->tk_rqstp; 1744e8680a24SChuck Lever struct xdr_buf integ_buf, *snd_buf = &rqstp->rq_snd_buf; 17451da177e4SLinus Torvalds struct xdr_netobj mic; 1746e8680a24SChuck Lever __be32 *p, *integ_len; 1747e8680a24SChuck Lever u32 offset, maj_stat; 17481da177e4SLinus Torvalds 1749e8680a24SChuck Lever p = xdr_reserve_space(xdr, 2 * sizeof(*p)); 1750e8680a24SChuck Lever if (!p) 1751e8680a24SChuck Lever goto wrap_failed; 17521da177e4SLinus Torvalds integ_len = p++; 1753e8680a24SChuck Lever *p = cpu_to_be32(rqstp->rq_seqno); 1754e8680a24SChuck Lever 1755e8680a24SChuck Lever if (rpcauth_wrap_req_encode(task, xdr)) 1756e8680a24SChuck Lever goto wrap_failed; 1757e8680a24SChuck Lever 17581da177e4SLinus Torvalds offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; 17591da177e4SLinus Torvalds if (xdr_buf_subsegment(snd_buf, &integ_buf, 17601da177e4SLinus Torvalds offset, snd_buf->len - offset)) 1761e8680a24SChuck Lever goto wrap_failed; 1762e8680a24SChuck Lever *integ_len = cpu_to_be32(integ_buf.len); 17631da177e4SLinus Torvalds 1764e8680a24SChuck Lever p = xdr_reserve_space(xdr, 0); 1765e8680a24SChuck Lever if (!p) 1766e8680a24SChuck Lever goto wrap_failed; 17671da177e4SLinus Torvalds mic.data = (u8 *)(p + 1); 176800fd6e14SJ. Bruce Fields maj_stat = gss_get_mic(ctx->gc_gss_ctx, &integ_buf, &mic); 17691da177e4SLinus Torvalds if (maj_stat == GSS_S_CONTEXT_EXPIRED) 1770fc432dd9STrond Myklebust clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 17711da177e4SLinus Torvalds else if (maj_stat) 17720c77668dSChuck Lever goto bad_mic; 1773e8680a24SChuck Lever /* Check that the trailing MIC fit in the buffer, after the fact */ 1774e8680a24SChuck Lever if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0) 1775e8680a24SChuck Lever goto wrap_failed; 17761da177e4SLinus Torvalds return 0; 1777e8680a24SChuck Lever wrap_failed: 1778e8680a24SChuck Lever return -EMSGSIZE; 17790c77668dSChuck Lever bad_mic: 17800c77668dSChuck Lever trace_rpcgss_get_mic(task, maj_stat); 17810c77668dSChuck Lever return -EIO; 17821da177e4SLinus Torvalds } 17831da177e4SLinus Torvalds 17842d2da60cSJ. Bruce Fields static void 17852d2da60cSJ. Bruce Fields priv_release_snd_buf(struct rpc_rqst *rqstp) 17862d2da60cSJ. Bruce Fields { 17872d2da60cSJ. Bruce Fields int i; 17882d2da60cSJ. Bruce Fields 17892d2da60cSJ. Bruce Fields for (i=0; i < rqstp->rq_enc_pages_num; i++) 17902d2da60cSJ. Bruce Fields __free_page(rqstp->rq_enc_pages[i]); 17912d2da60cSJ. Bruce Fields kfree(rqstp->rq_enc_pages); 17928dae5398SChuck Lever rqstp->rq_release_snd_buf = NULL; 17932d2da60cSJ. Bruce Fields } 17942d2da60cSJ. Bruce Fields 17952d2da60cSJ. Bruce Fields static int 17962d2da60cSJ. Bruce Fields alloc_enc_pages(struct rpc_rqst *rqstp) 17972d2da60cSJ. Bruce Fields { 17982d2da60cSJ. Bruce Fields struct xdr_buf *snd_buf = &rqstp->rq_snd_buf; 17992d2da60cSJ. Bruce Fields int first, last, i; 18002d2da60cSJ. Bruce Fields 18018dae5398SChuck Lever if (rqstp->rq_release_snd_buf) 18028dae5398SChuck Lever rqstp->rq_release_snd_buf(rqstp); 18038dae5398SChuck Lever 18042d2da60cSJ. Bruce Fields if (snd_buf->page_len == 0) { 18052d2da60cSJ. Bruce Fields rqstp->rq_enc_pages_num = 0; 18062d2da60cSJ. Bruce Fields return 0; 18072d2da60cSJ. Bruce Fields } 18082d2da60cSJ. Bruce Fields 180909cbfeafSKirill A. Shutemov first = snd_buf->page_base >> PAGE_SHIFT; 181009cbfeafSKirill A. Shutemov last = (snd_buf->page_base + snd_buf->page_len - 1) >> PAGE_SHIFT; 18112d2da60cSJ. Bruce Fields rqstp->rq_enc_pages_num = last - first + 1 + 1; 18122d2da60cSJ. Bruce Fields rqstp->rq_enc_pages 18136da2ec56SKees Cook = kmalloc_array(rqstp->rq_enc_pages_num, 18146da2ec56SKees Cook sizeof(struct page *), 18154c2883e7STrond Myklebust GFP_KERNEL); 18162d2da60cSJ. Bruce Fields if (!rqstp->rq_enc_pages) 18172d2da60cSJ. Bruce Fields goto out; 18182d2da60cSJ. Bruce Fields for (i=0; i < rqstp->rq_enc_pages_num; i++) { 18194c2883e7STrond Myklebust rqstp->rq_enc_pages[i] = alloc_page(GFP_KERNEL); 18202d2da60cSJ. Bruce Fields if (rqstp->rq_enc_pages[i] == NULL) 18212d2da60cSJ. Bruce Fields goto out_free; 18222d2da60cSJ. Bruce Fields } 18232d2da60cSJ. Bruce Fields rqstp->rq_release_snd_buf = priv_release_snd_buf; 18242d2da60cSJ. Bruce Fields return 0; 18252d2da60cSJ. Bruce Fields out_free: 1826cdead7cfSTrond Myklebust rqstp->rq_enc_pages_num = i; 1827cdead7cfSTrond Myklebust priv_release_snd_buf(rqstp); 18282d2da60cSJ. Bruce Fields out: 18292d2da60cSJ. Bruce Fields return -EAGAIN; 18302d2da60cSJ. Bruce Fields } 18312d2da60cSJ. Bruce Fields 1832d162372aSChuck Lever static noinline_for_stack int 1833d162372aSChuck Lever gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, 1834e8680a24SChuck Lever struct rpc_task *task, struct xdr_stream *xdr) 18352d2da60cSJ. Bruce Fields { 1836e8680a24SChuck Lever struct rpc_rqst *rqstp = task->tk_rqstp; 18372d2da60cSJ. Bruce Fields struct xdr_buf *snd_buf = &rqstp->rq_snd_buf; 1838e8680a24SChuck Lever u32 pad, offset, maj_stat; 18392d2da60cSJ. Bruce Fields int status; 1840e8680a24SChuck Lever __be32 *p, *opaque_len; 18412d2da60cSJ. Bruce Fields struct page **inpages; 18422d2da60cSJ. Bruce Fields int first; 18432d2da60cSJ. Bruce Fields struct kvec *iov; 18442d2da60cSJ. Bruce Fields 1845e8680a24SChuck Lever status = -EIO; 1846e8680a24SChuck Lever p = xdr_reserve_space(xdr, 2 * sizeof(*p)); 1847e8680a24SChuck Lever if (!p) 1848e8680a24SChuck Lever goto wrap_failed; 18492d2da60cSJ. Bruce Fields opaque_len = p++; 1850e8680a24SChuck Lever *p = cpu_to_be32(rqstp->rq_seqno); 18512d2da60cSJ. Bruce Fields 1852e8680a24SChuck Lever if (rpcauth_wrap_req_encode(task, xdr)) 1853e8680a24SChuck Lever goto wrap_failed; 18542d2da60cSJ. Bruce Fields 18552d2da60cSJ. Bruce Fields status = alloc_enc_pages(rqstp); 1856e8680a24SChuck Lever if (unlikely(status)) 1857e8680a24SChuck Lever goto wrap_failed; 185809cbfeafSKirill A. Shutemov first = snd_buf->page_base >> PAGE_SHIFT; 18592d2da60cSJ. Bruce Fields inpages = snd_buf->pages + first; 18602d2da60cSJ. Bruce Fields snd_buf->pages = rqstp->rq_enc_pages; 186109cbfeafSKirill A. Shutemov snd_buf->page_base -= first << PAGE_SHIFT; 18627561042fSKevin Coffman /* 1863e8680a24SChuck Lever * Move the tail into its own page, in case gss_wrap needs 1864e8680a24SChuck Lever * more space in the head when wrapping. 18657561042fSKevin Coffman * 1866e8680a24SChuck Lever * Still... Why can't gss_wrap just slide the tail down? 18677561042fSKevin Coffman */ 18682d2da60cSJ. Bruce Fields if (snd_buf->page_len || snd_buf->tail[0].iov_len) { 1869e8680a24SChuck Lever char *tmp; 1870e8680a24SChuck Lever 18712d2da60cSJ. Bruce Fields tmp = page_address(rqstp->rq_enc_pages[rqstp->rq_enc_pages_num - 1]); 18722d2da60cSJ. Bruce Fields memcpy(tmp, snd_buf->tail[0].iov_base, snd_buf->tail[0].iov_len); 18732d2da60cSJ. Bruce Fields snd_buf->tail[0].iov_base = tmp; 18742d2da60cSJ. Bruce Fields } 1875e8680a24SChuck Lever offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; 187600fd6e14SJ. Bruce Fields maj_stat = gss_wrap(ctx->gc_gss_ctx, offset, snd_buf, inpages); 18777561042fSKevin Coffman /* slack space should prevent this ever happening: */ 1878e8680a24SChuck Lever if (unlikely(snd_buf->len > snd_buf->buflen)) 1879e8680a24SChuck Lever goto wrap_failed; 18802d2da60cSJ. Bruce Fields /* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was 18812d2da60cSJ. Bruce Fields * done anyway, so it's safe to put the request on the wire: */ 18822d2da60cSJ. Bruce Fields if (maj_stat == GSS_S_CONTEXT_EXPIRED) 1883fc432dd9STrond Myklebust clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 18842d2da60cSJ. Bruce Fields else if (maj_stat) 18850c77668dSChuck Lever goto bad_wrap; 18862d2da60cSJ. Bruce Fields 1887e8680a24SChuck Lever *opaque_len = cpu_to_be32(snd_buf->len - offset); 1888e8680a24SChuck Lever /* guess whether the pad goes into the head or the tail: */ 18892d2da60cSJ. Bruce Fields if (snd_buf->page_len || snd_buf->tail[0].iov_len) 18902d2da60cSJ. Bruce Fields iov = snd_buf->tail; 18912d2da60cSJ. Bruce Fields else 18922d2da60cSJ. Bruce Fields iov = snd_buf->head; 18932d2da60cSJ. Bruce Fields p = iov->iov_base + iov->iov_len; 189496f194b7SChuck Lever pad = xdr_pad_size(snd_buf->len - offset); 18952d2da60cSJ. Bruce Fields memset(p, 0, pad); 18962d2da60cSJ. Bruce Fields iov->iov_len += pad; 18972d2da60cSJ. Bruce Fields snd_buf->len += pad; 18982d2da60cSJ. Bruce Fields 18992d2da60cSJ. Bruce Fields return 0; 1900e8680a24SChuck Lever wrap_failed: 1901e8680a24SChuck Lever return status; 19020c77668dSChuck Lever bad_wrap: 19030c77668dSChuck Lever trace_rpcgss_wrap(task, maj_stat); 19040c77668dSChuck Lever return -EIO; 19052d2da60cSJ. Bruce Fields } 19062d2da60cSJ. Bruce Fields 1907e8680a24SChuck Lever static int gss_wrap_req(struct rpc_task *task, struct xdr_stream *xdr) 19081da177e4SLinus Torvalds { 1909a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 19101da177e4SLinus Torvalds struct gss_cred *gss_cred = container_of(cred, struct gss_cred, 19111da177e4SLinus Torvalds gc_base); 19121da177e4SLinus Torvalds struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 1913e8680a24SChuck Lever int status; 19141da177e4SLinus Torvalds 1915e8680a24SChuck Lever status = -EIO; 19161da177e4SLinus Torvalds if (ctx->gc_proc != RPC_GSS_PROC_DATA) { 19171da177e4SLinus Torvalds /* The spec seems a little ambiguous here, but I think that not 19181da177e4SLinus Torvalds * wrapping context destruction requests makes the most sense. 19191da177e4SLinus Torvalds */ 1920e8680a24SChuck Lever status = rpcauth_wrap_req_encode(task, xdr); 19211da177e4SLinus Torvalds goto out; 19221da177e4SLinus Torvalds } 19231da177e4SLinus Torvalds switch (gss_cred->gc_service) { 19241da177e4SLinus Torvalds case RPC_GSS_SVC_NONE: 1925e8680a24SChuck Lever status = rpcauth_wrap_req_encode(task, xdr); 19261da177e4SLinus Torvalds break; 19271da177e4SLinus Torvalds case RPC_GSS_SVC_INTEGRITY: 1928e8680a24SChuck Lever status = gss_wrap_req_integ(cred, ctx, task, xdr); 19291da177e4SLinus Torvalds break; 19301da177e4SLinus Torvalds case RPC_GSS_SVC_PRIVACY: 1931e8680a24SChuck Lever status = gss_wrap_req_priv(cred, ctx, task, xdr); 19321da177e4SLinus Torvalds break; 19330c77668dSChuck Lever default: 19340c77668dSChuck Lever status = -EIO; 19351da177e4SLinus Torvalds } 19361da177e4SLinus Torvalds out: 19371da177e4SLinus Torvalds gss_put_ctx(ctx); 19381da177e4SLinus Torvalds return status; 19391da177e4SLinus Torvalds } 19401da177e4SLinus Torvalds 194153bc19f1SChuck Lever /** 194253bc19f1SChuck Lever * gss_update_rslack - Possibly update RPC receive buffer size estimates 194353bc19f1SChuck Lever * @task: rpc_task for incoming RPC Reply being unwrapped 194453bc19f1SChuck Lever * @cred: controlling rpc_cred for @task 194553bc19f1SChuck Lever * @before: XDR words needed before each RPC Reply message 194653bc19f1SChuck Lever * @after: XDR words needed following each RPC Reply message 194753bc19f1SChuck Lever * 194853bc19f1SChuck Lever */ 194953bc19f1SChuck Lever static void gss_update_rslack(struct rpc_task *task, struct rpc_cred *cred, 195053bc19f1SChuck Lever unsigned int before, unsigned int after) 19511da177e4SLinus Torvalds { 195235e77d21SChuck Lever struct rpc_auth *auth = cred->cr_auth; 195335e77d21SChuck Lever 195453bc19f1SChuck Lever if (test_and_clear_bit(RPCAUTH_AUTH_UPDATE_SLACK, &auth->au_flags)) { 195553bc19f1SChuck Lever auth->au_ralign = auth->au_verfsize + before; 195653bc19f1SChuck Lever auth->au_rslack = auth->au_verfsize + after; 195753bc19f1SChuck Lever trace_rpcgss_update_slack(task, auth); 195853bc19f1SChuck Lever } 195953bc19f1SChuck Lever } 196053bc19f1SChuck Lever 196153bc19f1SChuck Lever static int 196253bc19f1SChuck Lever gss_unwrap_resp_auth(struct rpc_task *task, struct rpc_cred *cred) 196353bc19f1SChuck Lever { 196453bc19f1SChuck Lever gss_update_rslack(task, cred, 0, 0); 1965a0584ee9SChuck Lever return 0; 1966a0584ee9SChuck Lever } 19671da177e4SLinus Torvalds 19684047aa90SChuck Lever /* 19694047aa90SChuck Lever * RFC 2203, Section 5.3.2.2 19704047aa90SChuck Lever * 19714047aa90SChuck Lever * struct rpc_gss_integ_data { 19724047aa90SChuck Lever * opaque databody_integ<>; 19734047aa90SChuck Lever * opaque checksum<>; 19744047aa90SChuck Lever * }; 19754047aa90SChuck Lever * 19764047aa90SChuck Lever * struct rpc_gss_data_t { 19774047aa90SChuck Lever * unsigned int seq_num; 19784047aa90SChuck Lever * proc_req_arg_t arg; 19794047aa90SChuck Lever * }; 19804047aa90SChuck Lever */ 1981d162372aSChuck Lever static noinline_for_stack int 19820c77668dSChuck Lever gss_unwrap_resp_integ(struct rpc_task *task, struct rpc_cred *cred, 19830c77668dSChuck Lever struct gss_cl_ctx *ctx, struct rpc_rqst *rqstp, 19840c77668dSChuck Lever struct xdr_stream *xdr) 1985a0584ee9SChuck Lever { 19864047aa90SChuck Lever struct xdr_buf gss_data, *rcv_buf = &rqstp->rq_rcv_buf; 19874047aa90SChuck Lever u32 len, offset, seqno, maj_stat; 1988a0584ee9SChuck Lever struct xdr_netobj mic; 19894047aa90SChuck Lever int ret; 1990a0584ee9SChuck Lever 19914047aa90SChuck Lever ret = -EIO; 19924047aa90SChuck Lever mic.data = NULL; 19934047aa90SChuck Lever 19944047aa90SChuck Lever /* opaque databody_integ<>; */ 19954047aa90SChuck Lever if (xdr_stream_decode_u32(xdr, &len)) 1996a0584ee9SChuck Lever goto unwrap_failed; 19974047aa90SChuck Lever if (len & 3) 1998a0584ee9SChuck Lever goto unwrap_failed; 19994047aa90SChuck Lever offset = rcv_buf->len - xdr_stream_remaining(xdr); 20004047aa90SChuck Lever if (xdr_stream_decode_u32(xdr, &seqno)) 2001a0584ee9SChuck Lever goto unwrap_failed; 20024047aa90SChuck Lever if (seqno != rqstp->rq_seqno) 20030c77668dSChuck Lever goto bad_seqno; 20044047aa90SChuck Lever if (xdr_buf_subsegment(rcv_buf, &gss_data, offset, len)) 20054047aa90SChuck Lever goto unwrap_failed; 20061da177e4SLinus Torvalds 20074047aa90SChuck Lever /* 20084047aa90SChuck Lever * The xdr_stream now points to the beginning of the 20094047aa90SChuck Lever * upper layer payload, to be passed below to 20104047aa90SChuck Lever * rpcauth_unwrap_resp_decode(). The checksum, which 20114047aa90SChuck Lever * follows the upper layer payload in @rcv_buf, is 20124047aa90SChuck Lever * located and parsed without updating the xdr_stream. 20134047aa90SChuck Lever */ 20144047aa90SChuck Lever 20154047aa90SChuck Lever /* opaque checksum<>; */ 20164047aa90SChuck Lever offset += len; 20174047aa90SChuck Lever if (xdr_decode_word(rcv_buf, offset, &len)) 2018a0584ee9SChuck Lever goto unwrap_failed; 20194047aa90SChuck Lever offset += sizeof(__be32); 20204047aa90SChuck Lever if (offset + len > rcv_buf->len) 2021a0584ee9SChuck Lever goto unwrap_failed; 20224047aa90SChuck Lever mic.len = len; 20234c2883e7STrond Myklebust mic.data = kmalloc(len, GFP_KERNEL); 20248a0fa3ffSChuck Lever if (ZERO_OR_NULL_PTR(mic.data)) 20254047aa90SChuck Lever goto unwrap_failed; 20264047aa90SChuck Lever if (read_bytes_from_xdr_buf(rcv_buf, offset, mic.data, mic.len)) 20274047aa90SChuck Lever goto unwrap_failed; 20284047aa90SChuck Lever 20294047aa90SChuck Lever maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &gss_data, &mic); 20301da177e4SLinus Torvalds if (maj_stat == GSS_S_CONTEXT_EXPIRED) 2031fc432dd9STrond Myklebust clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 20321da177e4SLinus Torvalds if (maj_stat != GSS_S_COMPLETE) 2033a0584ee9SChuck Lever goto bad_mic; 2034a0584ee9SChuck Lever 203553bc19f1SChuck Lever gss_update_rslack(task, cred, 2, 2 + 1 + XDR_QUADLEN(mic.len)); 20364047aa90SChuck Lever ret = 0; 20374047aa90SChuck Lever 20384047aa90SChuck Lever out: 20394047aa90SChuck Lever kfree(mic.data); 20404047aa90SChuck Lever return ret; 20414047aa90SChuck Lever 2042a0584ee9SChuck Lever unwrap_failed: 20430c77668dSChuck Lever trace_rpcgss_unwrap_failed(task); 20444047aa90SChuck Lever goto out; 20450c77668dSChuck Lever bad_seqno: 20464047aa90SChuck Lever trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, seqno); 20474047aa90SChuck Lever goto out; 2048a0584ee9SChuck Lever bad_mic: 20490c77668dSChuck Lever trace_rpcgss_verify_mic(task, maj_stat); 20504047aa90SChuck Lever goto out; 20511da177e4SLinus Torvalds } 20521da177e4SLinus Torvalds 2053d162372aSChuck Lever static noinline_for_stack int 20540c77668dSChuck Lever gss_unwrap_resp_priv(struct rpc_task *task, struct rpc_cred *cred, 20550c77668dSChuck Lever struct gss_cl_ctx *ctx, struct rpc_rqst *rqstp, 20560c77668dSChuck Lever struct xdr_stream *xdr) 20572d2da60cSJ. Bruce Fields { 20582d2da60cSJ. Bruce Fields struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf; 2059a0584ee9SChuck Lever struct kvec *head = rqstp->rq_rcv_buf.head; 2060a0584ee9SChuck Lever u32 offset, opaque_len, maj_stat; 2061a0584ee9SChuck Lever __be32 *p; 20622d2da60cSJ. Bruce Fields 2063a0584ee9SChuck Lever p = xdr_inline_decode(xdr, 2 * sizeof(*p)); 2064a0584ee9SChuck Lever if (unlikely(!p)) 2065a0584ee9SChuck Lever goto unwrap_failed; 2066a0584ee9SChuck Lever opaque_len = be32_to_cpup(p++); 2067a0584ee9SChuck Lever offset = (u8 *)(p) - (u8 *)head->iov_base; 20682d2da60cSJ. Bruce Fields if (offset + opaque_len > rcv_buf->len) 2069a0584ee9SChuck Lever goto unwrap_failed; 20702d2da60cSJ. Bruce Fields 207131c9590aSChuck Lever maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset, 207231c9590aSChuck Lever offset + opaque_len, rcv_buf); 20732d2da60cSJ. Bruce Fields if (maj_stat == GSS_S_CONTEXT_EXPIRED) 2074fc432dd9STrond Myklebust clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 20752d2da60cSJ. Bruce Fields if (maj_stat != GSS_S_COMPLETE) 2076a0584ee9SChuck Lever goto bad_unwrap; 2077a0584ee9SChuck Lever /* gss_unwrap decrypted the sequence number */ 2078a0584ee9SChuck Lever if (be32_to_cpup(p++) != rqstp->rq_seqno) 20790c77668dSChuck Lever goto bad_seqno; 20802d2da60cSJ. Bruce Fields 2081a0584ee9SChuck Lever /* gss_unwrap redacts the opaque blob from the head iovec. 2082a0584ee9SChuck Lever * rcv_buf has changed, thus the stream needs to be reset. 2083a0584ee9SChuck Lever */ 2084a0584ee9SChuck Lever xdr_init_decode(xdr, rcv_buf, p, rqstp); 2085a0584ee9SChuck Lever 208653bc19f1SChuck Lever gss_update_rslack(task, cred, 2 + ctx->gc_gss_ctx->align, 208753bc19f1SChuck Lever 2 + ctx->gc_gss_ctx->slack); 2088a7e429a6SChuck Lever 20892d2da60cSJ. Bruce Fields return 0; 2090a0584ee9SChuck Lever unwrap_failed: 20910c77668dSChuck Lever trace_rpcgss_unwrap_failed(task); 20920c77668dSChuck Lever return -EIO; 20930c77668dSChuck Lever bad_seqno: 20940c77668dSChuck Lever trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, be32_to_cpup(--p)); 2095a0584ee9SChuck Lever return -EIO; 2096a0584ee9SChuck Lever bad_unwrap: 20970c77668dSChuck Lever trace_rpcgss_unwrap(task, maj_stat); 2098a0584ee9SChuck Lever return -EIO; 2099bf269551SChuck Lever } 21002d2da60cSJ. Bruce Fields 21013021a5bbSTrond Myklebust static bool 21023021a5bbSTrond Myklebust gss_seq_is_newer(u32 new, u32 old) 21033021a5bbSTrond Myklebust { 21043021a5bbSTrond Myklebust return (s32)(new - old) > 0; 21053021a5bbSTrond Myklebust } 21063021a5bbSTrond Myklebust 21073021a5bbSTrond Myklebust static bool 21083021a5bbSTrond Myklebust gss_xmit_need_reencode(struct rpc_task *task) 21093021a5bbSTrond Myklebust { 21103021a5bbSTrond Myklebust struct rpc_rqst *req = task->tk_rqstp; 21113021a5bbSTrond Myklebust struct rpc_cred *cred = req->rq_cred; 21123021a5bbSTrond Myklebust struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 21130c77668dSChuck Lever u32 win, seq_xmit = 0; 21143021a5bbSTrond Myklebust bool ret = true; 21153021a5bbSTrond Myklebust 21163021a5bbSTrond Myklebust if (!ctx) 21170c77668dSChuck Lever goto out; 21183021a5bbSTrond Myklebust 21193021a5bbSTrond Myklebust if (gss_seq_is_newer(req->rq_seqno, READ_ONCE(ctx->gc_seq))) 21200c77668dSChuck Lever goto out_ctx; 21213021a5bbSTrond Myklebust 21223021a5bbSTrond Myklebust seq_xmit = READ_ONCE(ctx->gc_seq_xmit); 21233021a5bbSTrond Myklebust while (gss_seq_is_newer(req->rq_seqno, seq_xmit)) { 21243021a5bbSTrond Myklebust u32 tmp = seq_xmit; 21253021a5bbSTrond Myklebust 21263021a5bbSTrond Myklebust seq_xmit = cmpxchg(&ctx->gc_seq_xmit, tmp, req->rq_seqno); 21273021a5bbSTrond Myklebust if (seq_xmit == tmp) { 21283021a5bbSTrond Myklebust ret = false; 21290c77668dSChuck Lever goto out_ctx; 21303021a5bbSTrond Myklebust } 21313021a5bbSTrond Myklebust } 21323021a5bbSTrond Myklebust 21333021a5bbSTrond Myklebust win = ctx->gc_win; 21343021a5bbSTrond Myklebust if (win > 0) 21353021a5bbSTrond Myklebust ret = !gss_seq_is_newer(req->rq_seqno, seq_xmit - win); 21360c77668dSChuck Lever 21370c77668dSChuck Lever out_ctx: 21383021a5bbSTrond Myklebust gss_put_ctx(ctx); 21390c77668dSChuck Lever out: 21400c77668dSChuck Lever trace_rpcgss_need_reencode(task, seq_xmit, ret); 21413021a5bbSTrond Myklebust return ret; 21423021a5bbSTrond Myklebust } 21433021a5bbSTrond Myklebust 21441da177e4SLinus Torvalds static int 2145a0584ee9SChuck Lever gss_unwrap_resp(struct rpc_task *task, struct xdr_stream *xdr) 21461da177e4SLinus Torvalds { 2147a0584ee9SChuck Lever struct rpc_rqst *rqstp = task->tk_rqstp; 2148a0584ee9SChuck Lever struct rpc_cred *cred = rqstp->rq_cred; 21491da177e4SLinus Torvalds struct gss_cred *gss_cred = container_of(cred, struct gss_cred, 21501da177e4SLinus Torvalds gc_base); 21511da177e4SLinus Torvalds struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 21521da177e4SLinus Torvalds int status = -EIO; 21531da177e4SLinus Torvalds 21541da177e4SLinus Torvalds if (ctx->gc_proc != RPC_GSS_PROC_DATA) 21551da177e4SLinus Torvalds goto out_decode; 21561da177e4SLinus Torvalds switch (gss_cred->gc_service) { 21571da177e4SLinus Torvalds case RPC_GSS_SVC_NONE: 215853bc19f1SChuck Lever status = gss_unwrap_resp_auth(task, cred); 21591da177e4SLinus Torvalds break; 21601da177e4SLinus Torvalds case RPC_GSS_SVC_INTEGRITY: 21610c77668dSChuck Lever status = gss_unwrap_resp_integ(task, cred, ctx, rqstp, xdr); 21621da177e4SLinus Torvalds break; 21631da177e4SLinus Torvalds case RPC_GSS_SVC_PRIVACY: 21640c77668dSChuck Lever status = gss_unwrap_resp_priv(task, cred, ctx, rqstp, xdr); 21651da177e4SLinus Torvalds break; 21661da177e4SLinus Torvalds } 2167a0584ee9SChuck Lever if (status) 2168a0584ee9SChuck Lever goto out; 2169a0584ee9SChuck Lever 21701da177e4SLinus Torvalds out_decode: 2171a0584ee9SChuck Lever status = rpcauth_unwrap_resp_decode(task, xdr); 21721da177e4SLinus Torvalds out: 21731da177e4SLinus Torvalds gss_put_ctx(ctx); 21741da177e4SLinus Torvalds return status; 21751da177e4SLinus Torvalds } 21761da177e4SLinus Torvalds 2177f1c0a861STrond Myklebust static const struct rpc_authops authgss_ops = { 21781da177e4SLinus Torvalds .owner = THIS_MODULE, 21791da177e4SLinus Torvalds .au_flavor = RPC_AUTH_GSS, 21801da177e4SLinus Torvalds .au_name = "RPCSEC_GSS", 21811da177e4SLinus Torvalds .create = gss_create, 21821da177e4SLinus Torvalds .destroy = gss_destroy, 2183a960f8d6SFrank Sorenson .hash_cred = gss_hash_cred, 21841da177e4SLinus Torvalds .lookup_cred = gss_lookup_cred, 218580df9d20SStanislav Kinsbursky .crcreate = gss_create_cred, 21869568c5e9SChuck Lever .info2flavor = gss_mech_info2flavor, 2187a77c806fSChuck Lever .flavor2info = gss_mech_flavor2info, 21881da177e4SLinus Torvalds }; 21891da177e4SLinus Torvalds 2190f1c0a861STrond Myklebust static const struct rpc_credops gss_credops = { 21911da177e4SLinus Torvalds .cr_name = "AUTH_GSS", 21921da177e4SLinus Torvalds .crdestroy = gss_destroy_cred, 2193fba3bad4STrond Myklebust .cr_init = gss_cred_init, 21941da177e4SLinus Torvalds .crmatch = gss_match, 21951da177e4SLinus Torvalds .crmarshal = gss_marshal, 21961da177e4SLinus Torvalds .crrefresh = gss_refresh, 21971da177e4SLinus Torvalds .crvalidate = gss_validate, 21981da177e4SLinus Torvalds .crwrap_req = gss_wrap_req, 21991da177e4SLinus Torvalds .crunwrap_resp = gss_unwrap_resp, 22004de6caa2SAndy Adamson .crkey_timeout = gss_key_timeout, 2201a0337d1dSJeff Layton .crstringify_acceptor = gss_stringify_acceptor, 22023021a5bbSTrond Myklebust .crneed_reencode = gss_xmit_need_reencode, 22031da177e4SLinus Torvalds }; 22041da177e4SLinus Torvalds 22050df7fb74STrond Myklebust static const struct rpc_credops gss_nullops = { 22060df7fb74STrond Myklebust .cr_name = "AUTH_GSS", 22076dcd3926SJeff Layton .crdestroy = gss_destroy_nullcred, 22080df7fb74STrond Myklebust .crmatch = gss_match, 22090df7fb74STrond Myklebust .crmarshal = gss_marshal, 22100df7fb74STrond Myklebust .crrefresh = gss_refresh_null, 22110df7fb74STrond Myklebust .crvalidate = gss_validate, 22120df7fb74STrond Myklebust .crwrap_req = gss_wrap_req, 22130df7fb74STrond Myklebust .crunwrap_resp = gss_unwrap_resp, 2214a0337d1dSJeff Layton .crstringify_acceptor = gss_stringify_acceptor, 22150df7fb74STrond Myklebust }; 22160df7fb74STrond Myklebust 2217b693ba4aSTrond Myklebust static const struct rpc_pipe_ops gss_upcall_ops_v0 = { 2218ac83228aSTrond Myklebust .upcall = gss_v0_upcall, 22191da177e4SLinus Torvalds .downcall = gss_pipe_downcall, 22201da177e4SLinus Torvalds .destroy_msg = gss_pipe_destroy_msg, 222134769fc4S\"J. Bruce Fields\ .open_pipe = gss_pipe_open_v0, 222234769fc4S\"J. Bruce Fields\ .release_pipe = gss_pipe_release, 222334769fc4S\"J. Bruce Fields\ }; 222434769fc4S\"J. Bruce Fields\ 2225b693ba4aSTrond Myklebust static const struct rpc_pipe_ops gss_upcall_ops_v1 = { 2226ac83228aSTrond Myklebust .upcall = gss_v1_upcall, 222734769fc4S\"J. Bruce Fields\ .downcall = gss_pipe_downcall, 222834769fc4S\"J. Bruce Fields\ .destroy_msg = gss_pipe_destroy_msg, 222934769fc4S\"J. Bruce Fields\ .open_pipe = gss_pipe_open_v1, 22301da177e4SLinus Torvalds .release_pipe = gss_pipe_release, 22311da177e4SLinus Torvalds }; 22321da177e4SLinus Torvalds 2233a1db410dSStanislav Kinsbursky static __net_init int rpcsec_gss_init_net(struct net *net) 2234a1db410dSStanislav Kinsbursky { 2235a1db410dSStanislav Kinsbursky return gss_svc_init_net(net); 2236a1db410dSStanislav Kinsbursky } 2237a1db410dSStanislav Kinsbursky 2238a1db410dSStanislav Kinsbursky static __net_exit void rpcsec_gss_exit_net(struct net *net) 2239a1db410dSStanislav Kinsbursky { 2240a1db410dSStanislav Kinsbursky gss_svc_shutdown_net(net); 2241a1db410dSStanislav Kinsbursky } 2242a1db410dSStanislav Kinsbursky 2243a1db410dSStanislav Kinsbursky static struct pernet_operations rpcsec_gss_net_ops = { 2244a1db410dSStanislav Kinsbursky .init = rpcsec_gss_init_net, 2245a1db410dSStanislav Kinsbursky .exit = rpcsec_gss_exit_net, 2246a1db410dSStanislav Kinsbursky }; 2247a1db410dSStanislav Kinsbursky 22481da177e4SLinus Torvalds /* 22491da177e4SLinus Torvalds * Initialize RPCSEC_GSS module 22501da177e4SLinus Torvalds */ 22511da177e4SLinus Torvalds static int __init init_rpcsec_gss(void) 22521da177e4SLinus Torvalds { 22531da177e4SLinus Torvalds int err = 0; 22541da177e4SLinus Torvalds 22551da177e4SLinus Torvalds err = rpcauth_register(&authgss_ops); 22561da177e4SLinus Torvalds if (err) 22571da177e4SLinus Torvalds goto out; 22581da177e4SLinus Torvalds err = gss_svc_init(); 22591da177e4SLinus Torvalds if (err) 22601da177e4SLinus Torvalds goto out_unregister; 2261a1db410dSStanislav Kinsbursky err = register_pernet_subsys(&rpcsec_gss_net_ops); 2262a1db410dSStanislav Kinsbursky if (err) 2263a1db410dSStanislav Kinsbursky goto out_svc_exit; 226479a3f20bS\"J. Bruce Fields\ rpc_init_wait_queue(&pipe_version_rpc_waitqueue, "gss pipe version"); 22651da177e4SLinus Torvalds return 0; 2266a1db410dSStanislav Kinsbursky out_svc_exit: 2267a1db410dSStanislav Kinsbursky gss_svc_shutdown(); 22681da177e4SLinus Torvalds out_unregister: 22691da177e4SLinus Torvalds rpcauth_unregister(&authgss_ops); 22701da177e4SLinus Torvalds out: 22711da177e4SLinus Torvalds return err; 22721da177e4SLinus Torvalds } 22731da177e4SLinus Torvalds 22741da177e4SLinus Torvalds static void __exit exit_rpcsec_gss(void) 22751da177e4SLinus Torvalds { 2276a1db410dSStanislav Kinsbursky unregister_pernet_subsys(&rpcsec_gss_net_ops); 22771da177e4SLinus Torvalds gss_svc_shutdown(); 22781da177e4SLinus Torvalds rpcauth_unregister(&authgss_ops); 2279bf12691dSJesper Dangaard Brouer rcu_barrier(); /* Wait for completion of call_rcu()'s */ 22801da177e4SLinus Torvalds } 22811da177e4SLinus Torvalds 228271afa85eSChuck Lever MODULE_ALIAS("rpc-auth-6"); 22831da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 2284126e216aSTrond Myklebust module_param_named(expired_cred_retry_delay, 2285126e216aSTrond Myklebust gss_expired_cred_retry_delay, 2286126e216aSTrond Myklebust uint, 0644); 2287126e216aSTrond Myklebust MODULE_PARM_DESC(expired_cred_retry_delay, "Timeout (in seconds) until " 2288126e216aSTrond Myklebust "the RPC engine retries an expired credential"); 2289126e216aSTrond Myklebust 22904de6caa2SAndy Adamson module_param_named(key_expire_timeo, 22914de6caa2SAndy Adamson gss_key_expire_timeo, 22924de6caa2SAndy Adamson uint, 0644); 22934de6caa2SAndy Adamson MODULE_PARM_DESC(key_expire_timeo, "Time (in seconds) at the end of a " 22944de6caa2SAndy Adamson "credential keys lifetime where the NFS layer cleans up " 22954de6caa2SAndy Adamson "prior to key expiration"); 22964de6caa2SAndy Adamson 22971da177e4SLinus Torvalds module_init(init_rpcsec_gss) 22981da177e4SLinus Torvalds module_exit(exit_rpcsec_gss) 2299