162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/net/sunrpc/auth_gss/auth_gss.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * RPCSEC_GSS client authentication. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2000 The Regents of the University of Michigan. 862306a36Sopenharmony_ci * All rights reserved. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Dug Song <dugsong@monkey.org> 1162306a36Sopenharmony_ci * Andy Adamson <andros@umich.edu> 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/types.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/sched.h> 1962306a36Sopenharmony_ci#include <linux/pagemap.h> 2062306a36Sopenharmony_ci#include <linux/sunrpc/clnt.h> 2162306a36Sopenharmony_ci#include <linux/sunrpc/auth.h> 2262306a36Sopenharmony_ci#include <linux/sunrpc/auth_gss.h> 2362306a36Sopenharmony_ci#include <linux/sunrpc/gss_krb5.h> 2462306a36Sopenharmony_ci#include <linux/sunrpc/svcauth_gss.h> 2562306a36Sopenharmony_ci#include <linux/sunrpc/gss_err.h> 2662306a36Sopenharmony_ci#include <linux/workqueue.h> 2762306a36Sopenharmony_ci#include <linux/sunrpc/rpc_pipe_fs.h> 2862306a36Sopenharmony_ci#include <linux/sunrpc/gss_api.h> 2962306a36Sopenharmony_ci#include <linux/uaccess.h> 3062306a36Sopenharmony_ci#include <linux/hashtable.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "auth_gss_internal.h" 3362306a36Sopenharmony_ci#include "../netns.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <trace/events/rpcgss.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic const struct rpc_authops authgss_ops; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic const struct rpc_credops gss_credops; 4062306a36Sopenharmony_cistatic const struct rpc_credops gss_nullops; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define GSS_RETRY_EXPIRED 5 4362306a36Sopenharmony_cistatic unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define GSS_KEY_EXPIRE_TIMEO 240 4662306a36Sopenharmony_cistatic unsigned int gss_key_expire_timeo = GSS_KEY_EXPIRE_TIMEO; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) 4962306a36Sopenharmony_ci# define RPCDBG_FACILITY RPCDBG_AUTH 5062306a36Sopenharmony_ci#endif 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* 5362306a36Sopenharmony_ci * This compile-time check verifies that we will not exceed the 5462306a36Sopenharmony_ci * slack space allotted by the client and server auth_gss code 5562306a36Sopenharmony_ci * before they call gss_wrap(). 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci#define GSS_KRB5_MAX_SLACK_NEEDED \ 5862306a36Sopenharmony_ci (GSS_KRB5_TOK_HDR_LEN /* gss token header */ \ 5962306a36Sopenharmony_ci + GSS_KRB5_MAX_CKSUM_LEN /* gss token checksum */ \ 6062306a36Sopenharmony_ci + GSS_KRB5_MAX_BLOCKSIZE /* confounder */ \ 6162306a36Sopenharmony_ci + GSS_KRB5_MAX_BLOCKSIZE /* possible padding */ \ 6262306a36Sopenharmony_ci + GSS_KRB5_TOK_HDR_LEN /* encrypted hdr in v2 token */ \ 6362306a36Sopenharmony_ci + GSS_KRB5_MAX_CKSUM_LEN /* encryption hmac */ \ 6462306a36Sopenharmony_ci + XDR_UNIT * 2 /* RPC verifier */ \ 6562306a36Sopenharmony_ci + GSS_KRB5_TOK_HDR_LEN \ 6662306a36Sopenharmony_ci + GSS_KRB5_MAX_CKSUM_LEN) 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define GSS_CRED_SLACK (RPC_MAX_AUTH_SIZE * 2) 6962306a36Sopenharmony_ci/* length of a krb5 verifier (48), plus data added before arguments when 7062306a36Sopenharmony_ci * using integrity (two 4-byte integers): */ 7162306a36Sopenharmony_ci#define GSS_VERF_SLACK 100 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic DEFINE_HASHTABLE(gss_auth_hash_table, 4); 7462306a36Sopenharmony_cistatic DEFINE_SPINLOCK(gss_auth_hash_lock); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistruct gss_pipe { 7762306a36Sopenharmony_ci struct rpc_pipe_dir_object pdo; 7862306a36Sopenharmony_ci struct rpc_pipe *pipe; 7962306a36Sopenharmony_ci struct rpc_clnt *clnt; 8062306a36Sopenharmony_ci const char *name; 8162306a36Sopenharmony_ci struct kref kref; 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistruct gss_auth { 8562306a36Sopenharmony_ci struct kref kref; 8662306a36Sopenharmony_ci struct hlist_node hash; 8762306a36Sopenharmony_ci struct rpc_auth rpc_auth; 8862306a36Sopenharmony_ci struct gss_api_mech *mech; 8962306a36Sopenharmony_ci enum rpc_gss_svc service; 9062306a36Sopenharmony_ci struct rpc_clnt *client; 9162306a36Sopenharmony_ci struct net *net; 9262306a36Sopenharmony_ci netns_tracker ns_tracker; 9362306a36Sopenharmony_ci /* 9462306a36Sopenharmony_ci * There are two upcall pipes; dentry[1], named "gssd", is used 9562306a36Sopenharmony_ci * for the new text-based upcall; dentry[0] is named after the 9662306a36Sopenharmony_ci * mechanism (for example, "krb5") and exists for 9762306a36Sopenharmony_ci * backwards-compatibility with older gssd's. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_ci struct gss_pipe *gss_pipe[2]; 10062306a36Sopenharmony_ci const char *target_name; 10162306a36Sopenharmony_ci}; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* pipe_version >= 0 if and only if someone has a pipe open. */ 10462306a36Sopenharmony_cistatic DEFINE_SPINLOCK(pipe_version_lock); 10562306a36Sopenharmony_cistatic struct rpc_wait_queue pipe_version_rpc_waitqueue; 10662306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(pipe_version_waitqueue); 10762306a36Sopenharmony_cistatic void gss_put_auth(struct gss_auth *gss_auth); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic void gss_free_ctx(struct gss_cl_ctx *); 11062306a36Sopenharmony_cistatic const struct rpc_pipe_ops gss_upcall_ops_v0; 11162306a36Sopenharmony_cistatic const struct rpc_pipe_ops gss_upcall_ops_v1; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic inline struct gss_cl_ctx * 11462306a36Sopenharmony_cigss_get_ctx(struct gss_cl_ctx *ctx) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci refcount_inc(&ctx->count); 11762306a36Sopenharmony_ci return ctx; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic inline void 12162306a36Sopenharmony_cigss_put_ctx(struct gss_cl_ctx *ctx) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci if (refcount_dec_and_test(&ctx->count)) 12462306a36Sopenharmony_ci gss_free_ctx(ctx); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* gss_cred_set_ctx: 12862306a36Sopenharmony_ci * called by gss_upcall_callback and gss_create_upcall in order 12962306a36Sopenharmony_ci * to set the gss context. The actual exchange of an old context 13062306a36Sopenharmony_ci * and a new one is protected by the pipe->lock. 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_cistatic void 13362306a36Sopenharmony_cigss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags)) 13862306a36Sopenharmony_ci return; 13962306a36Sopenharmony_ci gss_get_ctx(ctx); 14062306a36Sopenharmony_ci rcu_assign_pointer(gss_cred->gc_ctx, ctx); 14162306a36Sopenharmony_ci set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 14262306a36Sopenharmony_ci smp_mb__before_atomic(); 14362306a36Sopenharmony_ci clear_bit(RPCAUTH_CRED_NEW, &cred->cr_flags); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic struct gss_cl_ctx * 14762306a36Sopenharmony_cigss_cred_get_ctx(struct rpc_cred *cred) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 15062306a36Sopenharmony_ci struct gss_cl_ctx *ctx = NULL; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci rcu_read_lock(); 15362306a36Sopenharmony_ci ctx = rcu_dereference(gss_cred->gc_ctx); 15462306a36Sopenharmony_ci if (ctx) 15562306a36Sopenharmony_ci gss_get_ctx(ctx); 15662306a36Sopenharmony_ci rcu_read_unlock(); 15762306a36Sopenharmony_ci return ctx; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic struct gss_cl_ctx * 16162306a36Sopenharmony_cigss_alloc_context(void) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct gss_cl_ctx *ctx; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 16662306a36Sopenharmony_ci if (ctx != NULL) { 16762306a36Sopenharmony_ci ctx->gc_proc = RPC_GSS_PROC_DATA; 16862306a36Sopenharmony_ci ctx->gc_seq = 1; /* NetApp 6.4R1 doesn't accept seq. no. 0 */ 16962306a36Sopenharmony_ci spin_lock_init(&ctx->gc_seq_lock); 17062306a36Sopenharmony_ci refcount_set(&ctx->count,1); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci return ctx; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci#define GSSD_MIN_TIMEOUT (60 * 60) 17662306a36Sopenharmony_cistatic const void * 17762306a36Sopenharmony_cigss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct gss_api_mech *gm) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci const void *q; 18062306a36Sopenharmony_ci unsigned int seclen; 18162306a36Sopenharmony_ci unsigned int timeout; 18262306a36Sopenharmony_ci unsigned long now = jiffies; 18362306a36Sopenharmony_ci u32 window_size; 18462306a36Sopenharmony_ci int ret; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* First unsigned int gives the remaining lifetime in seconds of the 18762306a36Sopenharmony_ci * credential - e.g. the remaining TGT lifetime for Kerberos or 18862306a36Sopenharmony_ci * the -t value passed to GSSD. 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_ci p = simple_get_bytes(p, end, &timeout, sizeof(timeout)); 19162306a36Sopenharmony_ci if (IS_ERR(p)) 19262306a36Sopenharmony_ci goto err; 19362306a36Sopenharmony_ci if (timeout == 0) 19462306a36Sopenharmony_ci timeout = GSSD_MIN_TIMEOUT; 19562306a36Sopenharmony_ci ctx->gc_expiry = now + ((unsigned long)timeout * HZ); 19662306a36Sopenharmony_ci /* Sequence number window. Determines the maximum number of 19762306a36Sopenharmony_ci * simultaneous requests 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_ci p = simple_get_bytes(p, end, &window_size, sizeof(window_size)); 20062306a36Sopenharmony_ci if (IS_ERR(p)) 20162306a36Sopenharmony_ci goto err; 20262306a36Sopenharmony_ci ctx->gc_win = window_size; 20362306a36Sopenharmony_ci /* gssd signals an error by passing ctx->gc_win = 0: */ 20462306a36Sopenharmony_ci if (ctx->gc_win == 0) { 20562306a36Sopenharmony_ci /* 20662306a36Sopenharmony_ci * in which case, p points to an error code. Anything other 20762306a36Sopenharmony_ci * than -EKEYEXPIRED gets converted to -EACCES. 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci p = simple_get_bytes(p, end, &ret, sizeof(ret)); 21062306a36Sopenharmony_ci if (!IS_ERR(p)) 21162306a36Sopenharmony_ci p = (ret == -EKEYEXPIRED) ? ERR_PTR(-EKEYEXPIRED) : 21262306a36Sopenharmony_ci ERR_PTR(-EACCES); 21362306a36Sopenharmony_ci goto err; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci /* copy the opaque wire context */ 21662306a36Sopenharmony_ci p = simple_get_netobj(p, end, &ctx->gc_wire_ctx); 21762306a36Sopenharmony_ci if (IS_ERR(p)) 21862306a36Sopenharmony_ci goto err; 21962306a36Sopenharmony_ci /* import the opaque security context */ 22062306a36Sopenharmony_ci p = simple_get_bytes(p, end, &seclen, sizeof(seclen)); 22162306a36Sopenharmony_ci if (IS_ERR(p)) 22262306a36Sopenharmony_ci goto err; 22362306a36Sopenharmony_ci q = (const void *)((const char *)p + seclen); 22462306a36Sopenharmony_ci if (unlikely(q > end || q < p)) { 22562306a36Sopenharmony_ci p = ERR_PTR(-EFAULT); 22662306a36Sopenharmony_ci goto err; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx, NULL, GFP_KERNEL); 22962306a36Sopenharmony_ci if (ret < 0) { 23062306a36Sopenharmony_ci trace_rpcgss_import_ctx(ret); 23162306a36Sopenharmony_ci p = ERR_PTR(ret); 23262306a36Sopenharmony_ci goto err; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* is there any trailing data? */ 23662306a36Sopenharmony_ci if (q == end) { 23762306a36Sopenharmony_ci p = q; 23862306a36Sopenharmony_ci goto done; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* pull in acceptor name (if there is one) */ 24262306a36Sopenharmony_ci p = simple_get_netobj(q, end, &ctx->gc_acceptor); 24362306a36Sopenharmony_ci if (IS_ERR(p)) 24462306a36Sopenharmony_ci goto err; 24562306a36Sopenharmony_cidone: 24662306a36Sopenharmony_ci trace_rpcgss_context(window_size, ctx->gc_expiry, now, timeout, 24762306a36Sopenharmony_ci ctx->gc_acceptor.len, ctx->gc_acceptor.data); 24862306a36Sopenharmony_cierr: 24962306a36Sopenharmony_ci return p; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/* XXX: Need some documentation about why UPCALL_BUF_LEN is so small. 25362306a36Sopenharmony_ci * Is user space expecting no more than UPCALL_BUF_LEN bytes? 25462306a36Sopenharmony_ci * Note that there are now _two_ NI_MAXHOST sized data items 25562306a36Sopenharmony_ci * being passed in this string. 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_ci#define UPCALL_BUF_LEN 256 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistruct gss_upcall_msg { 26062306a36Sopenharmony_ci refcount_t count; 26162306a36Sopenharmony_ci kuid_t uid; 26262306a36Sopenharmony_ci const char *service_name; 26362306a36Sopenharmony_ci struct rpc_pipe_msg msg; 26462306a36Sopenharmony_ci struct list_head list; 26562306a36Sopenharmony_ci struct gss_auth *auth; 26662306a36Sopenharmony_ci struct rpc_pipe *pipe; 26762306a36Sopenharmony_ci struct rpc_wait_queue rpc_waitqueue; 26862306a36Sopenharmony_ci wait_queue_head_t waitqueue; 26962306a36Sopenharmony_ci struct gss_cl_ctx *ctx; 27062306a36Sopenharmony_ci char databuf[UPCALL_BUF_LEN]; 27162306a36Sopenharmony_ci}; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic int get_pipe_version(struct net *net) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 27662306a36Sopenharmony_ci int ret; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci spin_lock(&pipe_version_lock); 27962306a36Sopenharmony_ci if (sn->pipe_version >= 0) { 28062306a36Sopenharmony_ci atomic_inc(&sn->pipe_users); 28162306a36Sopenharmony_ci ret = sn->pipe_version; 28262306a36Sopenharmony_ci } else 28362306a36Sopenharmony_ci ret = -EAGAIN; 28462306a36Sopenharmony_ci spin_unlock(&pipe_version_lock); 28562306a36Sopenharmony_ci return ret; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic void put_pipe_version(struct net *net) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (atomic_dec_and_lock(&sn->pipe_users, &pipe_version_lock)) { 29362306a36Sopenharmony_ci sn->pipe_version = -1; 29462306a36Sopenharmony_ci spin_unlock(&pipe_version_lock); 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic void 29962306a36Sopenharmony_cigss_release_msg(struct gss_upcall_msg *gss_msg) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct net *net = gss_msg->auth->net; 30262306a36Sopenharmony_ci if (!refcount_dec_and_test(&gss_msg->count)) 30362306a36Sopenharmony_ci return; 30462306a36Sopenharmony_ci put_pipe_version(net); 30562306a36Sopenharmony_ci BUG_ON(!list_empty(&gss_msg->list)); 30662306a36Sopenharmony_ci if (gss_msg->ctx != NULL) 30762306a36Sopenharmony_ci gss_put_ctx(gss_msg->ctx); 30862306a36Sopenharmony_ci rpc_destroy_wait_queue(&gss_msg->rpc_waitqueue); 30962306a36Sopenharmony_ci gss_put_auth(gss_msg->auth); 31062306a36Sopenharmony_ci kfree_const(gss_msg->service_name); 31162306a36Sopenharmony_ci kfree(gss_msg); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic struct gss_upcall_msg * 31562306a36Sopenharmony_ci__gss_find_upcall(struct rpc_pipe *pipe, kuid_t uid, const struct gss_auth *auth) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct gss_upcall_msg *pos; 31862306a36Sopenharmony_ci list_for_each_entry(pos, &pipe->in_downcall, list) { 31962306a36Sopenharmony_ci if (!uid_eq(pos->uid, uid)) 32062306a36Sopenharmony_ci continue; 32162306a36Sopenharmony_ci if (pos->auth->service != auth->service) 32262306a36Sopenharmony_ci continue; 32362306a36Sopenharmony_ci refcount_inc(&pos->count); 32462306a36Sopenharmony_ci return pos; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci return NULL; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci/* Try to add an upcall to the pipefs queue. 33062306a36Sopenharmony_ci * If an upcall owned by our uid already exists, then we return a reference 33162306a36Sopenharmony_ci * to that upcall instead of adding the new upcall. 33262306a36Sopenharmony_ci */ 33362306a36Sopenharmony_cistatic inline struct gss_upcall_msg * 33462306a36Sopenharmony_cigss_add_msg(struct gss_upcall_msg *gss_msg) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct rpc_pipe *pipe = gss_msg->pipe; 33762306a36Sopenharmony_ci struct gss_upcall_msg *old; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci spin_lock(&pipe->lock); 34062306a36Sopenharmony_ci old = __gss_find_upcall(pipe, gss_msg->uid, gss_msg->auth); 34162306a36Sopenharmony_ci if (old == NULL) { 34262306a36Sopenharmony_ci refcount_inc(&gss_msg->count); 34362306a36Sopenharmony_ci list_add(&gss_msg->list, &pipe->in_downcall); 34462306a36Sopenharmony_ci } else 34562306a36Sopenharmony_ci gss_msg = old; 34662306a36Sopenharmony_ci spin_unlock(&pipe->lock); 34762306a36Sopenharmony_ci return gss_msg; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic void 35162306a36Sopenharmony_ci__gss_unhash_msg(struct gss_upcall_msg *gss_msg) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci list_del_init(&gss_msg->list); 35462306a36Sopenharmony_ci rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); 35562306a36Sopenharmony_ci wake_up_all(&gss_msg->waitqueue); 35662306a36Sopenharmony_ci refcount_dec(&gss_msg->count); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic void 36062306a36Sopenharmony_cigss_unhash_msg(struct gss_upcall_msg *gss_msg) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci struct rpc_pipe *pipe = gss_msg->pipe; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (list_empty(&gss_msg->list)) 36562306a36Sopenharmony_ci return; 36662306a36Sopenharmony_ci spin_lock(&pipe->lock); 36762306a36Sopenharmony_ci if (!list_empty(&gss_msg->list)) 36862306a36Sopenharmony_ci __gss_unhash_msg(gss_msg); 36962306a36Sopenharmony_ci spin_unlock(&pipe->lock); 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic void 37362306a36Sopenharmony_cigss_handle_downcall_result(struct gss_cred *gss_cred, struct gss_upcall_msg *gss_msg) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci switch (gss_msg->msg.errno) { 37662306a36Sopenharmony_ci case 0: 37762306a36Sopenharmony_ci if (gss_msg->ctx == NULL) 37862306a36Sopenharmony_ci break; 37962306a36Sopenharmony_ci clear_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags); 38062306a36Sopenharmony_ci gss_cred_set_ctx(&gss_cred->gc_base, gss_msg->ctx); 38162306a36Sopenharmony_ci break; 38262306a36Sopenharmony_ci case -EKEYEXPIRED: 38362306a36Sopenharmony_ci set_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags); 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci gss_cred->gc_upcall_timestamp = jiffies; 38662306a36Sopenharmony_ci gss_cred->gc_upcall = NULL; 38762306a36Sopenharmony_ci rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic void 39162306a36Sopenharmony_cigss_upcall_callback(struct rpc_task *task) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct gss_cred *gss_cred = container_of(task->tk_rqstp->rq_cred, 39462306a36Sopenharmony_ci struct gss_cred, gc_base); 39562306a36Sopenharmony_ci struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall; 39662306a36Sopenharmony_ci struct rpc_pipe *pipe = gss_msg->pipe; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci spin_lock(&pipe->lock); 39962306a36Sopenharmony_ci gss_handle_downcall_result(gss_cred, gss_msg); 40062306a36Sopenharmony_ci spin_unlock(&pipe->lock); 40162306a36Sopenharmony_ci task->tk_status = gss_msg->msg.errno; 40262306a36Sopenharmony_ci gss_release_msg(gss_msg); 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg, 40662306a36Sopenharmony_ci const struct cred *cred) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci struct user_namespace *userns = cred->user_ns; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci uid_t uid = from_kuid_munged(userns, gss_msg->uid); 41162306a36Sopenharmony_ci memcpy(gss_msg->databuf, &uid, sizeof(uid)); 41262306a36Sopenharmony_ci gss_msg->msg.data = gss_msg->databuf; 41362306a36Sopenharmony_ci gss_msg->msg.len = sizeof(uid); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(uid) > sizeof(gss_msg->databuf)); 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic ssize_t 41962306a36Sopenharmony_cigss_v0_upcall(struct file *file, struct rpc_pipe_msg *msg, 42062306a36Sopenharmony_ci char __user *buf, size_t buflen) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci struct gss_upcall_msg *gss_msg = container_of(msg, 42362306a36Sopenharmony_ci struct gss_upcall_msg, 42462306a36Sopenharmony_ci msg); 42562306a36Sopenharmony_ci if (msg->copied == 0) 42662306a36Sopenharmony_ci gss_encode_v0_msg(gss_msg, file->f_cred); 42762306a36Sopenharmony_ci return rpc_pipe_generic_upcall(file, msg, buf, buflen); 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic int gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, 43162306a36Sopenharmony_ci const char *service_name, 43262306a36Sopenharmony_ci const char *target_name, 43362306a36Sopenharmony_ci const struct cred *cred) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct user_namespace *userns = cred->user_ns; 43662306a36Sopenharmony_ci struct gss_api_mech *mech = gss_msg->auth->mech; 43762306a36Sopenharmony_ci char *p = gss_msg->databuf; 43862306a36Sopenharmony_ci size_t buflen = sizeof(gss_msg->databuf); 43962306a36Sopenharmony_ci int len; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci len = scnprintf(p, buflen, "mech=%s uid=%d", mech->gm_name, 44262306a36Sopenharmony_ci from_kuid_munged(userns, gss_msg->uid)); 44362306a36Sopenharmony_ci buflen -= len; 44462306a36Sopenharmony_ci p += len; 44562306a36Sopenharmony_ci gss_msg->msg.len = len; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* 44862306a36Sopenharmony_ci * target= is a full service principal that names the remote 44962306a36Sopenharmony_ci * identity that we are authenticating to. 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_ci if (target_name) { 45262306a36Sopenharmony_ci len = scnprintf(p, buflen, " target=%s", target_name); 45362306a36Sopenharmony_ci buflen -= len; 45462306a36Sopenharmony_ci p += len; 45562306a36Sopenharmony_ci gss_msg->msg.len += len; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* 45962306a36Sopenharmony_ci * gssd uses service= and srchost= to select a matching key from 46062306a36Sopenharmony_ci * the system's keytab to use as the source principal. 46162306a36Sopenharmony_ci * 46262306a36Sopenharmony_ci * service= is the service name part of the source principal, 46362306a36Sopenharmony_ci * or "*" (meaning choose any). 46462306a36Sopenharmony_ci * 46562306a36Sopenharmony_ci * srchost= is the hostname part of the source principal. When 46662306a36Sopenharmony_ci * not provided, gssd uses the local hostname. 46762306a36Sopenharmony_ci */ 46862306a36Sopenharmony_ci if (service_name) { 46962306a36Sopenharmony_ci char *c = strchr(service_name, '@'); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (!c) 47262306a36Sopenharmony_ci len = scnprintf(p, buflen, " service=%s", 47362306a36Sopenharmony_ci service_name); 47462306a36Sopenharmony_ci else 47562306a36Sopenharmony_ci len = scnprintf(p, buflen, 47662306a36Sopenharmony_ci " service=%.*s srchost=%s", 47762306a36Sopenharmony_ci (int)(c - service_name), 47862306a36Sopenharmony_ci service_name, c + 1); 47962306a36Sopenharmony_ci buflen -= len; 48062306a36Sopenharmony_ci p += len; 48162306a36Sopenharmony_ci gss_msg->msg.len += len; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (mech->gm_upcall_enctypes) { 48562306a36Sopenharmony_ci len = scnprintf(p, buflen, " enctypes=%s", 48662306a36Sopenharmony_ci mech->gm_upcall_enctypes); 48762306a36Sopenharmony_ci buflen -= len; 48862306a36Sopenharmony_ci p += len; 48962306a36Sopenharmony_ci gss_msg->msg.len += len; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci trace_rpcgss_upcall_msg(gss_msg->databuf); 49262306a36Sopenharmony_ci len = scnprintf(p, buflen, "\n"); 49362306a36Sopenharmony_ci if (len == 0) 49462306a36Sopenharmony_ci goto out_overflow; 49562306a36Sopenharmony_ci gss_msg->msg.len += len; 49662306a36Sopenharmony_ci gss_msg->msg.data = gss_msg->databuf; 49762306a36Sopenharmony_ci return 0; 49862306a36Sopenharmony_ciout_overflow: 49962306a36Sopenharmony_ci WARN_ON_ONCE(1); 50062306a36Sopenharmony_ci return -ENOMEM; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic ssize_t 50462306a36Sopenharmony_cigss_v1_upcall(struct file *file, struct rpc_pipe_msg *msg, 50562306a36Sopenharmony_ci char __user *buf, size_t buflen) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci struct gss_upcall_msg *gss_msg = container_of(msg, 50862306a36Sopenharmony_ci struct gss_upcall_msg, 50962306a36Sopenharmony_ci msg); 51062306a36Sopenharmony_ci int err; 51162306a36Sopenharmony_ci if (msg->copied == 0) { 51262306a36Sopenharmony_ci err = gss_encode_v1_msg(gss_msg, 51362306a36Sopenharmony_ci gss_msg->service_name, 51462306a36Sopenharmony_ci gss_msg->auth->target_name, 51562306a36Sopenharmony_ci file->f_cred); 51662306a36Sopenharmony_ci if (err) 51762306a36Sopenharmony_ci return err; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci return rpc_pipe_generic_upcall(file, msg, buf, buflen); 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic struct gss_upcall_msg * 52362306a36Sopenharmony_cigss_alloc_msg(struct gss_auth *gss_auth, 52462306a36Sopenharmony_ci kuid_t uid, const char *service_name) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci struct gss_upcall_msg *gss_msg; 52762306a36Sopenharmony_ci int vers; 52862306a36Sopenharmony_ci int err = -ENOMEM; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci gss_msg = kzalloc(sizeof(*gss_msg), GFP_KERNEL); 53162306a36Sopenharmony_ci if (gss_msg == NULL) 53262306a36Sopenharmony_ci goto err; 53362306a36Sopenharmony_ci vers = get_pipe_version(gss_auth->net); 53462306a36Sopenharmony_ci err = vers; 53562306a36Sopenharmony_ci if (err < 0) 53662306a36Sopenharmony_ci goto err_free_msg; 53762306a36Sopenharmony_ci gss_msg->pipe = gss_auth->gss_pipe[vers]->pipe; 53862306a36Sopenharmony_ci INIT_LIST_HEAD(&gss_msg->list); 53962306a36Sopenharmony_ci rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq"); 54062306a36Sopenharmony_ci init_waitqueue_head(&gss_msg->waitqueue); 54162306a36Sopenharmony_ci refcount_set(&gss_msg->count, 1); 54262306a36Sopenharmony_ci gss_msg->uid = uid; 54362306a36Sopenharmony_ci gss_msg->auth = gss_auth; 54462306a36Sopenharmony_ci kref_get(&gss_auth->kref); 54562306a36Sopenharmony_ci if (service_name) { 54662306a36Sopenharmony_ci gss_msg->service_name = kstrdup_const(service_name, GFP_KERNEL); 54762306a36Sopenharmony_ci if (!gss_msg->service_name) { 54862306a36Sopenharmony_ci err = -ENOMEM; 54962306a36Sopenharmony_ci goto err_put_pipe_version; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci return gss_msg; 55362306a36Sopenharmony_cierr_put_pipe_version: 55462306a36Sopenharmony_ci put_pipe_version(gss_auth->net); 55562306a36Sopenharmony_cierr_free_msg: 55662306a36Sopenharmony_ci kfree(gss_msg); 55762306a36Sopenharmony_cierr: 55862306a36Sopenharmony_ci return ERR_PTR(err); 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic struct gss_upcall_msg * 56262306a36Sopenharmony_cigss_setup_upcall(struct gss_auth *gss_auth, struct rpc_cred *cred) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, 56562306a36Sopenharmony_ci struct gss_cred, gc_base); 56662306a36Sopenharmony_ci struct gss_upcall_msg *gss_new, *gss_msg; 56762306a36Sopenharmony_ci kuid_t uid = cred->cr_cred->fsuid; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci gss_new = gss_alloc_msg(gss_auth, uid, gss_cred->gc_principal); 57062306a36Sopenharmony_ci if (IS_ERR(gss_new)) 57162306a36Sopenharmony_ci return gss_new; 57262306a36Sopenharmony_ci gss_msg = gss_add_msg(gss_new); 57362306a36Sopenharmony_ci if (gss_msg == gss_new) { 57462306a36Sopenharmony_ci int res; 57562306a36Sopenharmony_ci refcount_inc(&gss_msg->count); 57662306a36Sopenharmony_ci res = rpc_queue_upcall(gss_new->pipe, &gss_new->msg); 57762306a36Sopenharmony_ci if (res) { 57862306a36Sopenharmony_ci gss_unhash_msg(gss_new); 57962306a36Sopenharmony_ci refcount_dec(&gss_msg->count); 58062306a36Sopenharmony_ci gss_release_msg(gss_new); 58162306a36Sopenharmony_ci gss_msg = ERR_PTR(res); 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci } else 58462306a36Sopenharmony_ci gss_release_msg(gss_new); 58562306a36Sopenharmony_ci return gss_msg; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic void warn_gssd(void) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci dprintk("AUTH_GSS upcall failed. Please check user daemon is running.\n"); 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic inline int 59462306a36Sopenharmony_cigss_refresh_upcall(struct rpc_task *task) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci struct rpc_cred *cred = task->tk_rqstp->rq_cred; 59762306a36Sopenharmony_ci struct gss_auth *gss_auth = container_of(cred->cr_auth, 59862306a36Sopenharmony_ci struct gss_auth, rpc_auth); 59962306a36Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, 60062306a36Sopenharmony_ci struct gss_cred, gc_base); 60162306a36Sopenharmony_ci struct gss_upcall_msg *gss_msg; 60262306a36Sopenharmony_ci struct rpc_pipe *pipe; 60362306a36Sopenharmony_ci int err = 0; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci gss_msg = gss_setup_upcall(gss_auth, cred); 60662306a36Sopenharmony_ci if (PTR_ERR(gss_msg) == -EAGAIN) { 60762306a36Sopenharmony_ci /* XXX: warning on the first, under the assumption we 60862306a36Sopenharmony_ci * shouldn't normally hit this case on a refresh. */ 60962306a36Sopenharmony_ci warn_gssd(); 61062306a36Sopenharmony_ci rpc_sleep_on_timeout(&pipe_version_rpc_waitqueue, 61162306a36Sopenharmony_ci task, NULL, jiffies + (15 * HZ)); 61262306a36Sopenharmony_ci err = -EAGAIN; 61362306a36Sopenharmony_ci goto out; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci if (IS_ERR(gss_msg)) { 61662306a36Sopenharmony_ci err = PTR_ERR(gss_msg); 61762306a36Sopenharmony_ci goto out; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci pipe = gss_msg->pipe; 62062306a36Sopenharmony_ci spin_lock(&pipe->lock); 62162306a36Sopenharmony_ci if (gss_cred->gc_upcall != NULL) 62262306a36Sopenharmony_ci rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL); 62362306a36Sopenharmony_ci else if (gss_msg->ctx == NULL && gss_msg->msg.errno >= 0) { 62462306a36Sopenharmony_ci gss_cred->gc_upcall = gss_msg; 62562306a36Sopenharmony_ci /* gss_upcall_callback will release the reference to gss_upcall_msg */ 62662306a36Sopenharmony_ci refcount_inc(&gss_msg->count); 62762306a36Sopenharmony_ci rpc_sleep_on(&gss_msg->rpc_waitqueue, task, gss_upcall_callback); 62862306a36Sopenharmony_ci } else { 62962306a36Sopenharmony_ci gss_handle_downcall_result(gss_cred, gss_msg); 63062306a36Sopenharmony_ci err = gss_msg->msg.errno; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci spin_unlock(&pipe->lock); 63362306a36Sopenharmony_ci gss_release_msg(gss_msg); 63462306a36Sopenharmony_ciout: 63562306a36Sopenharmony_ci trace_rpcgss_upcall_result(from_kuid(&init_user_ns, 63662306a36Sopenharmony_ci cred->cr_cred->fsuid), err); 63762306a36Sopenharmony_ci return err; 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic inline int 64162306a36Sopenharmony_cigss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci struct net *net = gss_auth->net; 64462306a36Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 64562306a36Sopenharmony_ci struct rpc_pipe *pipe; 64662306a36Sopenharmony_ci struct rpc_cred *cred = &gss_cred->gc_base; 64762306a36Sopenharmony_ci struct gss_upcall_msg *gss_msg; 64862306a36Sopenharmony_ci DEFINE_WAIT(wait); 64962306a36Sopenharmony_ci int err; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ciretry: 65262306a36Sopenharmony_ci err = 0; 65362306a36Sopenharmony_ci /* if gssd is down, just skip upcalling altogether */ 65462306a36Sopenharmony_ci if (!gssd_running(net)) { 65562306a36Sopenharmony_ci warn_gssd(); 65662306a36Sopenharmony_ci err = -EACCES; 65762306a36Sopenharmony_ci goto out; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci gss_msg = gss_setup_upcall(gss_auth, cred); 66062306a36Sopenharmony_ci if (PTR_ERR(gss_msg) == -EAGAIN) { 66162306a36Sopenharmony_ci err = wait_event_interruptible_timeout(pipe_version_waitqueue, 66262306a36Sopenharmony_ci sn->pipe_version >= 0, 15 * HZ); 66362306a36Sopenharmony_ci if (sn->pipe_version < 0) { 66462306a36Sopenharmony_ci warn_gssd(); 66562306a36Sopenharmony_ci err = -EACCES; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci if (err < 0) 66862306a36Sopenharmony_ci goto out; 66962306a36Sopenharmony_ci goto retry; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci if (IS_ERR(gss_msg)) { 67262306a36Sopenharmony_ci err = PTR_ERR(gss_msg); 67362306a36Sopenharmony_ci goto out; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci pipe = gss_msg->pipe; 67662306a36Sopenharmony_ci for (;;) { 67762306a36Sopenharmony_ci prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_KILLABLE); 67862306a36Sopenharmony_ci spin_lock(&pipe->lock); 67962306a36Sopenharmony_ci if (gss_msg->ctx != NULL || gss_msg->msg.errno < 0) { 68062306a36Sopenharmony_ci break; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci spin_unlock(&pipe->lock); 68362306a36Sopenharmony_ci if (fatal_signal_pending(current)) { 68462306a36Sopenharmony_ci err = -ERESTARTSYS; 68562306a36Sopenharmony_ci goto out_intr; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci schedule(); 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci if (gss_msg->ctx) { 69062306a36Sopenharmony_ci trace_rpcgss_ctx_init(gss_cred); 69162306a36Sopenharmony_ci gss_cred_set_ctx(cred, gss_msg->ctx); 69262306a36Sopenharmony_ci } else { 69362306a36Sopenharmony_ci err = gss_msg->msg.errno; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci spin_unlock(&pipe->lock); 69662306a36Sopenharmony_ciout_intr: 69762306a36Sopenharmony_ci finish_wait(&gss_msg->waitqueue, &wait); 69862306a36Sopenharmony_ci gss_release_msg(gss_msg); 69962306a36Sopenharmony_ciout: 70062306a36Sopenharmony_ci trace_rpcgss_upcall_result(from_kuid(&init_user_ns, 70162306a36Sopenharmony_ci cred->cr_cred->fsuid), err); 70262306a36Sopenharmony_ci return err; 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic struct gss_upcall_msg * 70662306a36Sopenharmony_cigss_find_downcall(struct rpc_pipe *pipe, kuid_t uid) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct gss_upcall_msg *pos; 70962306a36Sopenharmony_ci list_for_each_entry(pos, &pipe->in_downcall, list) { 71062306a36Sopenharmony_ci if (!uid_eq(pos->uid, uid)) 71162306a36Sopenharmony_ci continue; 71262306a36Sopenharmony_ci if (!rpc_msg_is_inflight(&pos->msg)) 71362306a36Sopenharmony_ci continue; 71462306a36Sopenharmony_ci refcount_inc(&pos->count); 71562306a36Sopenharmony_ci return pos; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci return NULL; 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci#define MSG_BUF_MAXSIZE 1024 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_cistatic ssize_t 72362306a36Sopenharmony_cigss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci const void *p, *end; 72662306a36Sopenharmony_ci void *buf; 72762306a36Sopenharmony_ci struct gss_upcall_msg *gss_msg; 72862306a36Sopenharmony_ci struct rpc_pipe *pipe = RPC_I(file_inode(filp))->pipe; 72962306a36Sopenharmony_ci struct gss_cl_ctx *ctx; 73062306a36Sopenharmony_ci uid_t id; 73162306a36Sopenharmony_ci kuid_t uid; 73262306a36Sopenharmony_ci ssize_t err = -EFBIG; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (mlen > MSG_BUF_MAXSIZE) 73562306a36Sopenharmony_ci goto out; 73662306a36Sopenharmony_ci err = -ENOMEM; 73762306a36Sopenharmony_ci buf = kmalloc(mlen, GFP_KERNEL); 73862306a36Sopenharmony_ci if (!buf) 73962306a36Sopenharmony_ci goto out; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci err = -EFAULT; 74262306a36Sopenharmony_ci if (copy_from_user(buf, src, mlen)) 74362306a36Sopenharmony_ci goto err; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci end = (const void *)((char *)buf + mlen); 74662306a36Sopenharmony_ci p = simple_get_bytes(buf, end, &id, sizeof(id)); 74762306a36Sopenharmony_ci if (IS_ERR(p)) { 74862306a36Sopenharmony_ci err = PTR_ERR(p); 74962306a36Sopenharmony_ci goto err; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci uid = make_kuid(current_user_ns(), id); 75362306a36Sopenharmony_ci if (!uid_valid(uid)) { 75462306a36Sopenharmony_ci err = -EINVAL; 75562306a36Sopenharmony_ci goto err; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci err = -ENOMEM; 75962306a36Sopenharmony_ci ctx = gss_alloc_context(); 76062306a36Sopenharmony_ci if (ctx == NULL) 76162306a36Sopenharmony_ci goto err; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci err = -ENOENT; 76462306a36Sopenharmony_ci /* Find a matching upcall */ 76562306a36Sopenharmony_ci spin_lock(&pipe->lock); 76662306a36Sopenharmony_ci gss_msg = gss_find_downcall(pipe, uid); 76762306a36Sopenharmony_ci if (gss_msg == NULL) { 76862306a36Sopenharmony_ci spin_unlock(&pipe->lock); 76962306a36Sopenharmony_ci goto err_put_ctx; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci list_del_init(&gss_msg->list); 77262306a36Sopenharmony_ci spin_unlock(&pipe->lock); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci p = gss_fill_context(p, end, ctx, gss_msg->auth->mech); 77562306a36Sopenharmony_ci if (IS_ERR(p)) { 77662306a36Sopenharmony_ci err = PTR_ERR(p); 77762306a36Sopenharmony_ci switch (err) { 77862306a36Sopenharmony_ci case -EACCES: 77962306a36Sopenharmony_ci case -EKEYEXPIRED: 78062306a36Sopenharmony_ci gss_msg->msg.errno = err; 78162306a36Sopenharmony_ci err = mlen; 78262306a36Sopenharmony_ci break; 78362306a36Sopenharmony_ci case -EFAULT: 78462306a36Sopenharmony_ci case -ENOMEM: 78562306a36Sopenharmony_ci case -EINVAL: 78662306a36Sopenharmony_ci case -ENOSYS: 78762306a36Sopenharmony_ci gss_msg->msg.errno = -EAGAIN; 78862306a36Sopenharmony_ci break; 78962306a36Sopenharmony_ci default: 79062306a36Sopenharmony_ci printk(KERN_CRIT "%s: bad return from " 79162306a36Sopenharmony_ci "gss_fill_context: %zd\n", __func__, err); 79262306a36Sopenharmony_ci gss_msg->msg.errno = -EIO; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci goto err_release_msg; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci gss_msg->ctx = gss_get_ctx(ctx); 79762306a36Sopenharmony_ci err = mlen; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cierr_release_msg: 80062306a36Sopenharmony_ci spin_lock(&pipe->lock); 80162306a36Sopenharmony_ci __gss_unhash_msg(gss_msg); 80262306a36Sopenharmony_ci spin_unlock(&pipe->lock); 80362306a36Sopenharmony_ci gss_release_msg(gss_msg); 80462306a36Sopenharmony_cierr_put_ctx: 80562306a36Sopenharmony_ci gss_put_ctx(ctx); 80662306a36Sopenharmony_cierr: 80762306a36Sopenharmony_ci kfree(buf); 80862306a36Sopenharmony_ciout: 80962306a36Sopenharmony_ci return err; 81062306a36Sopenharmony_ci} 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic int gss_pipe_open(struct inode *inode, int new_version) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci struct net *net = inode->i_sb->s_fs_info; 81562306a36Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 81662306a36Sopenharmony_ci int ret = 0; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci spin_lock(&pipe_version_lock); 81962306a36Sopenharmony_ci if (sn->pipe_version < 0) { 82062306a36Sopenharmony_ci /* First open of any gss pipe determines the version: */ 82162306a36Sopenharmony_ci sn->pipe_version = new_version; 82262306a36Sopenharmony_ci rpc_wake_up(&pipe_version_rpc_waitqueue); 82362306a36Sopenharmony_ci wake_up(&pipe_version_waitqueue); 82462306a36Sopenharmony_ci } else if (sn->pipe_version != new_version) { 82562306a36Sopenharmony_ci /* Trying to open a pipe of a different version */ 82662306a36Sopenharmony_ci ret = -EBUSY; 82762306a36Sopenharmony_ci goto out; 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci atomic_inc(&sn->pipe_users); 83062306a36Sopenharmony_ciout: 83162306a36Sopenharmony_ci spin_unlock(&pipe_version_lock); 83262306a36Sopenharmony_ci return ret; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic int gss_pipe_open_v0(struct inode *inode) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci return gss_pipe_open(inode, 0); 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cistatic int gss_pipe_open_v1(struct inode *inode) 84262306a36Sopenharmony_ci{ 84362306a36Sopenharmony_ci return gss_pipe_open(inode, 1); 84462306a36Sopenharmony_ci} 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_cistatic void 84762306a36Sopenharmony_cigss_pipe_release(struct inode *inode) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci struct net *net = inode->i_sb->s_fs_info; 85062306a36Sopenharmony_ci struct rpc_pipe *pipe = RPC_I(inode)->pipe; 85162306a36Sopenharmony_ci struct gss_upcall_msg *gss_msg; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_cirestart: 85462306a36Sopenharmony_ci spin_lock(&pipe->lock); 85562306a36Sopenharmony_ci list_for_each_entry(gss_msg, &pipe->in_downcall, list) { 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci if (!list_empty(&gss_msg->msg.list)) 85862306a36Sopenharmony_ci continue; 85962306a36Sopenharmony_ci gss_msg->msg.errno = -EPIPE; 86062306a36Sopenharmony_ci refcount_inc(&gss_msg->count); 86162306a36Sopenharmony_ci __gss_unhash_msg(gss_msg); 86262306a36Sopenharmony_ci spin_unlock(&pipe->lock); 86362306a36Sopenharmony_ci gss_release_msg(gss_msg); 86462306a36Sopenharmony_ci goto restart; 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci spin_unlock(&pipe->lock); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci put_pipe_version(net); 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic void 87262306a36Sopenharmony_cigss_pipe_destroy_msg(struct rpc_pipe_msg *msg) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci struct gss_upcall_msg *gss_msg = container_of(msg, struct gss_upcall_msg, msg); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (msg->errno < 0) { 87762306a36Sopenharmony_ci refcount_inc(&gss_msg->count); 87862306a36Sopenharmony_ci gss_unhash_msg(gss_msg); 87962306a36Sopenharmony_ci if (msg->errno == -ETIMEDOUT) 88062306a36Sopenharmony_ci warn_gssd(); 88162306a36Sopenharmony_ci gss_release_msg(gss_msg); 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci gss_release_msg(gss_msg); 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_cistatic void gss_pipe_dentry_destroy(struct dentry *dir, 88762306a36Sopenharmony_ci struct rpc_pipe_dir_object *pdo) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci struct gss_pipe *gss_pipe = pdo->pdo_data; 89062306a36Sopenharmony_ci struct rpc_pipe *pipe = gss_pipe->pipe; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci if (pipe->dentry != NULL) { 89362306a36Sopenharmony_ci rpc_unlink(pipe->dentry); 89462306a36Sopenharmony_ci pipe->dentry = NULL; 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cistatic int gss_pipe_dentry_create(struct dentry *dir, 89962306a36Sopenharmony_ci struct rpc_pipe_dir_object *pdo) 90062306a36Sopenharmony_ci{ 90162306a36Sopenharmony_ci struct gss_pipe *p = pdo->pdo_data; 90262306a36Sopenharmony_ci struct dentry *dentry; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci dentry = rpc_mkpipe_dentry(dir, p->name, p->clnt, p->pipe); 90562306a36Sopenharmony_ci if (IS_ERR(dentry)) 90662306a36Sopenharmony_ci return PTR_ERR(dentry); 90762306a36Sopenharmony_ci p->pipe->dentry = dentry; 90862306a36Sopenharmony_ci return 0; 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic const struct rpc_pipe_dir_object_ops gss_pipe_dir_object_ops = { 91262306a36Sopenharmony_ci .create = gss_pipe_dentry_create, 91362306a36Sopenharmony_ci .destroy = gss_pipe_dentry_destroy, 91462306a36Sopenharmony_ci}; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic struct gss_pipe *gss_pipe_alloc(struct rpc_clnt *clnt, 91762306a36Sopenharmony_ci const char *name, 91862306a36Sopenharmony_ci const struct rpc_pipe_ops *upcall_ops) 91962306a36Sopenharmony_ci{ 92062306a36Sopenharmony_ci struct gss_pipe *p; 92162306a36Sopenharmony_ci int err = -ENOMEM; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci p = kmalloc(sizeof(*p), GFP_KERNEL); 92462306a36Sopenharmony_ci if (p == NULL) 92562306a36Sopenharmony_ci goto err; 92662306a36Sopenharmony_ci p->pipe = rpc_mkpipe_data(upcall_ops, RPC_PIPE_WAIT_FOR_OPEN); 92762306a36Sopenharmony_ci if (IS_ERR(p->pipe)) { 92862306a36Sopenharmony_ci err = PTR_ERR(p->pipe); 92962306a36Sopenharmony_ci goto err_free_gss_pipe; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci p->name = name; 93262306a36Sopenharmony_ci p->clnt = clnt; 93362306a36Sopenharmony_ci kref_init(&p->kref); 93462306a36Sopenharmony_ci rpc_init_pipe_dir_object(&p->pdo, 93562306a36Sopenharmony_ci &gss_pipe_dir_object_ops, 93662306a36Sopenharmony_ci p); 93762306a36Sopenharmony_ci return p; 93862306a36Sopenharmony_cierr_free_gss_pipe: 93962306a36Sopenharmony_ci kfree(p); 94062306a36Sopenharmony_cierr: 94162306a36Sopenharmony_ci return ERR_PTR(err); 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_cistruct gss_alloc_pdo { 94562306a36Sopenharmony_ci struct rpc_clnt *clnt; 94662306a36Sopenharmony_ci const char *name; 94762306a36Sopenharmony_ci const struct rpc_pipe_ops *upcall_ops; 94862306a36Sopenharmony_ci}; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_cistatic int gss_pipe_match_pdo(struct rpc_pipe_dir_object *pdo, void *data) 95162306a36Sopenharmony_ci{ 95262306a36Sopenharmony_ci struct gss_pipe *gss_pipe; 95362306a36Sopenharmony_ci struct gss_alloc_pdo *args = data; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci if (pdo->pdo_ops != &gss_pipe_dir_object_ops) 95662306a36Sopenharmony_ci return 0; 95762306a36Sopenharmony_ci gss_pipe = container_of(pdo, struct gss_pipe, pdo); 95862306a36Sopenharmony_ci if (strcmp(gss_pipe->name, args->name) != 0) 95962306a36Sopenharmony_ci return 0; 96062306a36Sopenharmony_ci if (!kref_get_unless_zero(&gss_pipe->kref)) 96162306a36Sopenharmony_ci return 0; 96262306a36Sopenharmony_ci return 1; 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cistatic struct rpc_pipe_dir_object *gss_pipe_alloc_pdo(void *data) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci struct gss_pipe *gss_pipe; 96862306a36Sopenharmony_ci struct gss_alloc_pdo *args = data; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci gss_pipe = gss_pipe_alloc(args->clnt, args->name, args->upcall_ops); 97162306a36Sopenharmony_ci if (!IS_ERR(gss_pipe)) 97262306a36Sopenharmony_ci return &gss_pipe->pdo; 97362306a36Sopenharmony_ci return NULL; 97462306a36Sopenharmony_ci} 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_cistatic struct gss_pipe *gss_pipe_get(struct rpc_clnt *clnt, 97762306a36Sopenharmony_ci const char *name, 97862306a36Sopenharmony_ci const struct rpc_pipe_ops *upcall_ops) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci struct net *net = rpc_net_ns(clnt); 98162306a36Sopenharmony_ci struct rpc_pipe_dir_object *pdo; 98262306a36Sopenharmony_ci struct gss_alloc_pdo args = { 98362306a36Sopenharmony_ci .clnt = clnt, 98462306a36Sopenharmony_ci .name = name, 98562306a36Sopenharmony_ci .upcall_ops = upcall_ops, 98662306a36Sopenharmony_ci }; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci pdo = rpc_find_or_alloc_pipe_dir_object(net, 98962306a36Sopenharmony_ci &clnt->cl_pipedir_objects, 99062306a36Sopenharmony_ci gss_pipe_match_pdo, 99162306a36Sopenharmony_ci gss_pipe_alloc_pdo, 99262306a36Sopenharmony_ci &args); 99362306a36Sopenharmony_ci if (pdo != NULL) 99462306a36Sopenharmony_ci return container_of(pdo, struct gss_pipe, pdo); 99562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cistatic void __gss_pipe_free(struct gss_pipe *p) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci struct rpc_clnt *clnt = p->clnt; 100162306a36Sopenharmony_ci struct net *net = rpc_net_ns(clnt); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci rpc_remove_pipe_dir_object(net, 100462306a36Sopenharmony_ci &clnt->cl_pipedir_objects, 100562306a36Sopenharmony_ci &p->pdo); 100662306a36Sopenharmony_ci rpc_destroy_pipe_data(p->pipe); 100762306a36Sopenharmony_ci kfree(p); 100862306a36Sopenharmony_ci} 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_cistatic void __gss_pipe_release(struct kref *kref) 101162306a36Sopenharmony_ci{ 101262306a36Sopenharmony_ci struct gss_pipe *p = container_of(kref, struct gss_pipe, kref); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci __gss_pipe_free(p); 101562306a36Sopenharmony_ci} 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_cistatic void gss_pipe_free(struct gss_pipe *p) 101862306a36Sopenharmony_ci{ 101962306a36Sopenharmony_ci if (p != NULL) 102062306a36Sopenharmony_ci kref_put(&p->kref, __gss_pipe_release); 102162306a36Sopenharmony_ci} 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci/* 102462306a36Sopenharmony_ci * NOTE: we have the opportunity to use different 102562306a36Sopenharmony_ci * parameters based on the input flavor (which must be a pseudoflavor) 102662306a36Sopenharmony_ci */ 102762306a36Sopenharmony_cistatic struct gss_auth * 102862306a36Sopenharmony_cigss_create_new(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) 102962306a36Sopenharmony_ci{ 103062306a36Sopenharmony_ci rpc_authflavor_t flavor = args->pseudoflavor; 103162306a36Sopenharmony_ci struct gss_auth *gss_auth; 103262306a36Sopenharmony_ci struct gss_pipe *gss_pipe; 103362306a36Sopenharmony_ci struct rpc_auth * auth; 103462306a36Sopenharmony_ci int err = -ENOMEM; /* XXX? */ 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci if (!try_module_get(THIS_MODULE)) 103762306a36Sopenharmony_ci return ERR_PTR(err); 103862306a36Sopenharmony_ci if (!(gss_auth = kmalloc(sizeof(*gss_auth), GFP_KERNEL))) 103962306a36Sopenharmony_ci goto out_dec; 104062306a36Sopenharmony_ci INIT_HLIST_NODE(&gss_auth->hash); 104162306a36Sopenharmony_ci gss_auth->target_name = NULL; 104262306a36Sopenharmony_ci if (args->target_name) { 104362306a36Sopenharmony_ci gss_auth->target_name = kstrdup(args->target_name, GFP_KERNEL); 104462306a36Sopenharmony_ci if (gss_auth->target_name == NULL) 104562306a36Sopenharmony_ci goto err_free; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci gss_auth->client = clnt; 104862306a36Sopenharmony_ci gss_auth->net = get_net_track(rpc_net_ns(clnt), &gss_auth->ns_tracker, 104962306a36Sopenharmony_ci GFP_KERNEL); 105062306a36Sopenharmony_ci err = -EINVAL; 105162306a36Sopenharmony_ci gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor); 105262306a36Sopenharmony_ci if (!gss_auth->mech) 105362306a36Sopenharmony_ci goto err_put_net; 105462306a36Sopenharmony_ci gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor); 105562306a36Sopenharmony_ci if (gss_auth->service == 0) 105662306a36Sopenharmony_ci goto err_put_mech; 105762306a36Sopenharmony_ci if (!gssd_running(gss_auth->net)) 105862306a36Sopenharmony_ci goto err_put_mech; 105962306a36Sopenharmony_ci auth = &gss_auth->rpc_auth; 106062306a36Sopenharmony_ci auth->au_cslack = GSS_CRED_SLACK >> 2; 106162306a36Sopenharmony_ci BUILD_BUG_ON(GSS_KRB5_MAX_SLACK_NEEDED > RPC_MAX_AUTH_SIZE); 106262306a36Sopenharmony_ci auth->au_rslack = GSS_KRB5_MAX_SLACK_NEEDED >> 2; 106362306a36Sopenharmony_ci auth->au_verfsize = GSS_VERF_SLACK >> 2; 106462306a36Sopenharmony_ci auth->au_ralign = GSS_VERF_SLACK >> 2; 106562306a36Sopenharmony_ci __set_bit(RPCAUTH_AUTH_UPDATE_SLACK, &auth->au_flags); 106662306a36Sopenharmony_ci auth->au_ops = &authgss_ops; 106762306a36Sopenharmony_ci auth->au_flavor = flavor; 106862306a36Sopenharmony_ci if (gss_pseudoflavor_to_datatouch(gss_auth->mech, flavor)) 106962306a36Sopenharmony_ci __set_bit(RPCAUTH_AUTH_DATATOUCH, &auth->au_flags); 107062306a36Sopenharmony_ci refcount_set(&auth->au_count, 1); 107162306a36Sopenharmony_ci kref_init(&gss_auth->kref); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci err = rpcauth_init_credcache(auth); 107462306a36Sopenharmony_ci if (err) 107562306a36Sopenharmony_ci goto err_put_mech; 107662306a36Sopenharmony_ci /* 107762306a36Sopenharmony_ci * Note: if we created the old pipe first, then someone who 107862306a36Sopenharmony_ci * examined the directory at the right moment might conclude 107962306a36Sopenharmony_ci * that we supported only the old pipe. So we instead create 108062306a36Sopenharmony_ci * the new pipe first. 108162306a36Sopenharmony_ci */ 108262306a36Sopenharmony_ci gss_pipe = gss_pipe_get(clnt, "gssd", &gss_upcall_ops_v1); 108362306a36Sopenharmony_ci if (IS_ERR(gss_pipe)) { 108462306a36Sopenharmony_ci err = PTR_ERR(gss_pipe); 108562306a36Sopenharmony_ci goto err_destroy_credcache; 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci gss_auth->gss_pipe[1] = gss_pipe; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci gss_pipe = gss_pipe_get(clnt, gss_auth->mech->gm_name, 109062306a36Sopenharmony_ci &gss_upcall_ops_v0); 109162306a36Sopenharmony_ci if (IS_ERR(gss_pipe)) { 109262306a36Sopenharmony_ci err = PTR_ERR(gss_pipe); 109362306a36Sopenharmony_ci goto err_destroy_pipe_1; 109462306a36Sopenharmony_ci } 109562306a36Sopenharmony_ci gss_auth->gss_pipe[0] = gss_pipe; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci return gss_auth; 109862306a36Sopenharmony_cierr_destroy_pipe_1: 109962306a36Sopenharmony_ci gss_pipe_free(gss_auth->gss_pipe[1]); 110062306a36Sopenharmony_cierr_destroy_credcache: 110162306a36Sopenharmony_ci rpcauth_destroy_credcache(auth); 110262306a36Sopenharmony_cierr_put_mech: 110362306a36Sopenharmony_ci gss_mech_put(gss_auth->mech); 110462306a36Sopenharmony_cierr_put_net: 110562306a36Sopenharmony_ci put_net_track(gss_auth->net, &gss_auth->ns_tracker); 110662306a36Sopenharmony_cierr_free: 110762306a36Sopenharmony_ci kfree(gss_auth->target_name); 110862306a36Sopenharmony_ci kfree(gss_auth); 110962306a36Sopenharmony_ciout_dec: 111062306a36Sopenharmony_ci module_put(THIS_MODULE); 111162306a36Sopenharmony_ci trace_rpcgss_createauth(flavor, err); 111262306a36Sopenharmony_ci return ERR_PTR(err); 111362306a36Sopenharmony_ci} 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_cistatic void 111662306a36Sopenharmony_cigss_free(struct gss_auth *gss_auth) 111762306a36Sopenharmony_ci{ 111862306a36Sopenharmony_ci gss_pipe_free(gss_auth->gss_pipe[0]); 111962306a36Sopenharmony_ci gss_pipe_free(gss_auth->gss_pipe[1]); 112062306a36Sopenharmony_ci gss_mech_put(gss_auth->mech); 112162306a36Sopenharmony_ci put_net_track(gss_auth->net, &gss_auth->ns_tracker); 112262306a36Sopenharmony_ci kfree(gss_auth->target_name); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci kfree(gss_auth); 112562306a36Sopenharmony_ci module_put(THIS_MODULE); 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cistatic void 112962306a36Sopenharmony_cigss_free_callback(struct kref *kref) 113062306a36Sopenharmony_ci{ 113162306a36Sopenharmony_ci struct gss_auth *gss_auth = container_of(kref, struct gss_auth, kref); 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci gss_free(gss_auth); 113462306a36Sopenharmony_ci} 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_cistatic void 113762306a36Sopenharmony_cigss_put_auth(struct gss_auth *gss_auth) 113862306a36Sopenharmony_ci{ 113962306a36Sopenharmony_ci kref_put(&gss_auth->kref, gss_free_callback); 114062306a36Sopenharmony_ci} 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_cistatic void 114362306a36Sopenharmony_cigss_destroy(struct rpc_auth *auth) 114462306a36Sopenharmony_ci{ 114562306a36Sopenharmony_ci struct gss_auth *gss_auth = container_of(auth, 114662306a36Sopenharmony_ci struct gss_auth, rpc_auth); 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci if (hash_hashed(&gss_auth->hash)) { 114962306a36Sopenharmony_ci spin_lock(&gss_auth_hash_lock); 115062306a36Sopenharmony_ci hash_del(&gss_auth->hash); 115162306a36Sopenharmony_ci spin_unlock(&gss_auth_hash_lock); 115262306a36Sopenharmony_ci } 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci gss_pipe_free(gss_auth->gss_pipe[0]); 115562306a36Sopenharmony_ci gss_auth->gss_pipe[0] = NULL; 115662306a36Sopenharmony_ci gss_pipe_free(gss_auth->gss_pipe[1]); 115762306a36Sopenharmony_ci gss_auth->gss_pipe[1] = NULL; 115862306a36Sopenharmony_ci rpcauth_destroy_credcache(auth); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci gss_put_auth(gss_auth); 116162306a36Sopenharmony_ci} 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci/* 116462306a36Sopenharmony_ci * Auths may be shared between rpc clients that were cloned from a 116562306a36Sopenharmony_ci * common client with the same xprt, if they also share the flavor and 116662306a36Sopenharmony_ci * target_name. 116762306a36Sopenharmony_ci * 116862306a36Sopenharmony_ci * The auth is looked up from the oldest parent sharing the same 116962306a36Sopenharmony_ci * cl_xprt, and the auth itself references only that common parent 117062306a36Sopenharmony_ci * (which is guaranteed to last as long as any of its descendants). 117162306a36Sopenharmony_ci */ 117262306a36Sopenharmony_cistatic struct gss_auth * 117362306a36Sopenharmony_cigss_auth_find_or_add_hashed(const struct rpc_auth_create_args *args, 117462306a36Sopenharmony_ci struct rpc_clnt *clnt, 117562306a36Sopenharmony_ci struct gss_auth *new) 117662306a36Sopenharmony_ci{ 117762306a36Sopenharmony_ci struct gss_auth *gss_auth; 117862306a36Sopenharmony_ci unsigned long hashval = (unsigned long)clnt; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci spin_lock(&gss_auth_hash_lock); 118162306a36Sopenharmony_ci hash_for_each_possible(gss_auth_hash_table, 118262306a36Sopenharmony_ci gss_auth, 118362306a36Sopenharmony_ci hash, 118462306a36Sopenharmony_ci hashval) { 118562306a36Sopenharmony_ci if (gss_auth->client != clnt) 118662306a36Sopenharmony_ci continue; 118762306a36Sopenharmony_ci if (gss_auth->rpc_auth.au_flavor != args->pseudoflavor) 118862306a36Sopenharmony_ci continue; 118962306a36Sopenharmony_ci if (gss_auth->target_name != args->target_name) { 119062306a36Sopenharmony_ci if (gss_auth->target_name == NULL) 119162306a36Sopenharmony_ci continue; 119262306a36Sopenharmony_ci if (args->target_name == NULL) 119362306a36Sopenharmony_ci continue; 119462306a36Sopenharmony_ci if (strcmp(gss_auth->target_name, args->target_name)) 119562306a36Sopenharmony_ci continue; 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci if (!refcount_inc_not_zero(&gss_auth->rpc_auth.au_count)) 119862306a36Sopenharmony_ci continue; 119962306a36Sopenharmony_ci goto out; 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci if (new) 120262306a36Sopenharmony_ci hash_add(gss_auth_hash_table, &new->hash, hashval); 120362306a36Sopenharmony_ci gss_auth = new; 120462306a36Sopenharmony_ciout: 120562306a36Sopenharmony_ci spin_unlock(&gss_auth_hash_lock); 120662306a36Sopenharmony_ci return gss_auth; 120762306a36Sopenharmony_ci} 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_cistatic struct gss_auth * 121062306a36Sopenharmony_cigss_create_hashed(const struct rpc_auth_create_args *args, 121162306a36Sopenharmony_ci struct rpc_clnt *clnt) 121262306a36Sopenharmony_ci{ 121362306a36Sopenharmony_ci struct gss_auth *gss_auth; 121462306a36Sopenharmony_ci struct gss_auth *new; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci gss_auth = gss_auth_find_or_add_hashed(args, clnt, NULL); 121762306a36Sopenharmony_ci if (gss_auth != NULL) 121862306a36Sopenharmony_ci goto out; 121962306a36Sopenharmony_ci new = gss_create_new(args, clnt); 122062306a36Sopenharmony_ci if (IS_ERR(new)) 122162306a36Sopenharmony_ci return new; 122262306a36Sopenharmony_ci gss_auth = gss_auth_find_or_add_hashed(args, clnt, new); 122362306a36Sopenharmony_ci if (gss_auth != new) 122462306a36Sopenharmony_ci gss_destroy(&new->rpc_auth); 122562306a36Sopenharmony_ciout: 122662306a36Sopenharmony_ci return gss_auth; 122762306a36Sopenharmony_ci} 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_cistatic struct rpc_auth * 123062306a36Sopenharmony_cigss_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) 123162306a36Sopenharmony_ci{ 123262306a36Sopenharmony_ci struct gss_auth *gss_auth; 123362306a36Sopenharmony_ci struct rpc_xprt_switch *xps = rcu_access_pointer(clnt->cl_xpi.xpi_xpswitch); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci while (clnt != clnt->cl_parent) { 123662306a36Sopenharmony_ci struct rpc_clnt *parent = clnt->cl_parent; 123762306a36Sopenharmony_ci /* Find the original parent for this transport */ 123862306a36Sopenharmony_ci if (rcu_access_pointer(parent->cl_xpi.xpi_xpswitch) != xps) 123962306a36Sopenharmony_ci break; 124062306a36Sopenharmony_ci clnt = parent; 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci gss_auth = gss_create_hashed(args, clnt); 124462306a36Sopenharmony_ci if (IS_ERR(gss_auth)) 124562306a36Sopenharmony_ci return ERR_CAST(gss_auth); 124662306a36Sopenharmony_ci return &gss_auth->rpc_auth; 124762306a36Sopenharmony_ci} 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_cistatic struct gss_cred * 125062306a36Sopenharmony_cigss_dup_cred(struct gss_auth *gss_auth, struct gss_cred *gss_cred) 125162306a36Sopenharmony_ci{ 125262306a36Sopenharmony_ci struct gss_cred *new; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci /* Make a copy of the cred so that we can reference count it */ 125562306a36Sopenharmony_ci new = kzalloc(sizeof(*gss_cred), GFP_KERNEL); 125662306a36Sopenharmony_ci if (new) { 125762306a36Sopenharmony_ci struct auth_cred acred = { 125862306a36Sopenharmony_ci .cred = gss_cred->gc_base.cr_cred, 125962306a36Sopenharmony_ci }; 126062306a36Sopenharmony_ci struct gss_cl_ctx *ctx = 126162306a36Sopenharmony_ci rcu_dereference_protected(gss_cred->gc_ctx, 1); 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci rpcauth_init_cred(&new->gc_base, &acred, 126462306a36Sopenharmony_ci &gss_auth->rpc_auth, 126562306a36Sopenharmony_ci &gss_nullops); 126662306a36Sopenharmony_ci new->gc_base.cr_flags = 1UL << RPCAUTH_CRED_UPTODATE; 126762306a36Sopenharmony_ci new->gc_service = gss_cred->gc_service; 126862306a36Sopenharmony_ci new->gc_principal = gss_cred->gc_principal; 126962306a36Sopenharmony_ci kref_get(&gss_auth->kref); 127062306a36Sopenharmony_ci rcu_assign_pointer(new->gc_ctx, ctx); 127162306a36Sopenharmony_ci gss_get_ctx(ctx); 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci return new; 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci/* 127762306a36Sopenharmony_ci * gss_send_destroy_context will cause the RPCSEC_GSS to send a NULL RPC call 127862306a36Sopenharmony_ci * to the server with the GSS control procedure field set to 127962306a36Sopenharmony_ci * RPC_GSS_PROC_DESTROY. This should normally cause the server to release 128062306a36Sopenharmony_ci * all RPCSEC_GSS state associated with that context. 128162306a36Sopenharmony_ci */ 128262306a36Sopenharmony_cistatic void 128362306a36Sopenharmony_cigss_send_destroy_context(struct rpc_cred *cred) 128462306a36Sopenharmony_ci{ 128562306a36Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 128662306a36Sopenharmony_ci struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth); 128762306a36Sopenharmony_ci struct gss_cl_ctx *ctx = rcu_dereference_protected(gss_cred->gc_ctx, 1); 128862306a36Sopenharmony_ci struct gss_cred *new; 128962306a36Sopenharmony_ci struct rpc_task *task; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci new = gss_dup_cred(gss_auth, gss_cred); 129262306a36Sopenharmony_ci if (new) { 129362306a36Sopenharmony_ci ctx->gc_proc = RPC_GSS_PROC_DESTROY; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci trace_rpcgss_ctx_destroy(gss_cred); 129662306a36Sopenharmony_ci task = rpc_call_null(gss_auth->client, &new->gc_base, 129762306a36Sopenharmony_ci RPC_TASK_ASYNC); 129862306a36Sopenharmony_ci if (!IS_ERR(task)) 129962306a36Sopenharmony_ci rpc_put_task(task); 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci put_rpccred(&new->gc_base); 130262306a36Sopenharmony_ci } 130362306a36Sopenharmony_ci} 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci/* gss_destroy_cred (and gss_free_ctx) are used to clean up after failure 130662306a36Sopenharmony_ci * to create a new cred or context, so they check that things have been 130762306a36Sopenharmony_ci * allocated before freeing them. */ 130862306a36Sopenharmony_cistatic void 130962306a36Sopenharmony_cigss_do_free_ctx(struct gss_cl_ctx *ctx) 131062306a36Sopenharmony_ci{ 131162306a36Sopenharmony_ci gss_delete_sec_context(&ctx->gc_gss_ctx); 131262306a36Sopenharmony_ci kfree(ctx->gc_wire_ctx.data); 131362306a36Sopenharmony_ci kfree(ctx->gc_acceptor.data); 131462306a36Sopenharmony_ci kfree(ctx); 131562306a36Sopenharmony_ci} 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_cistatic void 131862306a36Sopenharmony_cigss_free_ctx_callback(struct rcu_head *head) 131962306a36Sopenharmony_ci{ 132062306a36Sopenharmony_ci struct gss_cl_ctx *ctx = container_of(head, struct gss_cl_ctx, gc_rcu); 132162306a36Sopenharmony_ci gss_do_free_ctx(ctx); 132262306a36Sopenharmony_ci} 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_cistatic void 132562306a36Sopenharmony_cigss_free_ctx(struct gss_cl_ctx *ctx) 132662306a36Sopenharmony_ci{ 132762306a36Sopenharmony_ci call_rcu(&ctx->gc_rcu, gss_free_ctx_callback); 132862306a36Sopenharmony_ci} 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_cistatic void 133162306a36Sopenharmony_cigss_free_cred(struct gss_cred *gss_cred) 133262306a36Sopenharmony_ci{ 133362306a36Sopenharmony_ci kfree(gss_cred); 133462306a36Sopenharmony_ci} 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_cistatic void 133762306a36Sopenharmony_cigss_free_cred_callback(struct rcu_head *head) 133862306a36Sopenharmony_ci{ 133962306a36Sopenharmony_ci struct gss_cred *gss_cred = container_of(head, struct gss_cred, gc_base.cr_rcu); 134062306a36Sopenharmony_ci gss_free_cred(gss_cred); 134162306a36Sopenharmony_ci} 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_cistatic void 134462306a36Sopenharmony_cigss_destroy_nullcred(struct rpc_cred *cred) 134562306a36Sopenharmony_ci{ 134662306a36Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 134762306a36Sopenharmony_ci struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth); 134862306a36Sopenharmony_ci struct gss_cl_ctx *ctx = rcu_dereference_protected(gss_cred->gc_ctx, 1); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci RCU_INIT_POINTER(gss_cred->gc_ctx, NULL); 135162306a36Sopenharmony_ci put_cred(cred->cr_cred); 135262306a36Sopenharmony_ci call_rcu(&cred->cr_rcu, gss_free_cred_callback); 135362306a36Sopenharmony_ci if (ctx) 135462306a36Sopenharmony_ci gss_put_ctx(ctx); 135562306a36Sopenharmony_ci gss_put_auth(gss_auth); 135662306a36Sopenharmony_ci} 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_cistatic void 135962306a36Sopenharmony_cigss_destroy_cred(struct rpc_cred *cred) 136062306a36Sopenharmony_ci{ 136162306a36Sopenharmony_ci if (test_and_clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0) 136262306a36Sopenharmony_ci gss_send_destroy_context(cred); 136362306a36Sopenharmony_ci gss_destroy_nullcred(cred); 136462306a36Sopenharmony_ci} 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_cistatic int 136762306a36Sopenharmony_cigss_hash_cred(struct auth_cred *acred, unsigned int hashbits) 136862306a36Sopenharmony_ci{ 136962306a36Sopenharmony_ci return hash_64(from_kuid(&init_user_ns, acred->cred->fsuid), hashbits); 137062306a36Sopenharmony_ci} 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci/* 137362306a36Sopenharmony_ci * Lookup RPCSEC_GSS cred for the current process 137462306a36Sopenharmony_ci */ 137562306a36Sopenharmony_cistatic struct rpc_cred *gss_lookup_cred(struct rpc_auth *auth, 137662306a36Sopenharmony_ci struct auth_cred *acred, int flags) 137762306a36Sopenharmony_ci{ 137862306a36Sopenharmony_ci return rpcauth_lookup_credcache(auth, acred, flags, 137962306a36Sopenharmony_ci rpc_task_gfp_mask()); 138062306a36Sopenharmony_ci} 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_cistatic struct rpc_cred * 138362306a36Sopenharmony_cigss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags, gfp_t gfp) 138462306a36Sopenharmony_ci{ 138562306a36Sopenharmony_ci struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth); 138662306a36Sopenharmony_ci struct gss_cred *cred = NULL; 138762306a36Sopenharmony_ci int err = -ENOMEM; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci if (!(cred = kzalloc(sizeof(*cred), gfp))) 139062306a36Sopenharmony_ci goto out_err; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci rpcauth_init_cred(&cred->gc_base, acred, auth, &gss_credops); 139362306a36Sopenharmony_ci /* 139462306a36Sopenharmony_ci * Note: in order to force a call to call_refresh(), we deliberately 139562306a36Sopenharmony_ci * fail to flag the credential as RPCAUTH_CRED_UPTODATE. 139662306a36Sopenharmony_ci */ 139762306a36Sopenharmony_ci cred->gc_base.cr_flags = 1UL << RPCAUTH_CRED_NEW; 139862306a36Sopenharmony_ci cred->gc_service = gss_auth->service; 139962306a36Sopenharmony_ci cred->gc_principal = acred->principal; 140062306a36Sopenharmony_ci kref_get(&gss_auth->kref); 140162306a36Sopenharmony_ci return &cred->gc_base; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ciout_err: 140462306a36Sopenharmony_ci return ERR_PTR(err); 140562306a36Sopenharmony_ci} 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_cistatic int 140862306a36Sopenharmony_cigss_cred_init(struct rpc_auth *auth, struct rpc_cred *cred) 140962306a36Sopenharmony_ci{ 141062306a36Sopenharmony_ci struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth); 141162306a36Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred,struct gss_cred, gc_base); 141262306a36Sopenharmony_ci int err; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci do { 141562306a36Sopenharmony_ci err = gss_create_upcall(gss_auth, gss_cred); 141662306a36Sopenharmony_ci } while (err == -EAGAIN); 141762306a36Sopenharmony_ci return err; 141862306a36Sopenharmony_ci} 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_cistatic char * 142162306a36Sopenharmony_cigss_stringify_acceptor(struct rpc_cred *cred) 142262306a36Sopenharmony_ci{ 142362306a36Sopenharmony_ci char *string = NULL; 142462306a36Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 142562306a36Sopenharmony_ci struct gss_cl_ctx *ctx; 142662306a36Sopenharmony_ci unsigned int len; 142762306a36Sopenharmony_ci struct xdr_netobj *acceptor; 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci rcu_read_lock(); 143062306a36Sopenharmony_ci ctx = rcu_dereference(gss_cred->gc_ctx); 143162306a36Sopenharmony_ci if (!ctx) 143262306a36Sopenharmony_ci goto out; 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci len = ctx->gc_acceptor.len; 143562306a36Sopenharmony_ci rcu_read_unlock(); 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci /* no point if there's no string */ 143862306a36Sopenharmony_ci if (!len) 143962306a36Sopenharmony_ci return NULL; 144062306a36Sopenharmony_cirealloc: 144162306a36Sopenharmony_ci string = kmalloc(len + 1, GFP_KERNEL); 144262306a36Sopenharmony_ci if (!string) 144362306a36Sopenharmony_ci return NULL; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci rcu_read_lock(); 144662306a36Sopenharmony_ci ctx = rcu_dereference(gss_cred->gc_ctx); 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci /* did the ctx disappear or was it replaced by one with no acceptor? */ 144962306a36Sopenharmony_ci if (!ctx || !ctx->gc_acceptor.len) { 145062306a36Sopenharmony_ci kfree(string); 145162306a36Sopenharmony_ci string = NULL; 145262306a36Sopenharmony_ci goto out; 145362306a36Sopenharmony_ci } 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci acceptor = &ctx->gc_acceptor; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci /* 145862306a36Sopenharmony_ci * Did we find a new acceptor that's longer than the original? Allocate 145962306a36Sopenharmony_ci * a longer buffer and try again. 146062306a36Sopenharmony_ci */ 146162306a36Sopenharmony_ci if (len < acceptor->len) { 146262306a36Sopenharmony_ci len = acceptor->len; 146362306a36Sopenharmony_ci rcu_read_unlock(); 146462306a36Sopenharmony_ci kfree(string); 146562306a36Sopenharmony_ci goto realloc; 146662306a36Sopenharmony_ci } 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci memcpy(string, acceptor->data, acceptor->len); 146962306a36Sopenharmony_ci string[acceptor->len] = '\0'; 147062306a36Sopenharmony_ciout: 147162306a36Sopenharmony_ci rcu_read_unlock(); 147262306a36Sopenharmony_ci return string; 147362306a36Sopenharmony_ci} 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci/* 147662306a36Sopenharmony_ci * Returns -EACCES if GSS context is NULL or will expire within the 147762306a36Sopenharmony_ci * timeout (miliseconds) 147862306a36Sopenharmony_ci */ 147962306a36Sopenharmony_cistatic int 148062306a36Sopenharmony_cigss_key_timeout(struct rpc_cred *rc) 148162306a36Sopenharmony_ci{ 148262306a36Sopenharmony_ci struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); 148362306a36Sopenharmony_ci struct gss_cl_ctx *ctx; 148462306a36Sopenharmony_ci unsigned long timeout = jiffies + (gss_key_expire_timeo * HZ); 148562306a36Sopenharmony_ci int ret = 0; 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci rcu_read_lock(); 148862306a36Sopenharmony_ci ctx = rcu_dereference(gss_cred->gc_ctx); 148962306a36Sopenharmony_ci if (!ctx || time_after(timeout, ctx->gc_expiry)) 149062306a36Sopenharmony_ci ret = -EACCES; 149162306a36Sopenharmony_ci rcu_read_unlock(); 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci return ret; 149462306a36Sopenharmony_ci} 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_cistatic int 149762306a36Sopenharmony_cigss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags) 149862306a36Sopenharmony_ci{ 149962306a36Sopenharmony_ci struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); 150062306a36Sopenharmony_ci struct gss_cl_ctx *ctx; 150162306a36Sopenharmony_ci int ret; 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci if (test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags)) 150462306a36Sopenharmony_ci goto out; 150562306a36Sopenharmony_ci /* Don't match with creds that have expired. */ 150662306a36Sopenharmony_ci rcu_read_lock(); 150762306a36Sopenharmony_ci ctx = rcu_dereference(gss_cred->gc_ctx); 150862306a36Sopenharmony_ci if (!ctx || time_after(jiffies, ctx->gc_expiry)) { 150962306a36Sopenharmony_ci rcu_read_unlock(); 151062306a36Sopenharmony_ci return 0; 151162306a36Sopenharmony_ci } 151262306a36Sopenharmony_ci rcu_read_unlock(); 151362306a36Sopenharmony_ci if (!test_bit(RPCAUTH_CRED_UPTODATE, &rc->cr_flags)) 151462306a36Sopenharmony_ci return 0; 151562306a36Sopenharmony_ciout: 151662306a36Sopenharmony_ci if (acred->principal != NULL) { 151762306a36Sopenharmony_ci if (gss_cred->gc_principal == NULL) 151862306a36Sopenharmony_ci return 0; 151962306a36Sopenharmony_ci ret = strcmp(acred->principal, gss_cred->gc_principal) == 0; 152062306a36Sopenharmony_ci } else { 152162306a36Sopenharmony_ci if (gss_cred->gc_principal != NULL) 152262306a36Sopenharmony_ci return 0; 152362306a36Sopenharmony_ci ret = uid_eq(rc->cr_cred->fsuid, acred->cred->fsuid); 152462306a36Sopenharmony_ci } 152562306a36Sopenharmony_ci return ret; 152662306a36Sopenharmony_ci} 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci/* 152962306a36Sopenharmony_ci * Marshal credentials. 153062306a36Sopenharmony_ci * 153162306a36Sopenharmony_ci * The expensive part is computing the verifier. We can't cache a 153262306a36Sopenharmony_ci * pre-computed version of the verifier because the seqno, which 153362306a36Sopenharmony_ci * is different every time, is included in the MIC. 153462306a36Sopenharmony_ci */ 153562306a36Sopenharmony_cistatic int gss_marshal(struct rpc_task *task, struct xdr_stream *xdr) 153662306a36Sopenharmony_ci{ 153762306a36Sopenharmony_ci struct rpc_rqst *req = task->tk_rqstp; 153862306a36Sopenharmony_ci struct rpc_cred *cred = req->rq_cred; 153962306a36Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, struct gss_cred, 154062306a36Sopenharmony_ci gc_base); 154162306a36Sopenharmony_ci struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 154262306a36Sopenharmony_ci __be32 *p, *cred_len; 154362306a36Sopenharmony_ci u32 maj_stat = 0; 154462306a36Sopenharmony_ci struct xdr_netobj mic; 154562306a36Sopenharmony_ci struct kvec iov; 154662306a36Sopenharmony_ci struct xdr_buf verf_buf; 154762306a36Sopenharmony_ci int status; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci /* Credential */ 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 7 * sizeof(*p) + 155262306a36Sopenharmony_ci ctx->gc_wire_ctx.len); 155362306a36Sopenharmony_ci if (!p) 155462306a36Sopenharmony_ci goto marshal_failed; 155562306a36Sopenharmony_ci *p++ = rpc_auth_gss; 155662306a36Sopenharmony_ci cred_len = p++; 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci spin_lock(&ctx->gc_seq_lock); 155962306a36Sopenharmony_ci req->rq_seqno = (ctx->gc_seq < MAXSEQ) ? ctx->gc_seq++ : MAXSEQ; 156062306a36Sopenharmony_ci spin_unlock(&ctx->gc_seq_lock); 156162306a36Sopenharmony_ci if (req->rq_seqno == MAXSEQ) 156262306a36Sopenharmony_ci goto expired; 156362306a36Sopenharmony_ci trace_rpcgss_seqno(task); 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci *p++ = cpu_to_be32(RPC_GSS_VERSION); 156662306a36Sopenharmony_ci *p++ = cpu_to_be32(ctx->gc_proc); 156762306a36Sopenharmony_ci *p++ = cpu_to_be32(req->rq_seqno); 156862306a36Sopenharmony_ci *p++ = cpu_to_be32(gss_cred->gc_service); 156962306a36Sopenharmony_ci p = xdr_encode_netobj(p, &ctx->gc_wire_ctx); 157062306a36Sopenharmony_ci *cred_len = cpu_to_be32((p - (cred_len + 1)) << 2); 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci /* Verifier */ 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci /* We compute the checksum for the verifier over the xdr-encoded bytes 157562306a36Sopenharmony_ci * starting with the xid and ending at the end of the credential: */ 157662306a36Sopenharmony_ci iov.iov_base = req->rq_snd_buf.head[0].iov_base; 157762306a36Sopenharmony_ci iov.iov_len = (u8 *)p - (u8 *)iov.iov_base; 157862306a36Sopenharmony_ci xdr_buf_from_iov(&iov, &verf_buf); 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci p = xdr_reserve_space(xdr, sizeof(*p)); 158162306a36Sopenharmony_ci if (!p) 158262306a36Sopenharmony_ci goto marshal_failed; 158362306a36Sopenharmony_ci *p++ = rpc_auth_gss; 158462306a36Sopenharmony_ci mic.data = (u8 *)(p + 1); 158562306a36Sopenharmony_ci maj_stat = gss_get_mic(ctx->gc_gss_ctx, &verf_buf, &mic); 158662306a36Sopenharmony_ci if (maj_stat == GSS_S_CONTEXT_EXPIRED) 158762306a36Sopenharmony_ci goto expired; 158862306a36Sopenharmony_ci else if (maj_stat != 0) 158962306a36Sopenharmony_ci goto bad_mic; 159062306a36Sopenharmony_ci if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0) 159162306a36Sopenharmony_ci goto marshal_failed; 159262306a36Sopenharmony_ci status = 0; 159362306a36Sopenharmony_ciout: 159462306a36Sopenharmony_ci gss_put_ctx(ctx); 159562306a36Sopenharmony_ci return status; 159662306a36Sopenharmony_ciexpired: 159762306a36Sopenharmony_ci clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 159862306a36Sopenharmony_ci status = -EKEYEXPIRED; 159962306a36Sopenharmony_ci goto out; 160062306a36Sopenharmony_cimarshal_failed: 160162306a36Sopenharmony_ci status = -EMSGSIZE; 160262306a36Sopenharmony_ci goto out; 160362306a36Sopenharmony_cibad_mic: 160462306a36Sopenharmony_ci trace_rpcgss_get_mic(task, maj_stat); 160562306a36Sopenharmony_ci status = -EIO; 160662306a36Sopenharmony_ci goto out; 160762306a36Sopenharmony_ci} 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_cistatic int gss_renew_cred(struct rpc_task *task) 161062306a36Sopenharmony_ci{ 161162306a36Sopenharmony_ci struct rpc_cred *oldcred = task->tk_rqstp->rq_cred; 161262306a36Sopenharmony_ci struct gss_cred *gss_cred = container_of(oldcred, 161362306a36Sopenharmony_ci struct gss_cred, 161462306a36Sopenharmony_ci gc_base); 161562306a36Sopenharmony_ci struct rpc_auth *auth = oldcred->cr_auth; 161662306a36Sopenharmony_ci struct auth_cred acred = { 161762306a36Sopenharmony_ci .cred = oldcred->cr_cred, 161862306a36Sopenharmony_ci .principal = gss_cred->gc_principal, 161962306a36Sopenharmony_ci }; 162062306a36Sopenharmony_ci struct rpc_cred *new; 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci new = gss_lookup_cred(auth, &acred, RPCAUTH_LOOKUP_NEW); 162362306a36Sopenharmony_ci if (IS_ERR(new)) 162462306a36Sopenharmony_ci return PTR_ERR(new); 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci task->tk_rqstp->rq_cred = new; 162762306a36Sopenharmony_ci put_rpccred(oldcred); 162862306a36Sopenharmony_ci return 0; 162962306a36Sopenharmony_ci} 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_cistatic int gss_cred_is_negative_entry(struct rpc_cred *cred) 163262306a36Sopenharmony_ci{ 163362306a36Sopenharmony_ci if (test_bit(RPCAUTH_CRED_NEGATIVE, &cred->cr_flags)) { 163462306a36Sopenharmony_ci unsigned long now = jiffies; 163562306a36Sopenharmony_ci unsigned long begin, expire; 163662306a36Sopenharmony_ci struct gss_cred *gss_cred; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci gss_cred = container_of(cred, struct gss_cred, gc_base); 163962306a36Sopenharmony_ci begin = gss_cred->gc_upcall_timestamp; 164062306a36Sopenharmony_ci expire = begin + gss_expired_cred_retry_delay * HZ; 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci if (time_in_range_open(now, begin, expire)) 164362306a36Sopenharmony_ci return 1; 164462306a36Sopenharmony_ci } 164562306a36Sopenharmony_ci return 0; 164662306a36Sopenharmony_ci} 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci/* 164962306a36Sopenharmony_ci* Refresh credentials. XXX - finish 165062306a36Sopenharmony_ci*/ 165162306a36Sopenharmony_cistatic int 165262306a36Sopenharmony_cigss_refresh(struct rpc_task *task) 165362306a36Sopenharmony_ci{ 165462306a36Sopenharmony_ci struct rpc_cred *cred = task->tk_rqstp->rq_cred; 165562306a36Sopenharmony_ci int ret = 0; 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci if (gss_cred_is_negative_entry(cred)) 165862306a36Sopenharmony_ci return -EKEYEXPIRED; 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) && 166162306a36Sopenharmony_ci !test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags)) { 166262306a36Sopenharmony_ci ret = gss_renew_cred(task); 166362306a36Sopenharmony_ci if (ret < 0) 166462306a36Sopenharmony_ci goto out; 166562306a36Sopenharmony_ci cred = task->tk_rqstp->rq_cred; 166662306a36Sopenharmony_ci } 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags)) 166962306a36Sopenharmony_ci ret = gss_refresh_upcall(task); 167062306a36Sopenharmony_ciout: 167162306a36Sopenharmony_ci return ret; 167262306a36Sopenharmony_ci} 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci/* Dummy refresh routine: used only when destroying the context */ 167562306a36Sopenharmony_cistatic int 167662306a36Sopenharmony_cigss_refresh_null(struct rpc_task *task) 167762306a36Sopenharmony_ci{ 167862306a36Sopenharmony_ci return 0; 167962306a36Sopenharmony_ci} 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_cistatic int 168262306a36Sopenharmony_cigss_validate(struct rpc_task *task, struct xdr_stream *xdr) 168362306a36Sopenharmony_ci{ 168462306a36Sopenharmony_ci struct rpc_cred *cred = task->tk_rqstp->rq_cred; 168562306a36Sopenharmony_ci struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 168662306a36Sopenharmony_ci __be32 *p, *seq = NULL; 168762306a36Sopenharmony_ci struct kvec iov; 168862306a36Sopenharmony_ci struct xdr_buf verf_buf; 168962306a36Sopenharmony_ci struct xdr_netobj mic; 169062306a36Sopenharmony_ci u32 len, maj_stat; 169162306a36Sopenharmony_ci int status; 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci p = xdr_inline_decode(xdr, 2 * sizeof(*p)); 169462306a36Sopenharmony_ci if (!p) 169562306a36Sopenharmony_ci goto validate_failed; 169662306a36Sopenharmony_ci if (*p++ != rpc_auth_gss) 169762306a36Sopenharmony_ci goto validate_failed; 169862306a36Sopenharmony_ci len = be32_to_cpup(p); 169962306a36Sopenharmony_ci if (len > RPC_MAX_AUTH_SIZE) 170062306a36Sopenharmony_ci goto validate_failed; 170162306a36Sopenharmony_ci p = xdr_inline_decode(xdr, len); 170262306a36Sopenharmony_ci if (!p) 170362306a36Sopenharmony_ci goto validate_failed; 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci seq = kmalloc(4, GFP_KERNEL); 170662306a36Sopenharmony_ci if (!seq) 170762306a36Sopenharmony_ci goto validate_failed; 170862306a36Sopenharmony_ci *seq = cpu_to_be32(task->tk_rqstp->rq_seqno); 170962306a36Sopenharmony_ci iov.iov_base = seq; 171062306a36Sopenharmony_ci iov.iov_len = 4; 171162306a36Sopenharmony_ci xdr_buf_from_iov(&iov, &verf_buf); 171262306a36Sopenharmony_ci mic.data = (u8 *)p; 171362306a36Sopenharmony_ci mic.len = len; 171462306a36Sopenharmony_ci maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic); 171562306a36Sopenharmony_ci if (maj_stat == GSS_S_CONTEXT_EXPIRED) 171662306a36Sopenharmony_ci clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 171762306a36Sopenharmony_ci if (maj_stat) 171862306a36Sopenharmony_ci goto bad_mic; 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci /* We leave it to unwrap to calculate au_rslack. For now we just 172162306a36Sopenharmony_ci * calculate the length of the verifier: */ 172262306a36Sopenharmony_ci if (test_bit(RPCAUTH_AUTH_UPDATE_SLACK, &cred->cr_auth->au_flags)) 172362306a36Sopenharmony_ci cred->cr_auth->au_verfsize = XDR_QUADLEN(len) + 2; 172462306a36Sopenharmony_ci status = 0; 172562306a36Sopenharmony_ciout: 172662306a36Sopenharmony_ci gss_put_ctx(ctx); 172762306a36Sopenharmony_ci kfree(seq); 172862306a36Sopenharmony_ci return status; 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_civalidate_failed: 173162306a36Sopenharmony_ci status = -EIO; 173262306a36Sopenharmony_ci goto out; 173362306a36Sopenharmony_cibad_mic: 173462306a36Sopenharmony_ci trace_rpcgss_verify_mic(task, maj_stat); 173562306a36Sopenharmony_ci status = -EACCES; 173662306a36Sopenharmony_ci goto out; 173762306a36Sopenharmony_ci} 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_cistatic noinline_for_stack int 174062306a36Sopenharmony_cigss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, 174162306a36Sopenharmony_ci struct rpc_task *task, struct xdr_stream *xdr) 174262306a36Sopenharmony_ci{ 174362306a36Sopenharmony_ci struct rpc_rqst *rqstp = task->tk_rqstp; 174462306a36Sopenharmony_ci struct xdr_buf integ_buf, *snd_buf = &rqstp->rq_snd_buf; 174562306a36Sopenharmony_ci struct xdr_netobj mic; 174662306a36Sopenharmony_ci __be32 *p, *integ_len; 174762306a36Sopenharmony_ci u32 offset, maj_stat; 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 2 * sizeof(*p)); 175062306a36Sopenharmony_ci if (!p) 175162306a36Sopenharmony_ci goto wrap_failed; 175262306a36Sopenharmony_ci integ_len = p++; 175362306a36Sopenharmony_ci *p = cpu_to_be32(rqstp->rq_seqno); 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci if (rpcauth_wrap_req_encode(task, xdr)) 175662306a36Sopenharmony_ci goto wrap_failed; 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; 175962306a36Sopenharmony_ci if (xdr_buf_subsegment(snd_buf, &integ_buf, 176062306a36Sopenharmony_ci offset, snd_buf->len - offset)) 176162306a36Sopenharmony_ci goto wrap_failed; 176262306a36Sopenharmony_ci *integ_len = cpu_to_be32(integ_buf.len); 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 0); 176562306a36Sopenharmony_ci if (!p) 176662306a36Sopenharmony_ci goto wrap_failed; 176762306a36Sopenharmony_ci mic.data = (u8 *)(p + 1); 176862306a36Sopenharmony_ci maj_stat = gss_get_mic(ctx->gc_gss_ctx, &integ_buf, &mic); 176962306a36Sopenharmony_ci if (maj_stat == GSS_S_CONTEXT_EXPIRED) 177062306a36Sopenharmony_ci clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 177162306a36Sopenharmony_ci else if (maj_stat) 177262306a36Sopenharmony_ci goto bad_mic; 177362306a36Sopenharmony_ci /* Check that the trailing MIC fit in the buffer, after the fact */ 177462306a36Sopenharmony_ci if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0) 177562306a36Sopenharmony_ci goto wrap_failed; 177662306a36Sopenharmony_ci return 0; 177762306a36Sopenharmony_ciwrap_failed: 177862306a36Sopenharmony_ci return -EMSGSIZE; 177962306a36Sopenharmony_cibad_mic: 178062306a36Sopenharmony_ci trace_rpcgss_get_mic(task, maj_stat); 178162306a36Sopenharmony_ci return -EIO; 178262306a36Sopenharmony_ci} 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_cistatic void 178562306a36Sopenharmony_cipriv_release_snd_buf(struct rpc_rqst *rqstp) 178662306a36Sopenharmony_ci{ 178762306a36Sopenharmony_ci int i; 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci for (i=0; i < rqstp->rq_enc_pages_num; i++) 179062306a36Sopenharmony_ci __free_page(rqstp->rq_enc_pages[i]); 179162306a36Sopenharmony_ci kfree(rqstp->rq_enc_pages); 179262306a36Sopenharmony_ci rqstp->rq_release_snd_buf = NULL; 179362306a36Sopenharmony_ci} 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_cistatic int 179662306a36Sopenharmony_cialloc_enc_pages(struct rpc_rqst *rqstp) 179762306a36Sopenharmony_ci{ 179862306a36Sopenharmony_ci struct xdr_buf *snd_buf = &rqstp->rq_snd_buf; 179962306a36Sopenharmony_ci int first, last, i; 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci if (rqstp->rq_release_snd_buf) 180262306a36Sopenharmony_ci rqstp->rq_release_snd_buf(rqstp); 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci if (snd_buf->page_len == 0) { 180562306a36Sopenharmony_ci rqstp->rq_enc_pages_num = 0; 180662306a36Sopenharmony_ci return 0; 180762306a36Sopenharmony_ci } 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci first = snd_buf->page_base >> PAGE_SHIFT; 181062306a36Sopenharmony_ci last = (snd_buf->page_base + snd_buf->page_len - 1) >> PAGE_SHIFT; 181162306a36Sopenharmony_ci rqstp->rq_enc_pages_num = last - first + 1 + 1; 181262306a36Sopenharmony_ci rqstp->rq_enc_pages 181362306a36Sopenharmony_ci = kmalloc_array(rqstp->rq_enc_pages_num, 181462306a36Sopenharmony_ci sizeof(struct page *), 181562306a36Sopenharmony_ci GFP_KERNEL); 181662306a36Sopenharmony_ci if (!rqstp->rq_enc_pages) 181762306a36Sopenharmony_ci goto out; 181862306a36Sopenharmony_ci for (i=0; i < rqstp->rq_enc_pages_num; i++) { 181962306a36Sopenharmony_ci rqstp->rq_enc_pages[i] = alloc_page(GFP_KERNEL); 182062306a36Sopenharmony_ci if (rqstp->rq_enc_pages[i] == NULL) 182162306a36Sopenharmony_ci goto out_free; 182262306a36Sopenharmony_ci } 182362306a36Sopenharmony_ci rqstp->rq_release_snd_buf = priv_release_snd_buf; 182462306a36Sopenharmony_ci return 0; 182562306a36Sopenharmony_ciout_free: 182662306a36Sopenharmony_ci rqstp->rq_enc_pages_num = i; 182762306a36Sopenharmony_ci priv_release_snd_buf(rqstp); 182862306a36Sopenharmony_ciout: 182962306a36Sopenharmony_ci return -EAGAIN; 183062306a36Sopenharmony_ci} 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_cistatic noinline_for_stack int 183362306a36Sopenharmony_cigss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, 183462306a36Sopenharmony_ci struct rpc_task *task, struct xdr_stream *xdr) 183562306a36Sopenharmony_ci{ 183662306a36Sopenharmony_ci struct rpc_rqst *rqstp = task->tk_rqstp; 183762306a36Sopenharmony_ci struct xdr_buf *snd_buf = &rqstp->rq_snd_buf; 183862306a36Sopenharmony_ci u32 pad, offset, maj_stat; 183962306a36Sopenharmony_ci int status; 184062306a36Sopenharmony_ci __be32 *p, *opaque_len; 184162306a36Sopenharmony_ci struct page **inpages; 184262306a36Sopenharmony_ci int first; 184362306a36Sopenharmony_ci struct kvec *iov; 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci status = -EIO; 184662306a36Sopenharmony_ci p = xdr_reserve_space(xdr, 2 * sizeof(*p)); 184762306a36Sopenharmony_ci if (!p) 184862306a36Sopenharmony_ci goto wrap_failed; 184962306a36Sopenharmony_ci opaque_len = p++; 185062306a36Sopenharmony_ci *p = cpu_to_be32(rqstp->rq_seqno); 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci if (rpcauth_wrap_req_encode(task, xdr)) 185362306a36Sopenharmony_ci goto wrap_failed; 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci status = alloc_enc_pages(rqstp); 185662306a36Sopenharmony_ci if (unlikely(status)) 185762306a36Sopenharmony_ci goto wrap_failed; 185862306a36Sopenharmony_ci first = snd_buf->page_base >> PAGE_SHIFT; 185962306a36Sopenharmony_ci inpages = snd_buf->pages + first; 186062306a36Sopenharmony_ci snd_buf->pages = rqstp->rq_enc_pages; 186162306a36Sopenharmony_ci snd_buf->page_base -= first << PAGE_SHIFT; 186262306a36Sopenharmony_ci /* 186362306a36Sopenharmony_ci * Move the tail into its own page, in case gss_wrap needs 186462306a36Sopenharmony_ci * more space in the head when wrapping. 186562306a36Sopenharmony_ci * 186662306a36Sopenharmony_ci * Still... Why can't gss_wrap just slide the tail down? 186762306a36Sopenharmony_ci */ 186862306a36Sopenharmony_ci if (snd_buf->page_len || snd_buf->tail[0].iov_len) { 186962306a36Sopenharmony_ci char *tmp; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci tmp = page_address(rqstp->rq_enc_pages[rqstp->rq_enc_pages_num - 1]); 187262306a36Sopenharmony_ci memcpy(tmp, snd_buf->tail[0].iov_base, snd_buf->tail[0].iov_len); 187362306a36Sopenharmony_ci snd_buf->tail[0].iov_base = tmp; 187462306a36Sopenharmony_ci } 187562306a36Sopenharmony_ci offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; 187662306a36Sopenharmony_ci maj_stat = gss_wrap(ctx->gc_gss_ctx, offset, snd_buf, inpages); 187762306a36Sopenharmony_ci /* slack space should prevent this ever happening: */ 187862306a36Sopenharmony_ci if (unlikely(snd_buf->len > snd_buf->buflen)) 187962306a36Sopenharmony_ci goto wrap_failed; 188062306a36Sopenharmony_ci /* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was 188162306a36Sopenharmony_ci * done anyway, so it's safe to put the request on the wire: */ 188262306a36Sopenharmony_ci if (maj_stat == GSS_S_CONTEXT_EXPIRED) 188362306a36Sopenharmony_ci clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 188462306a36Sopenharmony_ci else if (maj_stat) 188562306a36Sopenharmony_ci goto bad_wrap; 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci *opaque_len = cpu_to_be32(snd_buf->len - offset); 188862306a36Sopenharmony_ci /* guess whether the pad goes into the head or the tail: */ 188962306a36Sopenharmony_ci if (snd_buf->page_len || snd_buf->tail[0].iov_len) 189062306a36Sopenharmony_ci iov = snd_buf->tail; 189162306a36Sopenharmony_ci else 189262306a36Sopenharmony_ci iov = snd_buf->head; 189362306a36Sopenharmony_ci p = iov->iov_base + iov->iov_len; 189462306a36Sopenharmony_ci pad = xdr_pad_size(snd_buf->len - offset); 189562306a36Sopenharmony_ci memset(p, 0, pad); 189662306a36Sopenharmony_ci iov->iov_len += pad; 189762306a36Sopenharmony_ci snd_buf->len += pad; 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci return 0; 190062306a36Sopenharmony_ciwrap_failed: 190162306a36Sopenharmony_ci return status; 190262306a36Sopenharmony_cibad_wrap: 190362306a36Sopenharmony_ci trace_rpcgss_wrap(task, maj_stat); 190462306a36Sopenharmony_ci return -EIO; 190562306a36Sopenharmony_ci} 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_cistatic int gss_wrap_req(struct rpc_task *task, struct xdr_stream *xdr) 190862306a36Sopenharmony_ci{ 190962306a36Sopenharmony_ci struct rpc_cred *cred = task->tk_rqstp->rq_cred; 191062306a36Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, struct gss_cred, 191162306a36Sopenharmony_ci gc_base); 191262306a36Sopenharmony_ci struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 191362306a36Sopenharmony_ci int status; 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci status = -EIO; 191662306a36Sopenharmony_ci if (ctx->gc_proc != RPC_GSS_PROC_DATA) { 191762306a36Sopenharmony_ci /* The spec seems a little ambiguous here, but I think that not 191862306a36Sopenharmony_ci * wrapping context destruction requests makes the most sense. 191962306a36Sopenharmony_ci */ 192062306a36Sopenharmony_ci status = rpcauth_wrap_req_encode(task, xdr); 192162306a36Sopenharmony_ci goto out; 192262306a36Sopenharmony_ci } 192362306a36Sopenharmony_ci switch (gss_cred->gc_service) { 192462306a36Sopenharmony_ci case RPC_GSS_SVC_NONE: 192562306a36Sopenharmony_ci status = rpcauth_wrap_req_encode(task, xdr); 192662306a36Sopenharmony_ci break; 192762306a36Sopenharmony_ci case RPC_GSS_SVC_INTEGRITY: 192862306a36Sopenharmony_ci status = gss_wrap_req_integ(cred, ctx, task, xdr); 192962306a36Sopenharmony_ci break; 193062306a36Sopenharmony_ci case RPC_GSS_SVC_PRIVACY: 193162306a36Sopenharmony_ci status = gss_wrap_req_priv(cred, ctx, task, xdr); 193262306a36Sopenharmony_ci break; 193362306a36Sopenharmony_ci default: 193462306a36Sopenharmony_ci status = -EIO; 193562306a36Sopenharmony_ci } 193662306a36Sopenharmony_ciout: 193762306a36Sopenharmony_ci gss_put_ctx(ctx); 193862306a36Sopenharmony_ci return status; 193962306a36Sopenharmony_ci} 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci/** 194262306a36Sopenharmony_ci * gss_update_rslack - Possibly update RPC receive buffer size estimates 194362306a36Sopenharmony_ci * @task: rpc_task for incoming RPC Reply being unwrapped 194462306a36Sopenharmony_ci * @cred: controlling rpc_cred for @task 194562306a36Sopenharmony_ci * @before: XDR words needed before each RPC Reply message 194662306a36Sopenharmony_ci * @after: XDR words needed following each RPC Reply message 194762306a36Sopenharmony_ci * 194862306a36Sopenharmony_ci */ 194962306a36Sopenharmony_cistatic void gss_update_rslack(struct rpc_task *task, struct rpc_cred *cred, 195062306a36Sopenharmony_ci unsigned int before, unsigned int after) 195162306a36Sopenharmony_ci{ 195262306a36Sopenharmony_ci struct rpc_auth *auth = cred->cr_auth; 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci if (test_and_clear_bit(RPCAUTH_AUTH_UPDATE_SLACK, &auth->au_flags)) { 195562306a36Sopenharmony_ci auth->au_ralign = auth->au_verfsize + before; 195662306a36Sopenharmony_ci auth->au_rslack = auth->au_verfsize + after; 195762306a36Sopenharmony_ci trace_rpcgss_update_slack(task, auth); 195862306a36Sopenharmony_ci } 195962306a36Sopenharmony_ci} 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_cistatic int 196262306a36Sopenharmony_cigss_unwrap_resp_auth(struct rpc_task *task, struct rpc_cred *cred) 196362306a36Sopenharmony_ci{ 196462306a36Sopenharmony_ci gss_update_rslack(task, cred, 0, 0); 196562306a36Sopenharmony_ci return 0; 196662306a36Sopenharmony_ci} 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_ci/* 196962306a36Sopenharmony_ci * RFC 2203, Section 5.3.2.2 197062306a36Sopenharmony_ci * 197162306a36Sopenharmony_ci * struct rpc_gss_integ_data { 197262306a36Sopenharmony_ci * opaque databody_integ<>; 197362306a36Sopenharmony_ci * opaque checksum<>; 197462306a36Sopenharmony_ci * }; 197562306a36Sopenharmony_ci * 197662306a36Sopenharmony_ci * struct rpc_gss_data_t { 197762306a36Sopenharmony_ci * unsigned int seq_num; 197862306a36Sopenharmony_ci * proc_req_arg_t arg; 197962306a36Sopenharmony_ci * }; 198062306a36Sopenharmony_ci */ 198162306a36Sopenharmony_cistatic noinline_for_stack int 198262306a36Sopenharmony_cigss_unwrap_resp_integ(struct rpc_task *task, struct rpc_cred *cred, 198362306a36Sopenharmony_ci struct gss_cl_ctx *ctx, struct rpc_rqst *rqstp, 198462306a36Sopenharmony_ci struct xdr_stream *xdr) 198562306a36Sopenharmony_ci{ 198662306a36Sopenharmony_ci struct xdr_buf gss_data, *rcv_buf = &rqstp->rq_rcv_buf; 198762306a36Sopenharmony_ci u32 len, offset, seqno, maj_stat; 198862306a36Sopenharmony_ci struct xdr_netobj mic; 198962306a36Sopenharmony_ci int ret; 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci ret = -EIO; 199262306a36Sopenharmony_ci mic.data = NULL; 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci /* opaque databody_integ<>; */ 199562306a36Sopenharmony_ci if (xdr_stream_decode_u32(xdr, &len)) 199662306a36Sopenharmony_ci goto unwrap_failed; 199762306a36Sopenharmony_ci if (len & 3) 199862306a36Sopenharmony_ci goto unwrap_failed; 199962306a36Sopenharmony_ci offset = rcv_buf->len - xdr_stream_remaining(xdr); 200062306a36Sopenharmony_ci if (xdr_stream_decode_u32(xdr, &seqno)) 200162306a36Sopenharmony_ci goto unwrap_failed; 200262306a36Sopenharmony_ci if (seqno != rqstp->rq_seqno) 200362306a36Sopenharmony_ci goto bad_seqno; 200462306a36Sopenharmony_ci if (xdr_buf_subsegment(rcv_buf, &gss_data, offset, len)) 200562306a36Sopenharmony_ci goto unwrap_failed; 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci /* 200862306a36Sopenharmony_ci * The xdr_stream now points to the beginning of the 200962306a36Sopenharmony_ci * upper layer payload, to be passed below to 201062306a36Sopenharmony_ci * rpcauth_unwrap_resp_decode(). The checksum, which 201162306a36Sopenharmony_ci * follows the upper layer payload in @rcv_buf, is 201262306a36Sopenharmony_ci * located and parsed without updating the xdr_stream. 201362306a36Sopenharmony_ci */ 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci /* opaque checksum<>; */ 201662306a36Sopenharmony_ci offset += len; 201762306a36Sopenharmony_ci if (xdr_decode_word(rcv_buf, offset, &len)) 201862306a36Sopenharmony_ci goto unwrap_failed; 201962306a36Sopenharmony_ci offset += sizeof(__be32); 202062306a36Sopenharmony_ci if (offset + len > rcv_buf->len) 202162306a36Sopenharmony_ci goto unwrap_failed; 202262306a36Sopenharmony_ci mic.len = len; 202362306a36Sopenharmony_ci mic.data = kmalloc(len, GFP_KERNEL); 202462306a36Sopenharmony_ci if (ZERO_OR_NULL_PTR(mic.data)) 202562306a36Sopenharmony_ci goto unwrap_failed; 202662306a36Sopenharmony_ci if (read_bytes_from_xdr_buf(rcv_buf, offset, mic.data, mic.len)) 202762306a36Sopenharmony_ci goto unwrap_failed; 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_ci maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &gss_data, &mic); 203062306a36Sopenharmony_ci if (maj_stat == GSS_S_CONTEXT_EXPIRED) 203162306a36Sopenharmony_ci clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 203262306a36Sopenharmony_ci if (maj_stat != GSS_S_COMPLETE) 203362306a36Sopenharmony_ci goto bad_mic; 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci gss_update_rslack(task, cred, 2, 2 + 1 + XDR_QUADLEN(mic.len)); 203662306a36Sopenharmony_ci ret = 0; 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ciout: 203962306a36Sopenharmony_ci kfree(mic.data); 204062306a36Sopenharmony_ci return ret; 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ciunwrap_failed: 204362306a36Sopenharmony_ci trace_rpcgss_unwrap_failed(task); 204462306a36Sopenharmony_ci goto out; 204562306a36Sopenharmony_cibad_seqno: 204662306a36Sopenharmony_ci trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, seqno); 204762306a36Sopenharmony_ci goto out; 204862306a36Sopenharmony_cibad_mic: 204962306a36Sopenharmony_ci trace_rpcgss_verify_mic(task, maj_stat); 205062306a36Sopenharmony_ci goto out; 205162306a36Sopenharmony_ci} 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_cistatic noinline_for_stack int 205462306a36Sopenharmony_cigss_unwrap_resp_priv(struct rpc_task *task, struct rpc_cred *cred, 205562306a36Sopenharmony_ci struct gss_cl_ctx *ctx, struct rpc_rqst *rqstp, 205662306a36Sopenharmony_ci struct xdr_stream *xdr) 205762306a36Sopenharmony_ci{ 205862306a36Sopenharmony_ci struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf; 205962306a36Sopenharmony_ci struct kvec *head = rqstp->rq_rcv_buf.head; 206062306a36Sopenharmony_ci u32 offset, opaque_len, maj_stat; 206162306a36Sopenharmony_ci __be32 *p; 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_ci p = xdr_inline_decode(xdr, 2 * sizeof(*p)); 206462306a36Sopenharmony_ci if (unlikely(!p)) 206562306a36Sopenharmony_ci goto unwrap_failed; 206662306a36Sopenharmony_ci opaque_len = be32_to_cpup(p++); 206762306a36Sopenharmony_ci offset = (u8 *)(p) - (u8 *)head->iov_base; 206862306a36Sopenharmony_ci if (offset + opaque_len > rcv_buf->len) 206962306a36Sopenharmony_ci goto unwrap_failed; 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset, 207262306a36Sopenharmony_ci offset + opaque_len, rcv_buf); 207362306a36Sopenharmony_ci if (maj_stat == GSS_S_CONTEXT_EXPIRED) 207462306a36Sopenharmony_ci clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 207562306a36Sopenharmony_ci if (maj_stat != GSS_S_COMPLETE) 207662306a36Sopenharmony_ci goto bad_unwrap; 207762306a36Sopenharmony_ci /* gss_unwrap decrypted the sequence number */ 207862306a36Sopenharmony_ci if (be32_to_cpup(p++) != rqstp->rq_seqno) 207962306a36Sopenharmony_ci goto bad_seqno; 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci /* gss_unwrap redacts the opaque blob from the head iovec. 208262306a36Sopenharmony_ci * rcv_buf has changed, thus the stream needs to be reset. 208362306a36Sopenharmony_ci */ 208462306a36Sopenharmony_ci xdr_init_decode(xdr, rcv_buf, p, rqstp); 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci gss_update_rslack(task, cred, 2 + ctx->gc_gss_ctx->align, 208762306a36Sopenharmony_ci 2 + ctx->gc_gss_ctx->slack); 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci return 0; 209062306a36Sopenharmony_ciunwrap_failed: 209162306a36Sopenharmony_ci trace_rpcgss_unwrap_failed(task); 209262306a36Sopenharmony_ci return -EIO; 209362306a36Sopenharmony_cibad_seqno: 209462306a36Sopenharmony_ci trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, be32_to_cpup(--p)); 209562306a36Sopenharmony_ci return -EIO; 209662306a36Sopenharmony_cibad_unwrap: 209762306a36Sopenharmony_ci trace_rpcgss_unwrap(task, maj_stat); 209862306a36Sopenharmony_ci return -EIO; 209962306a36Sopenharmony_ci} 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_cistatic bool 210262306a36Sopenharmony_cigss_seq_is_newer(u32 new, u32 old) 210362306a36Sopenharmony_ci{ 210462306a36Sopenharmony_ci return (s32)(new - old) > 0; 210562306a36Sopenharmony_ci} 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_cistatic bool 210862306a36Sopenharmony_cigss_xmit_need_reencode(struct rpc_task *task) 210962306a36Sopenharmony_ci{ 211062306a36Sopenharmony_ci struct rpc_rqst *req = task->tk_rqstp; 211162306a36Sopenharmony_ci struct rpc_cred *cred = req->rq_cred; 211262306a36Sopenharmony_ci struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 211362306a36Sopenharmony_ci u32 win, seq_xmit = 0; 211462306a36Sopenharmony_ci bool ret = true; 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci if (!ctx) 211762306a36Sopenharmony_ci goto out; 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci if (gss_seq_is_newer(req->rq_seqno, READ_ONCE(ctx->gc_seq))) 212062306a36Sopenharmony_ci goto out_ctx; 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci seq_xmit = READ_ONCE(ctx->gc_seq_xmit); 212362306a36Sopenharmony_ci while (gss_seq_is_newer(req->rq_seqno, seq_xmit)) { 212462306a36Sopenharmony_ci u32 tmp = seq_xmit; 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci seq_xmit = cmpxchg(&ctx->gc_seq_xmit, tmp, req->rq_seqno); 212762306a36Sopenharmony_ci if (seq_xmit == tmp) { 212862306a36Sopenharmony_ci ret = false; 212962306a36Sopenharmony_ci goto out_ctx; 213062306a36Sopenharmony_ci } 213162306a36Sopenharmony_ci } 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci win = ctx->gc_win; 213462306a36Sopenharmony_ci if (win > 0) 213562306a36Sopenharmony_ci ret = !gss_seq_is_newer(req->rq_seqno, seq_xmit - win); 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ciout_ctx: 213862306a36Sopenharmony_ci gss_put_ctx(ctx); 213962306a36Sopenharmony_ciout: 214062306a36Sopenharmony_ci trace_rpcgss_need_reencode(task, seq_xmit, ret); 214162306a36Sopenharmony_ci return ret; 214262306a36Sopenharmony_ci} 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_cistatic int 214562306a36Sopenharmony_cigss_unwrap_resp(struct rpc_task *task, struct xdr_stream *xdr) 214662306a36Sopenharmony_ci{ 214762306a36Sopenharmony_ci struct rpc_rqst *rqstp = task->tk_rqstp; 214862306a36Sopenharmony_ci struct rpc_cred *cred = rqstp->rq_cred; 214962306a36Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, struct gss_cred, 215062306a36Sopenharmony_ci gc_base); 215162306a36Sopenharmony_ci struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 215262306a36Sopenharmony_ci int status = -EIO; 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci if (ctx->gc_proc != RPC_GSS_PROC_DATA) 215562306a36Sopenharmony_ci goto out_decode; 215662306a36Sopenharmony_ci switch (gss_cred->gc_service) { 215762306a36Sopenharmony_ci case RPC_GSS_SVC_NONE: 215862306a36Sopenharmony_ci status = gss_unwrap_resp_auth(task, cred); 215962306a36Sopenharmony_ci break; 216062306a36Sopenharmony_ci case RPC_GSS_SVC_INTEGRITY: 216162306a36Sopenharmony_ci status = gss_unwrap_resp_integ(task, cred, ctx, rqstp, xdr); 216262306a36Sopenharmony_ci break; 216362306a36Sopenharmony_ci case RPC_GSS_SVC_PRIVACY: 216462306a36Sopenharmony_ci status = gss_unwrap_resp_priv(task, cred, ctx, rqstp, xdr); 216562306a36Sopenharmony_ci break; 216662306a36Sopenharmony_ci } 216762306a36Sopenharmony_ci if (status) 216862306a36Sopenharmony_ci goto out; 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ciout_decode: 217162306a36Sopenharmony_ci status = rpcauth_unwrap_resp_decode(task, xdr); 217262306a36Sopenharmony_ciout: 217362306a36Sopenharmony_ci gss_put_ctx(ctx); 217462306a36Sopenharmony_ci return status; 217562306a36Sopenharmony_ci} 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_cistatic const struct rpc_authops authgss_ops = { 217862306a36Sopenharmony_ci .owner = THIS_MODULE, 217962306a36Sopenharmony_ci .au_flavor = RPC_AUTH_GSS, 218062306a36Sopenharmony_ci .au_name = "RPCSEC_GSS", 218162306a36Sopenharmony_ci .create = gss_create, 218262306a36Sopenharmony_ci .destroy = gss_destroy, 218362306a36Sopenharmony_ci .hash_cred = gss_hash_cred, 218462306a36Sopenharmony_ci .lookup_cred = gss_lookup_cred, 218562306a36Sopenharmony_ci .crcreate = gss_create_cred, 218662306a36Sopenharmony_ci .info2flavor = gss_mech_info2flavor, 218762306a36Sopenharmony_ci .flavor2info = gss_mech_flavor2info, 218862306a36Sopenharmony_ci}; 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_cistatic const struct rpc_credops gss_credops = { 219162306a36Sopenharmony_ci .cr_name = "AUTH_GSS", 219262306a36Sopenharmony_ci .crdestroy = gss_destroy_cred, 219362306a36Sopenharmony_ci .cr_init = gss_cred_init, 219462306a36Sopenharmony_ci .crmatch = gss_match, 219562306a36Sopenharmony_ci .crmarshal = gss_marshal, 219662306a36Sopenharmony_ci .crrefresh = gss_refresh, 219762306a36Sopenharmony_ci .crvalidate = gss_validate, 219862306a36Sopenharmony_ci .crwrap_req = gss_wrap_req, 219962306a36Sopenharmony_ci .crunwrap_resp = gss_unwrap_resp, 220062306a36Sopenharmony_ci .crkey_timeout = gss_key_timeout, 220162306a36Sopenharmony_ci .crstringify_acceptor = gss_stringify_acceptor, 220262306a36Sopenharmony_ci .crneed_reencode = gss_xmit_need_reencode, 220362306a36Sopenharmony_ci}; 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_cistatic const struct rpc_credops gss_nullops = { 220662306a36Sopenharmony_ci .cr_name = "AUTH_GSS", 220762306a36Sopenharmony_ci .crdestroy = gss_destroy_nullcred, 220862306a36Sopenharmony_ci .crmatch = gss_match, 220962306a36Sopenharmony_ci .crmarshal = gss_marshal, 221062306a36Sopenharmony_ci .crrefresh = gss_refresh_null, 221162306a36Sopenharmony_ci .crvalidate = gss_validate, 221262306a36Sopenharmony_ci .crwrap_req = gss_wrap_req, 221362306a36Sopenharmony_ci .crunwrap_resp = gss_unwrap_resp, 221462306a36Sopenharmony_ci .crstringify_acceptor = gss_stringify_acceptor, 221562306a36Sopenharmony_ci}; 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_cistatic const struct rpc_pipe_ops gss_upcall_ops_v0 = { 221862306a36Sopenharmony_ci .upcall = gss_v0_upcall, 221962306a36Sopenharmony_ci .downcall = gss_pipe_downcall, 222062306a36Sopenharmony_ci .destroy_msg = gss_pipe_destroy_msg, 222162306a36Sopenharmony_ci .open_pipe = gss_pipe_open_v0, 222262306a36Sopenharmony_ci .release_pipe = gss_pipe_release, 222362306a36Sopenharmony_ci}; 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_cistatic const struct rpc_pipe_ops gss_upcall_ops_v1 = { 222662306a36Sopenharmony_ci .upcall = gss_v1_upcall, 222762306a36Sopenharmony_ci .downcall = gss_pipe_downcall, 222862306a36Sopenharmony_ci .destroy_msg = gss_pipe_destroy_msg, 222962306a36Sopenharmony_ci .open_pipe = gss_pipe_open_v1, 223062306a36Sopenharmony_ci .release_pipe = gss_pipe_release, 223162306a36Sopenharmony_ci}; 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_cistatic __net_init int rpcsec_gss_init_net(struct net *net) 223462306a36Sopenharmony_ci{ 223562306a36Sopenharmony_ci return gss_svc_init_net(net); 223662306a36Sopenharmony_ci} 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_cistatic __net_exit void rpcsec_gss_exit_net(struct net *net) 223962306a36Sopenharmony_ci{ 224062306a36Sopenharmony_ci gss_svc_shutdown_net(net); 224162306a36Sopenharmony_ci} 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_cistatic struct pernet_operations rpcsec_gss_net_ops = { 224462306a36Sopenharmony_ci .init = rpcsec_gss_init_net, 224562306a36Sopenharmony_ci .exit = rpcsec_gss_exit_net, 224662306a36Sopenharmony_ci}; 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci/* 224962306a36Sopenharmony_ci * Initialize RPCSEC_GSS module 225062306a36Sopenharmony_ci */ 225162306a36Sopenharmony_cistatic int __init init_rpcsec_gss(void) 225262306a36Sopenharmony_ci{ 225362306a36Sopenharmony_ci int err = 0; 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci err = rpcauth_register(&authgss_ops); 225662306a36Sopenharmony_ci if (err) 225762306a36Sopenharmony_ci goto out; 225862306a36Sopenharmony_ci err = gss_svc_init(); 225962306a36Sopenharmony_ci if (err) 226062306a36Sopenharmony_ci goto out_unregister; 226162306a36Sopenharmony_ci err = register_pernet_subsys(&rpcsec_gss_net_ops); 226262306a36Sopenharmony_ci if (err) 226362306a36Sopenharmony_ci goto out_svc_exit; 226462306a36Sopenharmony_ci rpc_init_wait_queue(&pipe_version_rpc_waitqueue, "gss pipe version"); 226562306a36Sopenharmony_ci return 0; 226662306a36Sopenharmony_ciout_svc_exit: 226762306a36Sopenharmony_ci gss_svc_shutdown(); 226862306a36Sopenharmony_ciout_unregister: 226962306a36Sopenharmony_ci rpcauth_unregister(&authgss_ops); 227062306a36Sopenharmony_ciout: 227162306a36Sopenharmony_ci return err; 227262306a36Sopenharmony_ci} 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_cistatic void __exit exit_rpcsec_gss(void) 227562306a36Sopenharmony_ci{ 227662306a36Sopenharmony_ci unregister_pernet_subsys(&rpcsec_gss_net_ops); 227762306a36Sopenharmony_ci gss_svc_shutdown(); 227862306a36Sopenharmony_ci rpcauth_unregister(&authgss_ops); 227962306a36Sopenharmony_ci rcu_barrier(); /* Wait for completion of call_rcu()'s */ 228062306a36Sopenharmony_ci} 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ciMODULE_ALIAS("rpc-auth-6"); 228362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 228462306a36Sopenharmony_cimodule_param_named(expired_cred_retry_delay, 228562306a36Sopenharmony_ci gss_expired_cred_retry_delay, 228662306a36Sopenharmony_ci uint, 0644); 228762306a36Sopenharmony_ciMODULE_PARM_DESC(expired_cred_retry_delay, "Timeout (in seconds) until " 228862306a36Sopenharmony_ci "the RPC engine retries an expired credential"); 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_cimodule_param_named(key_expire_timeo, 229162306a36Sopenharmony_ci gss_key_expire_timeo, 229262306a36Sopenharmony_ci uint, 0644); 229362306a36Sopenharmony_ciMODULE_PARM_DESC(key_expire_timeo, "Time (in seconds) at the end of a " 229462306a36Sopenharmony_ci "credential keys lifetime where the NFS layer cleans up " 229562306a36Sopenharmony_ci "prior to key expiration"); 229662306a36Sopenharmony_ci 229762306a36Sopenharmony_cimodule_init(init_rpcsec_gss) 229862306a36Sopenharmony_cimodule_exit(exit_rpcsec_gss) 2299