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 52725f2865SKevin Coffman #define GSS_CRED_SLACK (RPC_MAX_AUTH_SIZE * 2) 531da177e4SLinus Torvalds /* length of a krb5 verifier (48), plus data added before arguments when 541da177e4SLinus Torvalds * using integrity (two 4-byte integers): */ 55adeb8133SOlga Kornievskaia #define GSS_VERF_SLACK 100 561da177e4SLinus Torvalds 5723c323afSTrond Myklebust static DEFINE_HASHTABLE(gss_auth_hash_table, 4); 58eb6dc19dSTrond Myklebust static DEFINE_SPINLOCK(gss_auth_hash_lock); 59eb6dc19dSTrond Myklebust 6019172284STrond Myklebust struct gss_pipe { 6119172284STrond Myklebust struct rpc_pipe_dir_object pdo; 6219172284STrond Myklebust struct rpc_pipe *pipe; 6319172284STrond Myklebust struct rpc_clnt *clnt; 6419172284STrond Myklebust const char *name; 65414a6295STrond Myklebust struct kref kref; 6619172284STrond Myklebust }; 6719172284STrond Myklebust 681da177e4SLinus Torvalds struct gss_auth { 690285ed1fSTrond Myklebust struct kref kref; 70eb6dc19dSTrond Myklebust struct hlist_node hash; 711da177e4SLinus Torvalds struct rpc_auth rpc_auth; 721da177e4SLinus Torvalds struct gss_api_mech *mech; 731da177e4SLinus Torvalds enum rpc_gss_svc service; 741da177e4SLinus Torvalds struct rpc_clnt *client; 75e726340aSTrond Myklebust struct net *net; 76*9b1831e5SEric Dumazet netns_tracker ns_tracker; 7734769fc4S\"J. Bruce Fields\ /* 7834769fc4S\"J. Bruce Fields\ * There are two upcall pipes; dentry[1], named "gssd", is used 7934769fc4S\"J. Bruce Fields\ * for the new text-based upcall; dentry[0] is named after the 8034769fc4S\"J. Bruce Fields\ * mechanism (for example, "krb5") and exists for 8134769fc4S\"J. Bruce Fields\ * backwards-compatibility with older gssd's. 8234769fc4S\"J. Bruce Fields\ */ 8319172284STrond Myklebust struct gss_pipe *gss_pipe[2]; 84bd4a3eb1STrond Myklebust const char *target_name; 851da177e4SLinus Torvalds }; 861da177e4SLinus Torvalds 8779a3f20bS\"J. Bruce Fields\ /* pipe_version >= 0 if and only if someone has a pipe open. */ 8879a3f20bS\"J. Bruce Fields\ static DEFINE_SPINLOCK(pipe_version_lock); 8979a3f20bS\"J. Bruce Fields\ static struct rpc_wait_queue pipe_version_rpc_waitqueue; 9079a3f20bS\"J. Bruce Fields\ static DECLARE_WAIT_QUEUE_HEAD(pipe_version_waitqueue); 919eb2ddb4STrond Myklebust static void gss_put_auth(struct gss_auth *gss_auth); 92cf81939dS\"J. Bruce Fields\ 935d28dc82STrond Myklebust static void gss_free_ctx(struct gss_cl_ctx *); 94b693ba4aSTrond Myklebust static const struct rpc_pipe_ops gss_upcall_ops_v0; 95b693ba4aSTrond Myklebust static const struct rpc_pipe_ops gss_upcall_ops_v1; 961da177e4SLinus Torvalds 971da177e4SLinus Torvalds static inline struct gss_cl_ctx * 981da177e4SLinus Torvalds gss_get_ctx(struct gss_cl_ctx *ctx) 991da177e4SLinus Torvalds { 1000fa10472SReshetova, Elena refcount_inc(&ctx->count); 1011da177e4SLinus Torvalds return ctx; 1021da177e4SLinus Torvalds } 1031da177e4SLinus Torvalds 1041da177e4SLinus Torvalds static inline void 1051da177e4SLinus Torvalds gss_put_ctx(struct gss_cl_ctx *ctx) 1061da177e4SLinus Torvalds { 1070fa10472SReshetova, Elena if (refcount_dec_and_test(&ctx->count)) 1085d28dc82STrond Myklebust gss_free_ctx(ctx); 1091da177e4SLinus Torvalds } 1101da177e4SLinus Torvalds 1115d28dc82STrond Myklebust /* gss_cred_set_ctx: 1125d28dc82STrond Myklebust * called by gss_upcall_callback and gss_create_upcall in order 1135d28dc82STrond Myklebust * to set the gss context. The actual exchange of an old context 1149beae467SStanislav Kinsbursky * and a new one is protected by the pipe->lock. 1155d28dc82STrond Myklebust */ 1161da177e4SLinus Torvalds static void 1171da177e4SLinus Torvalds gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx) 1181da177e4SLinus Torvalds { 1191da177e4SLinus Torvalds struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 1205d28dc82STrond Myklebust 121cd019f75STrond Myklebust if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags)) 122cd019f75STrond Myklebust return; 1237b6962b0STrond Myklebust gss_get_ctx(ctx); 124cf778b00SEric Dumazet rcu_assign_pointer(gss_cred->gc_ctx, ctx); 125fc432dd9STrond Myklebust set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 1264e857c58SPeter Zijlstra smp_mb__before_atomic(); 127fc432dd9STrond Myklebust clear_bit(RPCAUTH_CRED_NEW, &cred->cr_flags); 1281da177e4SLinus Torvalds } 1291da177e4SLinus Torvalds 1301da177e4SLinus Torvalds static struct gss_cl_ctx * 1311da177e4SLinus Torvalds gss_cred_get_ctx(struct rpc_cred *cred) 1321da177e4SLinus Torvalds { 1331da177e4SLinus Torvalds struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 1341da177e4SLinus Torvalds struct gss_cl_ctx *ctx = NULL; 1351da177e4SLinus Torvalds 1365d28dc82STrond Myklebust rcu_read_lock(); 137c5e6aecdSJeff Layton ctx = rcu_dereference(gss_cred->gc_ctx); 138c5e6aecdSJeff Layton if (ctx) 139c5e6aecdSJeff Layton gss_get_ctx(ctx); 1405d28dc82STrond Myklebust rcu_read_unlock(); 1411da177e4SLinus Torvalds return ctx; 1421da177e4SLinus Torvalds } 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds static struct gss_cl_ctx * 1451da177e4SLinus Torvalds gss_alloc_context(void) 1461da177e4SLinus Torvalds { 1471da177e4SLinus Torvalds struct gss_cl_ctx *ctx; 1481da177e4SLinus Torvalds 1490f38b873STrond Myklebust ctx = kzalloc(sizeof(*ctx), GFP_NOFS); 1501da177e4SLinus Torvalds if (ctx != NULL) { 1511da177e4SLinus Torvalds ctx->gc_proc = RPC_GSS_PROC_DATA; 1521da177e4SLinus Torvalds ctx->gc_seq = 1; /* NetApp 6.4R1 doesn't accept seq. no. 0 */ 1531da177e4SLinus Torvalds spin_lock_init(&ctx->gc_seq_lock); 1540fa10472SReshetova, Elena refcount_set(&ctx->count,1); 1551da177e4SLinus Torvalds } 1561da177e4SLinus Torvalds return ctx; 1571da177e4SLinus Torvalds } 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds #define GSSD_MIN_TIMEOUT (60 * 60) 1601da177e4SLinus Torvalds static const void * 1611da177e4SLinus Torvalds gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct gss_api_mech *gm) 1621da177e4SLinus Torvalds { 1631da177e4SLinus Torvalds const void *q; 1641da177e4SLinus Torvalds unsigned int seclen; 1651da177e4SLinus Torvalds unsigned int timeout; 166620038f6SAndy Adamson unsigned long now = jiffies; 1671da177e4SLinus Torvalds u32 window_size; 1681da177e4SLinus Torvalds int ret; 1691da177e4SLinus Torvalds 170620038f6SAndy Adamson /* First unsigned int gives the remaining lifetime in seconds of the 171620038f6SAndy Adamson * credential - e.g. the remaining TGT lifetime for Kerberos or 172620038f6SAndy Adamson * the -t value passed to GSSD. 173620038f6SAndy Adamson */ 1741da177e4SLinus Torvalds p = simple_get_bytes(p, end, &timeout, sizeof(timeout)); 1751da177e4SLinus Torvalds if (IS_ERR(p)) 1761da177e4SLinus Torvalds goto err; 1771da177e4SLinus Torvalds if (timeout == 0) 1781da177e4SLinus Torvalds timeout = GSSD_MIN_TIMEOUT; 179620038f6SAndy Adamson ctx->gc_expiry = now + ((unsigned long)timeout * HZ); 180620038f6SAndy Adamson /* Sequence number window. Determines the maximum number of 181620038f6SAndy Adamson * simultaneous requests 182620038f6SAndy Adamson */ 1831da177e4SLinus Torvalds p = simple_get_bytes(p, end, &window_size, sizeof(window_size)); 1841da177e4SLinus Torvalds if (IS_ERR(p)) 1851da177e4SLinus Torvalds goto err; 1861da177e4SLinus Torvalds ctx->gc_win = window_size; 1871da177e4SLinus Torvalds /* gssd signals an error by passing ctx->gc_win = 0: */ 1881da177e4SLinus Torvalds if (ctx->gc_win == 0) { 189dc5ddce9SJeff Layton /* 190dc5ddce9SJeff Layton * in which case, p points to an error code. Anything other 191dc5ddce9SJeff Layton * than -EKEYEXPIRED gets converted to -EACCES. 192dc5ddce9SJeff Layton */ 193dc5ddce9SJeff Layton p = simple_get_bytes(p, end, &ret, sizeof(ret)); 194dc5ddce9SJeff Layton if (!IS_ERR(p)) 195dc5ddce9SJeff Layton p = (ret == -EKEYEXPIRED) ? ERR_PTR(-EKEYEXPIRED) : 196dc5ddce9SJeff Layton ERR_PTR(-EACCES); 1971da177e4SLinus Torvalds goto err; 1981da177e4SLinus Torvalds } 1991da177e4SLinus Torvalds /* copy the opaque wire context */ 2001da177e4SLinus Torvalds p = simple_get_netobj(p, end, &ctx->gc_wire_ctx); 2011da177e4SLinus Torvalds if (IS_ERR(p)) 2021da177e4SLinus Torvalds goto err; 2031da177e4SLinus Torvalds /* import the opaque security context */ 2041da177e4SLinus Torvalds p = simple_get_bytes(p, end, &seclen, sizeof(seclen)); 2051da177e4SLinus Torvalds if (IS_ERR(p)) 2061da177e4SLinus Torvalds goto err; 2071da177e4SLinus Torvalds q = (const void *)((const char *)p + seclen); 2081da177e4SLinus Torvalds if (unlikely(q > end || q < p)) { 2091da177e4SLinus Torvalds p = ERR_PTR(-EFAULT); 2101da177e4SLinus Torvalds goto err; 2111da177e4SLinus Torvalds } 212400f26b5SSimo Sorce ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx, NULL, GFP_NOFS); 2131da177e4SLinus Torvalds if (ret < 0) { 2140c77668dSChuck Lever trace_rpcgss_import_ctx(ret); 2151da177e4SLinus Torvalds p = ERR_PTR(ret); 2161da177e4SLinus Torvalds goto err; 2171da177e4SLinus Torvalds } 2182004c726SJeff Layton 2192004c726SJeff Layton /* is there any trailing data? */ 2202004c726SJeff Layton if (q == end) { 2212004c726SJeff Layton p = q; 2222004c726SJeff Layton goto done; 2232004c726SJeff Layton } 2242004c726SJeff Layton 2252004c726SJeff Layton /* pull in acceptor name (if there is one) */ 2262004c726SJeff Layton p = simple_get_netobj(q, end, &ctx->gc_acceptor); 2272004c726SJeff Layton if (IS_ERR(p)) 2282004c726SJeff Layton goto err; 2292004c726SJeff Layton done: 23074fb8fecSChuck Lever trace_rpcgss_context(window_size, ctx->gc_expiry, now, timeout, 2310c77668dSChuck Lever ctx->gc_acceptor.len, ctx->gc_acceptor.data); 2321da177e4SLinus Torvalds err: 2331da177e4SLinus Torvalds return p; 2341da177e4SLinus Torvalds } 2351da177e4SLinus Torvalds 236a1a23777SChuck Lever /* XXX: Need some documentation about why UPCALL_BUF_LEN is so small. 237a1a23777SChuck Lever * Is user space expecting no more than UPCALL_BUF_LEN bytes? 238a1a23777SChuck Lever * Note that there are now _two_ NI_MAXHOST sized data items 239a1a23777SChuck Lever * being passed in this string. 240a1a23777SChuck Lever */ 241a1a23777SChuck Lever #define UPCALL_BUF_LEN 256 2421da177e4SLinus Torvalds 2431da177e4SLinus Torvalds struct gss_upcall_msg { 2447ff13969SReshetova, Elena refcount_t count; 2457eaf040bSEric W. Biederman kuid_t uid; 246ac83228aSTrond Myklebust const char *service_name; 2471da177e4SLinus Torvalds struct rpc_pipe_msg msg; 2481da177e4SLinus Torvalds struct list_head list; 2491da177e4SLinus Torvalds struct gss_auth *auth; 2509beae467SStanislav Kinsbursky struct rpc_pipe *pipe; 2511da177e4SLinus Torvalds struct rpc_wait_queue rpc_waitqueue; 2521da177e4SLinus Torvalds wait_queue_head_t waitqueue; 2531da177e4SLinus Torvalds struct gss_cl_ctx *ctx; 25434769fc4S\"J. Bruce Fields\ char databuf[UPCALL_BUF_LEN]; 2551da177e4SLinus Torvalds }; 2561da177e4SLinus Torvalds 2572aed8b47STrond Myklebust static int get_pipe_version(struct net *net) 25879a3f20bS\"J. Bruce Fields\ { 2592aed8b47STrond Myklebust struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 26079a3f20bS\"J. Bruce Fields\ int ret; 26179a3f20bS\"J. Bruce Fields\ 26279a3f20bS\"J. Bruce Fields\ spin_lock(&pipe_version_lock); 2632aed8b47STrond Myklebust if (sn->pipe_version >= 0) { 2642aed8b47STrond Myklebust atomic_inc(&sn->pipe_users); 2652aed8b47STrond Myklebust ret = sn->pipe_version; 26679a3f20bS\"J. Bruce Fields\ } else 26779a3f20bS\"J. Bruce Fields\ ret = -EAGAIN; 26879a3f20bS\"J. Bruce Fields\ spin_unlock(&pipe_version_lock); 26979a3f20bS\"J. Bruce Fields\ return ret; 27079a3f20bS\"J. Bruce Fields\ } 27179a3f20bS\"J. Bruce Fields\ 2722aed8b47STrond Myklebust static void put_pipe_version(struct net *net) 27379a3f20bS\"J. Bruce Fields\ { 2742aed8b47STrond Myklebust struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 2752aed8b47STrond Myklebust 2762aed8b47STrond Myklebust if (atomic_dec_and_lock(&sn->pipe_users, &pipe_version_lock)) { 2772aed8b47STrond Myklebust sn->pipe_version = -1; 27879a3f20bS\"J. Bruce Fields\ spin_unlock(&pipe_version_lock); 27979a3f20bS\"J. Bruce Fields\ } 28079a3f20bS\"J. Bruce Fields\ } 28179a3f20bS\"J. Bruce Fields\ 2821da177e4SLinus Torvalds static void 2831da177e4SLinus Torvalds gss_release_msg(struct gss_upcall_msg *gss_msg) 2841da177e4SLinus Torvalds { 285e726340aSTrond Myklebust struct net *net = gss_msg->auth->net; 2867ff13969SReshetova, Elena if (!refcount_dec_and_test(&gss_msg->count)) 2871da177e4SLinus Torvalds return; 2882aed8b47STrond Myklebust put_pipe_version(net); 2891da177e4SLinus Torvalds BUG_ON(!list_empty(&gss_msg->list)); 2901da177e4SLinus Torvalds if (gss_msg->ctx != NULL) 2911da177e4SLinus Torvalds gss_put_ctx(gss_msg->ctx); 292f6a1cc89STrond Myklebust rpc_destroy_wait_queue(&gss_msg->rpc_waitqueue); 2939eb2ddb4STrond Myklebust gss_put_auth(gss_msg->auth); 294ac83228aSTrond Myklebust kfree_const(gss_msg->service_name); 2951da177e4SLinus Torvalds kfree(gss_msg); 2961da177e4SLinus Torvalds } 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvalds static struct gss_upcall_msg * 2999130b8dbSOlga Kornievskaia __gss_find_upcall(struct rpc_pipe *pipe, kuid_t uid, const struct gss_auth *auth) 3001da177e4SLinus Torvalds { 3011da177e4SLinus Torvalds struct gss_upcall_msg *pos; 3029beae467SStanislav Kinsbursky list_for_each_entry(pos, &pipe->in_downcall, list) { 3030b4d51b0SEric W. Biederman if (!uid_eq(pos->uid, uid)) 3041da177e4SLinus Torvalds continue; 3059130b8dbSOlga Kornievskaia if (auth && pos->auth->service != auth->service) 3069130b8dbSOlga Kornievskaia continue; 3077ff13969SReshetova, Elena refcount_inc(&pos->count); 3081da177e4SLinus Torvalds return pos; 3091da177e4SLinus Torvalds } 3101da177e4SLinus Torvalds return NULL; 3111da177e4SLinus Torvalds } 3121da177e4SLinus Torvalds 313720b8f2dS\\\"J. Bruce Fields\\\ /* Try to add an upcall to the pipefs queue. 3141da177e4SLinus Torvalds * If an upcall owned by our uid already exists, then we return a reference 3151da177e4SLinus Torvalds * to that upcall instead of adding the new upcall. 3161da177e4SLinus Torvalds */ 3171da177e4SLinus Torvalds static inline struct gss_upcall_msg * 318053e324fSSuresh Jayaraman gss_add_msg(struct gss_upcall_msg *gss_msg) 3191da177e4SLinus Torvalds { 3209beae467SStanislav Kinsbursky struct rpc_pipe *pipe = gss_msg->pipe; 3211da177e4SLinus Torvalds struct gss_upcall_msg *old; 3221da177e4SLinus Torvalds 3239beae467SStanislav Kinsbursky spin_lock(&pipe->lock); 3249130b8dbSOlga Kornievskaia old = __gss_find_upcall(pipe, gss_msg->uid, gss_msg->auth); 3251da177e4SLinus Torvalds if (old == NULL) { 3267ff13969SReshetova, Elena refcount_inc(&gss_msg->count); 3279beae467SStanislav Kinsbursky list_add(&gss_msg->list, &pipe->in_downcall); 3281da177e4SLinus Torvalds } else 3291da177e4SLinus Torvalds gss_msg = old; 3309beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 3311da177e4SLinus Torvalds return gss_msg; 3321da177e4SLinus Torvalds } 3331da177e4SLinus Torvalds 3341da177e4SLinus Torvalds static void 3351da177e4SLinus Torvalds __gss_unhash_msg(struct gss_upcall_msg *gss_msg) 3361da177e4SLinus Torvalds { 3371da177e4SLinus Torvalds list_del_init(&gss_msg->list); 3381da177e4SLinus Torvalds rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); 3391da177e4SLinus Torvalds wake_up_all(&gss_msg->waitqueue); 3407ff13969SReshetova, Elena refcount_dec(&gss_msg->count); 3411da177e4SLinus Torvalds } 3421da177e4SLinus Torvalds 3431da177e4SLinus Torvalds static void 3441da177e4SLinus Torvalds gss_unhash_msg(struct gss_upcall_msg *gss_msg) 3451da177e4SLinus Torvalds { 3469beae467SStanislav Kinsbursky struct rpc_pipe *pipe = gss_msg->pipe; 3471da177e4SLinus Torvalds 3483b68aaeaSTrond Myklebust if (list_empty(&gss_msg->list)) 3493b68aaeaSTrond Myklebust return; 3509beae467SStanislav Kinsbursky spin_lock(&pipe->lock); 3513b68aaeaSTrond Myklebust if (!list_empty(&gss_msg->list)) 3521da177e4SLinus Torvalds __gss_unhash_msg(gss_msg); 3539beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 3541da177e4SLinus Torvalds } 3551da177e4SLinus Torvalds 3561da177e4SLinus Torvalds static void 357126e216aSTrond Myklebust gss_handle_downcall_result(struct gss_cred *gss_cred, struct gss_upcall_msg *gss_msg) 358126e216aSTrond Myklebust { 359126e216aSTrond Myklebust switch (gss_msg->msg.errno) { 360126e216aSTrond Myklebust case 0: 361126e216aSTrond Myklebust if (gss_msg->ctx == NULL) 362126e216aSTrond Myklebust break; 363126e216aSTrond Myklebust clear_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags); 364126e216aSTrond Myklebust gss_cred_set_ctx(&gss_cred->gc_base, gss_msg->ctx); 365126e216aSTrond Myklebust break; 366126e216aSTrond Myklebust case -EKEYEXPIRED: 367126e216aSTrond Myklebust set_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags); 368126e216aSTrond Myklebust } 369126e216aSTrond Myklebust gss_cred->gc_upcall_timestamp = jiffies; 370126e216aSTrond Myklebust gss_cred->gc_upcall = NULL; 371126e216aSTrond Myklebust rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); 372126e216aSTrond Myklebust } 373126e216aSTrond Myklebust 374126e216aSTrond Myklebust static void 3751da177e4SLinus Torvalds gss_upcall_callback(struct rpc_task *task) 3761da177e4SLinus Torvalds { 377a17c2153STrond Myklebust struct gss_cred *gss_cred = container_of(task->tk_rqstp->rq_cred, 3781da177e4SLinus Torvalds struct gss_cred, gc_base); 3791da177e4SLinus Torvalds struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall; 3809beae467SStanislav Kinsbursky struct rpc_pipe *pipe = gss_msg->pipe; 3811da177e4SLinus Torvalds 3829beae467SStanislav Kinsbursky spin_lock(&pipe->lock); 383126e216aSTrond Myklebust gss_handle_downcall_result(gss_cred, gss_msg); 3849beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 385126e216aSTrond Myklebust task->tk_status = gss_msg->msg.errno; 3861da177e4SLinus Torvalds gss_release_msg(gss_msg); 3871da177e4SLinus Torvalds } 3881da177e4SLinus Torvalds 389ac83228aSTrond Myklebust static void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg, 390ac83228aSTrond Myklebust const struct cred *cred) 39134769fc4S\"J. Bruce Fields\ { 392ac83228aSTrond Myklebust struct user_namespace *userns = cred->user_ns; 393283ebe3eSTrond Myklebust 394283ebe3eSTrond Myklebust uid_t uid = from_kuid_munged(userns, gss_msg->uid); 39590602c7bSEric W. Biederman memcpy(gss_msg->databuf, &uid, sizeof(uid)); 39690602c7bSEric W. Biederman gss_msg->msg.data = gss_msg->databuf; 39790602c7bSEric W. Biederman gss_msg->msg.len = sizeof(uid); 3989d3a2260STrond Myklebust 3999d3a2260STrond Myklebust BUILD_BUG_ON(sizeof(uid) > sizeof(gss_msg->databuf)); 40034769fc4S\"J. Bruce Fields\ } 40134769fc4S\"J. Bruce Fields\ 402ac83228aSTrond Myklebust static ssize_t 403ac83228aSTrond Myklebust gss_v0_upcall(struct file *file, struct rpc_pipe_msg *msg, 404ac83228aSTrond Myklebust char __user *buf, size_t buflen) 405ac83228aSTrond Myklebust { 406ac83228aSTrond Myklebust struct gss_upcall_msg *gss_msg = container_of(msg, 407ac83228aSTrond Myklebust struct gss_upcall_msg, 408ac83228aSTrond Myklebust msg); 409ac83228aSTrond Myklebust if (msg->copied == 0) 410ac83228aSTrond Myklebust gss_encode_v0_msg(gss_msg, file->f_cred); 411ac83228aSTrond Myklebust return rpc_pipe_generic_upcall(file, msg, buf, buflen); 412ac83228aSTrond Myklebust } 413ac83228aSTrond Myklebust 4149d3a2260STrond Myklebust static int gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, 415bd4a3eb1STrond Myklebust const char *service_name, 416ac83228aSTrond Myklebust const char *target_name, 417ac83228aSTrond Myklebust const struct cred *cred) 41834769fc4S\"J. Bruce Fields\ { 419ac83228aSTrond Myklebust struct user_namespace *userns = cred->user_ns; 420683ac665STrond Myklebust struct gss_api_mech *mech = gss_msg->auth->mech; 4218b1c7bf5SOlga Kornievskaia char *p = gss_msg->databuf; 4229d3a2260STrond Myklebust size_t buflen = sizeof(gss_msg->databuf); 4239d3a2260STrond Myklebust int len; 4248b1c7bf5SOlga Kornievskaia 4259d3a2260STrond Myklebust len = scnprintf(p, buflen, "mech=%s uid=%d", mech->gm_name, 426283ebe3eSTrond Myklebust from_kuid_munged(userns, gss_msg->uid)); 4279d3a2260STrond Myklebust buflen -= len; 4289d3a2260STrond Myklebust p += len; 4299d3a2260STrond Myklebust gss_msg->msg.len = len; 430108b833cSChuck Lever 431108b833cSChuck Lever /* 432108b833cSChuck Lever * target= is a full service principal that names the remote 433108b833cSChuck Lever * identity that we are authenticating to. 434108b833cSChuck Lever */ 435bd4a3eb1STrond Myklebust if (target_name) { 4369d3a2260STrond Myklebust len = scnprintf(p, buflen, " target=%s", target_name); 4379d3a2260STrond Myklebust buflen -= len; 4388b1c7bf5SOlga Kornievskaia p += len; 4398b1c7bf5SOlga Kornievskaia gss_msg->msg.len += len; 4408b1c7bf5SOlga Kornievskaia } 441108b833cSChuck Lever 442108b833cSChuck Lever /* 443108b833cSChuck Lever * gssd uses service= and srchost= to select a matching key from 444108b833cSChuck Lever * the system's keytab to use as the source principal. 445108b833cSChuck Lever * 446108b833cSChuck Lever * service= is the service name part of the source principal, 447108b833cSChuck Lever * or "*" (meaning choose any). 448108b833cSChuck Lever * 449108b833cSChuck Lever * srchost= is the hostname part of the source principal. When 450108b833cSChuck Lever * not provided, gssd uses the local hostname. 451108b833cSChuck Lever */ 452a1a23777SChuck Lever if (service_name) { 453a1a23777SChuck Lever char *c = strchr(service_name, '@'); 454a1a23777SChuck Lever 455a1a23777SChuck Lever if (!c) 456a1a23777SChuck Lever len = scnprintf(p, buflen, " service=%s", 457a1a23777SChuck Lever service_name); 458a1a23777SChuck Lever else 459a1a23777SChuck Lever len = scnprintf(p, buflen, 460a1a23777SChuck Lever " service=%.*s srchost=%s", 461a1a23777SChuck Lever (int)(c - service_name), 462a1a23777SChuck Lever service_name, c + 1); 4639d3a2260STrond Myklebust buflen -= len; 4642efef708SOlga Kornievskaia p += len; 4652efef708SOlga Kornievskaia gss_msg->msg.len += len; 4662efef708SOlga Kornievskaia } 467108b833cSChuck Lever 468683ac665STrond Myklebust if (mech->gm_upcall_enctypes) { 4699d3a2260STrond Myklebust len = scnprintf(p, buflen, " enctypes=%s", 4709d3a2260STrond Myklebust mech->gm_upcall_enctypes); 4719d3a2260STrond Myklebust buflen -= len; 472683ac665STrond Myklebust p += len; 473683ac665STrond Myklebust gss_msg->msg.len += len; 474683ac665STrond Myklebust } 4750c77668dSChuck Lever trace_rpcgss_upcall_msg(gss_msg->databuf); 4769d3a2260STrond Myklebust len = scnprintf(p, buflen, "\n"); 4779d3a2260STrond Myklebust if (len == 0) 4789d3a2260STrond Myklebust goto out_overflow; 4798b1c7bf5SOlga Kornievskaia gss_msg->msg.len += len; 48034769fc4S\"J. Bruce Fields\ gss_msg->msg.data = gss_msg->databuf; 4819d3a2260STrond Myklebust return 0; 4829d3a2260STrond Myklebust out_overflow: 4839d3a2260STrond Myklebust WARN_ON_ONCE(1); 4849d3a2260STrond Myklebust return -ENOMEM; 48534769fc4S\"J. Bruce Fields\ } 48634769fc4S\"J. Bruce Fields\ 487ac83228aSTrond Myklebust static ssize_t 488ac83228aSTrond Myklebust gss_v1_upcall(struct file *file, struct rpc_pipe_msg *msg, 489ac83228aSTrond Myklebust char __user *buf, size_t buflen) 490ac83228aSTrond Myklebust { 491ac83228aSTrond Myklebust struct gss_upcall_msg *gss_msg = container_of(msg, 492ac83228aSTrond Myklebust struct gss_upcall_msg, 493ac83228aSTrond Myklebust msg); 494ac83228aSTrond Myklebust int err; 495ac83228aSTrond Myklebust if (msg->copied == 0) { 496ac83228aSTrond Myklebust err = gss_encode_v1_msg(gss_msg, 497ac83228aSTrond Myklebust gss_msg->service_name, 498ac83228aSTrond Myklebust gss_msg->auth->target_name, 499ac83228aSTrond Myklebust file->f_cred); 500ac83228aSTrond Myklebust if (err) 501ac83228aSTrond Myklebust return err; 502ac83228aSTrond Myklebust } 503ac83228aSTrond Myklebust return rpc_pipe_generic_upcall(file, msg, buf, buflen); 504ac83228aSTrond Myklebust } 505ac83228aSTrond Myklebust 50668c97153STrond Myklebust static struct gss_upcall_msg * 507e726340aSTrond Myklebust gss_alloc_msg(struct gss_auth *gss_auth, 5087eaf040bSEric W. Biederman kuid_t uid, const char *service_name) 5091da177e4SLinus Torvalds { 5101da177e4SLinus Torvalds struct gss_upcall_msg *gss_msg; 51179a3f20bS\"J. Bruce Fields\ int vers; 5129d3a2260STrond Myklebust int err = -ENOMEM; 5131da177e4SLinus Torvalds 5140f38b873STrond Myklebust gss_msg = kzalloc(sizeof(*gss_msg), GFP_NOFS); 515db75b3d6S\"J. Bruce Fields\ if (gss_msg == NULL) 5169d3a2260STrond Myklebust goto err; 517e726340aSTrond Myklebust vers = get_pipe_version(gss_auth->net); 5189d3a2260STrond Myklebust err = vers; 5199d3a2260STrond Myklebust if (err < 0) 5209d3a2260STrond Myklebust goto err_free_msg; 52119172284STrond Myklebust gss_msg->pipe = gss_auth->gss_pipe[vers]->pipe; 5221da177e4SLinus Torvalds INIT_LIST_HEAD(&gss_msg->list); 5231da177e4SLinus Torvalds rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq"); 5241da177e4SLinus Torvalds init_waitqueue_head(&gss_msg->waitqueue); 5257ff13969SReshetova, Elena refcount_set(&gss_msg->count, 1); 5261da177e4SLinus Torvalds gss_msg->uid = uid; 5271da177e4SLinus Torvalds gss_msg->auth = gss_auth; 5285940d1cfSChuck Lever kref_get(&gss_auth->kref); 529ac83228aSTrond Myklebust if (service_name) { 530ac83228aSTrond Myklebust gss_msg->service_name = kstrdup_const(service_name, GFP_NOFS); 531fe31ce83SDan Carpenter if (!gss_msg->service_name) { 532fe31ce83SDan Carpenter err = -ENOMEM; 533e9776d0fSTrond Myklebust goto err_put_pipe_version; 53407d53ae4Szhong jiang } 535fe31ce83SDan Carpenter } 5361da177e4SLinus Torvalds return gss_msg; 537e9776d0fSTrond Myklebust err_put_pipe_version: 538e9776d0fSTrond Myklebust put_pipe_version(gss_auth->net); 5399d3a2260STrond Myklebust err_free_msg: 5409d3a2260STrond Myklebust kfree(gss_msg); 5419d3a2260STrond Myklebust err: 5429d3a2260STrond Myklebust return ERR_PTR(err); 5431da177e4SLinus Torvalds } 5441da177e4SLinus Torvalds 5451da177e4SLinus Torvalds static struct gss_upcall_msg * 546e726340aSTrond Myklebust gss_setup_upcall(struct gss_auth *gss_auth, struct rpc_cred *cred) 5471da177e4SLinus Torvalds { 5487c67db3aSTrond Myklebust struct gss_cred *gss_cred = container_of(cred, 5497c67db3aSTrond Myklebust struct gss_cred, gc_base); 5501da177e4SLinus Torvalds struct gss_upcall_msg *gss_new, *gss_msg; 55104d1532bSNeilBrown kuid_t uid = cred->cr_cred->fsuid; 5521da177e4SLinus Torvalds 553e726340aSTrond Myklebust gss_new = gss_alloc_msg(gss_auth, uid, gss_cred->gc_principal); 554db75b3d6S\"J. Bruce Fields\ if (IS_ERR(gss_new)) 555db75b3d6S\"J. Bruce Fields\ return gss_new; 556053e324fSSuresh Jayaraman gss_msg = gss_add_msg(gss_new); 5571da177e4SLinus Torvalds if (gss_msg == gss_new) { 5581cded9d2SNeilBrown int res; 5597ff13969SReshetova, Elena refcount_inc(&gss_msg->count); 5601cded9d2SNeilBrown res = rpc_queue_upcall(gss_new->pipe, &gss_new->msg); 5611da177e4SLinus Torvalds if (res) { 5621da177e4SLinus Torvalds gss_unhash_msg(gss_new); 5637ff13969SReshetova, Elena refcount_dec(&gss_msg->count); 5641cded9d2SNeilBrown gss_release_msg(gss_new); 5651da177e4SLinus Torvalds gss_msg = ERR_PTR(res); 5661da177e4SLinus Torvalds } 5671da177e4SLinus Torvalds } else 5681da177e4SLinus Torvalds gss_release_msg(gss_new); 5691da177e4SLinus Torvalds return gss_msg; 5701da177e4SLinus Torvalds } 5711da177e4SLinus Torvalds 572b03568c3S\"J. Bruce Fields\ static void warn_gssd(void) 573b03568c3S\"J. Bruce Fields\ { 5740ea9de0eSJeff Layton dprintk("AUTH_GSS upcall failed. Please check user daemon is running.\n"); 575b03568c3S\"J. Bruce Fields\ } 576b03568c3S\"J. Bruce Fields\ 5771da177e4SLinus Torvalds static inline int 5781da177e4SLinus Torvalds gss_refresh_upcall(struct rpc_task *task) 5791da177e4SLinus Torvalds { 580a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 5814a8c1344STrond Myklebust struct gss_auth *gss_auth = container_of(cred->cr_auth, 5821da177e4SLinus Torvalds struct gss_auth, rpc_auth); 5831da177e4SLinus Torvalds struct gss_cred *gss_cred = container_of(cred, 5841da177e4SLinus Torvalds struct gss_cred, gc_base); 5851da177e4SLinus Torvalds struct gss_upcall_msg *gss_msg; 5869beae467SStanislav Kinsbursky struct rpc_pipe *pipe; 5871da177e4SLinus Torvalds int err = 0; 5881da177e4SLinus Torvalds 589e726340aSTrond Myklebust gss_msg = gss_setup_upcall(gss_auth, cred); 590480e3243SRoel Kluin if (PTR_ERR(gss_msg) == -EAGAIN) { 59179a3f20bS\"J. Bruce Fields\ /* XXX: warning on the first, under the assumption we 59279a3f20bS\"J. Bruce Fields\ * shouldn't normally hit this case on a refresh. */ 59379a3f20bS\"J. Bruce Fields\ warn_gssd(); 5946b2e6856STrond Myklebust rpc_sleep_on_timeout(&pipe_version_rpc_waitqueue, 5956b2e6856STrond Myklebust task, NULL, jiffies + (15 * HZ)); 5960c77668dSChuck Lever err = -EAGAIN; 5970c77668dSChuck Lever goto out; 59879a3f20bS\"J. Bruce Fields\ } 5991da177e4SLinus Torvalds if (IS_ERR(gss_msg)) { 6001da177e4SLinus Torvalds err = PTR_ERR(gss_msg); 6011da177e4SLinus Torvalds goto out; 6021da177e4SLinus Torvalds } 6039beae467SStanislav Kinsbursky pipe = gss_msg->pipe; 6049beae467SStanislav Kinsbursky spin_lock(&pipe->lock); 6051da177e4SLinus Torvalds if (gss_cred->gc_upcall != NULL) 6065d00837bSTrond Myklebust rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL); 607126e216aSTrond Myklebust else if (gss_msg->ctx == NULL && gss_msg->msg.errno >= 0) { 6081da177e4SLinus Torvalds gss_cred->gc_upcall = gss_msg; 6091da177e4SLinus Torvalds /* gss_upcall_callback will release the reference to gss_upcall_msg */ 6107ff13969SReshetova, Elena refcount_inc(&gss_msg->count); 6115d00837bSTrond Myklebust rpc_sleep_on(&gss_msg->rpc_waitqueue, task, gss_upcall_callback); 612126e216aSTrond Myklebust } else { 613126e216aSTrond Myklebust gss_handle_downcall_result(gss_cred, gss_msg); 6141da177e4SLinus Torvalds err = gss_msg->msg.errno; 615126e216aSTrond Myklebust } 6169beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 6171da177e4SLinus Torvalds gss_release_msg(gss_msg); 6181da177e4SLinus Torvalds out: 6190c77668dSChuck Lever trace_rpcgss_upcall_result(from_kuid(&init_user_ns, 6200c77668dSChuck Lever cred->cr_cred->fsuid), err); 6211da177e4SLinus Torvalds return err; 6221da177e4SLinus Torvalds } 6231da177e4SLinus Torvalds 6241da177e4SLinus Torvalds static inline int 6251da177e4SLinus Torvalds gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred) 6261da177e4SLinus Torvalds { 627e726340aSTrond Myklebust struct net *net = gss_auth->net; 628abfdbd53STrond Myklebust struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 6299beae467SStanislav Kinsbursky struct rpc_pipe *pipe; 6301da177e4SLinus Torvalds struct rpc_cred *cred = &gss_cred->gc_base; 6311da177e4SLinus Torvalds struct gss_upcall_msg *gss_msg; 6321da177e4SLinus Torvalds DEFINE_WAIT(wait); 633d36ccb9cSTrond Myklebust int err; 6341da177e4SLinus Torvalds 63579a3f20bS\"J. Bruce Fields\ retry: 636d36ccb9cSTrond Myklebust err = 0; 63789f84243SJeff Layton /* if gssd is down, just skip upcalling altogether */ 63889f84243SJeff Layton if (!gssd_running(net)) { 63989f84243SJeff Layton warn_gssd(); 6400c77668dSChuck Lever err = -EACCES; 6410c77668dSChuck Lever goto out; 64289f84243SJeff Layton } 643e726340aSTrond Myklebust gss_msg = gss_setup_upcall(gss_auth, cred); 64479a3f20bS\"J. Bruce Fields\ if (PTR_ERR(gss_msg) == -EAGAIN) { 64579a3f20bS\"J. Bruce Fields\ err = wait_event_interruptible_timeout(pipe_version_waitqueue, 64689f84243SJeff Layton sn->pipe_version >= 0, 15 * HZ); 6472aed8b47STrond Myklebust if (sn->pipe_version < 0) { 648d1a8016aSBryan Schumaker warn_gssd(); 649d1a8016aSBryan Schumaker err = -EACCES; 650d1a8016aSBryan Schumaker } 651d36ccb9cSTrond Myklebust if (err < 0) 65279a3f20bS\"J. Bruce Fields\ goto out; 65379a3f20bS\"J. Bruce Fields\ goto retry; 65479a3f20bS\"J. Bruce Fields\ } 6551da177e4SLinus Torvalds if (IS_ERR(gss_msg)) { 6561da177e4SLinus Torvalds err = PTR_ERR(gss_msg); 6571da177e4SLinus Torvalds goto out; 6581da177e4SLinus Torvalds } 6599beae467SStanislav Kinsbursky pipe = gss_msg->pipe; 6601da177e4SLinus Torvalds for (;;) { 6615afa9133STrond Myklebust prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_KILLABLE); 6629beae467SStanislav Kinsbursky spin_lock(&pipe->lock); 6631da177e4SLinus Torvalds if (gss_msg->ctx != NULL || gss_msg->msg.errno < 0) { 6641da177e4SLinus Torvalds break; 6651da177e4SLinus Torvalds } 6669beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 6675afa9133STrond Myklebust if (fatal_signal_pending(current)) { 6681da177e4SLinus Torvalds err = -ERESTARTSYS; 6691da177e4SLinus Torvalds goto out_intr; 6701da177e4SLinus Torvalds } 6711da177e4SLinus Torvalds schedule(); 6721da177e4SLinus Torvalds } 67374fb8fecSChuck Lever if (gss_msg->ctx) { 67474fb8fecSChuck Lever trace_rpcgss_ctx_init(gss_cred); 6757b6962b0STrond Myklebust gss_cred_set_ctx(cred, gss_msg->ctx); 67674fb8fecSChuck Lever } else { 6771da177e4SLinus Torvalds err = gss_msg->msg.errno; 67874fb8fecSChuck Lever } 6799beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 6801da177e4SLinus Torvalds out_intr: 6811da177e4SLinus Torvalds finish_wait(&gss_msg->waitqueue, &wait); 6821da177e4SLinus Torvalds gss_release_msg(gss_msg); 6831da177e4SLinus Torvalds out: 6840c77668dSChuck Lever trace_rpcgss_upcall_result(from_kuid(&init_user_ns, 6850c77668dSChuck Lever cred->cr_cred->fsuid), err); 6861da177e4SLinus Torvalds return err; 6871da177e4SLinus Torvalds } 6881da177e4SLinus Torvalds 6891da177e4SLinus Torvalds #define MSG_BUF_MAXSIZE 1024 6901da177e4SLinus Torvalds 6911da177e4SLinus Torvalds static ssize_t 6921da177e4SLinus Torvalds gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) 6931da177e4SLinus Torvalds { 6941da177e4SLinus Torvalds const void *p, *end; 6951da177e4SLinus Torvalds void *buf; 6961da177e4SLinus Torvalds struct gss_upcall_msg *gss_msg; 697496ad9aaSAl Viro struct rpc_pipe *pipe = RPC_I(file_inode(filp))->pipe; 6981da177e4SLinus Torvalds struct gss_cl_ctx *ctx; 69990602c7bSEric W. Biederman uid_t id; 70090602c7bSEric W. Biederman kuid_t uid; 7013b68aaeaSTrond Myklebust ssize_t err = -EFBIG; 7021da177e4SLinus Torvalds 7031da177e4SLinus Torvalds if (mlen > MSG_BUF_MAXSIZE) 7041da177e4SLinus Torvalds goto out; 7051da177e4SLinus Torvalds err = -ENOMEM; 7060f38b873STrond Myklebust buf = kmalloc(mlen, GFP_NOFS); 7071da177e4SLinus Torvalds if (!buf) 7081da177e4SLinus Torvalds goto out; 7091da177e4SLinus Torvalds 7101da177e4SLinus Torvalds err = -EFAULT; 7111da177e4SLinus Torvalds if (copy_from_user(buf, src, mlen)) 7121da177e4SLinus Torvalds goto err; 7131da177e4SLinus Torvalds 7141da177e4SLinus Torvalds end = (const void *)((char *)buf + mlen); 71590602c7bSEric W. Biederman p = simple_get_bytes(buf, end, &id, sizeof(id)); 7161da177e4SLinus Torvalds if (IS_ERR(p)) { 7171da177e4SLinus Torvalds err = PTR_ERR(p); 7181da177e4SLinus Torvalds goto err; 7191da177e4SLinus Torvalds } 7201da177e4SLinus Torvalds 721283ebe3eSTrond Myklebust uid = make_kuid(current_user_ns(), id); 72290602c7bSEric W. Biederman if (!uid_valid(uid)) { 72390602c7bSEric W. Biederman err = -EINVAL; 72490602c7bSEric W. Biederman goto err; 72590602c7bSEric W. Biederman } 72690602c7bSEric W. Biederman 7271da177e4SLinus Torvalds err = -ENOMEM; 7281da177e4SLinus Torvalds ctx = gss_alloc_context(); 7291da177e4SLinus Torvalds if (ctx == NULL) 7301da177e4SLinus Torvalds goto err; 7313b68aaeaSTrond Myklebust 7323b68aaeaSTrond Myklebust err = -ENOENT; 7333b68aaeaSTrond Myklebust /* Find a matching upcall */ 7349beae467SStanislav Kinsbursky spin_lock(&pipe->lock); 7359130b8dbSOlga Kornievskaia gss_msg = __gss_find_upcall(pipe, uid, NULL); 7363b68aaeaSTrond Myklebust if (gss_msg == NULL) { 7379beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 7383b68aaeaSTrond Myklebust goto err_put_ctx; 7393b68aaeaSTrond Myklebust } 7403b68aaeaSTrond Myklebust list_del_init(&gss_msg->list); 7419beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 7423b68aaeaSTrond Myklebust 7436e84c7b6STrond Myklebust p = gss_fill_context(p, end, ctx, gss_msg->auth->mech); 7441da177e4SLinus Torvalds if (IS_ERR(p)) { 7451da177e4SLinus Torvalds err = PTR_ERR(p); 746486bad2eSJeff Layton switch (err) { 747486bad2eSJeff Layton case -EACCES: 748dc5ddce9SJeff Layton case -EKEYEXPIRED: 749486bad2eSJeff Layton gss_msg->msg.errno = err; 750486bad2eSJeff Layton err = mlen; 751486bad2eSJeff Layton break; 752486bad2eSJeff Layton case -EFAULT: 753486bad2eSJeff Layton case -ENOMEM: 754486bad2eSJeff Layton case -EINVAL: 755486bad2eSJeff Layton case -ENOSYS: 756486bad2eSJeff Layton gss_msg->msg.errno = -EAGAIN; 757486bad2eSJeff Layton break; 758486bad2eSJeff Layton default: 759486bad2eSJeff Layton printk(KERN_CRIT "%s: bad return from " 7606c853099SRandy Dunlap "gss_fill_context: %zd\n", __func__, err); 761437b300cSScott Mayhew gss_msg->msg.errno = -EIO; 762486bad2eSJeff Layton } 7633b68aaeaSTrond Myklebust goto err_release_msg; 7641da177e4SLinus Torvalds } 7651da177e4SLinus Torvalds gss_msg->ctx = gss_get_ctx(ctx); 7663b68aaeaSTrond Myklebust err = mlen; 7673b68aaeaSTrond Myklebust 7683b68aaeaSTrond Myklebust err_release_msg: 7699beae467SStanislav Kinsbursky spin_lock(&pipe->lock); 7701da177e4SLinus Torvalds __gss_unhash_msg(gss_msg); 7719beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 7721da177e4SLinus Torvalds gss_release_msg(gss_msg); 7731da177e4SLinus Torvalds err_put_ctx: 7741da177e4SLinus Torvalds gss_put_ctx(ctx); 7751da177e4SLinus Torvalds err: 7761da177e4SLinus Torvalds kfree(buf); 7771da177e4SLinus Torvalds out: 7781da177e4SLinus Torvalds return err; 7791da177e4SLinus Torvalds } 7801da177e4SLinus Torvalds 78134769fc4S\"J. Bruce Fields\ static int gss_pipe_open(struct inode *inode, int new_version) 782cf81939dS\"J. Bruce Fields\ { 7832aed8b47STrond Myklebust struct net *net = inode->i_sb->s_fs_info; 7842aed8b47STrond Myklebust struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 78534769fc4S\"J. Bruce Fields\ int ret = 0; 78634769fc4S\"J. Bruce Fields\ 78779a3f20bS\"J. Bruce Fields\ spin_lock(&pipe_version_lock); 7882aed8b47STrond Myklebust if (sn->pipe_version < 0) { 78934769fc4S\"J. Bruce Fields\ /* First open of any gss pipe determines the version: */ 7902aed8b47STrond Myklebust sn->pipe_version = new_version; 79179a3f20bS\"J. Bruce Fields\ rpc_wake_up(&pipe_version_rpc_waitqueue); 79279a3f20bS\"J. Bruce Fields\ wake_up(&pipe_version_waitqueue); 7932aed8b47STrond Myklebust } else if (sn->pipe_version != new_version) { 79434769fc4S\"J. Bruce Fields\ /* Trying to open a pipe of a different version */ 79534769fc4S\"J. Bruce Fields\ ret = -EBUSY; 79634769fc4S\"J. Bruce Fields\ goto out; 79779a3f20bS\"J. Bruce Fields\ } 7982aed8b47STrond Myklebust atomic_inc(&sn->pipe_users); 79934769fc4S\"J. Bruce Fields\ out: 80079a3f20bS\"J. Bruce Fields\ spin_unlock(&pipe_version_lock); 80134769fc4S\"J. Bruce Fields\ return ret; 80234769fc4S\"J. Bruce Fields\ 80334769fc4S\"J. Bruce Fields\ } 80434769fc4S\"J. Bruce Fields\ 80534769fc4S\"J. Bruce Fields\ static int gss_pipe_open_v0(struct inode *inode) 80634769fc4S\"J. Bruce Fields\ { 80734769fc4S\"J. Bruce Fields\ return gss_pipe_open(inode, 0); 80834769fc4S\"J. Bruce Fields\ } 80934769fc4S\"J. Bruce Fields\ 81034769fc4S\"J. Bruce Fields\ static int gss_pipe_open_v1(struct inode *inode) 81134769fc4S\"J. Bruce Fields\ { 81234769fc4S\"J. Bruce Fields\ return gss_pipe_open(inode, 1); 813cf81939dS\"J. Bruce Fields\ } 814cf81939dS\"J. Bruce Fields\ 8151da177e4SLinus Torvalds static void 8161da177e4SLinus Torvalds gss_pipe_release(struct inode *inode) 8171da177e4SLinus Torvalds { 8182aed8b47STrond Myklebust struct net *net = inode->i_sb->s_fs_info; 8199beae467SStanislav Kinsbursky struct rpc_pipe *pipe = RPC_I(inode)->pipe; 8201da177e4SLinus Torvalds struct gss_upcall_msg *gss_msg; 8211da177e4SLinus Torvalds 8225a67657aSTrond Myklebust restart: 8239beae467SStanislav Kinsbursky spin_lock(&pipe->lock); 8249beae467SStanislav Kinsbursky list_for_each_entry(gss_msg, &pipe->in_downcall, list) { 8256e84c7b6STrond Myklebust 8265a67657aSTrond Myklebust if (!list_empty(&gss_msg->msg.list)) 8275a67657aSTrond Myklebust continue; 8281da177e4SLinus Torvalds gss_msg->msg.errno = -EPIPE; 8297ff13969SReshetova, Elena refcount_inc(&gss_msg->count); 8301da177e4SLinus Torvalds __gss_unhash_msg(gss_msg); 8319beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 8321da177e4SLinus Torvalds gss_release_msg(gss_msg); 8335a67657aSTrond Myklebust goto restart; 8341da177e4SLinus Torvalds } 8359beae467SStanislav Kinsbursky spin_unlock(&pipe->lock); 836cf81939dS\"J. Bruce Fields\ 8372aed8b47STrond Myklebust put_pipe_version(net); 8381da177e4SLinus Torvalds } 8391da177e4SLinus Torvalds 8401da177e4SLinus Torvalds static void 8411da177e4SLinus Torvalds gss_pipe_destroy_msg(struct rpc_pipe_msg *msg) 8421da177e4SLinus Torvalds { 8431da177e4SLinus Torvalds struct gss_upcall_msg *gss_msg = container_of(msg, struct gss_upcall_msg, msg); 8441da177e4SLinus Torvalds 8451da177e4SLinus Torvalds if (msg->errno < 0) { 8467ff13969SReshetova, Elena refcount_inc(&gss_msg->count); 8471da177e4SLinus Torvalds gss_unhash_msg(gss_msg); 848b03568c3S\"J. Bruce Fields\ if (msg->errno == -ETIMEDOUT) 849b03568c3S\"J. Bruce Fields\ warn_gssd(); 8501da177e4SLinus Torvalds gss_release_msg(gss_msg); 8511da177e4SLinus Torvalds } 8521cded9d2SNeilBrown gss_release_msg(gss_msg); 8531da177e4SLinus Torvalds } 8541da177e4SLinus Torvalds 85519172284STrond Myklebust static void gss_pipe_dentry_destroy(struct dentry *dir, 85619172284STrond Myklebust struct rpc_pipe_dir_object *pdo) 857ccdc28f8SStanislav Kinsbursky { 85819172284STrond Myklebust struct gss_pipe *gss_pipe = pdo->pdo_data; 85919172284STrond Myklebust struct rpc_pipe *pipe = gss_pipe->pipe; 860ccdc28f8SStanislav Kinsbursky 86119172284STrond Myklebust if (pipe->dentry != NULL) { 86219172284STrond Myklebust rpc_unlink(pipe->dentry); 86319172284STrond Myklebust pipe->dentry = NULL; 8646b2fddd3STrond Myklebust } 865ccdc28f8SStanislav Kinsbursky } 866ccdc28f8SStanislav Kinsbursky 86719172284STrond Myklebust static int gss_pipe_dentry_create(struct dentry *dir, 86819172284STrond Myklebust struct rpc_pipe_dir_object *pdo) 869ccdc28f8SStanislav Kinsbursky { 87019172284STrond Myklebust struct gss_pipe *p = pdo->pdo_data; 8716b2fddd3STrond Myklebust struct dentry *dentry; 872ccdc28f8SStanislav Kinsbursky 87319172284STrond Myklebust dentry = rpc_mkpipe_dentry(dir, p->name, p->clnt, p->pipe); 87419172284STrond Myklebust if (IS_ERR(dentry)) 87519172284STrond Myklebust return PTR_ERR(dentry); 87619172284STrond Myklebust p->pipe->dentry = dentry; 877ccdc28f8SStanislav Kinsbursky return 0; 87819172284STrond Myklebust } 879ccdc28f8SStanislav Kinsbursky 88019172284STrond Myklebust static const struct rpc_pipe_dir_object_ops gss_pipe_dir_object_ops = { 88119172284STrond Myklebust .create = gss_pipe_dentry_create, 88219172284STrond Myklebust .destroy = gss_pipe_dentry_destroy, 88319172284STrond Myklebust }; 88419172284STrond Myklebust 88519172284STrond Myklebust static struct gss_pipe *gss_pipe_alloc(struct rpc_clnt *clnt, 88619172284STrond Myklebust const char *name, 88719172284STrond Myklebust const struct rpc_pipe_ops *upcall_ops) 88819172284STrond Myklebust { 88919172284STrond Myklebust struct gss_pipe *p; 89019172284STrond Myklebust int err = -ENOMEM; 89119172284STrond Myklebust 89219172284STrond Myklebust p = kmalloc(sizeof(*p), GFP_KERNEL); 89319172284STrond Myklebust if (p == NULL) 89419172284STrond Myklebust goto err; 89519172284STrond Myklebust p->pipe = rpc_mkpipe_data(upcall_ops, RPC_PIPE_WAIT_FOR_OPEN); 89619172284STrond Myklebust if (IS_ERR(p->pipe)) { 89719172284STrond Myklebust err = PTR_ERR(p->pipe); 89819172284STrond Myklebust goto err_free_gss_pipe; 89919172284STrond Myklebust } 90019172284STrond Myklebust p->name = name; 90119172284STrond Myklebust p->clnt = clnt; 902414a6295STrond Myklebust kref_init(&p->kref); 90319172284STrond Myklebust rpc_init_pipe_dir_object(&p->pdo, 90419172284STrond Myklebust &gss_pipe_dir_object_ops, 90519172284STrond Myklebust p); 90619172284STrond Myklebust return p; 90719172284STrond Myklebust err_free_gss_pipe: 90819172284STrond Myklebust kfree(p); 9096b2fddd3STrond Myklebust err: 91019172284STrond Myklebust return ERR_PTR(err); 911ccdc28f8SStanislav Kinsbursky } 912ccdc28f8SStanislav Kinsbursky 913414a6295STrond Myklebust struct gss_alloc_pdo { 914414a6295STrond Myklebust struct rpc_clnt *clnt; 915414a6295STrond Myklebust const char *name; 916414a6295STrond Myklebust const struct rpc_pipe_ops *upcall_ops; 917414a6295STrond Myklebust }; 918414a6295STrond Myklebust 919414a6295STrond Myklebust static int gss_pipe_match_pdo(struct rpc_pipe_dir_object *pdo, void *data) 920414a6295STrond Myklebust { 921414a6295STrond Myklebust struct gss_pipe *gss_pipe; 922414a6295STrond Myklebust struct gss_alloc_pdo *args = data; 923414a6295STrond Myklebust 924414a6295STrond Myklebust if (pdo->pdo_ops != &gss_pipe_dir_object_ops) 925414a6295STrond Myklebust return 0; 926414a6295STrond Myklebust gss_pipe = container_of(pdo, struct gss_pipe, pdo); 927414a6295STrond Myklebust if (strcmp(gss_pipe->name, args->name) != 0) 928414a6295STrond Myklebust return 0; 929414a6295STrond Myklebust if (!kref_get_unless_zero(&gss_pipe->kref)) 930414a6295STrond Myklebust return 0; 931414a6295STrond Myklebust return 1; 932414a6295STrond Myklebust } 933414a6295STrond Myklebust 934414a6295STrond Myklebust static struct rpc_pipe_dir_object *gss_pipe_alloc_pdo(void *data) 935414a6295STrond Myklebust { 936414a6295STrond Myklebust struct gss_pipe *gss_pipe; 937414a6295STrond Myklebust struct gss_alloc_pdo *args = data; 938414a6295STrond Myklebust 939414a6295STrond Myklebust gss_pipe = gss_pipe_alloc(args->clnt, args->name, args->upcall_ops); 940414a6295STrond Myklebust if (!IS_ERR(gss_pipe)) 941414a6295STrond Myklebust return &gss_pipe->pdo; 942414a6295STrond Myklebust return NULL; 943414a6295STrond Myklebust } 944414a6295STrond Myklebust 945414a6295STrond Myklebust static struct gss_pipe *gss_pipe_get(struct rpc_clnt *clnt, 946414a6295STrond Myklebust const char *name, 947414a6295STrond Myklebust const struct rpc_pipe_ops *upcall_ops) 948414a6295STrond Myklebust { 949414a6295STrond Myklebust struct net *net = rpc_net_ns(clnt); 950414a6295STrond Myklebust struct rpc_pipe_dir_object *pdo; 951414a6295STrond Myklebust struct gss_alloc_pdo args = { 952414a6295STrond Myklebust .clnt = clnt, 953414a6295STrond Myklebust .name = name, 954414a6295STrond Myklebust .upcall_ops = upcall_ops, 955414a6295STrond Myklebust }; 956414a6295STrond Myklebust 957414a6295STrond Myklebust pdo = rpc_find_or_alloc_pipe_dir_object(net, 958414a6295STrond Myklebust &clnt->cl_pipedir_objects, 959414a6295STrond Myklebust gss_pipe_match_pdo, 960414a6295STrond Myklebust gss_pipe_alloc_pdo, 961414a6295STrond Myklebust &args); 962414a6295STrond Myklebust if (pdo != NULL) 963414a6295STrond Myklebust return container_of(pdo, struct gss_pipe, pdo); 964414a6295STrond Myklebust return ERR_PTR(-ENOMEM); 965414a6295STrond Myklebust } 966414a6295STrond Myklebust 96719172284STrond Myklebust static void __gss_pipe_free(struct gss_pipe *p) 968ccdc28f8SStanislav Kinsbursky { 96919172284STrond Myklebust struct rpc_clnt *clnt = p->clnt; 97019172284STrond Myklebust struct net *net = rpc_net_ns(clnt); 971ccdc28f8SStanislav Kinsbursky 97219172284STrond Myklebust rpc_remove_pipe_dir_object(net, 97319172284STrond Myklebust &clnt->cl_pipedir_objects, 97419172284STrond Myklebust &p->pdo); 97519172284STrond Myklebust rpc_destroy_pipe_data(p->pipe); 97619172284STrond Myklebust kfree(p); 977ccdc28f8SStanislav Kinsbursky } 978ccdc28f8SStanislav Kinsbursky 979414a6295STrond Myklebust static void __gss_pipe_release(struct kref *kref) 980414a6295STrond Myklebust { 981414a6295STrond Myklebust struct gss_pipe *p = container_of(kref, struct gss_pipe, kref); 982414a6295STrond Myklebust 983414a6295STrond Myklebust __gss_pipe_free(p); 984414a6295STrond Myklebust } 985414a6295STrond Myklebust 98619172284STrond Myklebust static void gss_pipe_free(struct gss_pipe *p) 987ccdc28f8SStanislav Kinsbursky { 98819172284STrond Myklebust if (p != NULL) 989414a6295STrond Myklebust kref_put(&p->kref, __gss_pipe_release); 990ccdc28f8SStanislav Kinsbursky } 991ccdc28f8SStanislav Kinsbursky 9921da177e4SLinus Torvalds /* 9931da177e4SLinus Torvalds * NOTE: we have the opportunity to use different 9941da177e4SLinus Torvalds * parameters based on the input flavor (which must be a pseudoflavor) 9951da177e4SLinus Torvalds */ 996eb6dc19dSTrond Myklebust static struct gss_auth * 99782b98ca5SSargun Dhillon gss_create_new(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) 9981da177e4SLinus Torvalds { 999c2190661STrond Myklebust rpc_authflavor_t flavor = args->pseudoflavor; 10001da177e4SLinus Torvalds struct gss_auth *gss_auth; 100119172284STrond Myklebust struct gss_pipe *gss_pipe; 10021da177e4SLinus Torvalds struct rpc_auth * auth; 10036a19275aSJ. Bruce Fields int err = -ENOMEM; /* XXX? */ 10041da177e4SLinus Torvalds 10051da177e4SLinus Torvalds if (!try_module_get(THIS_MODULE)) 10066a19275aSJ. Bruce Fields return ERR_PTR(err); 10071da177e4SLinus Torvalds if (!(gss_auth = kmalloc(sizeof(*gss_auth), GFP_KERNEL))) 10081da177e4SLinus Torvalds goto out_dec; 1009eb6dc19dSTrond Myklebust INIT_HLIST_NODE(&gss_auth->hash); 1010bd4a3eb1STrond Myklebust gss_auth->target_name = NULL; 1011c2190661STrond Myklebust if (args->target_name) { 1012c2190661STrond Myklebust gss_auth->target_name = kstrdup(args->target_name, GFP_KERNEL); 1013bd4a3eb1STrond Myklebust if (gss_auth->target_name == NULL) 1014bd4a3eb1STrond Myklebust goto err_free; 1015bd4a3eb1STrond Myklebust } 10161da177e4SLinus Torvalds gss_auth->client = clnt; 1017*9b1831e5SEric Dumazet gss_auth->net = get_net_track(rpc_net_ns(clnt), &gss_auth->ns_tracker, 1018*9b1831e5SEric Dumazet GFP_KERNEL); 10196a19275aSJ. Bruce Fields err = -EINVAL; 10201da177e4SLinus Torvalds gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor); 10210c77668dSChuck Lever if (!gss_auth->mech) 1022e726340aSTrond Myklebust goto err_put_net; 10231da177e4SLinus Torvalds gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor); 1024438b6fdeSJ. Bruce Fields if (gss_auth->service == 0) 1025438b6fdeSJ. Bruce Fields goto err_put_mech; 1026a699d65eSTrond Myklebust if (!gssd_running(gss_auth->net)) 1027a699d65eSTrond Myklebust goto err_put_mech; 10281da177e4SLinus Torvalds auth = &gss_auth->rpc_auth; 10291da177e4SLinus Torvalds auth->au_cslack = GSS_CRED_SLACK >> 2; 1030df513a77SOlga Kornievskaia auth->au_rslack = GSS_KRB5_MAX_SLACK_NEEDED >> 2; 1031a00275baSChuck Lever auth->au_verfsize = GSS_VERF_SLACK >> 2; 103235e77d21SChuck Lever auth->au_ralign = GSS_VERF_SLACK >> 2; 103353bc19f1SChuck Lever __set_bit(RPCAUTH_AUTH_UPDATE_SLACK, &auth->au_flags); 10341da177e4SLinus Torvalds auth->au_ops = &authgss_ops; 10351da177e4SLinus Torvalds auth->au_flavor = flavor; 103665b80179SChuck Lever if (gss_pseudoflavor_to_datatouch(gss_auth->mech, flavor)) 103753bc19f1SChuck Lever __set_bit(RPCAUTH_AUTH_DATATOUCH, &auth->au_flags); 1038331bc71cSTrond Myklebust refcount_set(&auth->au_count, 1); 10390285ed1fSTrond Myklebust kref_init(&gss_auth->kref); 10401da177e4SLinus Torvalds 104119172284STrond Myklebust err = rpcauth_init_credcache(auth); 104219172284STrond Myklebust if (err) 104319172284STrond Myklebust goto err_put_mech; 104434769fc4S\"J. Bruce Fields\ /* 104534769fc4S\"J. Bruce Fields\ * Note: if we created the old pipe first, then someone who 104634769fc4S\"J. Bruce Fields\ * examined the directory at the right moment might conclude 104734769fc4S\"J. Bruce Fields\ * that we supported only the old pipe. So we instead create 104834769fc4S\"J. Bruce Fields\ * the new pipe first. 104934769fc4S\"J. Bruce Fields\ */ 1050414a6295STrond Myklebust gss_pipe = gss_pipe_get(clnt, "gssd", &gss_upcall_ops_v1); 105119172284STrond Myklebust if (IS_ERR(gss_pipe)) { 105219172284STrond Myklebust err = PTR_ERR(gss_pipe); 105319172284STrond Myklebust goto err_destroy_credcache; 10546a19275aSJ. Bruce Fields } 105519172284STrond Myklebust gss_auth->gss_pipe[1] = gss_pipe; 10561da177e4SLinus Torvalds 1057414a6295STrond Myklebust gss_pipe = gss_pipe_get(clnt, gss_auth->mech->gm_name, 105819172284STrond Myklebust &gss_upcall_ops_v0); 105919172284STrond Myklebust if (IS_ERR(gss_pipe)) { 106019172284STrond Myklebust err = PTR_ERR(gss_pipe); 1061c239d83bSStanislav Kinsbursky goto err_destroy_pipe_1; 1062c239d83bSStanislav Kinsbursky } 106319172284STrond Myklebust gss_auth->gss_pipe[0] = gss_pipe; 106407a2bf1dSTrond Myklebust 1065eb6dc19dSTrond Myklebust return gss_auth; 1066c239d83bSStanislav Kinsbursky err_destroy_pipe_1: 1067414a6295STrond Myklebust gss_pipe_free(gss_auth->gss_pipe[1]); 106819172284STrond Myklebust err_destroy_credcache: 106919172284STrond Myklebust rpcauth_destroy_credcache(auth); 10701da177e4SLinus Torvalds err_put_mech: 10711da177e4SLinus Torvalds gss_mech_put(gss_auth->mech); 1072e726340aSTrond Myklebust err_put_net: 1073*9b1831e5SEric Dumazet put_net_track(gss_auth->net, &gss_auth->ns_tracker); 10741da177e4SLinus Torvalds err_free: 1075bd4a3eb1STrond Myklebust kfree(gss_auth->target_name); 10761da177e4SLinus Torvalds kfree(gss_auth); 10771da177e4SLinus Torvalds out_dec: 10781da177e4SLinus Torvalds module_put(THIS_MODULE); 10790c77668dSChuck Lever trace_rpcgss_createauth(flavor, err); 10806a19275aSJ. Bruce Fields return ERR_PTR(err); 10811da177e4SLinus Torvalds } 10821da177e4SLinus Torvalds 10831da177e4SLinus Torvalds static void 10840285ed1fSTrond Myklebust gss_free(struct gss_auth *gss_auth) 10850285ed1fSTrond Myklebust { 108619172284STrond Myklebust gss_pipe_free(gss_auth->gss_pipe[0]); 108719172284STrond Myklebust gss_pipe_free(gss_auth->gss_pipe[1]); 10880285ed1fSTrond Myklebust gss_mech_put(gss_auth->mech); 1089*9b1831e5SEric Dumazet put_net_track(gss_auth->net, &gss_auth->ns_tracker); 1090bd4a3eb1STrond Myklebust kfree(gss_auth->target_name); 10910285ed1fSTrond Myklebust 10920285ed1fSTrond Myklebust kfree(gss_auth); 10930285ed1fSTrond Myklebust module_put(THIS_MODULE); 10940285ed1fSTrond Myklebust } 10950285ed1fSTrond Myklebust 10960285ed1fSTrond Myklebust static void 10970285ed1fSTrond Myklebust gss_free_callback(struct kref *kref) 10980285ed1fSTrond Myklebust { 10990285ed1fSTrond Myklebust struct gss_auth *gss_auth = container_of(kref, struct gss_auth, kref); 11000285ed1fSTrond Myklebust 11010285ed1fSTrond Myklebust gss_free(gss_auth); 11020285ed1fSTrond Myklebust } 11030285ed1fSTrond Myklebust 11040285ed1fSTrond Myklebust static void 11059eb2ddb4STrond Myklebust gss_put_auth(struct gss_auth *gss_auth) 11069eb2ddb4STrond Myklebust { 11079eb2ddb4STrond Myklebust kref_put(&gss_auth->kref, gss_free_callback); 11089eb2ddb4STrond Myklebust } 11099eb2ddb4STrond Myklebust 11109eb2ddb4STrond Myklebust static void 11111da177e4SLinus Torvalds gss_destroy(struct rpc_auth *auth) 11121da177e4SLinus Torvalds { 111319172284STrond Myklebust struct gss_auth *gss_auth = container_of(auth, 111419172284STrond Myklebust struct gss_auth, rpc_auth); 11151da177e4SLinus Torvalds 1116eb6dc19dSTrond Myklebust if (hash_hashed(&gss_auth->hash)) { 1117eb6dc19dSTrond Myklebust spin_lock(&gss_auth_hash_lock); 1118eb6dc19dSTrond Myklebust hash_del(&gss_auth->hash); 1119eb6dc19dSTrond Myklebust spin_unlock(&gss_auth_hash_lock); 1120eb6dc19dSTrond Myklebust } 1121eb6dc19dSTrond Myklebust 112219172284STrond Myklebust gss_pipe_free(gss_auth->gss_pipe[0]); 112319172284STrond Myklebust gss_auth->gss_pipe[0] = NULL; 112419172284STrond Myklebust gss_pipe_free(gss_auth->gss_pipe[1]); 112519172284STrond Myklebust gss_auth->gss_pipe[1] = NULL; 11263ab9bb72STrond Myklebust rpcauth_destroy_credcache(auth); 11273ab9bb72STrond Myklebust 11289eb2ddb4STrond Myklebust gss_put_auth(gss_auth); 11291da177e4SLinus Torvalds } 11301da177e4SLinus Torvalds 1131a0f6ed8eSJ. Bruce Fields /* 1132a0f6ed8eSJ. Bruce Fields * Auths may be shared between rpc clients that were cloned from a 1133a0f6ed8eSJ. Bruce Fields * common client with the same xprt, if they also share the flavor and 1134a0f6ed8eSJ. Bruce Fields * target_name. 1135a0f6ed8eSJ. Bruce Fields * 1136a0f6ed8eSJ. Bruce Fields * The auth is looked up from the oldest parent sharing the same 1137a0f6ed8eSJ. Bruce Fields * cl_xprt, and the auth itself references only that common parent 1138a0f6ed8eSJ. Bruce Fields * (which is guaranteed to last as long as any of its descendants). 1139a0f6ed8eSJ. Bruce Fields */ 1140eb6dc19dSTrond Myklebust static struct gss_auth * 114182b98ca5SSargun Dhillon gss_auth_find_or_add_hashed(const struct rpc_auth_create_args *args, 1142eb6dc19dSTrond Myklebust struct rpc_clnt *clnt, 1143eb6dc19dSTrond Myklebust struct gss_auth *new) 1144eb6dc19dSTrond Myklebust { 1145eb6dc19dSTrond Myklebust struct gss_auth *gss_auth; 1146eb6dc19dSTrond Myklebust unsigned long hashval = (unsigned long)clnt; 1147eb6dc19dSTrond Myklebust 1148eb6dc19dSTrond Myklebust spin_lock(&gss_auth_hash_lock); 1149eb6dc19dSTrond Myklebust hash_for_each_possible(gss_auth_hash_table, 1150eb6dc19dSTrond Myklebust gss_auth, 1151eb6dc19dSTrond Myklebust hash, 1152eb6dc19dSTrond Myklebust hashval) { 1153a0f6ed8eSJ. Bruce Fields if (gss_auth->client != clnt) 1154a0f6ed8eSJ. Bruce Fields continue; 1155eb6dc19dSTrond Myklebust if (gss_auth->rpc_auth.au_flavor != args->pseudoflavor) 1156eb6dc19dSTrond Myklebust continue; 1157eb6dc19dSTrond Myklebust if (gss_auth->target_name != args->target_name) { 1158eb6dc19dSTrond Myklebust if (gss_auth->target_name == NULL) 1159eb6dc19dSTrond Myklebust continue; 1160eb6dc19dSTrond Myklebust if (args->target_name == NULL) 1161eb6dc19dSTrond Myklebust continue; 1162eb6dc19dSTrond Myklebust if (strcmp(gss_auth->target_name, args->target_name)) 1163eb6dc19dSTrond Myklebust continue; 1164eb6dc19dSTrond Myklebust } 1165331bc71cSTrond Myklebust if (!refcount_inc_not_zero(&gss_auth->rpc_auth.au_count)) 1166eb6dc19dSTrond Myklebust continue; 1167eb6dc19dSTrond Myklebust goto out; 1168eb6dc19dSTrond Myklebust } 1169eb6dc19dSTrond Myklebust if (new) 1170eb6dc19dSTrond Myklebust hash_add(gss_auth_hash_table, &new->hash, hashval); 1171eb6dc19dSTrond Myklebust gss_auth = new; 1172eb6dc19dSTrond Myklebust out: 1173eb6dc19dSTrond Myklebust spin_unlock(&gss_auth_hash_lock); 1174eb6dc19dSTrond Myklebust return gss_auth; 1175eb6dc19dSTrond Myklebust } 1176eb6dc19dSTrond Myklebust 1177eb6dc19dSTrond Myklebust static struct gss_auth * 117882b98ca5SSargun Dhillon gss_create_hashed(const struct rpc_auth_create_args *args, 117982b98ca5SSargun Dhillon struct rpc_clnt *clnt) 1180eb6dc19dSTrond Myklebust { 1181eb6dc19dSTrond Myklebust struct gss_auth *gss_auth; 1182eb6dc19dSTrond Myklebust struct gss_auth *new; 1183eb6dc19dSTrond Myklebust 1184eb6dc19dSTrond Myklebust gss_auth = gss_auth_find_or_add_hashed(args, clnt, NULL); 1185eb6dc19dSTrond Myklebust if (gss_auth != NULL) 1186eb6dc19dSTrond Myklebust goto out; 1187eb6dc19dSTrond Myklebust new = gss_create_new(args, clnt); 1188eb6dc19dSTrond Myklebust if (IS_ERR(new)) 1189eb6dc19dSTrond Myklebust return new; 1190eb6dc19dSTrond Myklebust gss_auth = gss_auth_find_or_add_hashed(args, clnt, new); 1191eb6dc19dSTrond Myklebust if (gss_auth != new) 1192eb6dc19dSTrond Myklebust gss_destroy(&new->rpc_auth); 1193eb6dc19dSTrond Myklebust out: 1194eb6dc19dSTrond Myklebust return gss_auth; 1195eb6dc19dSTrond Myklebust } 1196eb6dc19dSTrond Myklebust 1197eb6dc19dSTrond Myklebust static struct rpc_auth * 119882b98ca5SSargun Dhillon gss_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) 1199eb6dc19dSTrond Myklebust { 1200eb6dc19dSTrond Myklebust struct gss_auth *gss_auth; 1201ad01b2c6STrond Myklebust struct rpc_xprt_switch *xps = rcu_access_pointer(clnt->cl_xpi.xpi_xpswitch); 1202eb6dc19dSTrond Myklebust 1203eb6dc19dSTrond Myklebust while (clnt != clnt->cl_parent) { 1204eb6dc19dSTrond Myklebust struct rpc_clnt *parent = clnt->cl_parent; 1205eb6dc19dSTrond Myklebust /* Find the original parent for this transport */ 1206ad01b2c6STrond Myklebust if (rcu_access_pointer(parent->cl_xpi.xpi_xpswitch) != xps) 1207eb6dc19dSTrond Myklebust break; 1208eb6dc19dSTrond Myklebust clnt = parent; 1209eb6dc19dSTrond Myklebust } 1210eb6dc19dSTrond Myklebust 1211eb6dc19dSTrond Myklebust gss_auth = gss_create_hashed(args, clnt); 1212eb6dc19dSTrond Myklebust if (IS_ERR(gss_auth)) 1213eb6dc19dSTrond Myklebust return ERR_CAST(gss_auth); 1214eb6dc19dSTrond Myklebust return &gss_auth->rpc_auth; 1215eb6dc19dSTrond Myklebust } 1216eb6dc19dSTrond Myklebust 1217a652a4bcSTrond Myklebust static struct gss_cred * 1218a652a4bcSTrond Myklebust gss_dup_cred(struct gss_auth *gss_auth, struct gss_cred *gss_cred) 1219a652a4bcSTrond Myklebust { 1220a652a4bcSTrond Myklebust struct gss_cred *new; 1221a652a4bcSTrond Myklebust 1222a652a4bcSTrond Myklebust /* Make a copy of the cred so that we can reference count it */ 122312a3ad61STrond Myklebust new = kzalloc(sizeof(*gss_cred), GFP_NOFS); 1224a652a4bcSTrond Myklebust if (new) { 1225a652a4bcSTrond Myklebust struct auth_cred acred = { 12268276c902SNeilBrown .cred = gss_cred->gc_base.cr_cred, 1227a652a4bcSTrond Myklebust }; 1228a652a4bcSTrond Myklebust struct gss_cl_ctx *ctx = 1229a652a4bcSTrond Myklebust rcu_dereference_protected(gss_cred->gc_ctx, 1); 1230a652a4bcSTrond Myklebust 1231a652a4bcSTrond Myklebust rpcauth_init_cred(&new->gc_base, &acred, 1232a652a4bcSTrond Myklebust &gss_auth->rpc_auth, 1233a652a4bcSTrond Myklebust &gss_nullops); 1234a652a4bcSTrond Myklebust new->gc_base.cr_flags = 1UL << RPCAUTH_CRED_UPTODATE; 1235a652a4bcSTrond Myklebust new->gc_service = gss_cred->gc_service; 1236a652a4bcSTrond Myklebust new->gc_principal = gss_cred->gc_principal; 1237a652a4bcSTrond Myklebust kref_get(&gss_auth->kref); 1238a652a4bcSTrond Myklebust rcu_assign_pointer(new->gc_ctx, ctx); 1239a652a4bcSTrond Myklebust gss_get_ctx(ctx); 1240a652a4bcSTrond Myklebust } 1241a652a4bcSTrond Myklebust return new; 1242a652a4bcSTrond Myklebust } 1243a652a4bcSTrond Myklebust 12440df7fb74STrond Myklebust /* 1245a652a4bcSTrond Myklebust * gss_send_destroy_context will cause the RPCSEC_GSS to send a NULL RPC call 12460df7fb74STrond Myklebust * to the server with the GSS control procedure field set to 12470df7fb74STrond Myklebust * RPC_GSS_PROC_DESTROY. This should normally cause the server to release 12480df7fb74STrond Myklebust * all RPCSEC_GSS state associated with that context. 12490df7fb74STrond Myklebust */ 1250a652a4bcSTrond Myklebust static void 1251a652a4bcSTrond Myklebust gss_send_destroy_context(struct rpc_cred *cred) 12520df7fb74STrond Myklebust { 12530df7fb74STrond Myklebust struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 12540df7fb74STrond Myklebust struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth); 1255c5e6aecdSJeff Layton struct gss_cl_ctx *ctx = rcu_dereference_protected(gss_cred->gc_ctx, 1); 1256a652a4bcSTrond Myklebust struct gss_cred *new; 12570df7fb74STrond Myklebust struct rpc_task *task; 12580df7fb74STrond Myklebust 1259a652a4bcSTrond Myklebust new = gss_dup_cred(gss_auth, gss_cred); 1260a652a4bcSTrond Myklebust if (new) { 1261c5e6aecdSJeff Layton ctx->gc_proc = RPC_GSS_PROC_DESTROY; 12620df7fb74STrond Myklebust 126374fb8fecSChuck Lever trace_rpcgss_ctx_destroy(gss_cred); 1264a652a4bcSTrond Myklebust task = rpc_call_null(gss_auth->client, &new->gc_base, 12656fc3737aSChuck Lever RPC_TASK_ASYNC); 12660df7fb74STrond Myklebust if (!IS_ERR(task)) 12670df7fb74STrond Myklebust rpc_put_task(task); 12680df7fb74STrond Myklebust 1269a652a4bcSTrond Myklebust put_rpccred(&new->gc_base); 1270a652a4bcSTrond Myklebust } 12710df7fb74STrond Myklebust } 12720df7fb74STrond Myklebust 12730df7fb74STrond Myklebust /* gss_destroy_cred (and gss_free_ctx) are used to clean up after failure 12741da177e4SLinus Torvalds * to create a new cred or context, so they check that things have been 12751da177e4SLinus Torvalds * allocated before freeing them. */ 12761da177e4SLinus Torvalds static void 12775d28dc82STrond Myklebust gss_do_free_ctx(struct gss_cl_ctx *ctx) 12781da177e4SLinus Torvalds { 12790d8a3746STrond Myklebust gss_delete_sec_context(&ctx->gc_gss_ctx); 12801da177e4SLinus Torvalds kfree(ctx->gc_wire_ctx.data); 12812004c726SJeff Layton kfree(ctx->gc_acceptor.data); 12821da177e4SLinus Torvalds kfree(ctx); 12831da177e4SLinus Torvalds } 12841da177e4SLinus Torvalds 12851da177e4SLinus Torvalds static void 12865d28dc82STrond Myklebust gss_free_ctx_callback(struct rcu_head *head) 12875d28dc82STrond Myklebust { 12885d28dc82STrond Myklebust struct gss_cl_ctx *ctx = container_of(head, struct gss_cl_ctx, gc_rcu); 12895d28dc82STrond Myklebust gss_do_free_ctx(ctx); 12905d28dc82STrond Myklebust } 12915d28dc82STrond Myklebust 12925d28dc82STrond Myklebust static void 12935d28dc82STrond Myklebust gss_free_ctx(struct gss_cl_ctx *ctx) 12945d28dc82STrond Myklebust { 12955d28dc82STrond Myklebust call_rcu(&ctx->gc_rcu, gss_free_ctx_callback); 12965d28dc82STrond Myklebust } 12975d28dc82STrond Myklebust 12985d28dc82STrond Myklebust static void 129931be5bf1STrond Myklebust gss_free_cred(struct gss_cred *gss_cred) 13001da177e4SLinus Torvalds { 130131be5bf1STrond Myklebust kfree(gss_cred); 130231be5bf1STrond Myklebust } 13031da177e4SLinus Torvalds 130431be5bf1STrond Myklebust static void 130531be5bf1STrond Myklebust gss_free_cred_callback(struct rcu_head *head) 130631be5bf1STrond Myklebust { 130731be5bf1STrond Myklebust struct gss_cred *gss_cred = container_of(head, struct gss_cred, gc_base.cr_rcu); 130831be5bf1STrond Myklebust gss_free_cred(gss_cred); 130931be5bf1STrond Myklebust } 13101da177e4SLinus Torvalds 131131be5bf1STrond Myklebust static void 13126dcd3926SJeff Layton gss_destroy_nullcred(struct rpc_cred *cred) 131331be5bf1STrond Myklebust { 13145d28dc82STrond Myklebust struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 13150285ed1fSTrond Myklebust struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth); 1316c5e6aecdSJeff Layton struct gss_cl_ctx *ctx = rcu_dereference_protected(gss_cred->gc_ctx, 1); 13175d28dc82STrond Myklebust 1318a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(gss_cred->gc_ctx, NULL); 131997f68c6bSNeilBrown put_cred(cred->cr_cred); 132031be5bf1STrond Myklebust call_rcu(&cred->cr_rcu, gss_free_cred_callback); 13215d28dc82STrond Myklebust if (ctx) 13225d28dc82STrond Myklebust gss_put_ctx(ctx); 13239eb2ddb4STrond Myklebust gss_put_auth(gss_auth); 13241da177e4SLinus Torvalds } 13251da177e4SLinus Torvalds 13266dcd3926SJeff Layton static void 13276dcd3926SJeff Layton gss_destroy_cred(struct rpc_cred *cred) 13286dcd3926SJeff Layton { 1329a652a4bcSTrond Myklebust if (test_and_clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0) 1330a652a4bcSTrond Myklebust gss_send_destroy_context(cred); 13316dcd3926SJeff Layton gss_destroy_nullcred(cred); 13326dcd3926SJeff Layton } 13336dcd3926SJeff Layton 1334a960f8d6SFrank Sorenson static int 1335a960f8d6SFrank Sorenson gss_hash_cred(struct auth_cred *acred, unsigned int hashbits) 1336a960f8d6SFrank Sorenson { 13378276c902SNeilBrown return hash_64(from_kuid(&init_user_ns, acred->cred->fsuid), hashbits); 1338a960f8d6SFrank Sorenson } 1339a960f8d6SFrank Sorenson 13401da177e4SLinus Torvalds /* 13411da177e4SLinus Torvalds * Lookup RPCSEC_GSS cred for the current process 13421da177e4SLinus Torvalds */ 13431da177e4SLinus Torvalds static struct rpc_cred * 13448a317760STrond Myklebust gss_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) 13451da177e4SLinus Torvalds { 13463c6e0bc8SJeff Layton return rpcauth_lookup_credcache(auth, acred, flags, GFP_NOFS); 13471da177e4SLinus Torvalds } 13481da177e4SLinus Torvalds 13491da177e4SLinus Torvalds static struct rpc_cred * 13503c6e0bc8SJeff Layton gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags, gfp_t gfp) 13511da177e4SLinus Torvalds { 13521da177e4SLinus Torvalds struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth); 13531da177e4SLinus Torvalds struct gss_cred *cred = NULL; 13541da177e4SLinus Torvalds int err = -ENOMEM; 13551da177e4SLinus Torvalds 13563c6e0bc8SJeff Layton if (!(cred = kzalloc(sizeof(*cred), gfp))) 13571da177e4SLinus Torvalds goto out_err; 13581da177e4SLinus Torvalds 13595fe4755eSTrond Myklebust rpcauth_init_cred(&cred->gc_base, acred, auth, &gss_credops); 13601da177e4SLinus Torvalds /* 13611da177e4SLinus Torvalds * Note: in order to force a call to call_refresh(), we deliberately 13621da177e4SLinus Torvalds * fail to flag the credential as RPCAUTH_CRED_UPTODATE. 13631da177e4SLinus Torvalds */ 1364fc432dd9STrond Myklebust cred->gc_base.cr_flags = 1UL << RPCAUTH_CRED_NEW; 13651da177e4SLinus Torvalds cred->gc_service = gss_auth->service; 136668c97153STrond Myklebust cred->gc_principal = acred->principal; 13670285ed1fSTrond Myklebust kref_get(&gss_auth->kref); 13681da177e4SLinus Torvalds return &cred->gc_base; 13691da177e4SLinus Torvalds 13701da177e4SLinus Torvalds out_err: 13711da177e4SLinus Torvalds return ERR_PTR(err); 13721da177e4SLinus Torvalds } 13731da177e4SLinus Torvalds 13741da177e4SLinus Torvalds static int 1375fba3bad4STrond Myklebust gss_cred_init(struct rpc_auth *auth, struct rpc_cred *cred) 1376fba3bad4STrond Myklebust { 1377fba3bad4STrond Myklebust struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth); 1378fba3bad4STrond Myklebust struct gss_cred *gss_cred = container_of(cred,struct gss_cred, gc_base); 1379fba3bad4STrond Myklebust int err; 1380fba3bad4STrond Myklebust 1381fba3bad4STrond Myklebust do { 1382fba3bad4STrond Myklebust err = gss_create_upcall(gss_auth, gss_cred); 1383fba3bad4STrond Myklebust } while (err == -EAGAIN); 1384fba3bad4STrond Myklebust return err; 1385fba3bad4STrond Myklebust } 1386fba3bad4STrond Myklebust 1387a0337d1dSJeff Layton static char * 1388a0337d1dSJeff Layton gss_stringify_acceptor(struct rpc_cred *cred) 1389a0337d1dSJeff Layton { 1390c5e6aecdSJeff Layton char *string = NULL; 1391a0337d1dSJeff Layton struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 1392c5e6aecdSJeff Layton struct gss_cl_ctx *ctx; 1393b3ecba09SJeff Layton unsigned int len; 1394c5e6aecdSJeff Layton struct xdr_netobj *acceptor; 1395c5e6aecdSJeff Layton 1396c5e6aecdSJeff Layton rcu_read_lock(); 1397c5e6aecdSJeff Layton ctx = rcu_dereference(gss_cred->gc_ctx); 1398c5e6aecdSJeff Layton if (!ctx) 1399c5e6aecdSJeff Layton goto out; 1400c5e6aecdSJeff Layton 1401b3ecba09SJeff Layton len = ctx->gc_acceptor.len; 1402b3ecba09SJeff Layton rcu_read_unlock(); 1403a0337d1dSJeff Layton 1404a0337d1dSJeff Layton /* no point if there's no string */ 1405b3ecba09SJeff Layton if (!len) 1406b3ecba09SJeff Layton return NULL; 1407b3ecba09SJeff Layton realloc: 1408b3ecba09SJeff Layton string = kmalloc(len + 1, GFP_KERNEL); 1409a0337d1dSJeff Layton if (!string) 1410b3ecba09SJeff Layton return NULL; 1411b3ecba09SJeff Layton 1412b3ecba09SJeff Layton rcu_read_lock(); 1413b3ecba09SJeff Layton ctx = rcu_dereference(gss_cred->gc_ctx); 1414b3ecba09SJeff Layton 1415b3ecba09SJeff Layton /* did the ctx disappear or was it replaced by one with no acceptor? */ 1416b3ecba09SJeff Layton if (!ctx || !ctx->gc_acceptor.len) { 1417b3ecba09SJeff Layton kfree(string); 1418b3ecba09SJeff Layton string = NULL; 1419c5e6aecdSJeff Layton goto out; 1420b3ecba09SJeff Layton } 1421b3ecba09SJeff Layton 1422b3ecba09SJeff Layton acceptor = &ctx->gc_acceptor; 1423b3ecba09SJeff Layton 1424b3ecba09SJeff Layton /* 1425b3ecba09SJeff Layton * Did we find a new acceptor that's longer than the original? Allocate 1426b3ecba09SJeff Layton * a longer buffer and try again. 1427b3ecba09SJeff Layton */ 1428b3ecba09SJeff Layton if (len < acceptor->len) { 1429b3ecba09SJeff Layton len = acceptor->len; 1430b3ecba09SJeff Layton rcu_read_unlock(); 1431b3ecba09SJeff Layton kfree(string); 1432b3ecba09SJeff Layton goto realloc; 1433b3ecba09SJeff Layton } 1434a0337d1dSJeff Layton 1435a0337d1dSJeff Layton memcpy(string, acceptor->data, acceptor->len); 1436a0337d1dSJeff Layton string[acceptor->len] = '\0'; 1437c5e6aecdSJeff Layton out: 1438c5e6aecdSJeff Layton rcu_read_unlock(); 1439a0337d1dSJeff Layton return string; 1440a0337d1dSJeff Layton } 1441a0337d1dSJeff Layton 14424de6caa2SAndy Adamson /* 14434de6caa2SAndy Adamson * Returns -EACCES if GSS context is NULL or will expire within the 14444de6caa2SAndy Adamson * timeout (miliseconds) 14454de6caa2SAndy Adamson */ 14464de6caa2SAndy Adamson static int 14474de6caa2SAndy Adamson gss_key_timeout(struct rpc_cred *rc) 14484de6caa2SAndy Adamson { 14494de6caa2SAndy Adamson struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); 1450c5e6aecdSJeff Layton struct gss_cl_ctx *ctx; 1451cc6a7aabSArnd Bergmann unsigned long timeout = jiffies + (gss_key_expire_timeo * HZ); 1452cc6a7aabSArnd Bergmann int ret = 0; 14534de6caa2SAndy Adamson 1454c5e6aecdSJeff Layton rcu_read_lock(); 1455c5e6aecdSJeff Layton ctx = rcu_dereference(gss_cred->gc_ctx); 1456cc6a7aabSArnd Bergmann if (!ctx || time_after(timeout, ctx->gc_expiry)) 1457cc6a7aabSArnd Bergmann ret = -EACCES; 1458c5e6aecdSJeff Layton rcu_read_unlock(); 1459cc6a7aabSArnd Bergmann 1460cc6a7aabSArnd Bergmann return ret; 14614de6caa2SAndy Adamson } 14624de6caa2SAndy Adamson 1463fba3bad4STrond Myklebust static int 14648a317760STrond Myklebust gss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags) 14651da177e4SLinus Torvalds { 14661da177e4SLinus Torvalds struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); 1467c5e6aecdSJeff Layton struct gss_cl_ctx *ctx; 14684de6caa2SAndy Adamson int ret; 14691da177e4SLinus Torvalds 1470cd019f75STrond Myklebust if (test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags)) 14718a317760STrond Myklebust goto out; 14721da177e4SLinus Torvalds /* Don't match with creds that have expired. */ 1473c5e6aecdSJeff Layton rcu_read_lock(); 1474c5e6aecdSJeff Layton ctx = rcu_dereference(gss_cred->gc_ctx); 1475c5e6aecdSJeff Layton if (!ctx || time_after(jiffies, ctx->gc_expiry)) { 1476c5e6aecdSJeff Layton rcu_read_unlock(); 1477cd019f75STrond Myklebust return 0; 1478c5e6aecdSJeff Layton } 1479c5e6aecdSJeff Layton rcu_read_unlock(); 1480cd019f75STrond Myklebust if (!test_bit(RPCAUTH_CRED_UPTODATE, &rc->cr_flags)) 14811da177e4SLinus Torvalds return 0; 14828a317760STrond Myklebust out: 148368c97153STrond Myklebust if (acred->principal != NULL) { 148468c97153STrond Myklebust if (gss_cred->gc_principal == NULL) 148568c97153STrond Myklebust return 0; 14864de6caa2SAndy Adamson ret = strcmp(acred->principal, gss_cred->gc_principal) == 0; 1487ddf529eeSNeilBrown } else { 148868c97153STrond Myklebust if (gss_cred->gc_principal != NULL) 14897c67db3aSTrond Myklebust return 0; 149004d1532bSNeilBrown ret = uid_eq(rc->cr_cred->fsuid, acred->cred->fsuid); 14914de6caa2SAndy Adamson } 14924de6caa2SAndy Adamson return ret; 14931da177e4SLinus Torvalds } 14941da177e4SLinus Torvalds 14951da177e4SLinus Torvalds /* 14961da177e4SLinus Torvalds * Marshal credentials. 1497e8680a24SChuck Lever * 1498e8680a24SChuck Lever * The expensive part is computing the verifier. We can't cache a 1499e8680a24SChuck Lever * pre-computed version of the verifier because the seqno, which 1500e8680a24SChuck Lever * is different every time, is included in the MIC. 15011da177e4SLinus Torvalds */ 1502e8680a24SChuck Lever static int gss_marshal(struct rpc_task *task, struct xdr_stream *xdr) 15031da177e4SLinus Torvalds { 1504a17c2153STrond Myklebust struct rpc_rqst *req = task->tk_rqstp; 1505a17c2153STrond Myklebust struct rpc_cred *cred = req->rq_cred; 15061da177e4SLinus Torvalds struct gss_cred *gss_cred = container_of(cred, struct gss_cred, 15071da177e4SLinus Torvalds gc_base); 15081da177e4SLinus Torvalds struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 1509e8680a24SChuck Lever __be32 *p, *cred_len; 15101da177e4SLinus Torvalds u32 maj_stat = 0; 15111da177e4SLinus Torvalds struct xdr_netobj mic; 15121da177e4SLinus Torvalds struct kvec iov; 15131da177e4SLinus Torvalds struct xdr_buf verf_buf; 15140c77668dSChuck Lever int status; 15151da177e4SLinus Torvalds 1516e8680a24SChuck Lever /* Credential */ 1517e8680a24SChuck Lever 1518e8680a24SChuck Lever p = xdr_reserve_space(xdr, 7 * sizeof(*p) + 1519e8680a24SChuck Lever ctx->gc_wire_ctx.len); 1520e8680a24SChuck Lever if (!p) 15210c77668dSChuck Lever goto marshal_failed; 1522e8680a24SChuck Lever *p++ = rpc_auth_gss; 15231da177e4SLinus Torvalds cred_len = p++; 15241da177e4SLinus Torvalds 15251da177e4SLinus Torvalds spin_lock(&ctx->gc_seq_lock); 152697b78ae9STrond Myklebust req->rq_seqno = (ctx->gc_seq < MAXSEQ) ? ctx->gc_seq++ : MAXSEQ; 15271da177e4SLinus Torvalds spin_unlock(&ctx->gc_seq_lock); 152897b78ae9STrond Myklebust if (req->rq_seqno == MAXSEQ) 15290c77668dSChuck Lever goto expired; 15300c77668dSChuck Lever trace_rpcgss_seqno(task); 15311da177e4SLinus Torvalds 1532e8680a24SChuck Lever *p++ = cpu_to_be32(RPC_GSS_VERSION); 1533e8680a24SChuck Lever *p++ = cpu_to_be32(ctx->gc_proc); 1534e8680a24SChuck Lever *p++ = cpu_to_be32(req->rq_seqno); 1535e8680a24SChuck Lever *p++ = cpu_to_be32(gss_cred->gc_service); 15361da177e4SLinus Torvalds p = xdr_encode_netobj(p, &ctx->gc_wire_ctx); 1537e8680a24SChuck Lever *cred_len = cpu_to_be32((p - (cred_len + 1)) << 2); 1538e8680a24SChuck Lever 1539e8680a24SChuck Lever /* Verifier */ 15401da177e4SLinus Torvalds 15411da177e4SLinus Torvalds /* We compute the checksum for the verifier over the xdr-encoded bytes 15421da177e4SLinus Torvalds * starting with the xid and ending at the end of the credential: */ 1543067fb11bSChuck Lever iov.iov_base = req->rq_snd_buf.head[0].iov_base; 15441da177e4SLinus Torvalds iov.iov_len = (u8 *)p - (u8 *)iov.iov_base; 15451da177e4SLinus Torvalds xdr_buf_from_iov(&iov, &verf_buf); 15461da177e4SLinus Torvalds 1547e8680a24SChuck Lever p = xdr_reserve_space(xdr, sizeof(*p)); 1548e8680a24SChuck Lever if (!p) 15490c77668dSChuck Lever goto marshal_failed; 1550e8680a24SChuck Lever *p++ = rpc_auth_gss; 15511da177e4SLinus Torvalds mic.data = (u8 *)(p + 1); 155200fd6e14SJ. Bruce Fields maj_stat = gss_get_mic(ctx->gc_gss_ctx, &verf_buf, &mic); 1553e8680a24SChuck Lever if (maj_stat == GSS_S_CONTEXT_EXPIRED) 15540c77668dSChuck Lever goto expired; 1555e8680a24SChuck Lever else if (maj_stat != 0) 15560c77668dSChuck Lever goto bad_mic; 1557e8680a24SChuck Lever if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0) 15580c77668dSChuck Lever goto marshal_failed; 15590c77668dSChuck Lever status = 0; 15600c77668dSChuck Lever out: 15611da177e4SLinus Torvalds gss_put_ctx(ctx); 15620c77668dSChuck Lever return status; 15630c77668dSChuck Lever expired: 156497b78ae9STrond Myklebust clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 15650c77668dSChuck Lever status = -EKEYEXPIRED; 15660c77668dSChuck Lever goto out; 15670c77668dSChuck Lever marshal_failed: 15680c77668dSChuck Lever status = -EMSGSIZE; 15690c77668dSChuck Lever goto out; 15700c77668dSChuck Lever bad_mic: 15710c77668dSChuck Lever trace_rpcgss_get_mic(task, maj_stat); 15720c77668dSChuck Lever status = -EIO; 15730c77668dSChuck Lever goto out; 15741da177e4SLinus Torvalds } 15751da177e4SLinus Torvalds 1576cd019f75STrond Myklebust static int gss_renew_cred(struct rpc_task *task) 1577cd019f75STrond Myklebust { 1578a17c2153STrond Myklebust struct rpc_cred *oldcred = task->tk_rqstp->rq_cred; 1579cd019f75STrond Myklebust struct gss_cred *gss_cred = container_of(oldcred, 1580cd019f75STrond Myklebust struct gss_cred, 1581cd019f75STrond Myklebust gc_base); 1582cd019f75STrond Myklebust struct rpc_auth *auth = oldcred->cr_auth; 1583cd019f75STrond Myklebust struct auth_cred acred = { 158497f68c6bSNeilBrown .cred = oldcred->cr_cred, 158568c97153STrond Myklebust .principal = gss_cred->gc_principal, 1586cd019f75STrond Myklebust }; 1587cd019f75STrond Myklebust struct rpc_cred *new; 1588cd019f75STrond Myklebust 1589cd019f75STrond Myklebust new = gss_lookup_cred(auth, &acred, RPCAUTH_LOOKUP_NEW); 1590cd019f75STrond Myklebust if (IS_ERR(new)) 1591cd019f75STrond Myklebust return PTR_ERR(new); 159253bc19f1SChuck Lever 1593a17c2153STrond Myklebust task->tk_rqstp->rq_cred = new; 1594cd019f75STrond Myklebust put_rpccred(oldcred); 1595cd019f75STrond Myklebust return 0; 1596cd019f75STrond Myklebust } 1597cd019f75STrond Myklebust 1598126e216aSTrond Myklebust static int gss_cred_is_negative_entry(struct rpc_cred *cred) 1599126e216aSTrond Myklebust { 1600126e216aSTrond Myklebust if (test_bit(RPCAUTH_CRED_NEGATIVE, &cred->cr_flags)) { 1601126e216aSTrond Myklebust unsigned long now = jiffies; 1602126e216aSTrond Myklebust unsigned long begin, expire; 1603126e216aSTrond Myklebust struct gss_cred *gss_cred; 1604126e216aSTrond Myklebust 1605126e216aSTrond Myklebust gss_cred = container_of(cred, struct gss_cred, gc_base); 1606126e216aSTrond Myklebust begin = gss_cred->gc_upcall_timestamp; 1607126e216aSTrond Myklebust expire = begin + gss_expired_cred_retry_delay * HZ; 1608126e216aSTrond Myklebust 1609126e216aSTrond Myklebust if (time_in_range_open(now, begin, expire)) 1610126e216aSTrond Myklebust return 1; 1611126e216aSTrond Myklebust } 1612126e216aSTrond Myklebust return 0; 1613126e216aSTrond Myklebust } 1614126e216aSTrond Myklebust 16151da177e4SLinus Torvalds /* 16161da177e4SLinus Torvalds * Refresh credentials. XXX - finish 16171da177e4SLinus Torvalds */ 16181da177e4SLinus Torvalds static int 16191da177e4SLinus Torvalds gss_refresh(struct rpc_task *task) 16201da177e4SLinus Torvalds { 1621a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 1622cd019f75STrond Myklebust int ret = 0; 16231da177e4SLinus Torvalds 1624126e216aSTrond Myklebust if (gss_cred_is_negative_entry(cred)) 1625126e216aSTrond Myklebust return -EKEYEXPIRED; 1626126e216aSTrond Myklebust 1627cd019f75STrond Myklebust if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) && 1628cd019f75STrond Myklebust !test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags)) { 1629cd019f75STrond Myklebust ret = gss_renew_cred(task); 1630cd019f75STrond Myklebust if (ret < 0) 1631cd019f75STrond Myklebust goto out; 1632a17c2153STrond Myklebust cred = task->tk_rqstp->rq_cred; 1633cd019f75STrond Myklebust } 1634cd019f75STrond Myklebust 1635cd019f75STrond Myklebust if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags)) 1636cd019f75STrond Myklebust ret = gss_refresh_upcall(task); 1637cd019f75STrond Myklebust out: 1638cd019f75STrond Myklebust return ret; 16391da177e4SLinus Torvalds } 16401da177e4SLinus Torvalds 16410df7fb74STrond Myklebust /* Dummy refresh routine: used only when destroying the context */ 16420df7fb74STrond Myklebust static int 16430df7fb74STrond Myklebust gss_refresh_null(struct rpc_task *task) 16440df7fb74STrond Myklebust { 1645c297c8b9SAndy Adamson return 0; 16460df7fb74STrond Myklebust } 16470df7fb74STrond Myklebust 1648a0584ee9SChuck Lever static int 1649a0584ee9SChuck Lever gss_validate(struct rpc_task *task, struct xdr_stream *xdr) 16501da177e4SLinus Torvalds { 1651a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 16521da177e4SLinus Torvalds struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 1653a0584ee9SChuck Lever __be32 *p, *seq = NULL; 16541da177e4SLinus Torvalds struct kvec iov; 16551da177e4SLinus Torvalds struct xdr_buf verf_buf; 16561da177e4SLinus Torvalds struct xdr_netobj mic; 1657a0584ee9SChuck Lever u32 len, maj_stat; 1658a0584ee9SChuck Lever int status; 16591da177e4SLinus Torvalds 1660a0584ee9SChuck Lever p = xdr_inline_decode(xdr, 2 * sizeof(*p)); 1661a0584ee9SChuck Lever if (!p) 1662a0584ee9SChuck Lever goto validate_failed; 1663a0584ee9SChuck Lever if (*p++ != rpc_auth_gss) 1664a0584ee9SChuck Lever goto validate_failed; 1665a0584ee9SChuck Lever len = be32_to_cpup(p); 1666a0584ee9SChuck Lever if (len > RPC_MAX_AUTH_SIZE) 1667a0584ee9SChuck Lever goto validate_failed; 1668a0584ee9SChuck Lever p = xdr_inline_decode(xdr, len); 1669a0584ee9SChuck Lever if (!p) 1670a0584ee9SChuck Lever goto validate_failed; 16711da177e4SLinus Torvalds 16722876a344SJ. Bruce Fields seq = kmalloc(4, GFP_NOFS); 16732876a344SJ. Bruce Fields if (!seq) 1674a0584ee9SChuck Lever goto validate_failed; 1675a0584ee9SChuck Lever *seq = cpu_to_be32(task->tk_rqstp->rq_seqno); 16762876a344SJ. Bruce Fields iov.iov_base = seq; 16772876a344SJ. Bruce Fields iov.iov_len = 4; 16781da177e4SLinus Torvalds xdr_buf_from_iov(&iov, &verf_buf); 16791da177e4SLinus Torvalds mic.data = (u8 *)p; 16801da177e4SLinus Torvalds mic.len = len; 168100fd6e14SJ. Bruce Fields maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic); 16821da177e4SLinus Torvalds if (maj_stat == GSS_S_CONTEXT_EXPIRED) 1683fc432dd9STrond Myklebust clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 1684a0584ee9SChuck Lever if (maj_stat) 1685a0584ee9SChuck Lever goto bad_mic; 1686a0584ee9SChuck Lever 168724b2605bSJ. Bruce Fields /* We leave it to unwrap to calculate au_rslack. For now we just 168824b2605bSJ. Bruce Fields * calculate the length of the verifier: */ 168953bc19f1SChuck Lever if (test_bit(RPCAUTH_AUTH_UPDATE_SLACK, &cred->cr_auth->au_flags)) 16901be27f36STrond Myklebust cred->cr_auth->au_verfsize = XDR_QUADLEN(len) + 2; 1691a0584ee9SChuck Lever status = 0; 1692a0584ee9SChuck Lever out: 16931da177e4SLinus Torvalds gss_put_ctx(ctx); 16942876a344SJ. Bruce Fields kfree(seq); 1695a0584ee9SChuck Lever return status; 1696a0584ee9SChuck Lever 1697a0584ee9SChuck Lever validate_failed: 1698a0584ee9SChuck Lever status = -EIO; 1699a0584ee9SChuck Lever goto out; 1700a0584ee9SChuck Lever bad_mic: 17010c77668dSChuck Lever trace_rpcgss_verify_mic(task, maj_stat); 1702a0584ee9SChuck Lever status = -EACCES; 1703a0584ee9SChuck Lever goto out; 17041da177e4SLinus Torvalds } 17051da177e4SLinus Torvalds 1706d162372aSChuck Lever static noinline_for_stack int 1707d162372aSChuck Lever gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, 1708e8680a24SChuck Lever struct rpc_task *task, struct xdr_stream *xdr) 17099f06c719SChuck Lever { 1710e8680a24SChuck Lever struct rpc_rqst *rqstp = task->tk_rqstp; 1711e8680a24SChuck Lever struct xdr_buf integ_buf, *snd_buf = &rqstp->rq_snd_buf; 17121da177e4SLinus Torvalds struct xdr_netobj mic; 1713e8680a24SChuck Lever __be32 *p, *integ_len; 1714e8680a24SChuck Lever u32 offset, maj_stat; 17151da177e4SLinus Torvalds 1716e8680a24SChuck Lever p = xdr_reserve_space(xdr, 2 * sizeof(*p)); 1717e8680a24SChuck Lever if (!p) 1718e8680a24SChuck Lever goto wrap_failed; 17191da177e4SLinus Torvalds integ_len = p++; 1720e8680a24SChuck Lever *p = cpu_to_be32(rqstp->rq_seqno); 1721e8680a24SChuck Lever 1722e8680a24SChuck Lever if (rpcauth_wrap_req_encode(task, xdr)) 1723e8680a24SChuck Lever goto wrap_failed; 1724e8680a24SChuck Lever 17251da177e4SLinus Torvalds offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; 17261da177e4SLinus Torvalds if (xdr_buf_subsegment(snd_buf, &integ_buf, 17271da177e4SLinus Torvalds offset, snd_buf->len - offset)) 1728e8680a24SChuck Lever goto wrap_failed; 1729e8680a24SChuck Lever *integ_len = cpu_to_be32(integ_buf.len); 17301da177e4SLinus Torvalds 1731e8680a24SChuck Lever p = xdr_reserve_space(xdr, 0); 1732e8680a24SChuck Lever if (!p) 1733e8680a24SChuck Lever goto wrap_failed; 17341da177e4SLinus Torvalds mic.data = (u8 *)(p + 1); 173500fd6e14SJ. Bruce Fields maj_stat = gss_get_mic(ctx->gc_gss_ctx, &integ_buf, &mic); 17361da177e4SLinus Torvalds if (maj_stat == GSS_S_CONTEXT_EXPIRED) 1737fc432dd9STrond Myklebust clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 17381da177e4SLinus Torvalds else if (maj_stat) 17390c77668dSChuck Lever goto bad_mic; 1740e8680a24SChuck Lever /* Check that the trailing MIC fit in the buffer, after the fact */ 1741e8680a24SChuck Lever if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0) 1742e8680a24SChuck Lever goto wrap_failed; 17431da177e4SLinus Torvalds return 0; 1744e8680a24SChuck Lever wrap_failed: 1745e8680a24SChuck Lever return -EMSGSIZE; 17460c77668dSChuck Lever bad_mic: 17470c77668dSChuck Lever trace_rpcgss_get_mic(task, maj_stat); 17480c77668dSChuck Lever return -EIO; 17491da177e4SLinus Torvalds } 17501da177e4SLinus Torvalds 17512d2da60cSJ. Bruce Fields static void 17522d2da60cSJ. Bruce Fields priv_release_snd_buf(struct rpc_rqst *rqstp) 17532d2da60cSJ. Bruce Fields { 17542d2da60cSJ. Bruce Fields int i; 17552d2da60cSJ. Bruce Fields 17562d2da60cSJ. Bruce Fields for (i=0; i < rqstp->rq_enc_pages_num; i++) 17572d2da60cSJ. Bruce Fields __free_page(rqstp->rq_enc_pages[i]); 17582d2da60cSJ. Bruce Fields kfree(rqstp->rq_enc_pages); 17598dae5398SChuck Lever rqstp->rq_release_snd_buf = NULL; 17602d2da60cSJ. Bruce Fields } 17612d2da60cSJ. Bruce Fields 17622d2da60cSJ. Bruce Fields static int 17632d2da60cSJ. Bruce Fields alloc_enc_pages(struct rpc_rqst *rqstp) 17642d2da60cSJ. Bruce Fields { 17652d2da60cSJ. Bruce Fields struct xdr_buf *snd_buf = &rqstp->rq_snd_buf; 17662d2da60cSJ. Bruce Fields int first, last, i; 17672d2da60cSJ. Bruce Fields 17688dae5398SChuck Lever if (rqstp->rq_release_snd_buf) 17698dae5398SChuck Lever rqstp->rq_release_snd_buf(rqstp); 17708dae5398SChuck Lever 17712d2da60cSJ. Bruce Fields if (snd_buf->page_len == 0) { 17722d2da60cSJ. Bruce Fields rqstp->rq_enc_pages_num = 0; 17732d2da60cSJ. Bruce Fields return 0; 17742d2da60cSJ. Bruce Fields } 17752d2da60cSJ. Bruce Fields 177609cbfeafSKirill A. Shutemov first = snd_buf->page_base >> PAGE_SHIFT; 177709cbfeafSKirill A. Shutemov last = (snd_buf->page_base + snd_buf->page_len - 1) >> PAGE_SHIFT; 17782d2da60cSJ. Bruce Fields rqstp->rq_enc_pages_num = last - first + 1 + 1; 17792d2da60cSJ. Bruce Fields rqstp->rq_enc_pages 17806da2ec56SKees Cook = kmalloc_array(rqstp->rq_enc_pages_num, 17816da2ec56SKees Cook sizeof(struct page *), 17822d2da60cSJ. Bruce Fields GFP_NOFS); 17832d2da60cSJ. Bruce Fields if (!rqstp->rq_enc_pages) 17842d2da60cSJ. Bruce Fields goto out; 17852d2da60cSJ. Bruce Fields for (i=0; i < rqstp->rq_enc_pages_num; i++) { 17862d2da60cSJ. Bruce Fields rqstp->rq_enc_pages[i] = alloc_page(GFP_NOFS); 17872d2da60cSJ. Bruce Fields if (rqstp->rq_enc_pages[i] == NULL) 17882d2da60cSJ. Bruce Fields goto out_free; 17892d2da60cSJ. Bruce Fields } 17902d2da60cSJ. Bruce Fields rqstp->rq_release_snd_buf = priv_release_snd_buf; 17912d2da60cSJ. Bruce Fields return 0; 17922d2da60cSJ. Bruce Fields out_free: 1793cdead7cfSTrond Myklebust rqstp->rq_enc_pages_num = i; 1794cdead7cfSTrond Myklebust priv_release_snd_buf(rqstp); 17952d2da60cSJ. Bruce Fields out: 17962d2da60cSJ. Bruce Fields return -EAGAIN; 17972d2da60cSJ. Bruce Fields } 17982d2da60cSJ. Bruce Fields 1799d162372aSChuck Lever static noinline_for_stack int 1800d162372aSChuck Lever gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, 1801e8680a24SChuck Lever struct rpc_task *task, struct xdr_stream *xdr) 18022d2da60cSJ. Bruce Fields { 1803e8680a24SChuck Lever struct rpc_rqst *rqstp = task->tk_rqstp; 18042d2da60cSJ. Bruce Fields struct xdr_buf *snd_buf = &rqstp->rq_snd_buf; 1805e8680a24SChuck Lever u32 pad, offset, maj_stat; 18062d2da60cSJ. Bruce Fields int status; 1807e8680a24SChuck Lever __be32 *p, *opaque_len; 18082d2da60cSJ. Bruce Fields struct page **inpages; 18092d2da60cSJ. Bruce Fields int first; 18102d2da60cSJ. Bruce Fields struct kvec *iov; 18112d2da60cSJ. Bruce Fields 1812e8680a24SChuck Lever status = -EIO; 1813e8680a24SChuck Lever p = xdr_reserve_space(xdr, 2 * sizeof(*p)); 1814e8680a24SChuck Lever if (!p) 1815e8680a24SChuck Lever goto wrap_failed; 18162d2da60cSJ. Bruce Fields opaque_len = p++; 1817e8680a24SChuck Lever *p = cpu_to_be32(rqstp->rq_seqno); 18182d2da60cSJ. Bruce Fields 1819e8680a24SChuck Lever if (rpcauth_wrap_req_encode(task, xdr)) 1820e8680a24SChuck Lever goto wrap_failed; 18212d2da60cSJ. Bruce Fields 18222d2da60cSJ. Bruce Fields status = alloc_enc_pages(rqstp); 1823e8680a24SChuck Lever if (unlikely(status)) 1824e8680a24SChuck Lever goto wrap_failed; 182509cbfeafSKirill A. Shutemov first = snd_buf->page_base >> PAGE_SHIFT; 18262d2da60cSJ. Bruce Fields inpages = snd_buf->pages + first; 18272d2da60cSJ. Bruce Fields snd_buf->pages = rqstp->rq_enc_pages; 182809cbfeafSKirill A. Shutemov snd_buf->page_base -= first << PAGE_SHIFT; 18297561042fSKevin Coffman /* 1830e8680a24SChuck Lever * Move the tail into its own page, in case gss_wrap needs 1831e8680a24SChuck Lever * more space in the head when wrapping. 18327561042fSKevin Coffman * 1833e8680a24SChuck Lever * Still... Why can't gss_wrap just slide the tail down? 18347561042fSKevin Coffman */ 18352d2da60cSJ. Bruce Fields if (snd_buf->page_len || snd_buf->tail[0].iov_len) { 1836e8680a24SChuck Lever char *tmp; 1837e8680a24SChuck Lever 18382d2da60cSJ. Bruce Fields tmp = page_address(rqstp->rq_enc_pages[rqstp->rq_enc_pages_num - 1]); 18392d2da60cSJ. Bruce Fields memcpy(tmp, snd_buf->tail[0].iov_base, snd_buf->tail[0].iov_len); 18402d2da60cSJ. Bruce Fields snd_buf->tail[0].iov_base = tmp; 18412d2da60cSJ. Bruce Fields } 1842e8680a24SChuck Lever offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; 184300fd6e14SJ. Bruce Fields maj_stat = gss_wrap(ctx->gc_gss_ctx, offset, snd_buf, inpages); 18447561042fSKevin Coffman /* slack space should prevent this ever happening: */ 1845e8680a24SChuck Lever if (unlikely(snd_buf->len > snd_buf->buflen)) 1846e8680a24SChuck Lever goto wrap_failed; 18472d2da60cSJ. Bruce Fields /* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was 18482d2da60cSJ. Bruce Fields * done anyway, so it's safe to put the request on the wire: */ 18492d2da60cSJ. Bruce Fields if (maj_stat == GSS_S_CONTEXT_EXPIRED) 1850fc432dd9STrond Myklebust clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 18512d2da60cSJ. Bruce Fields else if (maj_stat) 18520c77668dSChuck Lever goto bad_wrap; 18532d2da60cSJ. Bruce Fields 1854e8680a24SChuck Lever *opaque_len = cpu_to_be32(snd_buf->len - offset); 1855e8680a24SChuck Lever /* guess whether the pad goes into the head or the tail: */ 18562d2da60cSJ. Bruce Fields if (snd_buf->page_len || snd_buf->tail[0].iov_len) 18572d2da60cSJ. Bruce Fields iov = snd_buf->tail; 18582d2da60cSJ. Bruce Fields else 18592d2da60cSJ. Bruce Fields iov = snd_buf->head; 18602d2da60cSJ. Bruce Fields p = iov->iov_base + iov->iov_len; 186196f194b7SChuck Lever pad = xdr_pad_size(snd_buf->len - offset); 18622d2da60cSJ. Bruce Fields memset(p, 0, pad); 18632d2da60cSJ. Bruce Fields iov->iov_len += pad; 18642d2da60cSJ. Bruce Fields snd_buf->len += pad; 18652d2da60cSJ. Bruce Fields 18662d2da60cSJ. Bruce Fields return 0; 1867e8680a24SChuck Lever wrap_failed: 1868e8680a24SChuck Lever return status; 18690c77668dSChuck Lever bad_wrap: 18700c77668dSChuck Lever trace_rpcgss_wrap(task, maj_stat); 18710c77668dSChuck Lever return -EIO; 18722d2da60cSJ. Bruce Fields } 18732d2da60cSJ. Bruce Fields 1874e8680a24SChuck Lever static int gss_wrap_req(struct rpc_task *task, struct xdr_stream *xdr) 18751da177e4SLinus Torvalds { 1876a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 18771da177e4SLinus Torvalds struct gss_cred *gss_cred = container_of(cred, struct gss_cred, 18781da177e4SLinus Torvalds gc_base); 18791da177e4SLinus Torvalds struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 1880e8680a24SChuck Lever int status; 18811da177e4SLinus Torvalds 1882e8680a24SChuck Lever status = -EIO; 18831da177e4SLinus Torvalds if (ctx->gc_proc != RPC_GSS_PROC_DATA) { 18841da177e4SLinus Torvalds /* The spec seems a little ambiguous here, but I think that not 18851da177e4SLinus Torvalds * wrapping context destruction requests makes the most sense. 18861da177e4SLinus Torvalds */ 1887e8680a24SChuck Lever status = rpcauth_wrap_req_encode(task, xdr); 18881da177e4SLinus Torvalds goto out; 18891da177e4SLinus Torvalds } 18901da177e4SLinus Torvalds switch (gss_cred->gc_service) { 18911da177e4SLinus Torvalds case RPC_GSS_SVC_NONE: 1892e8680a24SChuck Lever status = rpcauth_wrap_req_encode(task, xdr); 18931da177e4SLinus Torvalds break; 18941da177e4SLinus Torvalds case RPC_GSS_SVC_INTEGRITY: 1895e8680a24SChuck Lever status = gss_wrap_req_integ(cred, ctx, task, xdr); 18961da177e4SLinus Torvalds break; 18971da177e4SLinus Torvalds case RPC_GSS_SVC_PRIVACY: 1898e8680a24SChuck Lever status = gss_wrap_req_priv(cred, ctx, task, xdr); 18991da177e4SLinus Torvalds break; 19000c77668dSChuck Lever default: 19010c77668dSChuck Lever status = -EIO; 19021da177e4SLinus Torvalds } 19031da177e4SLinus Torvalds out: 19041da177e4SLinus Torvalds gss_put_ctx(ctx); 19051da177e4SLinus Torvalds return status; 19061da177e4SLinus Torvalds } 19071da177e4SLinus Torvalds 190853bc19f1SChuck Lever /** 190953bc19f1SChuck Lever * gss_update_rslack - Possibly update RPC receive buffer size estimates 191053bc19f1SChuck Lever * @task: rpc_task for incoming RPC Reply being unwrapped 191153bc19f1SChuck Lever * @cred: controlling rpc_cred for @task 191253bc19f1SChuck Lever * @before: XDR words needed before each RPC Reply message 191353bc19f1SChuck Lever * @after: XDR words needed following each RPC Reply message 191453bc19f1SChuck Lever * 191553bc19f1SChuck Lever */ 191653bc19f1SChuck Lever static void gss_update_rslack(struct rpc_task *task, struct rpc_cred *cred, 191753bc19f1SChuck Lever unsigned int before, unsigned int after) 19181da177e4SLinus Torvalds { 191935e77d21SChuck Lever struct rpc_auth *auth = cred->cr_auth; 192035e77d21SChuck Lever 192153bc19f1SChuck Lever if (test_and_clear_bit(RPCAUTH_AUTH_UPDATE_SLACK, &auth->au_flags)) { 192253bc19f1SChuck Lever auth->au_ralign = auth->au_verfsize + before; 192353bc19f1SChuck Lever auth->au_rslack = auth->au_verfsize + after; 192453bc19f1SChuck Lever trace_rpcgss_update_slack(task, auth); 192553bc19f1SChuck Lever } 192653bc19f1SChuck Lever } 192753bc19f1SChuck Lever 192853bc19f1SChuck Lever static int 192953bc19f1SChuck Lever gss_unwrap_resp_auth(struct rpc_task *task, struct rpc_cred *cred) 193053bc19f1SChuck Lever { 193153bc19f1SChuck Lever gss_update_rslack(task, cred, 0, 0); 1932a0584ee9SChuck Lever return 0; 1933a0584ee9SChuck Lever } 19341da177e4SLinus Torvalds 19354047aa90SChuck Lever /* 19364047aa90SChuck Lever * RFC 2203, Section 5.3.2.2 19374047aa90SChuck Lever * 19384047aa90SChuck Lever * struct rpc_gss_integ_data { 19394047aa90SChuck Lever * opaque databody_integ<>; 19404047aa90SChuck Lever * opaque checksum<>; 19414047aa90SChuck Lever * }; 19424047aa90SChuck Lever * 19434047aa90SChuck Lever * struct rpc_gss_data_t { 19444047aa90SChuck Lever * unsigned int seq_num; 19454047aa90SChuck Lever * proc_req_arg_t arg; 19464047aa90SChuck Lever * }; 19474047aa90SChuck Lever */ 1948d162372aSChuck Lever static noinline_for_stack int 19490c77668dSChuck Lever gss_unwrap_resp_integ(struct rpc_task *task, struct rpc_cred *cred, 19500c77668dSChuck Lever struct gss_cl_ctx *ctx, struct rpc_rqst *rqstp, 19510c77668dSChuck Lever struct xdr_stream *xdr) 1952a0584ee9SChuck Lever { 19534047aa90SChuck Lever struct xdr_buf gss_data, *rcv_buf = &rqstp->rq_rcv_buf; 19544047aa90SChuck Lever u32 len, offset, seqno, maj_stat; 1955a0584ee9SChuck Lever struct xdr_netobj mic; 19564047aa90SChuck Lever int ret; 1957a0584ee9SChuck Lever 19584047aa90SChuck Lever ret = -EIO; 19594047aa90SChuck Lever mic.data = NULL; 19604047aa90SChuck Lever 19614047aa90SChuck Lever /* opaque databody_integ<>; */ 19624047aa90SChuck Lever if (xdr_stream_decode_u32(xdr, &len)) 1963a0584ee9SChuck Lever goto unwrap_failed; 19644047aa90SChuck Lever if (len & 3) 1965a0584ee9SChuck Lever goto unwrap_failed; 19664047aa90SChuck Lever offset = rcv_buf->len - xdr_stream_remaining(xdr); 19674047aa90SChuck Lever if (xdr_stream_decode_u32(xdr, &seqno)) 1968a0584ee9SChuck Lever goto unwrap_failed; 19694047aa90SChuck Lever if (seqno != rqstp->rq_seqno) 19700c77668dSChuck Lever goto bad_seqno; 19714047aa90SChuck Lever if (xdr_buf_subsegment(rcv_buf, &gss_data, offset, len)) 19724047aa90SChuck Lever goto unwrap_failed; 19731da177e4SLinus Torvalds 19744047aa90SChuck Lever /* 19754047aa90SChuck Lever * The xdr_stream now points to the beginning of the 19764047aa90SChuck Lever * upper layer payload, to be passed below to 19774047aa90SChuck Lever * rpcauth_unwrap_resp_decode(). The checksum, which 19784047aa90SChuck Lever * follows the upper layer payload in @rcv_buf, is 19794047aa90SChuck Lever * located and parsed without updating the xdr_stream. 19804047aa90SChuck Lever */ 19814047aa90SChuck Lever 19824047aa90SChuck Lever /* opaque checksum<>; */ 19834047aa90SChuck Lever offset += len; 19844047aa90SChuck Lever if (xdr_decode_word(rcv_buf, offset, &len)) 1985a0584ee9SChuck Lever goto unwrap_failed; 19864047aa90SChuck Lever offset += sizeof(__be32); 19874047aa90SChuck Lever if (offset + len > rcv_buf->len) 1988a0584ee9SChuck Lever goto unwrap_failed; 19894047aa90SChuck Lever mic.len = len; 19904047aa90SChuck Lever mic.data = kmalloc(len, GFP_NOFS); 19914047aa90SChuck Lever if (!mic.data) 19924047aa90SChuck Lever goto unwrap_failed; 19934047aa90SChuck Lever if (read_bytes_from_xdr_buf(rcv_buf, offset, mic.data, mic.len)) 19944047aa90SChuck Lever goto unwrap_failed; 19954047aa90SChuck Lever 19964047aa90SChuck Lever maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &gss_data, &mic); 19971da177e4SLinus Torvalds if (maj_stat == GSS_S_CONTEXT_EXPIRED) 1998fc432dd9STrond Myklebust clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 19991da177e4SLinus Torvalds if (maj_stat != GSS_S_COMPLETE) 2000a0584ee9SChuck Lever goto bad_mic; 2001a0584ee9SChuck Lever 200253bc19f1SChuck Lever gss_update_rslack(task, cred, 2, 2 + 1 + XDR_QUADLEN(mic.len)); 20034047aa90SChuck Lever ret = 0; 20044047aa90SChuck Lever 20054047aa90SChuck Lever out: 20064047aa90SChuck Lever kfree(mic.data); 20074047aa90SChuck Lever return ret; 20084047aa90SChuck Lever 2009a0584ee9SChuck Lever unwrap_failed: 20100c77668dSChuck Lever trace_rpcgss_unwrap_failed(task); 20114047aa90SChuck Lever goto out; 20120c77668dSChuck Lever bad_seqno: 20134047aa90SChuck Lever trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, seqno); 20144047aa90SChuck Lever goto out; 2015a0584ee9SChuck Lever bad_mic: 20160c77668dSChuck Lever trace_rpcgss_verify_mic(task, maj_stat); 20174047aa90SChuck Lever goto out; 20181da177e4SLinus Torvalds } 20191da177e4SLinus Torvalds 2020d162372aSChuck Lever static noinline_for_stack int 20210c77668dSChuck Lever gss_unwrap_resp_priv(struct rpc_task *task, struct rpc_cred *cred, 20220c77668dSChuck Lever struct gss_cl_ctx *ctx, struct rpc_rqst *rqstp, 20230c77668dSChuck Lever struct xdr_stream *xdr) 20242d2da60cSJ. Bruce Fields { 20252d2da60cSJ. Bruce Fields struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf; 2026a0584ee9SChuck Lever struct kvec *head = rqstp->rq_rcv_buf.head; 2027a0584ee9SChuck Lever u32 offset, opaque_len, maj_stat; 2028a0584ee9SChuck Lever __be32 *p; 20292d2da60cSJ. Bruce Fields 2030a0584ee9SChuck Lever p = xdr_inline_decode(xdr, 2 * sizeof(*p)); 2031a0584ee9SChuck Lever if (unlikely(!p)) 2032a0584ee9SChuck Lever goto unwrap_failed; 2033a0584ee9SChuck Lever opaque_len = be32_to_cpup(p++); 2034a0584ee9SChuck Lever offset = (u8 *)(p) - (u8 *)head->iov_base; 20352d2da60cSJ. Bruce Fields if (offset + opaque_len > rcv_buf->len) 2036a0584ee9SChuck Lever goto unwrap_failed; 20372d2da60cSJ. Bruce Fields 203831c9590aSChuck Lever maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset, 203931c9590aSChuck Lever offset + opaque_len, rcv_buf); 20402d2da60cSJ. Bruce Fields if (maj_stat == GSS_S_CONTEXT_EXPIRED) 2041fc432dd9STrond Myklebust clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 20422d2da60cSJ. Bruce Fields if (maj_stat != GSS_S_COMPLETE) 2043a0584ee9SChuck Lever goto bad_unwrap; 2044a0584ee9SChuck Lever /* gss_unwrap decrypted the sequence number */ 2045a0584ee9SChuck Lever if (be32_to_cpup(p++) != rqstp->rq_seqno) 20460c77668dSChuck Lever goto bad_seqno; 20472d2da60cSJ. Bruce Fields 2048a0584ee9SChuck Lever /* gss_unwrap redacts the opaque blob from the head iovec. 2049a0584ee9SChuck Lever * rcv_buf has changed, thus the stream needs to be reset. 2050a0584ee9SChuck Lever */ 2051a0584ee9SChuck Lever xdr_init_decode(xdr, rcv_buf, p, rqstp); 2052a0584ee9SChuck Lever 205353bc19f1SChuck Lever gss_update_rslack(task, cred, 2 + ctx->gc_gss_ctx->align, 205453bc19f1SChuck Lever 2 + ctx->gc_gss_ctx->slack); 2055a7e429a6SChuck Lever 20562d2da60cSJ. Bruce Fields return 0; 2057a0584ee9SChuck Lever unwrap_failed: 20580c77668dSChuck Lever trace_rpcgss_unwrap_failed(task); 20590c77668dSChuck Lever return -EIO; 20600c77668dSChuck Lever bad_seqno: 20610c77668dSChuck Lever trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, be32_to_cpup(--p)); 2062a0584ee9SChuck Lever return -EIO; 2063a0584ee9SChuck Lever bad_unwrap: 20640c77668dSChuck Lever trace_rpcgss_unwrap(task, maj_stat); 2065a0584ee9SChuck Lever return -EIO; 2066bf269551SChuck Lever } 20672d2da60cSJ. Bruce Fields 20683021a5bbSTrond Myklebust static bool 20693021a5bbSTrond Myklebust gss_seq_is_newer(u32 new, u32 old) 20703021a5bbSTrond Myklebust { 20713021a5bbSTrond Myklebust return (s32)(new - old) > 0; 20723021a5bbSTrond Myklebust } 20733021a5bbSTrond Myklebust 20743021a5bbSTrond Myklebust static bool 20753021a5bbSTrond Myklebust gss_xmit_need_reencode(struct rpc_task *task) 20763021a5bbSTrond Myklebust { 20773021a5bbSTrond Myklebust struct rpc_rqst *req = task->tk_rqstp; 20783021a5bbSTrond Myklebust struct rpc_cred *cred = req->rq_cred; 20793021a5bbSTrond Myklebust struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 20800c77668dSChuck Lever u32 win, seq_xmit = 0; 20813021a5bbSTrond Myklebust bool ret = true; 20823021a5bbSTrond Myklebust 20833021a5bbSTrond Myklebust if (!ctx) 20840c77668dSChuck Lever goto out; 20853021a5bbSTrond Myklebust 20863021a5bbSTrond Myklebust if (gss_seq_is_newer(req->rq_seqno, READ_ONCE(ctx->gc_seq))) 20870c77668dSChuck Lever goto out_ctx; 20883021a5bbSTrond Myklebust 20893021a5bbSTrond Myklebust seq_xmit = READ_ONCE(ctx->gc_seq_xmit); 20903021a5bbSTrond Myklebust while (gss_seq_is_newer(req->rq_seqno, seq_xmit)) { 20913021a5bbSTrond Myklebust u32 tmp = seq_xmit; 20923021a5bbSTrond Myklebust 20933021a5bbSTrond Myklebust seq_xmit = cmpxchg(&ctx->gc_seq_xmit, tmp, req->rq_seqno); 20943021a5bbSTrond Myklebust if (seq_xmit == tmp) { 20953021a5bbSTrond Myklebust ret = false; 20960c77668dSChuck Lever goto out_ctx; 20973021a5bbSTrond Myklebust } 20983021a5bbSTrond Myklebust } 20993021a5bbSTrond Myklebust 21003021a5bbSTrond Myklebust win = ctx->gc_win; 21013021a5bbSTrond Myklebust if (win > 0) 21023021a5bbSTrond Myklebust ret = !gss_seq_is_newer(req->rq_seqno, seq_xmit - win); 21030c77668dSChuck Lever 21040c77668dSChuck Lever out_ctx: 21053021a5bbSTrond Myklebust gss_put_ctx(ctx); 21060c77668dSChuck Lever out: 21070c77668dSChuck Lever trace_rpcgss_need_reencode(task, seq_xmit, ret); 21083021a5bbSTrond Myklebust return ret; 21093021a5bbSTrond Myklebust } 21103021a5bbSTrond Myklebust 21111da177e4SLinus Torvalds static int 2112a0584ee9SChuck Lever gss_unwrap_resp(struct rpc_task *task, struct xdr_stream *xdr) 21131da177e4SLinus Torvalds { 2114a0584ee9SChuck Lever struct rpc_rqst *rqstp = task->tk_rqstp; 2115a0584ee9SChuck Lever struct rpc_cred *cred = rqstp->rq_cred; 21161da177e4SLinus Torvalds struct gss_cred *gss_cred = container_of(cred, struct gss_cred, 21171da177e4SLinus Torvalds gc_base); 21181da177e4SLinus Torvalds struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 21191da177e4SLinus Torvalds int status = -EIO; 21201da177e4SLinus Torvalds 21211da177e4SLinus Torvalds if (ctx->gc_proc != RPC_GSS_PROC_DATA) 21221da177e4SLinus Torvalds goto out_decode; 21231da177e4SLinus Torvalds switch (gss_cred->gc_service) { 21241da177e4SLinus Torvalds case RPC_GSS_SVC_NONE: 212553bc19f1SChuck Lever status = gss_unwrap_resp_auth(task, cred); 21261da177e4SLinus Torvalds break; 21271da177e4SLinus Torvalds case RPC_GSS_SVC_INTEGRITY: 21280c77668dSChuck Lever status = gss_unwrap_resp_integ(task, cred, ctx, rqstp, xdr); 21291da177e4SLinus Torvalds break; 21301da177e4SLinus Torvalds case RPC_GSS_SVC_PRIVACY: 21310c77668dSChuck Lever status = gss_unwrap_resp_priv(task, cred, ctx, rqstp, xdr); 21321da177e4SLinus Torvalds break; 21331da177e4SLinus Torvalds } 2134a0584ee9SChuck Lever if (status) 2135a0584ee9SChuck Lever goto out; 2136a0584ee9SChuck Lever 21371da177e4SLinus Torvalds out_decode: 2138a0584ee9SChuck Lever status = rpcauth_unwrap_resp_decode(task, xdr); 21391da177e4SLinus Torvalds out: 21401da177e4SLinus Torvalds gss_put_ctx(ctx); 21411da177e4SLinus Torvalds return status; 21421da177e4SLinus Torvalds } 21431da177e4SLinus Torvalds 2144f1c0a861STrond Myklebust static const struct rpc_authops authgss_ops = { 21451da177e4SLinus Torvalds .owner = THIS_MODULE, 21461da177e4SLinus Torvalds .au_flavor = RPC_AUTH_GSS, 21471da177e4SLinus Torvalds .au_name = "RPCSEC_GSS", 21481da177e4SLinus Torvalds .create = gss_create, 21491da177e4SLinus Torvalds .destroy = gss_destroy, 2150a960f8d6SFrank Sorenson .hash_cred = gss_hash_cred, 21511da177e4SLinus Torvalds .lookup_cred = gss_lookup_cred, 215280df9d20SStanislav Kinsbursky .crcreate = gss_create_cred, 21539568c5e9SChuck Lever .info2flavor = gss_mech_info2flavor, 2154a77c806fSChuck Lever .flavor2info = gss_mech_flavor2info, 21551da177e4SLinus Torvalds }; 21561da177e4SLinus Torvalds 2157f1c0a861STrond Myklebust static const struct rpc_credops gss_credops = { 21581da177e4SLinus Torvalds .cr_name = "AUTH_GSS", 21591da177e4SLinus Torvalds .crdestroy = gss_destroy_cred, 2160fba3bad4STrond Myklebust .cr_init = gss_cred_init, 21611da177e4SLinus Torvalds .crmatch = gss_match, 21621da177e4SLinus Torvalds .crmarshal = gss_marshal, 21631da177e4SLinus Torvalds .crrefresh = gss_refresh, 21641da177e4SLinus Torvalds .crvalidate = gss_validate, 21651da177e4SLinus Torvalds .crwrap_req = gss_wrap_req, 21661da177e4SLinus Torvalds .crunwrap_resp = gss_unwrap_resp, 21674de6caa2SAndy Adamson .crkey_timeout = gss_key_timeout, 2168a0337d1dSJeff Layton .crstringify_acceptor = gss_stringify_acceptor, 21693021a5bbSTrond Myklebust .crneed_reencode = gss_xmit_need_reencode, 21701da177e4SLinus Torvalds }; 21711da177e4SLinus Torvalds 21720df7fb74STrond Myklebust static const struct rpc_credops gss_nullops = { 21730df7fb74STrond Myklebust .cr_name = "AUTH_GSS", 21746dcd3926SJeff Layton .crdestroy = gss_destroy_nullcred, 21750df7fb74STrond Myklebust .crmatch = gss_match, 21760df7fb74STrond Myklebust .crmarshal = gss_marshal, 21770df7fb74STrond Myklebust .crrefresh = gss_refresh_null, 21780df7fb74STrond Myklebust .crvalidate = gss_validate, 21790df7fb74STrond Myklebust .crwrap_req = gss_wrap_req, 21800df7fb74STrond Myklebust .crunwrap_resp = gss_unwrap_resp, 2181a0337d1dSJeff Layton .crstringify_acceptor = gss_stringify_acceptor, 21820df7fb74STrond Myklebust }; 21830df7fb74STrond Myklebust 2184b693ba4aSTrond Myklebust static const struct rpc_pipe_ops gss_upcall_ops_v0 = { 2185ac83228aSTrond Myklebust .upcall = gss_v0_upcall, 21861da177e4SLinus Torvalds .downcall = gss_pipe_downcall, 21871da177e4SLinus Torvalds .destroy_msg = gss_pipe_destroy_msg, 218834769fc4S\"J. Bruce Fields\ .open_pipe = gss_pipe_open_v0, 218934769fc4S\"J. Bruce Fields\ .release_pipe = gss_pipe_release, 219034769fc4S\"J. Bruce Fields\ }; 219134769fc4S\"J. Bruce Fields\ 2192b693ba4aSTrond Myklebust static const struct rpc_pipe_ops gss_upcall_ops_v1 = { 2193ac83228aSTrond Myklebust .upcall = gss_v1_upcall, 219434769fc4S\"J. Bruce Fields\ .downcall = gss_pipe_downcall, 219534769fc4S\"J. Bruce Fields\ .destroy_msg = gss_pipe_destroy_msg, 219634769fc4S\"J. Bruce Fields\ .open_pipe = gss_pipe_open_v1, 21971da177e4SLinus Torvalds .release_pipe = gss_pipe_release, 21981da177e4SLinus Torvalds }; 21991da177e4SLinus Torvalds 2200a1db410dSStanislav Kinsbursky static __net_init int rpcsec_gss_init_net(struct net *net) 2201a1db410dSStanislav Kinsbursky { 2202a1db410dSStanislav Kinsbursky return gss_svc_init_net(net); 2203a1db410dSStanislav Kinsbursky } 2204a1db410dSStanislav Kinsbursky 2205a1db410dSStanislav Kinsbursky static __net_exit void rpcsec_gss_exit_net(struct net *net) 2206a1db410dSStanislav Kinsbursky { 2207a1db410dSStanislav Kinsbursky gss_svc_shutdown_net(net); 2208a1db410dSStanislav Kinsbursky } 2209a1db410dSStanislav Kinsbursky 2210a1db410dSStanislav Kinsbursky static struct pernet_operations rpcsec_gss_net_ops = { 2211a1db410dSStanislav Kinsbursky .init = rpcsec_gss_init_net, 2212a1db410dSStanislav Kinsbursky .exit = rpcsec_gss_exit_net, 2213a1db410dSStanislav Kinsbursky }; 2214a1db410dSStanislav Kinsbursky 22151da177e4SLinus Torvalds /* 22161da177e4SLinus Torvalds * Initialize RPCSEC_GSS module 22171da177e4SLinus Torvalds */ 22181da177e4SLinus Torvalds static int __init init_rpcsec_gss(void) 22191da177e4SLinus Torvalds { 22201da177e4SLinus Torvalds int err = 0; 22211da177e4SLinus Torvalds 22221da177e4SLinus Torvalds err = rpcauth_register(&authgss_ops); 22231da177e4SLinus Torvalds if (err) 22241da177e4SLinus Torvalds goto out; 22251da177e4SLinus Torvalds err = gss_svc_init(); 22261da177e4SLinus Torvalds if (err) 22271da177e4SLinus Torvalds goto out_unregister; 2228a1db410dSStanislav Kinsbursky err = register_pernet_subsys(&rpcsec_gss_net_ops); 2229a1db410dSStanislav Kinsbursky if (err) 2230a1db410dSStanislav Kinsbursky goto out_svc_exit; 223179a3f20bS\"J. Bruce Fields\ rpc_init_wait_queue(&pipe_version_rpc_waitqueue, "gss pipe version"); 22321da177e4SLinus Torvalds return 0; 2233a1db410dSStanislav Kinsbursky out_svc_exit: 2234a1db410dSStanislav Kinsbursky gss_svc_shutdown(); 22351da177e4SLinus Torvalds out_unregister: 22361da177e4SLinus Torvalds rpcauth_unregister(&authgss_ops); 22371da177e4SLinus Torvalds out: 22381da177e4SLinus Torvalds return err; 22391da177e4SLinus Torvalds } 22401da177e4SLinus Torvalds 22411da177e4SLinus Torvalds static void __exit exit_rpcsec_gss(void) 22421da177e4SLinus Torvalds { 2243a1db410dSStanislav Kinsbursky unregister_pernet_subsys(&rpcsec_gss_net_ops); 22441da177e4SLinus Torvalds gss_svc_shutdown(); 22451da177e4SLinus Torvalds rpcauth_unregister(&authgss_ops); 2246bf12691dSJesper Dangaard Brouer rcu_barrier(); /* Wait for completion of call_rcu()'s */ 22471da177e4SLinus Torvalds } 22481da177e4SLinus Torvalds 224971afa85eSChuck Lever MODULE_ALIAS("rpc-auth-6"); 22501da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 2251126e216aSTrond Myklebust module_param_named(expired_cred_retry_delay, 2252126e216aSTrond Myklebust gss_expired_cred_retry_delay, 2253126e216aSTrond Myklebust uint, 0644); 2254126e216aSTrond Myklebust MODULE_PARM_DESC(expired_cred_retry_delay, "Timeout (in seconds) until " 2255126e216aSTrond Myklebust "the RPC engine retries an expired credential"); 2256126e216aSTrond Myklebust 22574de6caa2SAndy Adamson module_param_named(key_expire_timeo, 22584de6caa2SAndy Adamson gss_key_expire_timeo, 22594de6caa2SAndy Adamson uint, 0644); 22604de6caa2SAndy Adamson MODULE_PARM_DESC(key_expire_timeo, "Time (in seconds) at the end of a " 22614de6caa2SAndy Adamson "credential keys lifetime where the NFS layer cleans up " 22624de6caa2SAndy Adamson "prior to key expiration"); 22634de6caa2SAndy Adamson 22641da177e4SLinus Torvalds module_init(init_rpcsec_gss) 22651da177e4SLinus Torvalds module_exit(exit_rpcsec_gss) 2266