18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/net/sunrpc/auth_gss/auth_gss.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * RPCSEC_GSS client authentication. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2000 The Regents of the University of Michigan. 88c2ecf20Sopenharmony_ci * All rights reserved. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Dug Song <dugsong@monkey.org> 118c2ecf20Sopenharmony_ci * Andy Adamson <andros@umich.edu> 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/types.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/sched.h> 198c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 208c2ecf20Sopenharmony_ci#include <linux/sunrpc/clnt.h> 218c2ecf20Sopenharmony_ci#include <linux/sunrpc/auth.h> 228c2ecf20Sopenharmony_ci#include <linux/sunrpc/auth_gss.h> 238c2ecf20Sopenharmony_ci#include <linux/sunrpc/gss_krb5.h> 248c2ecf20Sopenharmony_ci#include <linux/sunrpc/svcauth_gss.h> 258c2ecf20Sopenharmony_ci#include <linux/sunrpc/gss_err.h> 268c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 278c2ecf20Sopenharmony_ci#include <linux/sunrpc/rpc_pipe_fs.h> 288c2ecf20Sopenharmony_ci#include <linux/sunrpc/gss_api.h> 298c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 308c2ecf20Sopenharmony_ci#include <linux/hashtable.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include "auth_gss_internal.h" 338c2ecf20Sopenharmony_ci#include "../netns.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <trace/events/rpcgss.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic const struct rpc_authops authgss_ops; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic const struct rpc_credops gss_credops; 408c2ecf20Sopenharmony_cistatic const struct rpc_credops gss_nullops; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define GSS_RETRY_EXPIRED 5 438c2ecf20Sopenharmony_cistatic unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define GSS_KEY_EXPIRE_TIMEO 240 468c2ecf20Sopenharmony_cistatic unsigned int gss_key_expire_timeo = GSS_KEY_EXPIRE_TIMEO; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) 498c2ecf20Sopenharmony_ci# define RPCDBG_FACILITY RPCDBG_AUTH 508c2ecf20Sopenharmony_ci#endif 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define GSS_CRED_SLACK (RPC_MAX_AUTH_SIZE * 2) 538c2ecf20Sopenharmony_ci/* length of a krb5 verifier (48), plus data added before arguments when 548c2ecf20Sopenharmony_ci * using integrity (two 4-byte integers): */ 558c2ecf20Sopenharmony_ci#define GSS_VERF_SLACK 100 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic DEFINE_HASHTABLE(gss_auth_hash_table, 4); 588c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(gss_auth_hash_lock); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistruct gss_pipe { 618c2ecf20Sopenharmony_ci struct rpc_pipe_dir_object pdo; 628c2ecf20Sopenharmony_ci struct rpc_pipe *pipe; 638c2ecf20Sopenharmony_ci struct rpc_clnt *clnt; 648c2ecf20Sopenharmony_ci const char *name; 658c2ecf20Sopenharmony_ci struct kref kref; 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistruct gss_auth { 698c2ecf20Sopenharmony_ci struct kref kref; 708c2ecf20Sopenharmony_ci struct hlist_node hash; 718c2ecf20Sopenharmony_ci struct rpc_auth rpc_auth; 728c2ecf20Sopenharmony_ci struct gss_api_mech *mech; 738c2ecf20Sopenharmony_ci enum rpc_gss_svc service; 748c2ecf20Sopenharmony_ci struct rpc_clnt *client; 758c2ecf20Sopenharmony_ci struct net *net; 768c2ecf20Sopenharmony_ci /* 778c2ecf20Sopenharmony_ci * There are two upcall pipes; dentry[1], named "gssd", is used 788c2ecf20Sopenharmony_ci * for the new text-based upcall; dentry[0] is named after the 798c2ecf20Sopenharmony_ci * mechanism (for example, "krb5") and exists for 808c2ecf20Sopenharmony_ci * backwards-compatibility with older gssd's. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci struct gss_pipe *gss_pipe[2]; 838c2ecf20Sopenharmony_ci const char *target_name; 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* pipe_version >= 0 if and only if someone has a pipe open. */ 878c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(pipe_version_lock); 888c2ecf20Sopenharmony_cistatic struct rpc_wait_queue pipe_version_rpc_waitqueue; 898c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(pipe_version_waitqueue); 908c2ecf20Sopenharmony_cistatic void gss_put_auth(struct gss_auth *gss_auth); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void gss_free_ctx(struct gss_cl_ctx *); 938c2ecf20Sopenharmony_cistatic const struct rpc_pipe_ops gss_upcall_ops_v0; 948c2ecf20Sopenharmony_cistatic const struct rpc_pipe_ops gss_upcall_ops_v1; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic inline struct gss_cl_ctx * 978c2ecf20Sopenharmony_cigss_get_ctx(struct gss_cl_ctx *ctx) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci refcount_inc(&ctx->count); 1008c2ecf20Sopenharmony_ci return ctx; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic inline void 1048c2ecf20Sopenharmony_cigss_put_ctx(struct gss_cl_ctx *ctx) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci if (refcount_dec_and_test(&ctx->count)) 1078c2ecf20Sopenharmony_ci gss_free_ctx(ctx); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* gss_cred_set_ctx: 1118c2ecf20Sopenharmony_ci * called by gss_upcall_callback and gss_create_upcall in order 1128c2ecf20Sopenharmony_ci * to set the gss context. The actual exchange of an old context 1138c2ecf20Sopenharmony_ci * and a new one is protected by the pipe->lock. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_cistatic void 1168c2ecf20Sopenharmony_cigss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags)) 1218c2ecf20Sopenharmony_ci return; 1228c2ecf20Sopenharmony_ci gss_get_ctx(ctx); 1238c2ecf20Sopenharmony_ci rcu_assign_pointer(gss_cred->gc_ctx, ctx); 1248c2ecf20Sopenharmony_ci set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 1258c2ecf20Sopenharmony_ci smp_mb__before_atomic(); 1268c2ecf20Sopenharmony_ci clear_bit(RPCAUTH_CRED_NEW, &cred->cr_flags); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic struct gss_cl_ctx * 1308c2ecf20Sopenharmony_cigss_cred_get_ctx(struct rpc_cred *cred) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 1338c2ecf20Sopenharmony_ci struct gss_cl_ctx *ctx = NULL; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci rcu_read_lock(); 1368c2ecf20Sopenharmony_ci ctx = rcu_dereference(gss_cred->gc_ctx); 1378c2ecf20Sopenharmony_ci if (ctx) 1388c2ecf20Sopenharmony_ci gss_get_ctx(ctx); 1398c2ecf20Sopenharmony_ci rcu_read_unlock(); 1408c2ecf20Sopenharmony_ci return ctx; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic struct gss_cl_ctx * 1448c2ecf20Sopenharmony_cigss_alloc_context(void) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct gss_cl_ctx *ctx; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_NOFS); 1498c2ecf20Sopenharmony_ci if (ctx != NULL) { 1508c2ecf20Sopenharmony_ci ctx->gc_proc = RPC_GSS_PROC_DATA; 1518c2ecf20Sopenharmony_ci ctx->gc_seq = 1; /* NetApp 6.4R1 doesn't accept seq. no. 0 */ 1528c2ecf20Sopenharmony_ci spin_lock_init(&ctx->gc_seq_lock); 1538c2ecf20Sopenharmony_ci refcount_set(&ctx->count,1); 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci return ctx; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci#define GSSD_MIN_TIMEOUT (60 * 60) 1598c2ecf20Sopenharmony_cistatic const void * 1608c2ecf20Sopenharmony_cigss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct gss_api_mech *gm) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci const void *q; 1638c2ecf20Sopenharmony_ci unsigned int seclen; 1648c2ecf20Sopenharmony_ci unsigned int timeout; 1658c2ecf20Sopenharmony_ci unsigned long now = jiffies; 1668c2ecf20Sopenharmony_ci u32 window_size; 1678c2ecf20Sopenharmony_ci int ret; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* First unsigned int gives the remaining lifetime in seconds of the 1708c2ecf20Sopenharmony_ci * credential - e.g. the remaining TGT lifetime for Kerberos or 1718c2ecf20Sopenharmony_ci * the -t value passed to GSSD. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ci p = simple_get_bytes(p, end, &timeout, sizeof(timeout)); 1748c2ecf20Sopenharmony_ci if (IS_ERR(p)) 1758c2ecf20Sopenharmony_ci goto err; 1768c2ecf20Sopenharmony_ci if (timeout == 0) 1778c2ecf20Sopenharmony_ci timeout = GSSD_MIN_TIMEOUT; 1788c2ecf20Sopenharmony_ci ctx->gc_expiry = now + ((unsigned long)timeout * HZ); 1798c2ecf20Sopenharmony_ci /* Sequence number window. Determines the maximum number of 1808c2ecf20Sopenharmony_ci * simultaneous requests 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci p = simple_get_bytes(p, end, &window_size, sizeof(window_size)); 1838c2ecf20Sopenharmony_ci if (IS_ERR(p)) 1848c2ecf20Sopenharmony_ci goto err; 1858c2ecf20Sopenharmony_ci ctx->gc_win = window_size; 1868c2ecf20Sopenharmony_ci /* gssd signals an error by passing ctx->gc_win = 0: */ 1878c2ecf20Sopenharmony_ci if (ctx->gc_win == 0) { 1888c2ecf20Sopenharmony_ci /* 1898c2ecf20Sopenharmony_ci * in which case, p points to an error code. Anything other 1908c2ecf20Sopenharmony_ci * than -EKEYEXPIRED gets converted to -EACCES. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_ci p = simple_get_bytes(p, end, &ret, sizeof(ret)); 1938c2ecf20Sopenharmony_ci if (!IS_ERR(p)) 1948c2ecf20Sopenharmony_ci p = (ret == -EKEYEXPIRED) ? ERR_PTR(-EKEYEXPIRED) : 1958c2ecf20Sopenharmony_ci ERR_PTR(-EACCES); 1968c2ecf20Sopenharmony_ci goto err; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci /* copy the opaque wire context */ 1998c2ecf20Sopenharmony_ci p = simple_get_netobj(p, end, &ctx->gc_wire_ctx); 2008c2ecf20Sopenharmony_ci if (IS_ERR(p)) 2018c2ecf20Sopenharmony_ci goto err; 2028c2ecf20Sopenharmony_ci /* import the opaque security context */ 2038c2ecf20Sopenharmony_ci p = simple_get_bytes(p, end, &seclen, sizeof(seclen)); 2048c2ecf20Sopenharmony_ci if (IS_ERR(p)) 2058c2ecf20Sopenharmony_ci goto err; 2068c2ecf20Sopenharmony_ci q = (const void *)((const char *)p + seclen); 2078c2ecf20Sopenharmony_ci if (unlikely(q > end || q < p)) { 2088c2ecf20Sopenharmony_ci p = ERR_PTR(-EFAULT); 2098c2ecf20Sopenharmony_ci goto err; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx, NULL, GFP_NOFS); 2128c2ecf20Sopenharmony_ci if (ret < 0) { 2138c2ecf20Sopenharmony_ci trace_rpcgss_import_ctx(ret); 2148c2ecf20Sopenharmony_ci p = ERR_PTR(ret); 2158c2ecf20Sopenharmony_ci goto err; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* is there any trailing data? */ 2198c2ecf20Sopenharmony_ci if (q == end) { 2208c2ecf20Sopenharmony_ci p = q; 2218c2ecf20Sopenharmony_ci goto done; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* pull in acceptor name (if there is one) */ 2258c2ecf20Sopenharmony_ci p = simple_get_netobj(q, end, &ctx->gc_acceptor); 2268c2ecf20Sopenharmony_ci if (IS_ERR(p)) 2278c2ecf20Sopenharmony_ci goto err; 2288c2ecf20Sopenharmony_cidone: 2298c2ecf20Sopenharmony_ci trace_rpcgss_context(window_size, ctx->gc_expiry, now, timeout, 2308c2ecf20Sopenharmony_ci ctx->gc_acceptor.len, ctx->gc_acceptor.data); 2318c2ecf20Sopenharmony_cierr: 2328c2ecf20Sopenharmony_ci return p; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/* XXX: Need some documentation about why UPCALL_BUF_LEN is so small. 2368c2ecf20Sopenharmony_ci * Is user space expecting no more than UPCALL_BUF_LEN bytes? 2378c2ecf20Sopenharmony_ci * Note that there are now _two_ NI_MAXHOST sized data items 2388c2ecf20Sopenharmony_ci * being passed in this string. 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_ci#define UPCALL_BUF_LEN 256 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistruct gss_upcall_msg { 2438c2ecf20Sopenharmony_ci refcount_t count; 2448c2ecf20Sopenharmony_ci kuid_t uid; 2458c2ecf20Sopenharmony_ci const char *service_name; 2468c2ecf20Sopenharmony_ci struct rpc_pipe_msg msg; 2478c2ecf20Sopenharmony_ci struct list_head list; 2488c2ecf20Sopenharmony_ci struct gss_auth *auth; 2498c2ecf20Sopenharmony_ci struct rpc_pipe *pipe; 2508c2ecf20Sopenharmony_ci struct rpc_wait_queue rpc_waitqueue; 2518c2ecf20Sopenharmony_ci wait_queue_head_t waitqueue; 2528c2ecf20Sopenharmony_ci struct gss_cl_ctx *ctx; 2538c2ecf20Sopenharmony_ci char databuf[UPCALL_BUF_LEN]; 2548c2ecf20Sopenharmony_ci}; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int get_pipe_version(struct net *net) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 2598c2ecf20Sopenharmony_ci int ret; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci spin_lock(&pipe_version_lock); 2628c2ecf20Sopenharmony_ci if (sn->pipe_version >= 0) { 2638c2ecf20Sopenharmony_ci atomic_inc(&sn->pipe_users); 2648c2ecf20Sopenharmony_ci ret = sn->pipe_version; 2658c2ecf20Sopenharmony_ci } else 2668c2ecf20Sopenharmony_ci ret = -EAGAIN; 2678c2ecf20Sopenharmony_ci spin_unlock(&pipe_version_lock); 2688c2ecf20Sopenharmony_ci return ret; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic void put_pipe_version(struct net *net) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (atomic_dec_and_lock(&sn->pipe_users, &pipe_version_lock)) { 2768c2ecf20Sopenharmony_ci sn->pipe_version = -1; 2778c2ecf20Sopenharmony_ci spin_unlock(&pipe_version_lock); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic void 2828c2ecf20Sopenharmony_cigss_release_msg(struct gss_upcall_msg *gss_msg) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct net *net = gss_msg->auth->net; 2858c2ecf20Sopenharmony_ci if (!refcount_dec_and_test(&gss_msg->count)) 2868c2ecf20Sopenharmony_ci return; 2878c2ecf20Sopenharmony_ci put_pipe_version(net); 2888c2ecf20Sopenharmony_ci BUG_ON(!list_empty(&gss_msg->list)); 2898c2ecf20Sopenharmony_ci if (gss_msg->ctx != NULL) 2908c2ecf20Sopenharmony_ci gss_put_ctx(gss_msg->ctx); 2918c2ecf20Sopenharmony_ci rpc_destroy_wait_queue(&gss_msg->rpc_waitqueue); 2928c2ecf20Sopenharmony_ci gss_put_auth(gss_msg->auth); 2938c2ecf20Sopenharmony_ci kfree_const(gss_msg->service_name); 2948c2ecf20Sopenharmony_ci kfree(gss_msg); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic struct gss_upcall_msg * 2988c2ecf20Sopenharmony_ci__gss_find_upcall(struct rpc_pipe *pipe, kuid_t uid, const struct gss_auth *auth) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct gss_upcall_msg *pos; 3018c2ecf20Sopenharmony_ci list_for_each_entry(pos, &pipe->in_downcall, list) { 3028c2ecf20Sopenharmony_ci if (!uid_eq(pos->uid, uid)) 3038c2ecf20Sopenharmony_ci continue; 3048c2ecf20Sopenharmony_ci if (pos->auth->service != auth->service) 3058c2ecf20Sopenharmony_ci continue; 3068c2ecf20Sopenharmony_ci refcount_inc(&pos->count); 3078c2ecf20Sopenharmony_ci return pos; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci return NULL; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci/* Try to add an upcall to the pipefs queue. 3138c2ecf20Sopenharmony_ci * If an upcall owned by our uid already exists, then we return a reference 3148c2ecf20Sopenharmony_ci * to that upcall instead of adding the new upcall. 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_cistatic inline struct gss_upcall_msg * 3178c2ecf20Sopenharmony_cigss_add_msg(struct gss_upcall_msg *gss_msg) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct rpc_pipe *pipe = gss_msg->pipe; 3208c2ecf20Sopenharmony_ci struct gss_upcall_msg *old; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci spin_lock(&pipe->lock); 3238c2ecf20Sopenharmony_ci old = __gss_find_upcall(pipe, gss_msg->uid, gss_msg->auth); 3248c2ecf20Sopenharmony_ci if (old == NULL) { 3258c2ecf20Sopenharmony_ci refcount_inc(&gss_msg->count); 3268c2ecf20Sopenharmony_ci list_add(&gss_msg->list, &pipe->in_downcall); 3278c2ecf20Sopenharmony_ci } else 3288c2ecf20Sopenharmony_ci gss_msg = old; 3298c2ecf20Sopenharmony_ci spin_unlock(&pipe->lock); 3308c2ecf20Sopenharmony_ci return gss_msg; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic void 3348c2ecf20Sopenharmony_ci__gss_unhash_msg(struct gss_upcall_msg *gss_msg) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci list_del_init(&gss_msg->list); 3378c2ecf20Sopenharmony_ci rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); 3388c2ecf20Sopenharmony_ci wake_up_all(&gss_msg->waitqueue); 3398c2ecf20Sopenharmony_ci refcount_dec(&gss_msg->count); 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic void 3438c2ecf20Sopenharmony_cigss_unhash_msg(struct gss_upcall_msg *gss_msg) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct rpc_pipe *pipe = gss_msg->pipe; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (list_empty(&gss_msg->list)) 3488c2ecf20Sopenharmony_ci return; 3498c2ecf20Sopenharmony_ci spin_lock(&pipe->lock); 3508c2ecf20Sopenharmony_ci if (!list_empty(&gss_msg->list)) 3518c2ecf20Sopenharmony_ci __gss_unhash_msg(gss_msg); 3528c2ecf20Sopenharmony_ci spin_unlock(&pipe->lock); 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic void 3568c2ecf20Sopenharmony_cigss_handle_downcall_result(struct gss_cred *gss_cred, struct gss_upcall_msg *gss_msg) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci switch (gss_msg->msg.errno) { 3598c2ecf20Sopenharmony_ci case 0: 3608c2ecf20Sopenharmony_ci if (gss_msg->ctx == NULL) 3618c2ecf20Sopenharmony_ci break; 3628c2ecf20Sopenharmony_ci clear_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags); 3638c2ecf20Sopenharmony_ci gss_cred_set_ctx(&gss_cred->gc_base, gss_msg->ctx); 3648c2ecf20Sopenharmony_ci break; 3658c2ecf20Sopenharmony_ci case -EKEYEXPIRED: 3668c2ecf20Sopenharmony_ci set_bit(RPCAUTH_CRED_NEGATIVE, &gss_cred->gc_base.cr_flags); 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci gss_cred->gc_upcall_timestamp = jiffies; 3698c2ecf20Sopenharmony_ci gss_cred->gc_upcall = NULL; 3708c2ecf20Sopenharmony_ci rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic void 3748c2ecf20Sopenharmony_cigss_upcall_callback(struct rpc_task *task) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct gss_cred *gss_cred = container_of(task->tk_rqstp->rq_cred, 3778c2ecf20Sopenharmony_ci struct gss_cred, gc_base); 3788c2ecf20Sopenharmony_ci struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall; 3798c2ecf20Sopenharmony_ci struct rpc_pipe *pipe = gss_msg->pipe; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci spin_lock(&pipe->lock); 3828c2ecf20Sopenharmony_ci gss_handle_downcall_result(gss_cred, gss_msg); 3838c2ecf20Sopenharmony_ci spin_unlock(&pipe->lock); 3848c2ecf20Sopenharmony_ci task->tk_status = gss_msg->msg.errno; 3858c2ecf20Sopenharmony_ci gss_release_msg(gss_msg); 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg, 3898c2ecf20Sopenharmony_ci const struct cred *cred) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct user_namespace *userns = cred->user_ns; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci uid_t uid = from_kuid_munged(userns, gss_msg->uid); 3948c2ecf20Sopenharmony_ci memcpy(gss_msg->databuf, &uid, sizeof(uid)); 3958c2ecf20Sopenharmony_ci gss_msg->msg.data = gss_msg->databuf; 3968c2ecf20Sopenharmony_ci gss_msg->msg.len = sizeof(uid); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(uid) > sizeof(gss_msg->databuf)); 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic ssize_t 4028c2ecf20Sopenharmony_cigss_v0_upcall(struct file *file, struct rpc_pipe_msg *msg, 4038c2ecf20Sopenharmony_ci char __user *buf, size_t buflen) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct gss_upcall_msg *gss_msg = container_of(msg, 4068c2ecf20Sopenharmony_ci struct gss_upcall_msg, 4078c2ecf20Sopenharmony_ci msg); 4088c2ecf20Sopenharmony_ci if (msg->copied == 0) 4098c2ecf20Sopenharmony_ci gss_encode_v0_msg(gss_msg, file->f_cred); 4108c2ecf20Sopenharmony_ci return rpc_pipe_generic_upcall(file, msg, buf, buflen); 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic int gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, 4148c2ecf20Sopenharmony_ci const char *service_name, 4158c2ecf20Sopenharmony_ci const char *target_name, 4168c2ecf20Sopenharmony_ci const struct cred *cred) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct user_namespace *userns = cred->user_ns; 4198c2ecf20Sopenharmony_ci struct gss_api_mech *mech = gss_msg->auth->mech; 4208c2ecf20Sopenharmony_ci char *p = gss_msg->databuf; 4218c2ecf20Sopenharmony_ci size_t buflen = sizeof(gss_msg->databuf); 4228c2ecf20Sopenharmony_ci int len; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci len = scnprintf(p, buflen, "mech=%s uid=%d", mech->gm_name, 4258c2ecf20Sopenharmony_ci from_kuid_munged(userns, gss_msg->uid)); 4268c2ecf20Sopenharmony_ci buflen -= len; 4278c2ecf20Sopenharmony_ci p += len; 4288c2ecf20Sopenharmony_ci gss_msg->msg.len = len; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* 4318c2ecf20Sopenharmony_ci * target= is a full service principal that names the remote 4328c2ecf20Sopenharmony_ci * identity that we are authenticating to. 4338c2ecf20Sopenharmony_ci */ 4348c2ecf20Sopenharmony_ci if (target_name) { 4358c2ecf20Sopenharmony_ci len = scnprintf(p, buflen, " target=%s", target_name); 4368c2ecf20Sopenharmony_ci buflen -= len; 4378c2ecf20Sopenharmony_ci p += len; 4388c2ecf20Sopenharmony_ci gss_msg->msg.len += len; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* 4428c2ecf20Sopenharmony_ci * gssd uses service= and srchost= to select a matching key from 4438c2ecf20Sopenharmony_ci * the system's keytab to use as the source principal. 4448c2ecf20Sopenharmony_ci * 4458c2ecf20Sopenharmony_ci * service= is the service name part of the source principal, 4468c2ecf20Sopenharmony_ci * or "*" (meaning choose any). 4478c2ecf20Sopenharmony_ci * 4488c2ecf20Sopenharmony_ci * srchost= is the hostname part of the source principal. When 4498c2ecf20Sopenharmony_ci * not provided, gssd uses the local hostname. 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_ci if (service_name) { 4528c2ecf20Sopenharmony_ci char *c = strchr(service_name, '@'); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (!c) 4558c2ecf20Sopenharmony_ci len = scnprintf(p, buflen, " service=%s", 4568c2ecf20Sopenharmony_ci service_name); 4578c2ecf20Sopenharmony_ci else 4588c2ecf20Sopenharmony_ci len = scnprintf(p, buflen, 4598c2ecf20Sopenharmony_ci " service=%.*s srchost=%s", 4608c2ecf20Sopenharmony_ci (int)(c - service_name), 4618c2ecf20Sopenharmony_ci service_name, c + 1); 4628c2ecf20Sopenharmony_ci buflen -= len; 4638c2ecf20Sopenharmony_ci p += len; 4648c2ecf20Sopenharmony_ci gss_msg->msg.len += len; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (mech->gm_upcall_enctypes) { 4688c2ecf20Sopenharmony_ci len = scnprintf(p, buflen, " enctypes=%s", 4698c2ecf20Sopenharmony_ci mech->gm_upcall_enctypes); 4708c2ecf20Sopenharmony_ci buflen -= len; 4718c2ecf20Sopenharmony_ci p += len; 4728c2ecf20Sopenharmony_ci gss_msg->msg.len += len; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci trace_rpcgss_upcall_msg(gss_msg->databuf); 4758c2ecf20Sopenharmony_ci len = scnprintf(p, buflen, "\n"); 4768c2ecf20Sopenharmony_ci if (len == 0) 4778c2ecf20Sopenharmony_ci goto out_overflow; 4788c2ecf20Sopenharmony_ci gss_msg->msg.len += len; 4798c2ecf20Sopenharmony_ci gss_msg->msg.data = gss_msg->databuf; 4808c2ecf20Sopenharmony_ci return 0; 4818c2ecf20Sopenharmony_ciout_overflow: 4828c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 4838c2ecf20Sopenharmony_ci return -ENOMEM; 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic ssize_t 4878c2ecf20Sopenharmony_cigss_v1_upcall(struct file *file, struct rpc_pipe_msg *msg, 4888c2ecf20Sopenharmony_ci char __user *buf, size_t buflen) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct gss_upcall_msg *gss_msg = container_of(msg, 4918c2ecf20Sopenharmony_ci struct gss_upcall_msg, 4928c2ecf20Sopenharmony_ci msg); 4938c2ecf20Sopenharmony_ci int err; 4948c2ecf20Sopenharmony_ci if (msg->copied == 0) { 4958c2ecf20Sopenharmony_ci err = gss_encode_v1_msg(gss_msg, 4968c2ecf20Sopenharmony_ci gss_msg->service_name, 4978c2ecf20Sopenharmony_ci gss_msg->auth->target_name, 4988c2ecf20Sopenharmony_ci file->f_cred); 4998c2ecf20Sopenharmony_ci if (err) 5008c2ecf20Sopenharmony_ci return err; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci return rpc_pipe_generic_upcall(file, msg, buf, buflen); 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic struct gss_upcall_msg * 5068c2ecf20Sopenharmony_cigss_alloc_msg(struct gss_auth *gss_auth, 5078c2ecf20Sopenharmony_ci kuid_t uid, const char *service_name) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct gss_upcall_msg *gss_msg; 5108c2ecf20Sopenharmony_ci int vers; 5118c2ecf20Sopenharmony_ci int err = -ENOMEM; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci gss_msg = kzalloc(sizeof(*gss_msg), GFP_NOFS); 5148c2ecf20Sopenharmony_ci if (gss_msg == NULL) 5158c2ecf20Sopenharmony_ci goto err; 5168c2ecf20Sopenharmony_ci vers = get_pipe_version(gss_auth->net); 5178c2ecf20Sopenharmony_ci err = vers; 5188c2ecf20Sopenharmony_ci if (err < 0) 5198c2ecf20Sopenharmony_ci goto err_free_msg; 5208c2ecf20Sopenharmony_ci gss_msg->pipe = gss_auth->gss_pipe[vers]->pipe; 5218c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&gss_msg->list); 5228c2ecf20Sopenharmony_ci rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq"); 5238c2ecf20Sopenharmony_ci init_waitqueue_head(&gss_msg->waitqueue); 5248c2ecf20Sopenharmony_ci refcount_set(&gss_msg->count, 1); 5258c2ecf20Sopenharmony_ci gss_msg->uid = uid; 5268c2ecf20Sopenharmony_ci gss_msg->auth = gss_auth; 5278c2ecf20Sopenharmony_ci kref_get(&gss_auth->kref); 5288c2ecf20Sopenharmony_ci if (service_name) { 5298c2ecf20Sopenharmony_ci gss_msg->service_name = kstrdup_const(service_name, GFP_NOFS); 5308c2ecf20Sopenharmony_ci if (!gss_msg->service_name) { 5318c2ecf20Sopenharmony_ci err = -ENOMEM; 5328c2ecf20Sopenharmony_ci goto err_put_pipe_version; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci return gss_msg; 5368c2ecf20Sopenharmony_cierr_put_pipe_version: 5378c2ecf20Sopenharmony_ci put_pipe_version(gss_auth->net); 5388c2ecf20Sopenharmony_cierr_free_msg: 5398c2ecf20Sopenharmony_ci kfree(gss_msg); 5408c2ecf20Sopenharmony_cierr: 5418c2ecf20Sopenharmony_ci return ERR_PTR(err); 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic struct gss_upcall_msg * 5458c2ecf20Sopenharmony_cigss_setup_upcall(struct gss_auth *gss_auth, struct rpc_cred *cred) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, 5488c2ecf20Sopenharmony_ci struct gss_cred, gc_base); 5498c2ecf20Sopenharmony_ci struct gss_upcall_msg *gss_new, *gss_msg; 5508c2ecf20Sopenharmony_ci kuid_t uid = cred->cr_cred->fsuid; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci gss_new = gss_alloc_msg(gss_auth, uid, gss_cred->gc_principal); 5538c2ecf20Sopenharmony_ci if (IS_ERR(gss_new)) 5548c2ecf20Sopenharmony_ci return gss_new; 5558c2ecf20Sopenharmony_ci gss_msg = gss_add_msg(gss_new); 5568c2ecf20Sopenharmony_ci if (gss_msg == gss_new) { 5578c2ecf20Sopenharmony_ci int res; 5588c2ecf20Sopenharmony_ci refcount_inc(&gss_msg->count); 5598c2ecf20Sopenharmony_ci res = rpc_queue_upcall(gss_new->pipe, &gss_new->msg); 5608c2ecf20Sopenharmony_ci if (res) { 5618c2ecf20Sopenharmony_ci gss_unhash_msg(gss_new); 5628c2ecf20Sopenharmony_ci refcount_dec(&gss_msg->count); 5638c2ecf20Sopenharmony_ci gss_release_msg(gss_new); 5648c2ecf20Sopenharmony_ci gss_msg = ERR_PTR(res); 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci } else 5678c2ecf20Sopenharmony_ci gss_release_msg(gss_new); 5688c2ecf20Sopenharmony_ci return gss_msg; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic void warn_gssd(void) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci dprintk("AUTH_GSS upcall failed. Please check user daemon is running.\n"); 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cistatic inline int 5778c2ecf20Sopenharmony_cigss_refresh_upcall(struct rpc_task *task) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci struct rpc_cred *cred = task->tk_rqstp->rq_cred; 5808c2ecf20Sopenharmony_ci struct gss_auth *gss_auth = container_of(cred->cr_auth, 5818c2ecf20Sopenharmony_ci struct gss_auth, rpc_auth); 5828c2ecf20Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, 5838c2ecf20Sopenharmony_ci struct gss_cred, gc_base); 5848c2ecf20Sopenharmony_ci struct gss_upcall_msg *gss_msg; 5858c2ecf20Sopenharmony_ci struct rpc_pipe *pipe; 5868c2ecf20Sopenharmony_ci int err = 0; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci gss_msg = gss_setup_upcall(gss_auth, cred); 5898c2ecf20Sopenharmony_ci if (PTR_ERR(gss_msg) == -EAGAIN) { 5908c2ecf20Sopenharmony_ci /* XXX: warning on the first, under the assumption we 5918c2ecf20Sopenharmony_ci * shouldn't normally hit this case on a refresh. */ 5928c2ecf20Sopenharmony_ci warn_gssd(); 5938c2ecf20Sopenharmony_ci rpc_sleep_on_timeout(&pipe_version_rpc_waitqueue, 5948c2ecf20Sopenharmony_ci task, NULL, jiffies + (15 * HZ)); 5958c2ecf20Sopenharmony_ci err = -EAGAIN; 5968c2ecf20Sopenharmony_ci goto out; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci if (IS_ERR(gss_msg)) { 5998c2ecf20Sopenharmony_ci err = PTR_ERR(gss_msg); 6008c2ecf20Sopenharmony_ci goto out; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci pipe = gss_msg->pipe; 6038c2ecf20Sopenharmony_ci spin_lock(&pipe->lock); 6048c2ecf20Sopenharmony_ci if (gss_cred->gc_upcall != NULL) 6058c2ecf20Sopenharmony_ci rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL); 6068c2ecf20Sopenharmony_ci else if (gss_msg->ctx == NULL && gss_msg->msg.errno >= 0) { 6078c2ecf20Sopenharmony_ci gss_cred->gc_upcall = gss_msg; 6088c2ecf20Sopenharmony_ci /* gss_upcall_callback will release the reference to gss_upcall_msg */ 6098c2ecf20Sopenharmony_ci refcount_inc(&gss_msg->count); 6108c2ecf20Sopenharmony_ci rpc_sleep_on(&gss_msg->rpc_waitqueue, task, gss_upcall_callback); 6118c2ecf20Sopenharmony_ci } else { 6128c2ecf20Sopenharmony_ci gss_handle_downcall_result(gss_cred, gss_msg); 6138c2ecf20Sopenharmony_ci err = gss_msg->msg.errno; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci spin_unlock(&pipe->lock); 6168c2ecf20Sopenharmony_ci gss_release_msg(gss_msg); 6178c2ecf20Sopenharmony_ciout: 6188c2ecf20Sopenharmony_ci trace_rpcgss_upcall_result(from_kuid(&init_user_ns, 6198c2ecf20Sopenharmony_ci cred->cr_cred->fsuid), err); 6208c2ecf20Sopenharmony_ci return err; 6218c2ecf20Sopenharmony_ci} 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cistatic inline int 6248c2ecf20Sopenharmony_cigss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci struct net *net = gss_auth->net; 6278c2ecf20Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 6288c2ecf20Sopenharmony_ci struct rpc_pipe *pipe; 6298c2ecf20Sopenharmony_ci struct rpc_cred *cred = &gss_cred->gc_base; 6308c2ecf20Sopenharmony_ci struct gss_upcall_msg *gss_msg; 6318c2ecf20Sopenharmony_ci DEFINE_WAIT(wait); 6328c2ecf20Sopenharmony_ci int err; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ciretry: 6358c2ecf20Sopenharmony_ci err = 0; 6368c2ecf20Sopenharmony_ci /* if gssd is down, just skip upcalling altogether */ 6378c2ecf20Sopenharmony_ci if (!gssd_running(net)) { 6388c2ecf20Sopenharmony_ci warn_gssd(); 6398c2ecf20Sopenharmony_ci err = -EACCES; 6408c2ecf20Sopenharmony_ci goto out; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci gss_msg = gss_setup_upcall(gss_auth, cred); 6438c2ecf20Sopenharmony_ci if (PTR_ERR(gss_msg) == -EAGAIN) { 6448c2ecf20Sopenharmony_ci err = wait_event_interruptible_timeout(pipe_version_waitqueue, 6458c2ecf20Sopenharmony_ci sn->pipe_version >= 0, 15 * HZ); 6468c2ecf20Sopenharmony_ci if (sn->pipe_version < 0) { 6478c2ecf20Sopenharmony_ci warn_gssd(); 6488c2ecf20Sopenharmony_ci err = -EACCES; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci if (err < 0) 6518c2ecf20Sopenharmony_ci goto out; 6528c2ecf20Sopenharmony_ci goto retry; 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci if (IS_ERR(gss_msg)) { 6558c2ecf20Sopenharmony_ci err = PTR_ERR(gss_msg); 6568c2ecf20Sopenharmony_ci goto out; 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci pipe = gss_msg->pipe; 6598c2ecf20Sopenharmony_ci for (;;) { 6608c2ecf20Sopenharmony_ci prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_KILLABLE); 6618c2ecf20Sopenharmony_ci spin_lock(&pipe->lock); 6628c2ecf20Sopenharmony_ci if (gss_msg->ctx != NULL || gss_msg->msg.errno < 0) { 6638c2ecf20Sopenharmony_ci break; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci spin_unlock(&pipe->lock); 6668c2ecf20Sopenharmony_ci if (fatal_signal_pending(current)) { 6678c2ecf20Sopenharmony_ci err = -ERESTARTSYS; 6688c2ecf20Sopenharmony_ci goto out_intr; 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci schedule(); 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci if (gss_msg->ctx) { 6738c2ecf20Sopenharmony_ci trace_rpcgss_ctx_init(gss_cred); 6748c2ecf20Sopenharmony_ci gss_cred_set_ctx(cred, gss_msg->ctx); 6758c2ecf20Sopenharmony_ci } else { 6768c2ecf20Sopenharmony_ci err = gss_msg->msg.errno; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci spin_unlock(&pipe->lock); 6798c2ecf20Sopenharmony_ciout_intr: 6808c2ecf20Sopenharmony_ci finish_wait(&gss_msg->waitqueue, &wait); 6818c2ecf20Sopenharmony_ci gss_release_msg(gss_msg); 6828c2ecf20Sopenharmony_ciout: 6838c2ecf20Sopenharmony_ci trace_rpcgss_upcall_result(from_kuid(&init_user_ns, 6848c2ecf20Sopenharmony_ci cred->cr_cred->fsuid), err); 6858c2ecf20Sopenharmony_ci return err; 6868c2ecf20Sopenharmony_ci} 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_cistatic struct gss_upcall_msg * 6898c2ecf20Sopenharmony_cigss_find_downcall(struct rpc_pipe *pipe, kuid_t uid) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci struct gss_upcall_msg *pos; 6928c2ecf20Sopenharmony_ci list_for_each_entry(pos, &pipe->in_downcall, list) { 6938c2ecf20Sopenharmony_ci if (!uid_eq(pos->uid, uid)) 6948c2ecf20Sopenharmony_ci continue; 6958c2ecf20Sopenharmony_ci if (!rpc_msg_is_inflight(&pos->msg)) 6968c2ecf20Sopenharmony_ci continue; 6978c2ecf20Sopenharmony_ci refcount_inc(&pos->count); 6988c2ecf20Sopenharmony_ci return pos; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci return NULL; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci#define MSG_BUF_MAXSIZE 1024 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_cistatic ssize_t 7068c2ecf20Sopenharmony_cigss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci const void *p, *end; 7098c2ecf20Sopenharmony_ci void *buf; 7108c2ecf20Sopenharmony_ci struct gss_upcall_msg *gss_msg; 7118c2ecf20Sopenharmony_ci struct rpc_pipe *pipe = RPC_I(file_inode(filp))->pipe; 7128c2ecf20Sopenharmony_ci struct gss_cl_ctx *ctx; 7138c2ecf20Sopenharmony_ci uid_t id; 7148c2ecf20Sopenharmony_ci kuid_t uid; 7158c2ecf20Sopenharmony_ci ssize_t err = -EFBIG; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (mlen > MSG_BUF_MAXSIZE) 7188c2ecf20Sopenharmony_ci goto out; 7198c2ecf20Sopenharmony_ci err = -ENOMEM; 7208c2ecf20Sopenharmony_ci buf = kmalloc(mlen, GFP_NOFS); 7218c2ecf20Sopenharmony_ci if (!buf) 7228c2ecf20Sopenharmony_ci goto out; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci err = -EFAULT; 7258c2ecf20Sopenharmony_ci if (copy_from_user(buf, src, mlen)) 7268c2ecf20Sopenharmony_ci goto err; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci end = (const void *)((char *)buf + mlen); 7298c2ecf20Sopenharmony_ci p = simple_get_bytes(buf, end, &id, sizeof(id)); 7308c2ecf20Sopenharmony_ci if (IS_ERR(p)) { 7318c2ecf20Sopenharmony_ci err = PTR_ERR(p); 7328c2ecf20Sopenharmony_ci goto err; 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci uid = make_kuid(current_user_ns(), id); 7368c2ecf20Sopenharmony_ci if (!uid_valid(uid)) { 7378c2ecf20Sopenharmony_ci err = -EINVAL; 7388c2ecf20Sopenharmony_ci goto err; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci err = -ENOMEM; 7428c2ecf20Sopenharmony_ci ctx = gss_alloc_context(); 7438c2ecf20Sopenharmony_ci if (ctx == NULL) 7448c2ecf20Sopenharmony_ci goto err; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci err = -ENOENT; 7478c2ecf20Sopenharmony_ci /* Find a matching upcall */ 7488c2ecf20Sopenharmony_ci spin_lock(&pipe->lock); 7498c2ecf20Sopenharmony_ci gss_msg = gss_find_downcall(pipe, uid); 7508c2ecf20Sopenharmony_ci if (gss_msg == NULL) { 7518c2ecf20Sopenharmony_ci spin_unlock(&pipe->lock); 7528c2ecf20Sopenharmony_ci goto err_put_ctx; 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci list_del_init(&gss_msg->list); 7558c2ecf20Sopenharmony_ci spin_unlock(&pipe->lock); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci p = gss_fill_context(p, end, ctx, gss_msg->auth->mech); 7588c2ecf20Sopenharmony_ci if (IS_ERR(p)) { 7598c2ecf20Sopenharmony_ci err = PTR_ERR(p); 7608c2ecf20Sopenharmony_ci switch (err) { 7618c2ecf20Sopenharmony_ci case -EACCES: 7628c2ecf20Sopenharmony_ci case -EKEYEXPIRED: 7638c2ecf20Sopenharmony_ci gss_msg->msg.errno = err; 7648c2ecf20Sopenharmony_ci err = mlen; 7658c2ecf20Sopenharmony_ci break; 7668c2ecf20Sopenharmony_ci case -EFAULT: 7678c2ecf20Sopenharmony_ci case -ENOMEM: 7688c2ecf20Sopenharmony_ci case -EINVAL: 7698c2ecf20Sopenharmony_ci case -ENOSYS: 7708c2ecf20Sopenharmony_ci gss_msg->msg.errno = -EAGAIN; 7718c2ecf20Sopenharmony_ci break; 7728c2ecf20Sopenharmony_ci default: 7738c2ecf20Sopenharmony_ci printk(KERN_CRIT "%s: bad return from " 7748c2ecf20Sopenharmony_ci "gss_fill_context: %zd\n", __func__, err); 7758c2ecf20Sopenharmony_ci gss_msg->msg.errno = -EIO; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci goto err_release_msg; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci gss_msg->ctx = gss_get_ctx(ctx); 7808c2ecf20Sopenharmony_ci err = mlen; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_cierr_release_msg: 7838c2ecf20Sopenharmony_ci spin_lock(&pipe->lock); 7848c2ecf20Sopenharmony_ci __gss_unhash_msg(gss_msg); 7858c2ecf20Sopenharmony_ci spin_unlock(&pipe->lock); 7868c2ecf20Sopenharmony_ci gss_release_msg(gss_msg); 7878c2ecf20Sopenharmony_cierr_put_ctx: 7888c2ecf20Sopenharmony_ci gss_put_ctx(ctx); 7898c2ecf20Sopenharmony_cierr: 7908c2ecf20Sopenharmony_ci kfree(buf); 7918c2ecf20Sopenharmony_ciout: 7928c2ecf20Sopenharmony_ci return err; 7938c2ecf20Sopenharmony_ci} 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_cistatic int gss_pipe_open(struct inode *inode, int new_version) 7968c2ecf20Sopenharmony_ci{ 7978c2ecf20Sopenharmony_ci struct net *net = inode->i_sb->s_fs_info; 7988c2ecf20Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 7998c2ecf20Sopenharmony_ci int ret = 0; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci spin_lock(&pipe_version_lock); 8028c2ecf20Sopenharmony_ci if (sn->pipe_version < 0) { 8038c2ecf20Sopenharmony_ci /* First open of any gss pipe determines the version: */ 8048c2ecf20Sopenharmony_ci sn->pipe_version = new_version; 8058c2ecf20Sopenharmony_ci rpc_wake_up(&pipe_version_rpc_waitqueue); 8068c2ecf20Sopenharmony_ci wake_up(&pipe_version_waitqueue); 8078c2ecf20Sopenharmony_ci } else if (sn->pipe_version != new_version) { 8088c2ecf20Sopenharmony_ci /* Trying to open a pipe of a different version */ 8098c2ecf20Sopenharmony_ci ret = -EBUSY; 8108c2ecf20Sopenharmony_ci goto out; 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci atomic_inc(&sn->pipe_users); 8138c2ecf20Sopenharmony_ciout: 8148c2ecf20Sopenharmony_ci spin_unlock(&pipe_version_lock); 8158c2ecf20Sopenharmony_ci return ret; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci} 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_cistatic int gss_pipe_open_v0(struct inode *inode) 8208c2ecf20Sopenharmony_ci{ 8218c2ecf20Sopenharmony_ci return gss_pipe_open(inode, 0); 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic int gss_pipe_open_v1(struct inode *inode) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci return gss_pipe_open(inode, 1); 8278c2ecf20Sopenharmony_ci} 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_cistatic void 8308c2ecf20Sopenharmony_cigss_pipe_release(struct inode *inode) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci struct net *net = inode->i_sb->s_fs_info; 8338c2ecf20Sopenharmony_ci struct rpc_pipe *pipe = RPC_I(inode)->pipe; 8348c2ecf20Sopenharmony_ci struct gss_upcall_msg *gss_msg; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_cirestart: 8378c2ecf20Sopenharmony_ci spin_lock(&pipe->lock); 8388c2ecf20Sopenharmony_ci list_for_each_entry(gss_msg, &pipe->in_downcall, list) { 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci if (!list_empty(&gss_msg->msg.list)) 8418c2ecf20Sopenharmony_ci continue; 8428c2ecf20Sopenharmony_ci gss_msg->msg.errno = -EPIPE; 8438c2ecf20Sopenharmony_ci refcount_inc(&gss_msg->count); 8448c2ecf20Sopenharmony_ci __gss_unhash_msg(gss_msg); 8458c2ecf20Sopenharmony_ci spin_unlock(&pipe->lock); 8468c2ecf20Sopenharmony_ci gss_release_msg(gss_msg); 8478c2ecf20Sopenharmony_ci goto restart; 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci spin_unlock(&pipe->lock); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci put_pipe_version(net); 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_cistatic void 8558c2ecf20Sopenharmony_cigss_pipe_destroy_msg(struct rpc_pipe_msg *msg) 8568c2ecf20Sopenharmony_ci{ 8578c2ecf20Sopenharmony_ci struct gss_upcall_msg *gss_msg = container_of(msg, struct gss_upcall_msg, msg); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if (msg->errno < 0) { 8608c2ecf20Sopenharmony_ci refcount_inc(&gss_msg->count); 8618c2ecf20Sopenharmony_ci gss_unhash_msg(gss_msg); 8628c2ecf20Sopenharmony_ci if (msg->errno == -ETIMEDOUT) 8638c2ecf20Sopenharmony_ci warn_gssd(); 8648c2ecf20Sopenharmony_ci gss_release_msg(gss_msg); 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci gss_release_msg(gss_msg); 8678c2ecf20Sopenharmony_ci} 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_cistatic void gss_pipe_dentry_destroy(struct dentry *dir, 8708c2ecf20Sopenharmony_ci struct rpc_pipe_dir_object *pdo) 8718c2ecf20Sopenharmony_ci{ 8728c2ecf20Sopenharmony_ci struct gss_pipe *gss_pipe = pdo->pdo_data; 8738c2ecf20Sopenharmony_ci struct rpc_pipe *pipe = gss_pipe->pipe; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci if (pipe->dentry != NULL) { 8768c2ecf20Sopenharmony_ci rpc_unlink(pipe->dentry); 8778c2ecf20Sopenharmony_ci pipe->dentry = NULL; 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci} 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_cistatic int gss_pipe_dentry_create(struct dentry *dir, 8828c2ecf20Sopenharmony_ci struct rpc_pipe_dir_object *pdo) 8838c2ecf20Sopenharmony_ci{ 8848c2ecf20Sopenharmony_ci struct gss_pipe *p = pdo->pdo_data; 8858c2ecf20Sopenharmony_ci struct dentry *dentry; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci dentry = rpc_mkpipe_dentry(dir, p->name, p->clnt, p->pipe); 8888c2ecf20Sopenharmony_ci if (IS_ERR(dentry)) 8898c2ecf20Sopenharmony_ci return PTR_ERR(dentry); 8908c2ecf20Sopenharmony_ci p->pipe->dentry = dentry; 8918c2ecf20Sopenharmony_ci return 0; 8928c2ecf20Sopenharmony_ci} 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_cistatic const struct rpc_pipe_dir_object_ops gss_pipe_dir_object_ops = { 8958c2ecf20Sopenharmony_ci .create = gss_pipe_dentry_create, 8968c2ecf20Sopenharmony_ci .destroy = gss_pipe_dentry_destroy, 8978c2ecf20Sopenharmony_ci}; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic struct gss_pipe *gss_pipe_alloc(struct rpc_clnt *clnt, 9008c2ecf20Sopenharmony_ci const char *name, 9018c2ecf20Sopenharmony_ci const struct rpc_pipe_ops *upcall_ops) 9028c2ecf20Sopenharmony_ci{ 9038c2ecf20Sopenharmony_ci struct gss_pipe *p; 9048c2ecf20Sopenharmony_ci int err = -ENOMEM; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci p = kmalloc(sizeof(*p), GFP_KERNEL); 9078c2ecf20Sopenharmony_ci if (p == NULL) 9088c2ecf20Sopenharmony_ci goto err; 9098c2ecf20Sopenharmony_ci p->pipe = rpc_mkpipe_data(upcall_ops, RPC_PIPE_WAIT_FOR_OPEN); 9108c2ecf20Sopenharmony_ci if (IS_ERR(p->pipe)) { 9118c2ecf20Sopenharmony_ci err = PTR_ERR(p->pipe); 9128c2ecf20Sopenharmony_ci goto err_free_gss_pipe; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci p->name = name; 9158c2ecf20Sopenharmony_ci p->clnt = clnt; 9168c2ecf20Sopenharmony_ci kref_init(&p->kref); 9178c2ecf20Sopenharmony_ci rpc_init_pipe_dir_object(&p->pdo, 9188c2ecf20Sopenharmony_ci &gss_pipe_dir_object_ops, 9198c2ecf20Sopenharmony_ci p); 9208c2ecf20Sopenharmony_ci return p; 9218c2ecf20Sopenharmony_cierr_free_gss_pipe: 9228c2ecf20Sopenharmony_ci kfree(p); 9238c2ecf20Sopenharmony_cierr: 9248c2ecf20Sopenharmony_ci return ERR_PTR(err); 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_cistruct gss_alloc_pdo { 9288c2ecf20Sopenharmony_ci struct rpc_clnt *clnt; 9298c2ecf20Sopenharmony_ci const char *name; 9308c2ecf20Sopenharmony_ci const struct rpc_pipe_ops *upcall_ops; 9318c2ecf20Sopenharmony_ci}; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_cistatic int gss_pipe_match_pdo(struct rpc_pipe_dir_object *pdo, void *data) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci struct gss_pipe *gss_pipe; 9368c2ecf20Sopenharmony_ci struct gss_alloc_pdo *args = data; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci if (pdo->pdo_ops != &gss_pipe_dir_object_ops) 9398c2ecf20Sopenharmony_ci return 0; 9408c2ecf20Sopenharmony_ci gss_pipe = container_of(pdo, struct gss_pipe, pdo); 9418c2ecf20Sopenharmony_ci if (strcmp(gss_pipe->name, args->name) != 0) 9428c2ecf20Sopenharmony_ci return 0; 9438c2ecf20Sopenharmony_ci if (!kref_get_unless_zero(&gss_pipe->kref)) 9448c2ecf20Sopenharmony_ci return 0; 9458c2ecf20Sopenharmony_ci return 1; 9468c2ecf20Sopenharmony_ci} 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_cistatic struct rpc_pipe_dir_object *gss_pipe_alloc_pdo(void *data) 9498c2ecf20Sopenharmony_ci{ 9508c2ecf20Sopenharmony_ci struct gss_pipe *gss_pipe; 9518c2ecf20Sopenharmony_ci struct gss_alloc_pdo *args = data; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci gss_pipe = gss_pipe_alloc(args->clnt, args->name, args->upcall_ops); 9548c2ecf20Sopenharmony_ci if (!IS_ERR(gss_pipe)) 9558c2ecf20Sopenharmony_ci return &gss_pipe->pdo; 9568c2ecf20Sopenharmony_ci return NULL; 9578c2ecf20Sopenharmony_ci} 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_cistatic struct gss_pipe *gss_pipe_get(struct rpc_clnt *clnt, 9608c2ecf20Sopenharmony_ci const char *name, 9618c2ecf20Sopenharmony_ci const struct rpc_pipe_ops *upcall_ops) 9628c2ecf20Sopenharmony_ci{ 9638c2ecf20Sopenharmony_ci struct net *net = rpc_net_ns(clnt); 9648c2ecf20Sopenharmony_ci struct rpc_pipe_dir_object *pdo; 9658c2ecf20Sopenharmony_ci struct gss_alloc_pdo args = { 9668c2ecf20Sopenharmony_ci .clnt = clnt, 9678c2ecf20Sopenharmony_ci .name = name, 9688c2ecf20Sopenharmony_ci .upcall_ops = upcall_ops, 9698c2ecf20Sopenharmony_ci }; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci pdo = rpc_find_or_alloc_pipe_dir_object(net, 9728c2ecf20Sopenharmony_ci &clnt->cl_pipedir_objects, 9738c2ecf20Sopenharmony_ci gss_pipe_match_pdo, 9748c2ecf20Sopenharmony_ci gss_pipe_alloc_pdo, 9758c2ecf20Sopenharmony_ci &args); 9768c2ecf20Sopenharmony_ci if (pdo != NULL) 9778c2ecf20Sopenharmony_ci return container_of(pdo, struct gss_pipe, pdo); 9788c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 9798c2ecf20Sopenharmony_ci} 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_cistatic void __gss_pipe_free(struct gss_pipe *p) 9828c2ecf20Sopenharmony_ci{ 9838c2ecf20Sopenharmony_ci struct rpc_clnt *clnt = p->clnt; 9848c2ecf20Sopenharmony_ci struct net *net = rpc_net_ns(clnt); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci rpc_remove_pipe_dir_object(net, 9878c2ecf20Sopenharmony_ci &clnt->cl_pipedir_objects, 9888c2ecf20Sopenharmony_ci &p->pdo); 9898c2ecf20Sopenharmony_ci rpc_destroy_pipe_data(p->pipe); 9908c2ecf20Sopenharmony_ci kfree(p); 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_cistatic void __gss_pipe_release(struct kref *kref) 9948c2ecf20Sopenharmony_ci{ 9958c2ecf20Sopenharmony_ci struct gss_pipe *p = container_of(kref, struct gss_pipe, kref); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci __gss_pipe_free(p); 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_cistatic void gss_pipe_free(struct gss_pipe *p) 10018c2ecf20Sopenharmony_ci{ 10028c2ecf20Sopenharmony_ci if (p != NULL) 10038c2ecf20Sopenharmony_ci kref_put(&p->kref, __gss_pipe_release); 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci/* 10078c2ecf20Sopenharmony_ci * NOTE: we have the opportunity to use different 10088c2ecf20Sopenharmony_ci * parameters based on the input flavor (which must be a pseudoflavor) 10098c2ecf20Sopenharmony_ci */ 10108c2ecf20Sopenharmony_cistatic struct gss_auth * 10118c2ecf20Sopenharmony_cigss_create_new(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) 10128c2ecf20Sopenharmony_ci{ 10138c2ecf20Sopenharmony_ci rpc_authflavor_t flavor = args->pseudoflavor; 10148c2ecf20Sopenharmony_ci struct gss_auth *gss_auth; 10158c2ecf20Sopenharmony_ci struct gss_pipe *gss_pipe; 10168c2ecf20Sopenharmony_ci struct rpc_auth * auth; 10178c2ecf20Sopenharmony_ci int err = -ENOMEM; /* XXX? */ 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci if (!try_module_get(THIS_MODULE)) 10208c2ecf20Sopenharmony_ci return ERR_PTR(err); 10218c2ecf20Sopenharmony_ci if (!(gss_auth = kmalloc(sizeof(*gss_auth), GFP_KERNEL))) 10228c2ecf20Sopenharmony_ci goto out_dec; 10238c2ecf20Sopenharmony_ci INIT_HLIST_NODE(&gss_auth->hash); 10248c2ecf20Sopenharmony_ci gss_auth->target_name = NULL; 10258c2ecf20Sopenharmony_ci if (args->target_name) { 10268c2ecf20Sopenharmony_ci gss_auth->target_name = kstrdup(args->target_name, GFP_KERNEL); 10278c2ecf20Sopenharmony_ci if (gss_auth->target_name == NULL) 10288c2ecf20Sopenharmony_ci goto err_free; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci gss_auth->client = clnt; 10318c2ecf20Sopenharmony_ci gss_auth->net = get_net(rpc_net_ns(clnt)); 10328c2ecf20Sopenharmony_ci err = -EINVAL; 10338c2ecf20Sopenharmony_ci gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor); 10348c2ecf20Sopenharmony_ci if (!gss_auth->mech) 10358c2ecf20Sopenharmony_ci goto err_put_net; 10368c2ecf20Sopenharmony_ci gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor); 10378c2ecf20Sopenharmony_ci if (gss_auth->service == 0) 10388c2ecf20Sopenharmony_ci goto err_put_mech; 10398c2ecf20Sopenharmony_ci if (!gssd_running(gss_auth->net)) 10408c2ecf20Sopenharmony_ci goto err_put_mech; 10418c2ecf20Sopenharmony_ci auth = &gss_auth->rpc_auth; 10428c2ecf20Sopenharmony_ci auth->au_cslack = GSS_CRED_SLACK >> 2; 10438c2ecf20Sopenharmony_ci auth->au_rslack = GSS_KRB5_MAX_SLACK_NEEDED >> 2; 10448c2ecf20Sopenharmony_ci auth->au_verfsize = GSS_VERF_SLACK >> 2; 10458c2ecf20Sopenharmony_ci auth->au_ralign = GSS_VERF_SLACK >> 2; 10468c2ecf20Sopenharmony_ci __set_bit(RPCAUTH_AUTH_UPDATE_SLACK, &auth->au_flags); 10478c2ecf20Sopenharmony_ci auth->au_ops = &authgss_ops; 10488c2ecf20Sopenharmony_ci auth->au_flavor = flavor; 10498c2ecf20Sopenharmony_ci if (gss_pseudoflavor_to_datatouch(gss_auth->mech, flavor)) 10508c2ecf20Sopenharmony_ci __set_bit(RPCAUTH_AUTH_DATATOUCH, &auth->au_flags); 10518c2ecf20Sopenharmony_ci refcount_set(&auth->au_count, 1); 10528c2ecf20Sopenharmony_ci kref_init(&gss_auth->kref); 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci err = rpcauth_init_credcache(auth); 10558c2ecf20Sopenharmony_ci if (err) 10568c2ecf20Sopenharmony_ci goto err_put_mech; 10578c2ecf20Sopenharmony_ci /* 10588c2ecf20Sopenharmony_ci * Note: if we created the old pipe first, then someone who 10598c2ecf20Sopenharmony_ci * examined the directory at the right moment might conclude 10608c2ecf20Sopenharmony_ci * that we supported only the old pipe. So we instead create 10618c2ecf20Sopenharmony_ci * the new pipe first. 10628c2ecf20Sopenharmony_ci */ 10638c2ecf20Sopenharmony_ci gss_pipe = gss_pipe_get(clnt, "gssd", &gss_upcall_ops_v1); 10648c2ecf20Sopenharmony_ci if (IS_ERR(gss_pipe)) { 10658c2ecf20Sopenharmony_ci err = PTR_ERR(gss_pipe); 10668c2ecf20Sopenharmony_ci goto err_destroy_credcache; 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci gss_auth->gss_pipe[1] = gss_pipe; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci gss_pipe = gss_pipe_get(clnt, gss_auth->mech->gm_name, 10718c2ecf20Sopenharmony_ci &gss_upcall_ops_v0); 10728c2ecf20Sopenharmony_ci if (IS_ERR(gss_pipe)) { 10738c2ecf20Sopenharmony_ci err = PTR_ERR(gss_pipe); 10748c2ecf20Sopenharmony_ci goto err_destroy_pipe_1; 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci gss_auth->gss_pipe[0] = gss_pipe; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci return gss_auth; 10798c2ecf20Sopenharmony_cierr_destroy_pipe_1: 10808c2ecf20Sopenharmony_ci gss_pipe_free(gss_auth->gss_pipe[1]); 10818c2ecf20Sopenharmony_cierr_destroy_credcache: 10828c2ecf20Sopenharmony_ci rpcauth_destroy_credcache(auth); 10838c2ecf20Sopenharmony_cierr_put_mech: 10848c2ecf20Sopenharmony_ci gss_mech_put(gss_auth->mech); 10858c2ecf20Sopenharmony_cierr_put_net: 10868c2ecf20Sopenharmony_ci put_net(gss_auth->net); 10878c2ecf20Sopenharmony_cierr_free: 10888c2ecf20Sopenharmony_ci kfree(gss_auth->target_name); 10898c2ecf20Sopenharmony_ci kfree(gss_auth); 10908c2ecf20Sopenharmony_ciout_dec: 10918c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 10928c2ecf20Sopenharmony_ci trace_rpcgss_createauth(flavor, err); 10938c2ecf20Sopenharmony_ci return ERR_PTR(err); 10948c2ecf20Sopenharmony_ci} 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_cistatic void 10978c2ecf20Sopenharmony_cigss_free(struct gss_auth *gss_auth) 10988c2ecf20Sopenharmony_ci{ 10998c2ecf20Sopenharmony_ci gss_pipe_free(gss_auth->gss_pipe[0]); 11008c2ecf20Sopenharmony_ci gss_pipe_free(gss_auth->gss_pipe[1]); 11018c2ecf20Sopenharmony_ci gss_mech_put(gss_auth->mech); 11028c2ecf20Sopenharmony_ci put_net(gss_auth->net); 11038c2ecf20Sopenharmony_ci kfree(gss_auth->target_name); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci kfree(gss_auth); 11068c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 11078c2ecf20Sopenharmony_ci} 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_cistatic void 11108c2ecf20Sopenharmony_cigss_free_callback(struct kref *kref) 11118c2ecf20Sopenharmony_ci{ 11128c2ecf20Sopenharmony_ci struct gss_auth *gss_auth = container_of(kref, struct gss_auth, kref); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci gss_free(gss_auth); 11158c2ecf20Sopenharmony_ci} 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_cistatic void 11188c2ecf20Sopenharmony_cigss_put_auth(struct gss_auth *gss_auth) 11198c2ecf20Sopenharmony_ci{ 11208c2ecf20Sopenharmony_ci kref_put(&gss_auth->kref, gss_free_callback); 11218c2ecf20Sopenharmony_ci} 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_cistatic void 11248c2ecf20Sopenharmony_cigss_destroy(struct rpc_auth *auth) 11258c2ecf20Sopenharmony_ci{ 11268c2ecf20Sopenharmony_ci struct gss_auth *gss_auth = container_of(auth, 11278c2ecf20Sopenharmony_ci struct gss_auth, rpc_auth); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci if (hash_hashed(&gss_auth->hash)) { 11308c2ecf20Sopenharmony_ci spin_lock(&gss_auth_hash_lock); 11318c2ecf20Sopenharmony_ci hash_del(&gss_auth->hash); 11328c2ecf20Sopenharmony_ci spin_unlock(&gss_auth_hash_lock); 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci gss_pipe_free(gss_auth->gss_pipe[0]); 11368c2ecf20Sopenharmony_ci gss_auth->gss_pipe[0] = NULL; 11378c2ecf20Sopenharmony_ci gss_pipe_free(gss_auth->gss_pipe[1]); 11388c2ecf20Sopenharmony_ci gss_auth->gss_pipe[1] = NULL; 11398c2ecf20Sopenharmony_ci rpcauth_destroy_credcache(auth); 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci gss_put_auth(gss_auth); 11428c2ecf20Sopenharmony_ci} 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci/* 11458c2ecf20Sopenharmony_ci * Auths may be shared between rpc clients that were cloned from a 11468c2ecf20Sopenharmony_ci * common client with the same xprt, if they also share the flavor and 11478c2ecf20Sopenharmony_ci * target_name. 11488c2ecf20Sopenharmony_ci * 11498c2ecf20Sopenharmony_ci * The auth is looked up from the oldest parent sharing the same 11508c2ecf20Sopenharmony_ci * cl_xprt, and the auth itself references only that common parent 11518c2ecf20Sopenharmony_ci * (which is guaranteed to last as long as any of its descendants). 11528c2ecf20Sopenharmony_ci */ 11538c2ecf20Sopenharmony_cistatic struct gss_auth * 11548c2ecf20Sopenharmony_cigss_auth_find_or_add_hashed(const struct rpc_auth_create_args *args, 11558c2ecf20Sopenharmony_ci struct rpc_clnt *clnt, 11568c2ecf20Sopenharmony_ci struct gss_auth *new) 11578c2ecf20Sopenharmony_ci{ 11588c2ecf20Sopenharmony_ci struct gss_auth *gss_auth; 11598c2ecf20Sopenharmony_ci unsigned long hashval = (unsigned long)clnt; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci spin_lock(&gss_auth_hash_lock); 11628c2ecf20Sopenharmony_ci hash_for_each_possible(gss_auth_hash_table, 11638c2ecf20Sopenharmony_ci gss_auth, 11648c2ecf20Sopenharmony_ci hash, 11658c2ecf20Sopenharmony_ci hashval) { 11668c2ecf20Sopenharmony_ci if (gss_auth->client != clnt) 11678c2ecf20Sopenharmony_ci continue; 11688c2ecf20Sopenharmony_ci if (gss_auth->rpc_auth.au_flavor != args->pseudoflavor) 11698c2ecf20Sopenharmony_ci continue; 11708c2ecf20Sopenharmony_ci if (gss_auth->target_name != args->target_name) { 11718c2ecf20Sopenharmony_ci if (gss_auth->target_name == NULL) 11728c2ecf20Sopenharmony_ci continue; 11738c2ecf20Sopenharmony_ci if (args->target_name == NULL) 11748c2ecf20Sopenharmony_ci continue; 11758c2ecf20Sopenharmony_ci if (strcmp(gss_auth->target_name, args->target_name)) 11768c2ecf20Sopenharmony_ci continue; 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci if (!refcount_inc_not_zero(&gss_auth->rpc_auth.au_count)) 11798c2ecf20Sopenharmony_ci continue; 11808c2ecf20Sopenharmony_ci goto out; 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci if (new) 11838c2ecf20Sopenharmony_ci hash_add(gss_auth_hash_table, &new->hash, hashval); 11848c2ecf20Sopenharmony_ci gss_auth = new; 11858c2ecf20Sopenharmony_ciout: 11868c2ecf20Sopenharmony_ci spin_unlock(&gss_auth_hash_lock); 11878c2ecf20Sopenharmony_ci return gss_auth; 11888c2ecf20Sopenharmony_ci} 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_cistatic struct gss_auth * 11918c2ecf20Sopenharmony_cigss_create_hashed(const struct rpc_auth_create_args *args, 11928c2ecf20Sopenharmony_ci struct rpc_clnt *clnt) 11938c2ecf20Sopenharmony_ci{ 11948c2ecf20Sopenharmony_ci struct gss_auth *gss_auth; 11958c2ecf20Sopenharmony_ci struct gss_auth *new; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci gss_auth = gss_auth_find_or_add_hashed(args, clnt, NULL); 11988c2ecf20Sopenharmony_ci if (gss_auth != NULL) 11998c2ecf20Sopenharmony_ci goto out; 12008c2ecf20Sopenharmony_ci new = gss_create_new(args, clnt); 12018c2ecf20Sopenharmony_ci if (IS_ERR(new)) 12028c2ecf20Sopenharmony_ci return new; 12038c2ecf20Sopenharmony_ci gss_auth = gss_auth_find_or_add_hashed(args, clnt, new); 12048c2ecf20Sopenharmony_ci if (gss_auth != new) 12058c2ecf20Sopenharmony_ci gss_destroy(&new->rpc_auth); 12068c2ecf20Sopenharmony_ciout: 12078c2ecf20Sopenharmony_ci return gss_auth; 12088c2ecf20Sopenharmony_ci} 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_cistatic struct rpc_auth * 12118c2ecf20Sopenharmony_cigss_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) 12128c2ecf20Sopenharmony_ci{ 12138c2ecf20Sopenharmony_ci struct gss_auth *gss_auth; 12148c2ecf20Sopenharmony_ci struct rpc_xprt_switch *xps = rcu_access_pointer(clnt->cl_xpi.xpi_xpswitch); 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci while (clnt != clnt->cl_parent) { 12178c2ecf20Sopenharmony_ci struct rpc_clnt *parent = clnt->cl_parent; 12188c2ecf20Sopenharmony_ci /* Find the original parent for this transport */ 12198c2ecf20Sopenharmony_ci if (rcu_access_pointer(parent->cl_xpi.xpi_xpswitch) != xps) 12208c2ecf20Sopenharmony_ci break; 12218c2ecf20Sopenharmony_ci clnt = parent; 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci gss_auth = gss_create_hashed(args, clnt); 12258c2ecf20Sopenharmony_ci if (IS_ERR(gss_auth)) 12268c2ecf20Sopenharmony_ci return ERR_CAST(gss_auth); 12278c2ecf20Sopenharmony_ci return &gss_auth->rpc_auth; 12288c2ecf20Sopenharmony_ci} 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_cistatic struct gss_cred * 12318c2ecf20Sopenharmony_cigss_dup_cred(struct gss_auth *gss_auth, struct gss_cred *gss_cred) 12328c2ecf20Sopenharmony_ci{ 12338c2ecf20Sopenharmony_ci struct gss_cred *new; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci /* Make a copy of the cred so that we can reference count it */ 12368c2ecf20Sopenharmony_ci new = kzalloc(sizeof(*gss_cred), GFP_NOFS); 12378c2ecf20Sopenharmony_ci if (new) { 12388c2ecf20Sopenharmony_ci struct auth_cred acred = { 12398c2ecf20Sopenharmony_ci .cred = gss_cred->gc_base.cr_cred, 12408c2ecf20Sopenharmony_ci }; 12418c2ecf20Sopenharmony_ci struct gss_cl_ctx *ctx = 12428c2ecf20Sopenharmony_ci rcu_dereference_protected(gss_cred->gc_ctx, 1); 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci rpcauth_init_cred(&new->gc_base, &acred, 12458c2ecf20Sopenharmony_ci &gss_auth->rpc_auth, 12468c2ecf20Sopenharmony_ci &gss_nullops); 12478c2ecf20Sopenharmony_ci new->gc_base.cr_flags = 1UL << RPCAUTH_CRED_UPTODATE; 12488c2ecf20Sopenharmony_ci new->gc_service = gss_cred->gc_service; 12498c2ecf20Sopenharmony_ci new->gc_principal = gss_cred->gc_principal; 12508c2ecf20Sopenharmony_ci kref_get(&gss_auth->kref); 12518c2ecf20Sopenharmony_ci rcu_assign_pointer(new->gc_ctx, ctx); 12528c2ecf20Sopenharmony_ci gss_get_ctx(ctx); 12538c2ecf20Sopenharmony_ci } 12548c2ecf20Sopenharmony_ci return new; 12558c2ecf20Sopenharmony_ci} 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci/* 12588c2ecf20Sopenharmony_ci * gss_send_destroy_context will cause the RPCSEC_GSS to send a NULL RPC call 12598c2ecf20Sopenharmony_ci * to the server with the GSS control procedure field set to 12608c2ecf20Sopenharmony_ci * RPC_GSS_PROC_DESTROY. This should normally cause the server to release 12618c2ecf20Sopenharmony_ci * all RPCSEC_GSS state associated with that context. 12628c2ecf20Sopenharmony_ci */ 12638c2ecf20Sopenharmony_cistatic void 12648c2ecf20Sopenharmony_cigss_send_destroy_context(struct rpc_cred *cred) 12658c2ecf20Sopenharmony_ci{ 12668c2ecf20Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 12678c2ecf20Sopenharmony_ci struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth); 12688c2ecf20Sopenharmony_ci struct gss_cl_ctx *ctx = rcu_dereference_protected(gss_cred->gc_ctx, 1); 12698c2ecf20Sopenharmony_ci struct gss_cred *new; 12708c2ecf20Sopenharmony_ci struct rpc_task *task; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci new = gss_dup_cred(gss_auth, gss_cred); 12738c2ecf20Sopenharmony_ci if (new) { 12748c2ecf20Sopenharmony_ci ctx->gc_proc = RPC_GSS_PROC_DESTROY; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci trace_rpcgss_ctx_destroy(gss_cred); 12778c2ecf20Sopenharmony_ci task = rpc_call_null(gss_auth->client, &new->gc_base, 12788c2ecf20Sopenharmony_ci RPC_TASK_ASYNC); 12798c2ecf20Sopenharmony_ci if (!IS_ERR(task)) 12808c2ecf20Sopenharmony_ci rpc_put_task(task); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci put_rpccred(&new->gc_base); 12838c2ecf20Sopenharmony_ci } 12848c2ecf20Sopenharmony_ci} 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci/* gss_destroy_cred (and gss_free_ctx) are used to clean up after failure 12878c2ecf20Sopenharmony_ci * to create a new cred or context, so they check that things have been 12888c2ecf20Sopenharmony_ci * allocated before freeing them. */ 12898c2ecf20Sopenharmony_cistatic void 12908c2ecf20Sopenharmony_cigss_do_free_ctx(struct gss_cl_ctx *ctx) 12918c2ecf20Sopenharmony_ci{ 12928c2ecf20Sopenharmony_ci gss_delete_sec_context(&ctx->gc_gss_ctx); 12938c2ecf20Sopenharmony_ci kfree(ctx->gc_wire_ctx.data); 12948c2ecf20Sopenharmony_ci kfree(ctx->gc_acceptor.data); 12958c2ecf20Sopenharmony_ci kfree(ctx); 12968c2ecf20Sopenharmony_ci} 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_cistatic void 12998c2ecf20Sopenharmony_cigss_free_ctx_callback(struct rcu_head *head) 13008c2ecf20Sopenharmony_ci{ 13018c2ecf20Sopenharmony_ci struct gss_cl_ctx *ctx = container_of(head, struct gss_cl_ctx, gc_rcu); 13028c2ecf20Sopenharmony_ci gss_do_free_ctx(ctx); 13038c2ecf20Sopenharmony_ci} 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_cistatic void 13068c2ecf20Sopenharmony_cigss_free_ctx(struct gss_cl_ctx *ctx) 13078c2ecf20Sopenharmony_ci{ 13088c2ecf20Sopenharmony_ci call_rcu(&ctx->gc_rcu, gss_free_ctx_callback); 13098c2ecf20Sopenharmony_ci} 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_cistatic void 13128c2ecf20Sopenharmony_cigss_free_cred(struct gss_cred *gss_cred) 13138c2ecf20Sopenharmony_ci{ 13148c2ecf20Sopenharmony_ci kfree(gss_cred); 13158c2ecf20Sopenharmony_ci} 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_cistatic void 13188c2ecf20Sopenharmony_cigss_free_cred_callback(struct rcu_head *head) 13198c2ecf20Sopenharmony_ci{ 13208c2ecf20Sopenharmony_ci struct gss_cred *gss_cred = container_of(head, struct gss_cred, gc_base.cr_rcu); 13218c2ecf20Sopenharmony_ci gss_free_cred(gss_cred); 13228c2ecf20Sopenharmony_ci} 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_cistatic void 13258c2ecf20Sopenharmony_cigss_destroy_nullcred(struct rpc_cred *cred) 13268c2ecf20Sopenharmony_ci{ 13278c2ecf20Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 13288c2ecf20Sopenharmony_ci struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth); 13298c2ecf20Sopenharmony_ci struct gss_cl_ctx *ctx = rcu_dereference_protected(gss_cred->gc_ctx, 1); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci RCU_INIT_POINTER(gss_cred->gc_ctx, NULL); 13328c2ecf20Sopenharmony_ci put_cred(cred->cr_cred); 13338c2ecf20Sopenharmony_ci call_rcu(&cred->cr_rcu, gss_free_cred_callback); 13348c2ecf20Sopenharmony_ci if (ctx) 13358c2ecf20Sopenharmony_ci gss_put_ctx(ctx); 13368c2ecf20Sopenharmony_ci gss_put_auth(gss_auth); 13378c2ecf20Sopenharmony_ci} 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_cistatic void 13408c2ecf20Sopenharmony_cigss_destroy_cred(struct rpc_cred *cred) 13418c2ecf20Sopenharmony_ci{ 13428c2ecf20Sopenharmony_ci if (test_and_clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0) 13438c2ecf20Sopenharmony_ci gss_send_destroy_context(cred); 13448c2ecf20Sopenharmony_ci gss_destroy_nullcred(cred); 13458c2ecf20Sopenharmony_ci} 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_cistatic int 13488c2ecf20Sopenharmony_cigss_hash_cred(struct auth_cred *acred, unsigned int hashbits) 13498c2ecf20Sopenharmony_ci{ 13508c2ecf20Sopenharmony_ci return hash_64(from_kuid(&init_user_ns, acred->cred->fsuid), hashbits); 13518c2ecf20Sopenharmony_ci} 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci/* 13548c2ecf20Sopenharmony_ci * Lookup RPCSEC_GSS cred for the current process 13558c2ecf20Sopenharmony_ci */ 13568c2ecf20Sopenharmony_cistatic struct rpc_cred * 13578c2ecf20Sopenharmony_cigss_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) 13588c2ecf20Sopenharmony_ci{ 13598c2ecf20Sopenharmony_ci return rpcauth_lookup_credcache(auth, acred, flags, GFP_NOFS); 13608c2ecf20Sopenharmony_ci} 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_cistatic struct rpc_cred * 13638c2ecf20Sopenharmony_cigss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags, gfp_t gfp) 13648c2ecf20Sopenharmony_ci{ 13658c2ecf20Sopenharmony_ci struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth); 13668c2ecf20Sopenharmony_ci struct gss_cred *cred = NULL; 13678c2ecf20Sopenharmony_ci int err = -ENOMEM; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci if (!(cred = kzalloc(sizeof(*cred), gfp))) 13708c2ecf20Sopenharmony_ci goto out_err; 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci rpcauth_init_cred(&cred->gc_base, acred, auth, &gss_credops); 13738c2ecf20Sopenharmony_ci /* 13748c2ecf20Sopenharmony_ci * Note: in order to force a call to call_refresh(), we deliberately 13758c2ecf20Sopenharmony_ci * fail to flag the credential as RPCAUTH_CRED_UPTODATE. 13768c2ecf20Sopenharmony_ci */ 13778c2ecf20Sopenharmony_ci cred->gc_base.cr_flags = 1UL << RPCAUTH_CRED_NEW; 13788c2ecf20Sopenharmony_ci cred->gc_service = gss_auth->service; 13798c2ecf20Sopenharmony_ci cred->gc_principal = acred->principal; 13808c2ecf20Sopenharmony_ci kref_get(&gss_auth->kref); 13818c2ecf20Sopenharmony_ci return &cred->gc_base; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ciout_err: 13848c2ecf20Sopenharmony_ci return ERR_PTR(err); 13858c2ecf20Sopenharmony_ci} 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_cistatic int 13888c2ecf20Sopenharmony_cigss_cred_init(struct rpc_auth *auth, struct rpc_cred *cred) 13898c2ecf20Sopenharmony_ci{ 13908c2ecf20Sopenharmony_ci struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth); 13918c2ecf20Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred,struct gss_cred, gc_base); 13928c2ecf20Sopenharmony_ci int err; 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci do { 13958c2ecf20Sopenharmony_ci err = gss_create_upcall(gss_auth, gss_cred); 13968c2ecf20Sopenharmony_ci } while (err == -EAGAIN); 13978c2ecf20Sopenharmony_ci return err; 13988c2ecf20Sopenharmony_ci} 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_cistatic char * 14018c2ecf20Sopenharmony_cigss_stringify_acceptor(struct rpc_cred *cred) 14028c2ecf20Sopenharmony_ci{ 14038c2ecf20Sopenharmony_ci char *string = NULL; 14048c2ecf20Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); 14058c2ecf20Sopenharmony_ci struct gss_cl_ctx *ctx; 14068c2ecf20Sopenharmony_ci unsigned int len; 14078c2ecf20Sopenharmony_ci struct xdr_netobj *acceptor; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci rcu_read_lock(); 14108c2ecf20Sopenharmony_ci ctx = rcu_dereference(gss_cred->gc_ctx); 14118c2ecf20Sopenharmony_ci if (!ctx) 14128c2ecf20Sopenharmony_ci goto out; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci len = ctx->gc_acceptor.len; 14158c2ecf20Sopenharmony_ci rcu_read_unlock(); 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci /* no point if there's no string */ 14188c2ecf20Sopenharmony_ci if (!len) 14198c2ecf20Sopenharmony_ci return NULL; 14208c2ecf20Sopenharmony_cirealloc: 14218c2ecf20Sopenharmony_ci string = kmalloc(len + 1, GFP_KERNEL); 14228c2ecf20Sopenharmony_ci if (!string) 14238c2ecf20Sopenharmony_ci return NULL; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci rcu_read_lock(); 14268c2ecf20Sopenharmony_ci ctx = rcu_dereference(gss_cred->gc_ctx); 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci /* did the ctx disappear or was it replaced by one with no acceptor? */ 14298c2ecf20Sopenharmony_ci if (!ctx || !ctx->gc_acceptor.len) { 14308c2ecf20Sopenharmony_ci kfree(string); 14318c2ecf20Sopenharmony_ci string = NULL; 14328c2ecf20Sopenharmony_ci goto out; 14338c2ecf20Sopenharmony_ci } 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci acceptor = &ctx->gc_acceptor; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci /* 14388c2ecf20Sopenharmony_ci * Did we find a new acceptor that's longer than the original? Allocate 14398c2ecf20Sopenharmony_ci * a longer buffer and try again. 14408c2ecf20Sopenharmony_ci */ 14418c2ecf20Sopenharmony_ci if (len < acceptor->len) { 14428c2ecf20Sopenharmony_ci len = acceptor->len; 14438c2ecf20Sopenharmony_ci rcu_read_unlock(); 14448c2ecf20Sopenharmony_ci kfree(string); 14458c2ecf20Sopenharmony_ci goto realloc; 14468c2ecf20Sopenharmony_ci } 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci memcpy(string, acceptor->data, acceptor->len); 14498c2ecf20Sopenharmony_ci string[acceptor->len] = '\0'; 14508c2ecf20Sopenharmony_ciout: 14518c2ecf20Sopenharmony_ci rcu_read_unlock(); 14528c2ecf20Sopenharmony_ci return string; 14538c2ecf20Sopenharmony_ci} 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci/* 14568c2ecf20Sopenharmony_ci * Returns -EACCES if GSS context is NULL or will expire within the 14578c2ecf20Sopenharmony_ci * timeout (miliseconds) 14588c2ecf20Sopenharmony_ci */ 14598c2ecf20Sopenharmony_cistatic int 14608c2ecf20Sopenharmony_cigss_key_timeout(struct rpc_cred *rc) 14618c2ecf20Sopenharmony_ci{ 14628c2ecf20Sopenharmony_ci struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); 14638c2ecf20Sopenharmony_ci struct gss_cl_ctx *ctx; 14648c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + (gss_key_expire_timeo * HZ); 14658c2ecf20Sopenharmony_ci int ret = 0; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci rcu_read_lock(); 14688c2ecf20Sopenharmony_ci ctx = rcu_dereference(gss_cred->gc_ctx); 14698c2ecf20Sopenharmony_ci if (!ctx || time_after(timeout, ctx->gc_expiry)) 14708c2ecf20Sopenharmony_ci ret = -EACCES; 14718c2ecf20Sopenharmony_ci rcu_read_unlock(); 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci return ret; 14748c2ecf20Sopenharmony_ci} 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_cistatic int 14778c2ecf20Sopenharmony_cigss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags) 14788c2ecf20Sopenharmony_ci{ 14798c2ecf20Sopenharmony_ci struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); 14808c2ecf20Sopenharmony_ci struct gss_cl_ctx *ctx; 14818c2ecf20Sopenharmony_ci int ret; 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci if (test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags)) 14848c2ecf20Sopenharmony_ci goto out; 14858c2ecf20Sopenharmony_ci /* Don't match with creds that have expired. */ 14868c2ecf20Sopenharmony_ci rcu_read_lock(); 14878c2ecf20Sopenharmony_ci ctx = rcu_dereference(gss_cred->gc_ctx); 14888c2ecf20Sopenharmony_ci if (!ctx || time_after(jiffies, ctx->gc_expiry)) { 14898c2ecf20Sopenharmony_ci rcu_read_unlock(); 14908c2ecf20Sopenharmony_ci return 0; 14918c2ecf20Sopenharmony_ci } 14928c2ecf20Sopenharmony_ci rcu_read_unlock(); 14938c2ecf20Sopenharmony_ci if (!test_bit(RPCAUTH_CRED_UPTODATE, &rc->cr_flags)) 14948c2ecf20Sopenharmony_ci return 0; 14958c2ecf20Sopenharmony_ciout: 14968c2ecf20Sopenharmony_ci if (acred->principal != NULL) { 14978c2ecf20Sopenharmony_ci if (gss_cred->gc_principal == NULL) 14988c2ecf20Sopenharmony_ci return 0; 14998c2ecf20Sopenharmony_ci ret = strcmp(acred->principal, gss_cred->gc_principal) == 0; 15008c2ecf20Sopenharmony_ci } else { 15018c2ecf20Sopenharmony_ci if (gss_cred->gc_principal != NULL) 15028c2ecf20Sopenharmony_ci return 0; 15038c2ecf20Sopenharmony_ci ret = uid_eq(rc->cr_cred->fsuid, acred->cred->fsuid); 15048c2ecf20Sopenharmony_ci } 15058c2ecf20Sopenharmony_ci return ret; 15068c2ecf20Sopenharmony_ci} 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci/* 15098c2ecf20Sopenharmony_ci * Marshal credentials. 15108c2ecf20Sopenharmony_ci * 15118c2ecf20Sopenharmony_ci * The expensive part is computing the verifier. We can't cache a 15128c2ecf20Sopenharmony_ci * pre-computed version of the verifier because the seqno, which 15138c2ecf20Sopenharmony_ci * is different every time, is included in the MIC. 15148c2ecf20Sopenharmony_ci */ 15158c2ecf20Sopenharmony_cistatic int gss_marshal(struct rpc_task *task, struct xdr_stream *xdr) 15168c2ecf20Sopenharmony_ci{ 15178c2ecf20Sopenharmony_ci struct rpc_rqst *req = task->tk_rqstp; 15188c2ecf20Sopenharmony_ci struct rpc_cred *cred = req->rq_cred; 15198c2ecf20Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, struct gss_cred, 15208c2ecf20Sopenharmony_ci gc_base); 15218c2ecf20Sopenharmony_ci struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 15228c2ecf20Sopenharmony_ci __be32 *p, *cred_len; 15238c2ecf20Sopenharmony_ci u32 maj_stat = 0; 15248c2ecf20Sopenharmony_ci struct xdr_netobj mic; 15258c2ecf20Sopenharmony_ci struct kvec iov; 15268c2ecf20Sopenharmony_ci struct xdr_buf verf_buf; 15278c2ecf20Sopenharmony_ci int status; 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci /* Credential */ 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci p = xdr_reserve_space(xdr, 7 * sizeof(*p) + 15328c2ecf20Sopenharmony_ci ctx->gc_wire_ctx.len); 15338c2ecf20Sopenharmony_ci if (!p) 15348c2ecf20Sopenharmony_ci goto marshal_failed; 15358c2ecf20Sopenharmony_ci *p++ = rpc_auth_gss; 15368c2ecf20Sopenharmony_ci cred_len = p++; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci spin_lock(&ctx->gc_seq_lock); 15398c2ecf20Sopenharmony_ci req->rq_seqno = (ctx->gc_seq < MAXSEQ) ? ctx->gc_seq++ : MAXSEQ; 15408c2ecf20Sopenharmony_ci spin_unlock(&ctx->gc_seq_lock); 15418c2ecf20Sopenharmony_ci if (req->rq_seqno == MAXSEQ) 15428c2ecf20Sopenharmony_ci goto expired; 15438c2ecf20Sopenharmony_ci trace_rpcgss_seqno(task); 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci *p++ = cpu_to_be32(RPC_GSS_VERSION); 15468c2ecf20Sopenharmony_ci *p++ = cpu_to_be32(ctx->gc_proc); 15478c2ecf20Sopenharmony_ci *p++ = cpu_to_be32(req->rq_seqno); 15488c2ecf20Sopenharmony_ci *p++ = cpu_to_be32(gss_cred->gc_service); 15498c2ecf20Sopenharmony_ci p = xdr_encode_netobj(p, &ctx->gc_wire_ctx); 15508c2ecf20Sopenharmony_ci *cred_len = cpu_to_be32((p - (cred_len + 1)) << 2); 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci /* Verifier */ 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci /* We compute the checksum for the verifier over the xdr-encoded bytes 15558c2ecf20Sopenharmony_ci * starting with the xid and ending at the end of the credential: */ 15568c2ecf20Sopenharmony_ci iov.iov_base = req->rq_snd_buf.head[0].iov_base; 15578c2ecf20Sopenharmony_ci iov.iov_len = (u8 *)p - (u8 *)iov.iov_base; 15588c2ecf20Sopenharmony_ci xdr_buf_from_iov(&iov, &verf_buf); 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci p = xdr_reserve_space(xdr, sizeof(*p)); 15618c2ecf20Sopenharmony_ci if (!p) 15628c2ecf20Sopenharmony_ci goto marshal_failed; 15638c2ecf20Sopenharmony_ci *p++ = rpc_auth_gss; 15648c2ecf20Sopenharmony_ci mic.data = (u8 *)(p + 1); 15658c2ecf20Sopenharmony_ci maj_stat = gss_get_mic(ctx->gc_gss_ctx, &verf_buf, &mic); 15668c2ecf20Sopenharmony_ci if (maj_stat == GSS_S_CONTEXT_EXPIRED) 15678c2ecf20Sopenharmony_ci goto expired; 15688c2ecf20Sopenharmony_ci else if (maj_stat != 0) 15698c2ecf20Sopenharmony_ci goto bad_mic; 15708c2ecf20Sopenharmony_ci if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0) 15718c2ecf20Sopenharmony_ci goto marshal_failed; 15728c2ecf20Sopenharmony_ci status = 0; 15738c2ecf20Sopenharmony_ciout: 15748c2ecf20Sopenharmony_ci gss_put_ctx(ctx); 15758c2ecf20Sopenharmony_ci return status; 15768c2ecf20Sopenharmony_ciexpired: 15778c2ecf20Sopenharmony_ci clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 15788c2ecf20Sopenharmony_ci status = -EKEYEXPIRED; 15798c2ecf20Sopenharmony_ci goto out; 15808c2ecf20Sopenharmony_cimarshal_failed: 15818c2ecf20Sopenharmony_ci status = -EMSGSIZE; 15828c2ecf20Sopenharmony_ci goto out; 15838c2ecf20Sopenharmony_cibad_mic: 15848c2ecf20Sopenharmony_ci trace_rpcgss_get_mic(task, maj_stat); 15858c2ecf20Sopenharmony_ci status = -EIO; 15868c2ecf20Sopenharmony_ci goto out; 15878c2ecf20Sopenharmony_ci} 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_cistatic int gss_renew_cred(struct rpc_task *task) 15908c2ecf20Sopenharmony_ci{ 15918c2ecf20Sopenharmony_ci struct rpc_cred *oldcred = task->tk_rqstp->rq_cred; 15928c2ecf20Sopenharmony_ci struct gss_cred *gss_cred = container_of(oldcred, 15938c2ecf20Sopenharmony_ci struct gss_cred, 15948c2ecf20Sopenharmony_ci gc_base); 15958c2ecf20Sopenharmony_ci struct rpc_auth *auth = oldcred->cr_auth; 15968c2ecf20Sopenharmony_ci struct auth_cred acred = { 15978c2ecf20Sopenharmony_ci .cred = oldcred->cr_cred, 15988c2ecf20Sopenharmony_ci .principal = gss_cred->gc_principal, 15998c2ecf20Sopenharmony_ci }; 16008c2ecf20Sopenharmony_ci struct rpc_cred *new; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci new = gss_lookup_cred(auth, &acred, RPCAUTH_LOOKUP_NEW); 16038c2ecf20Sopenharmony_ci if (IS_ERR(new)) 16048c2ecf20Sopenharmony_ci return PTR_ERR(new); 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci task->tk_rqstp->rq_cred = new; 16078c2ecf20Sopenharmony_ci put_rpccred(oldcred); 16088c2ecf20Sopenharmony_ci return 0; 16098c2ecf20Sopenharmony_ci} 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_cistatic int gss_cred_is_negative_entry(struct rpc_cred *cred) 16128c2ecf20Sopenharmony_ci{ 16138c2ecf20Sopenharmony_ci if (test_bit(RPCAUTH_CRED_NEGATIVE, &cred->cr_flags)) { 16148c2ecf20Sopenharmony_ci unsigned long now = jiffies; 16158c2ecf20Sopenharmony_ci unsigned long begin, expire; 16168c2ecf20Sopenharmony_ci struct gss_cred *gss_cred; 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci gss_cred = container_of(cred, struct gss_cred, gc_base); 16198c2ecf20Sopenharmony_ci begin = gss_cred->gc_upcall_timestamp; 16208c2ecf20Sopenharmony_ci expire = begin + gss_expired_cred_retry_delay * HZ; 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci if (time_in_range_open(now, begin, expire)) 16238c2ecf20Sopenharmony_ci return 1; 16248c2ecf20Sopenharmony_ci } 16258c2ecf20Sopenharmony_ci return 0; 16268c2ecf20Sopenharmony_ci} 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci/* 16298c2ecf20Sopenharmony_ci* Refresh credentials. XXX - finish 16308c2ecf20Sopenharmony_ci*/ 16318c2ecf20Sopenharmony_cistatic int 16328c2ecf20Sopenharmony_cigss_refresh(struct rpc_task *task) 16338c2ecf20Sopenharmony_ci{ 16348c2ecf20Sopenharmony_ci struct rpc_cred *cred = task->tk_rqstp->rq_cred; 16358c2ecf20Sopenharmony_ci int ret = 0; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci if (gss_cred_is_negative_entry(cred)) 16388c2ecf20Sopenharmony_ci return -EKEYEXPIRED; 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci if (!test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) && 16418c2ecf20Sopenharmony_ci !test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags)) { 16428c2ecf20Sopenharmony_ci ret = gss_renew_cred(task); 16438c2ecf20Sopenharmony_ci if (ret < 0) 16448c2ecf20Sopenharmony_ci goto out; 16458c2ecf20Sopenharmony_ci cred = task->tk_rqstp->rq_cred; 16468c2ecf20Sopenharmony_ci } 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags)) 16498c2ecf20Sopenharmony_ci ret = gss_refresh_upcall(task); 16508c2ecf20Sopenharmony_ciout: 16518c2ecf20Sopenharmony_ci return ret; 16528c2ecf20Sopenharmony_ci} 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci/* Dummy refresh routine: used only when destroying the context */ 16558c2ecf20Sopenharmony_cistatic int 16568c2ecf20Sopenharmony_cigss_refresh_null(struct rpc_task *task) 16578c2ecf20Sopenharmony_ci{ 16588c2ecf20Sopenharmony_ci return 0; 16598c2ecf20Sopenharmony_ci} 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_cistatic int 16628c2ecf20Sopenharmony_cigss_validate(struct rpc_task *task, struct xdr_stream *xdr) 16638c2ecf20Sopenharmony_ci{ 16648c2ecf20Sopenharmony_ci struct rpc_cred *cred = task->tk_rqstp->rq_cred; 16658c2ecf20Sopenharmony_ci struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 16668c2ecf20Sopenharmony_ci __be32 *p, *seq = NULL; 16678c2ecf20Sopenharmony_ci struct kvec iov; 16688c2ecf20Sopenharmony_ci struct xdr_buf verf_buf; 16698c2ecf20Sopenharmony_ci struct xdr_netobj mic; 16708c2ecf20Sopenharmony_ci u32 len, maj_stat; 16718c2ecf20Sopenharmony_ci int status; 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci p = xdr_inline_decode(xdr, 2 * sizeof(*p)); 16748c2ecf20Sopenharmony_ci if (!p) 16758c2ecf20Sopenharmony_ci goto validate_failed; 16768c2ecf20Sopenharmony_ci if (*p++ != rpc_auth_gss) 16778c2ecf20Sopenharmony_ci goto validate_failed; 16788c2ecf20Sopenharmony_ci len = be32_to_cpup(p); 16798c2ecf20Sopenharmony_ci if (len > RPC_MAX_AUTH_SIZE) 16808c2ecf20Sopenharmony_ci goto validate_failed; 16818c2ecf20Sopenharmony_ci p = xdr_inline_decode(xdr, len); 16828c2ecf20Sopenharmony_ci if (!p) 16838c2ecf20Sopenharmony_ci goto validate_failed; 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci seq = kmalloc(4, GFP_NOFS); 16868c2ecf20Sopenharmony_ci if (!seq) 16878c2ecf20Sopenharmony_ci goto validate_failed; 16888c2ecf20Sopenharmony_ci *seq = cpu_to_be32(task->tk_rqstp->rq_seqno); 16898c2ecf20Sopenharmony_ci iov.iov_base = seq; 16908c2ecf20Sopenharmony_ci iov.iov_len = 4; 16918c2ecf20Sopenharmony_ci xdr_buf_from_iov(&iov, &verf_buf); 16928c2ecf20Sopenharmony_ci mic.data = (u8 *)p; 16938c2ecf20Sopenharmony_ci mic.len = len; 16948c2ecf20Sopenharmony_ci maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic); 16958c2ecf20Sopenharmony_ci if (maj_stat == GSS_S_CONTEXT_EXPIRED) 16968c2ecf20Sopenharmony_ci clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 16978c2ecf20Sopenharmony_ci if (maj_stat) 16988c2ecf20Sopenharmony_ci goto bad_mic; 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci /* We leave it to unwrap to calculate au_rslack. For now we just 17018c2ecf20Sopenharmony_ci * calculate the length of the verifier: */ 17028c2ecf20Sopenharmony_ci if (test_bit(RPCAUTH_AUTH_UPDATE_SLACK, &cred->cr_auth->au_flags)) 17038c2ecf20Sopenharmony_ci cred->cr_auth->au_verfsize = XDR_QUADLEN(len) + 2; 17048c2ecf20Sopenharmony_ci status = 0; 17058c2ecf20Sopenharmony_ciout: 17068c2ecf20Sopenharmony_ci gss_put_ctx(ctx); 17078c2ecf20Sopenharmony_ci kfree(seq); 17088c2ecf20Sopenharmony_ci return status; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_civalidate_failed: 17118c2ecf20Sopenharmony_ci status = -EIO; 17128c2ecf20Sopenharmony_ci goto out; 17138c2ecf20Sopenharmony_cibad_mic: 17148c2ecf20Sopenharmony_ci trace_rpcgss_verify_mic(task, maj_stat); 17158c2ecf20Sopenharmony_ci status = -EACCES; 17168c2ecf20Sopenharmony_ci goto out; 17178c2ecf20Sopenharmony_ci} 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_cistatic noinline_for_stack int 17208c2ecf20Sopenharmony_cigss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, 17218c2ecf20Sopenharmony_ci struct rpc_task *task, struct xdr_stream *xdr) 17228c2ecf20Sopenharmony_ci{ 17238c2ecf20Sopenharmony_ci struct rpc_rqst *rqstp = task->tk_rqstp; 17248c2ecf20Sopenharmony_ci struct xdr_buf integ_buf, *snd_buf = &rqstp->rq_snd_buf; 17258c2ecf20Sopenharmony_ci struct xdr_netobj mic; 17268c2ecf20Sopenharmony_ci __be32 *p, *integ_len; 17278c2ecf20Sopenharmony_ci u32 offset, maj_stat; 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci p = xdr_reserve_space(xdr, 2 * sizeof(*p)); 17308c2ecf20Sopenharmony_ci if (!p) 17318c2ecf20Sopenharmony_ci goto wrap_failed; 17328c2ecf20Sopenharmony_ci integ_len = p++; 17338c2ecf20Sopenharmony_ci *p = cpu_to_be32(rqstp->rq_seqno); 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci if (rpcauth_wrap_req_encode(task, xdr)) 17368c2ecf20Sopenharmony_ci goto wrap_failed; 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; 17398c2ecf20Sopenharmony_ci if (xdr_buf_subsegment(snd_buf, &integ_buf, 17408c2ecf20Sopenharmony_ci offset, snd_buf->len - offset)) 17418c2ecf20Sopenharmony_ci goto wrap_failed; 17428c2ecf20Sopenharmony_ci *integ_len = cpu_to_be32(integ_buf.len); 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci p = xdr_reserve_space(xdr, 0); 17458c2ecf20Sopenharmony_ci if (!p) 17468c2ecf20Sopenharmony_ci goto wrap_failed; 17478c2ecf20Sopenharmony_ci mic.data = (u8 *)(p + 1); 17488c2ecf20Sopenharmony_ci maj_stat = gss_get_mic(ctx->gc_gss_ctx, &integ_buf, &mic); 17498c2ecf20Sopenharmony_ci if (maj_stat == GSS_S_CONTEXT_EXPIRED) 17508c2ecf20Sopenharmony_ci clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 17518c2ecf20Sopenharmony_ci else if (maj_stat) 17528c2ecf20Sopenharmony_ci goto bad_mic; 17538c2ecf20Sopenharmony_ci /* Check that the trailing MIC fit in the buffer, after the fact */ 17548c2ecf20Sopenharmony_ci if (xdr_stream_encode_opaque_inline(xdr, (void **)&p, mic.len) < 0) 17558c2ecf20Sopenharmony_ci goto wrap_failed; 17568c2ecf20Sopenharmony_ci return 0; 17578c2ecf20Sopenharmony_ciwrap_failed: 17588c2ecf20Sopenharmony_ci return -EMSGSIZE; 17598c2ecf20Sopenharmony_cibad_mic: 17608c2ecf20Sopenharmony_ci trace_rpcgss_get_mic(task, maj_stat); 17618c2ecf20Sopenharmony_ci return -EIO; 17628c2ecf20Sopenharmony_ci} 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_cistatic void 17658c2ecf20Sopenharmony_cipriv_release_snd_buf(struct rpc_rqst *rqstp) 17668c2ecf20Sopenharmony_ci{ 17678c2ecf20Sopenharmony_ci int i; 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci for (i=0; i < rqstp->rq_enc_pages_num; i++) 17708c2ecf20Sopenharmony_ci __free_page(rqstp->rq_enc_pages[i]); 17718c2ecf20Sopenharmony_ci kfree(rqstp->rq_enc_pages); 17728c2ecf20Sopenharmony_ci rqstp->rq_release_snd_buf = NULL; 17738c2ecf20Sopenharmony_ci} 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_cistatic int 17768c2ecf20Sopenharmony_cialloc_enc_pages(struct rpc_rqst *rqstp) 17778c2ecf20Sopenharmony_ci{ 17788c2ecf20Sopenharmony_ci struct xdr_buf *snd_buf = &rqstp->rq_snd_buf; 17798c2ecf20Sopenharmony_ci int first, last, i; 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci if (rqstp->rq_release_snd_buf) 17828c2ecf20Sopenharmony_ci rqstp->rq_release_snd_buf(rqstp); 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci if (snd_buf->page_len == 0) { 17858c2ecf20Sopenharmony_ci rqstp->rq_enc_pages_num = 0; 17868c2ecf20Sopenharmony_ci return 0; 17878c2ecf20Sopenharmony_ci } 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci first = snd_buf->page_base >> PAGE_SHIFT; 17908c2ecf20Sopenharmony_ci last = (snd_buf->page_base + snd_buf->page_len - 1) >> PAGE_SHIFT; 17918c2ecf20Sopenharmony_ci rqstp->rq_enc_pages_num = last - first + 1 + 1; 17928c2ecf20Sopenharmony_ci rqstp->rq_enc_pages 17938c2ecf20Sopenharmony_ci = kmalloc_array(rqstp->rq_enc_pages_num, 17948c2ecf20Sopenharmony_ci sizeof(struct page *), 17958c2ecf20Sopenharmony_ci GFP_NOFS); 17968c2ecf20Sopenharmony_ci if (!rqstp->rq_enc_pages) 17978c2ecf20Sopenharmony_ci goto out; 17988c2ecf20Sopenharmony_ci for (i=0; i < rqstp->rq_enc_pages_num; i++) { 17998c2ecf20Sopenharmony_ci rqstp->rq_enc_pages[i] = alloc_page(GFP_NOFS); 18008c2ecf20Sopenharmony_ci if (rqstp->rq_enc_pages[i] == NULL) 18018c2ecf20Sopenharmony_ci goto out_free; 18028c2ecf20Sopenharmony_ci } 18038c2ecf20Sopenharmony_ci rqstp->rq_release_snd_buf = priv_release_snd_buf; 18048c2ecf20Sopenharmony_ci return 0; 18058c2ecf20Sopenharmony_ciout_free: 18068c2ecf20Sopenharmony_ci rqstp->rq_enc_pages_num = i; 18078c2ecf20Sopenharmony_ci priv_release_snd_buf(rqstp); 18088c2ecf20Sopenharmony_ciout: 18098c2ecf20Sopenharmony_ci return -EAGAIN; 18108c2ecf20Sopenharmony_ci} 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_cistatic noinline_for_stack int 18138c2ecf20Sopenharmony_cigss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, 18148c2ecf20Sopenharmony_ci struct rpc_task *task, struct xdr_stream *xdr) 18158c2ecf20Sopenharmony_ci{ 18168c2ecf20Sopenharmony_ci struct rpc_rqst *rqstp = task->tk_rqstp; 18178c2ecf20Sopenharmony_ci struct xdr_buf *snd_buf = &rqstp->rq_snd_buf; 18188c2ecf20Sopenharmony_ci u32 pad, offset, maj_stat; 18198c2ecf20Sopenharmony_ci int status; 18208c2ecf20Sopenharmony_ci __be32 *p, *opaque_len; 18218c2ecf20Sopenharmony_ci struct page **inpages; 18228c2ecf20Sopenharmony_ci int first; 18238c2ecf20Sopenharmony_ci struct kvec *iov; 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci status = -EIO; 18268c2ecf20Sopenharmony_ci p = xdr_reserve_space(xdr, 2 * sizeof(*p)); 18278c2ecf20Sopenharmony_ci if (!p) 18288c2ecf20Sopenharmony_ci goto wrap_failed; 18298c2ecf20Sopenharmony_ci opaque_len = p++; 18308c2ecf20Sopenharmony_ci *p = cpu_to_be32(rqstp->rq_seqno); 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci if (rpcauth_wrap_req_encode(task, xdr)) 18338c2ecf20Sopenharmony_ci goto wrap_failed; 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci status = alloc_enc_pages(rqstp); 18368c2ecf20Sopenharmony_ci if (unlikely(status)) 18378c2ecf20Sopenharmony_ci goto wrap_failed; 18388c2ecf20Sopenharmony_ci first = snd_buf->page_base >> PAGE_SHIFT; 18398c2ecf20Sopenharmony_ci inpages = snd_buf->pages + first; 18408c2ecf20Sopenharmony_ci snd_buf->pages = rqstp->rq_enc_pages; 18418c2ecf20Sopenharmony_ci snd_buf->page_base -= first << PAGE_SHIFT; 18428c2ecf20Sopenharmony_ci /* 18438c2ecf20Sopenharmony_ci * Move the tail into its own page, in case gss_wrap needs 18448c2ecf20Sopenharmony_ci * more space in the head when wrapping. 18458c2ecf20Sopenharmony_ci * 18468c2ecf20Sopenharmony_ci * Still... Why can't gss_wrap just slide the tail down? 18478c2ecf20Sopenharmony_ci */ 18488c2ecf20Sopenharmony_ci if (snd_buf->page_len || snd_buf->tail[0].iov_len) { 18498c2ecf20Sopenharmony_ci char *tmp; 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci tmp = page_address(rqstp->rq_enc_pages[rqstp->rq_enc_pages_num - 1]); 18528c2ecf20Sopenharmony_ci memcpy(tmp, snd_buf->tail[0].iov_base, snd_buf->tail[0].iov_len); 18538c2ecf20Sopenharmony_ci snd_buf->tail[0].iov_base = tmp; 18548c2ecf20Sopenharmony_ci } 18558c2ecf20Sopenharmony_ci offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; 18568c2ecf20Sopenharmony_ci maj_stat = gss_wrap(ctx->gc_gss_ctx, offset, snd_buf, inpages); 18578c2ecf20Sopenharmony_ci /* slack space should prevent this ever happening: */ 18588c2ecf20Sopenharmony_ci if (unlikely(snd_buf->len > snd_buf->buflen)) 18598c2ecf20Sopenharmony_ci goto wrap_failed; 18608c2ecf20Sopenharmony_ci /* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was 18618c2ecf20Sopenharmony_ci * done anyway, so it's safe to put the request on the wire: */ 18628c2ecf20Sopenharmony_ci if (maj_stat == GSS_S_CONTEXT_EXPIRED) 18638c2ecf20Sopenharmony_ci clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 18648c2ecf20Sopenharmony_ci else if (maj_stat) 18658c2ecf20Sopenharmony_ci goto bad_wrap; 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci *opaque_len = cpu_to_be32(snd_buf->len - offset); 18688c2ecf20Sopenharmony_ci /* guess whether the pad goes into the head or the tail: */ 18698c2ecf20Sopenharmony_ci if (snd_buf->page_len || snd_buf->tail[0].iov_len) 18708c2ecf20Sopenharmony_ci iov = snd_buf->tail; 18718c2ecf20Sopenharmony_ci else 18728c2ecf20Sopenharmony_ci iov = snd_buf->head; 18738c2ecf20Sopenharmony_ci p = iov->iov_base + iov->iov_len; 18748c2ecf20Sopenharmony_ci pad = xdr_pad_size(snd_buf->len - offset); 18758c2ecf20Sopenharmony_ci memset(p, 0, pad); 18768c2ecf20Sopenharmony_ci iov->iov_len += pad; 18778c2ecf20Sopenharmony_ci snd_buf->len += pad; 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci return 0; 18808c2ecf20Sopenharmony_ciwrap_failed: 18818c2ecf20Sopenharmony_ci return status; 18828c2ecf20Sopenharmony_cibad_wrap: 18838c2ecf20Sopenharmony_ci trace_rpcgss_wrap(task, maj_stat); 18848c2ecf20Sopenharmony_ci return -EIO; 18858c2ecf20Sopenharmony_ci} 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_cistatic int gss_wrap_req(struct rpc_task *task, struct xdr_stream *xdr) 18888c2ecf20Sopenharmony_ci{ 18898c2ecf20Sopenharmony_ci struct rpc_cred *cred = task->tk_rqstp->rq_cred; 18908c2ecf20Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, struct gss_cred, 18918c2ecf20Sopenharmony_ci gc_base); 18928c2ecf20Sopenharmony_ci struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 18938c2ecf20Sopenharmony_ci int status; 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci status = -EIO; 18968c2ecf20Sopenharmony_ci if (ctx->gc_proc != RPC_GSS_PROC_DATA) { 18978c2ecf20Sopenharmony_ci /* The spec seems a little ambiguous here, but I think that not 18988c2ecf20Sopenharmony_ci * wrapping context destruction requests makes the most sense. 18998c2ecf20Sopenharmony_ci */ 19008c2ecf20Sopenharmony_ci status = rpcauth_wrap_req_encode(task, xdr); 19018c2ecf20Sopenharmony_ci goto out; 19028c2ecf20Sopenharmony_ci } 19038c2ecf20Sopenharmony_ci switch (gss_cred->gc_service) { 19048c2ecf20Sopenharmony_ci case RPC_GSS_SVC_NONE: 19058c2ecf20Sopenharmony_ci status = rpcauth_wrap_req_encode(task, xdr); 19068c2ecf20Sopenharmony_ci break; 19078c2ecf20Sopenharmony_ci case RPC_GSS_SVC_INTEGRITY: 19088c2ecf20Sopenharmony_ci status = gss_wrap_req_integ(cred, ctx, task, xdr); 19098c2ecf20Sopenharmony_ci break; 19108c2ecf20Sopenharmony_ci case RPC_GSS_SVC_PRIVACY: 19118c2ecf20Sopenharmony_ci status = gss_wrap_req_priv(cred, ctx, task, xdr); 19128c2ecf20Sopenharmony_ci break; 19138c2ecf20Sopenharmony_ci default: 19148c2ecf20Sopenharmony_ci status = -EIO; 19158c2ecf20Sopenharmony_ci } 19168c2ecf20Sopenharmony_ciout: 19178c2ecf20Sopenharmony_ci gss_put_ctx(ctx); 19188c2ecf20Sopenharmony_ci return status; 19198c2ecf20Sopenharmony_ci} 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci/** 19228c2ecf20Sopenharmony_ci * gss_update_rslack - Possibly update RPC receive buffer size estimates 19238c2ecf20Sopenharmony_ci * @task: rpc_task for incoming RPC Reply being unwrapped 19248c2ecf20Sopenharmony_ci * @cred: controlling rpc_cred for @task 19258c2ecf20Sopenharmony_ci * @before: XDR words needed before each RPC Reply message 19268c2ecf20Sopenharmony_ci * @after: XDR words needed following each RPC Reply message 19278c2ecf20Sopenharmony_ci * 19288c2ecf20Sopenharmony_ci */ 19298c2ecf20Sopenharmony_cistatic void gss_update_rslack(struct rpc_task *task, struct rpc_cred *cred, 19308c2ecf20Sopenharmony_ci unsigned int before, unsigned int after) 19318c2ecf20Sopenharmony_ci{ 19328c2ecf20Sopenharmony_ci struct rpc_auth *auth = cred->cr_auth; 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci if (test_and_clear_bit(RPCAUTH_AUTH_UPDATE_SLACK, &auth->au_flags)) { 19358c2ecf20Sopenharmony_ci auth->au_ralign = auth->au_verfsize + before; 19368c2ecf20Sopenharmony_ci auth->au_rslack = auth->au_verfsize + after; 19378c2ecf20Sopenharmony_ci trace_rpcgss_update_slack(task, auth); 19388c2ecf20Sopenharmony_ci } 19398c2ecf20Sopenharmony_ci} 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_cistatic int 19428c2ecf20Sopenharmony_cigss_unwrap_resp_auth(struct rpc_task *task, struct rpc_cred *cred) 19438c2ecf20Sopenharmony_ci{ 19448c2ecf20Sopenharmony_ci gss_update_rslack(task, cred, 0, 0); 19458c2ecf20Sopenharmony_ci return 0; 19468c2ecf20Sopenharmony_ci} 19478c2ecf20Sopenharmony_ci 19488c2ecf20Sopenharmony_ci/* 19498c2ecf20Sopenharmony_ci * RFC 2203, Section 5.3.2.2 19508c2ecf20Sopenharmony_ci * 19518c2ecf20Sopenharmony_ci * struct rpc_gss_integ_data { 19528c2ecf20Sopenharmony_ci * opaque databody_integ<>; 19538c2ecf20Sopenharmony_ci * opaque checksum<>; 19548c2ecf20Sopenharmony_ci * }; 19558c2ecf20Sopenharmony_ci * 19568c2ecf20Sopenharmony_ci * struct rpc_gss_data_t { 19578c2ecf20Sopenharmony_ci * unsigned int seq_num; 19588c2ecf20Sopenharmony_ci * proc_req_arg_t arg; 19598c2ecf20Sopenharmony_ci * }; 19608c2ecf20Sopenharmony_ci */ 19618c2ecf20Sopenharmony_cistatic noinline_for_stack int 19628c2ecf20Sopenharmony_cigss_unwrap_resp_integ(struct rpc_task *task, struct rpc_cred *cred, 19638c2ecf20Sopenharmony_ci struct gss_cl_ctx *ctx, struct rpc_rqst *rqstp, 19648c2ecf20Sopenharmony_ci struct xdr_stream *xdr) 19658c2ecf20Sopenharmony_ci{ 19668c2ecf20Sopenharmony_ci struct xdr_buf gss_data, *rcv_buf = &rqstp->rq_rcv_buf; 19678c2ecf20Sopenharmony_ci u32 len, offset, seqno, maj_stat; 19688c2ecf20Sopenharmony_ci struct xdr_netobj mic; 19698c2ecf20Sopenharmony_ci int ret; 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci ret = -EIO; 19728c2ecf20Sopenharmony_ci mic.data = NULL; 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci /* opaque databody_integ<>; */ 19758c2ecf20Sopenharmony_ci if (xdr_stream_decode_u32(xdr, &len)) 19768c2ecf20Sopenharmony_ci goto unwrap_failed; 19778c2ecf20Sopenharmony_ci if (len & 3) 19788c2ecf20Sopenharmony_ci goto unwrap_failed; 19798c2ecf20Sopenharmony_ci offset = rcv_buf->len - xdr_stream_remaining(xdr); 19808c2ecf20Sopenharmony_ci if (xdr_stream_decode_u32(xdr, &seqno)) 19818c2ecf20Sopenharmony_ci goto unwrap_failed; 19828c2ecf20Sopenharmony_ci if (seqno != rqstp->rq_seqno) 19838c2ecf20Sopenharmony_ci goto bad_seqno; 19848c2ecf20Sopenharmony_ci if (xdr_buf_subsegment(rcv_buf, &gss_data, offset, len)) 19858c2ecf20Sopenharmony_ci goto unwrap_failed; 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci /* 19888c2ecf20Sopenharmony_ci * The xdr_stream now points to the beginning of the 19898c2ecf20Sopenharmony_ci * upper layer payload, to be passed below to 19908c2ecf20Sopenharmony_ci * rpcauth_unwrap_resp_decode(). The checksum, which 19918c2ecf20Sopenharmony_ci * follows the upper layer payload in @rcv_buf, is 19928c2ecf20Sopenharmony_ci * located and parsed without updating the xdr_stream. 19938c2ecf20Sopenharmony_ci */ 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci /* opaque checksum<>; */ 19968c2ecf20Sopenharmony_ci offset += len; 19978c2ecf20Sopenharmony_ci if (xdr_decode_word(rcv_buf, offset, &len)) 19988c2ecf20Sopenharmony_ci goto unwrap_failed; 19998c2ecf20Sopenharmony_ci offset += sizeof(__be32); 20008c2ecf20Sopenharmony_ci if (offset + len > rcv_buf->len) 20018c2ecf20Sopenharmony_ci goto unwrap_failed; 20028c2ecf20Sopenharmony_ci mic.len = len; 20038c2ecf20Sopenharmony_ci mic.data = kmalloc(len, GFP_NOFS); 20048c2ecf20Sopenharmony_ci if (!mic.data) 20058c2ecf20Sopenharmony_ci goto unwrap_failed; 20068c2ecf20Sopenharmony_ci if (read_bytes_from_xdr_buf(rcv_buf, offset, mic.data, mic.len)) 20078c2ecf20Sopenharmony_ci goto unwrap_failed; 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &gss_data, &mic); 20108c2ecf20Sopenharmony_ci if (maj_stat == GSS_S_CONTEXT_EXPIRED) 20118c2ecf20Sopenharmony_ci clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 20128c2ecf20Sopenharmony_ci if (maj_stat != GSS_S_COMPLETE) 20138c2ecf20Sopenharmony_ci goto bad_mic; 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci gss_update_rslack(task, cred, 2, 2 + 1 + XDR_QUADLEN(mic.len)); 20168c2ecf20Sopenharmony_ci ret = 0; 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ciout: 20198c2ecf20Sopenharmony_ci kfree(mic.data); 20208c2ecf20Sopenharmony_ci return ret; 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ciunwrap_failed: 20238c2ecf20Sopenharmony_ci trace_rpcgss_unwrap_failed(task); 20248c2ecf20Sopenharmony_ci goto out; 20258c2ecf20Sopenharmony_cibad_seqno: 20268c2ecf20Sopenharmony_ci trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, seqno); 20278c2ecf20Sopenharmony_ci goto out; 20288c2ecf20Sopenharmony_cibad_mic: 20298c2ecf20Sopenharmony_ci trace_rpcgss_verify_mic(task, maj_stat); 20308c2ecf20Sopenharmony_ci goto out; 20318c2ecf20Sopenharmony_ci} 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_cistatic noinline_for_stack int 20348c2ecf20Sopenharmony_cigss_unwrap_resp_priv(struct rpc_task *task, struct rpc_cred *cred, 20358c2ecf20Sopenharmony_ci struct gss_cl_ctx *ctx, struct rpc_rqst *rqstp, 20368c2ecf20Sopenharmony_ci struct xdr_stream *xdr) 20378c2ecf20Sopenharmony_ci{ 20388c2ecf20Sopenharmony_ci struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf; 20398c2ecf20Sopenharmony_ci struct kvec *head = rqstp->rq_rcv_buf.head; 20408c2ecf20Sopenharmony_ci u32 offset, opaque_len, maj_stat; 20418c2ecf20Sopenharmony_ci __be32 *p; 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci p = xdr_inline_decode(xdr, 2 * sizeof(*p)); 20448c2ecf20Sopenharmony_ci if (unlikely(!p)) 20458c2ecf20Sopenharmony_ci goto unwrap_failed; 20468c2ecf20Sopenharmony_ci opaque_len = be32_to_cpup(p++); 20478c2ecf20Sopenharmony_ci offset = (u8 *)(p) - (u8 *)head->iov_base; 20488c2ecf20Sopenharmony_ci if (offset + opaque_len > rcv_buf->len) 20498c2ecf20Sopenharmony_ci goto unwrap_failed; 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset, 20528c2ecf20Sopenharmony_ci offset + opaque_len, rcv_buf); 20538c2ecf20Sopenharmony_ci if (maj_stat == GSS_S_CONTEXT_EXPIRED) 20548c2ecf20Sopenharmony_ci clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 20558c2ecf20Sopenharmony_ci if (maj_stat != GSS_S_COMPLETE) 20568c2ecf20Sopenharmony_ci goto bad_unwrap; 20578c2ecf20Sopenharmony_ci /* gss_unwrap decrypted the sequence number */ 20588c2ecf20Sopenharmony_ci if (be32_to_cpup(p++) != rqstp->rq_seqno) 20598c2ecf20Sopenharmony_ci goto bad_seqno; 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci /* gss_unwrap redacts the opaque blob from the head iovec. 20628c2ecf20Sopenharmony_ci * rcv_buf has changed, thus the stream needs to be reset. 20638c2ecf20Sopenharmony_ci */ 20648c2ecf20Sopenharmony_ci xdr_init_decode(xdr, rcv_buf, p, rqstp); 20658c2ecf20Sopenharmony_ci 20668c2ecf20Sopenharmony_ci gss_update_rslack(task, cred, 2 + ctx->gc_gss_ctx->align, 20678c2ecf20Sopenharmony_ci 2 + ctx->gc_gss_ctx->slack); 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci return 0; 20708c2ecf20Sopenharmony_ciunwrap_failed: 20718c2ecf20Sopenharmony_ci trace_rpcgss_unwrap_failed(task); 20728c2ecf20Sopenharmony_ci return -EIO; 20738c2ecf20Sopenharmony_cibad_seqno: 20748c2ecf20Sopenharmony_ci trace_rpcgss_bad_seqno(task, rqstp->rq_seqno, be32_to_cpup(--p)); 20758c2ecf20Sopenharmony_ci return -EIO; 20768c2ecf20Sopenharmony_cibad_unwrap: 20778c2ecf20Sopenharmony_ci trace_rpcgss_unwrap(task, maj_stat); 20788c2ecf20Sopenharmony_ci return -EIO; 20798c2ecf20Sopenharmony_ci} 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_cistatic bool 20828c2ecf20Sopenharmony_cigss_seq_is_newer(u32 new, u32 old) 20838c2ecf20Sopenharmony_ci{ 20848c2ecf20Sopenharmony_ci return (s32)(new - old) > 0; 20858c2ecf20Sopenharmony_ci} 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_cistatic bool 20888c2ecf20Sopenharmony_cigss_xmit_need_reencode(struct rpc_task *task) 20898c2ecf20Sopenharmony_ci{ 20908c2ecf20Sopenharmony_ci struct rpc_rqst *req = task->tk_rqstp; 20918c2ecf20Sopenharmony_ci struct rpc_cred *cred = req->rq_cred; 20928c2ecf20Sopenharmony_ci struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 20938c2ecf20Sopenharmony_ci u32 win, seq_xmit = 0; 20948c2ecf20Sopenharmony_ci bool ret = true; 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci if (!ctx) 20978c2ecf20Sopenharmony_ci goto out; 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci if (gss_seq_is_newer(req->rq_seqno, READ_ONCE(ctx->gc_seq))) 21008c2ecf20Sopenharmony_ci goto out_ctx; 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ci seq_xmit = READ_ONCE(ctx->gc_seq_xmit); 21038c2ecf20Sopenharmony_ci while (gss_seq_is_newer(req->rq_seqno, seq_xmit)) { 21048c2ecf20Sopenharmony_ci u32 tmp = seq_xmit; 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci seq_xmit = cmpxchg(&ctx->gc_seq_xmit, tmp, req->rq_seqno); 21078c2ecf20Sopenharmony_ci if (seq_xmit == tmp) { 21088c2ecf20Sopenharmony_ci ret = false; 21098c2ecf20Sopenharmony_ci goto out_ctx; 21108c2ecf20Sopenharmony_ci } 21118c2ecf20Sopenharmony_ci } 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci win = ctx->gc_win; 21148c2ecf20Sopenharmony_ci if (win > 0) 21158c2ecf20Sopenharmony_ci ret = !gss_seq_is_newer(req->rq_seqno, seq_xmit - win); 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ciout_ctx: 21188c2ecf20Sopenharmony_ci gss_put_ctx(ctx); 21198c2ecf20Sopenharmony_ciout: 21208c2ecf20Sopenharmony_ci trace_rpcgss_need_reencode(task, seq_xmit, ret); 21218c2ecf20Sopenharmony_ci return ret; 21228c2ecf20Sopenharmony_ci} 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_cistatic int 21258c2ecf20Sopenharmony_cigss_unwrap_resp(struct rpc_task *task, struct xdr_stream *xdr) 21268c2ecf20Sopenharmony_ci{ 21278c2ecf20Sopenharmony_ci struct rpc_rqst *rqstp = task->tk_rqstp; 21288c2ecf20Sopenharmony_ci struct rpc_cred *cred = rqstp->rq_cred; 21298c2ecf20Sopenharmony_ci struct gss_cred *gss_cred = container_of(cred, struct gss_cred, 21308c2ecf20Sopenharmony_ci gc_base); 21318c2ecf20Sopenharmony_ci struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); 21328c2ecf20Sopenharmony_ci int status = -EIO; 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci if (ctx->gc_proc != RPC_GSS_PROC_DATA) 21358c2ecf20Sopenharmony_ci goto out_decode; 21368c2ecf20Sopenharmony_ci switch (gss_cred->gc_service) { 21378c2ecf20Sopenharmony_ci case RPC_GSS_SVC_NONE: 21388c2ecf20Sopenharmony_ci status = gss_unwrap_resp_auth(task, cred); 21398c2ecf20Sopenharmony_ci break; 21408c2ecf20Sopenharmony_ci case RPC_GSS_SVC_INTEGRITY: 21418c2ecf20Sopenharmony_ci status = gss_unwrap_resp_integ(task, cred, ctx, rqstp, xdr); 21428c2ecf20Sopenharmony_ci break; 21438c2ecf20Sopenharmony_ci case RPC_GSS_SVC_PRIVACY: 21448c2ecf20Sopenharmony_ci status = gss_unwrap_resp_priv(task, cred, ctx, rqstp, xdr); 21458c2ecf20Sopenharmony_ci break; 21468c2ecf20Sopenharmony_ci } 21478c2ecf20Sopenharmony_ci if (status) 21488c2ecf20Sopenharmony_ci goto out; 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_ciout_decode: 21518c2ecf20Sopenharmony_ci status = rpcauth_unwrap_resp_decode(task, xdr); 21528c2ecf20Sopenharmony_ciout: 21538c2ecf20Sopenharmony_ci gss_put_ctx(ctx); 21548c2ecf20Sopenharmony_ci return status; 21558c2ecf20Sopenharmony_ci} 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_cistatic const struct rpc_authops authgss_ops = { 21588c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 21598c2ecf20Sopenharmony_ci .au_flavor = RPC_AUTH_GSS, 21608c2ecf20Sopenharmony_ci .au_name = "RPCSEC_GSS", 21618c2ecf20Sopenharmony_ci .create = gss_create, 21628c2ecf20Sopenharmony_ci .destroy = gss_destroy, 21638c2ecf20Sopenharmony_ci .hash_cred = gss_hash_cred, 21648c2ecf20Sopenharmony_ci .lookup_cred = gss_lookup_cred, 21658c2ecf20Sopenharmony_ci .crcreate = gss_create_cred, 21668c2ecf20Sopenharmony_ci .info2flavor = gss_mech_info2flavor, 21678c2ecf20Sopenharmony_ci .flavor2info = gss_mech_flavor2info, 21688c2ecf20Sopenharmony_ci}; 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_cistatic const struct rpc_credops gss_credops = { 21718c2ecf20Sopenharmony_ci .cr_name = "AUTH_GSS", 21728c2ecf20Sopenharmony_ci .crdestroy = gss_destroy_cred, 21738c2ecf20Sopenharmony_ci .cr_init = gss_cred_init, 21748c2ecf20Sopenharmony_ci .crmatch = gss_match, 21758c2ecf20Sopenharmony_ci .crmarshal = gss_marshal, 21768c2ecf20Sopenharmony_ci .crrefresh = gss_refresh, 21778c2ecf20Sopenharmony_ci .crvalidate = gss_validate, 21788c2ecf20Sopenharmony_ci .crwrap_req = gss_wrap_req, 21798c2ecf20Sopenharmony_ci .crunwrap_resp = gss_unwrap_resp, 21808c2ecf20Sopenharmony_ci .crkey_timeout = gss_key_timeout, 21818c2ecf20Sopenharmony_ci .crstringify_acceptor = gss_stringify_acceptor, 21828c2ecf20Sopenharmony_ci .crneed_reencode = gss_xmit_need_reencode, 21838c2ecf20Sopenharmony_ci}; 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_cistatic const struct rpc_credops gss_nullops = { 21868c2ecf20Sopenharmony_ci .cr_name = "AUTH_GSS", 21878c2ecf20Sopenharmony_ci .crdestroy = gss_destroy_nullcred, 21888c2ecf20Sopenharmony_ci .crmatch = gss_match, 21898c2ecf20Sopenharmony_ci .crmarshal = gss_marshal, 21908c2ecf20Sopenharmony_ci .crrefresh = gss_refresh_null, 21918c2ecf20Sopenharmony_ci .crvalidate = gss_validate, 21928c2ecf20Sopenharmony_ci .crwrap_req = gss_wrap_req, 21938c2ecf20Sopenharmony_ci .crunwrap_resp = gss_unwrap_resp, 21948c2ecf20Sopenharmony_ci .crstringify_acceptor = gss_stringify_acceptor, 21958c2ecf20Sopenharmony_ci}; 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_cistatic const struct rpc_pipe_ops gss_upcall_ops_v0 = { 21988c2ecf20Sopenharmony_ci .upcall = gss_v0_upcall, 21998c2ecf20Sopenharmony_ci .downcall = gss_pipe_downcall, 22008c2ecf20Sopenharmony_ci .destroy_msg = gss_pipe_destroy_msg, 22018c2ecf20Sopenharmony_ci .open_pipe = gss_pipe_open_v0, 22028c2ecf20Sopenharmony_ci .release_pipe = gss_pipe_release, 22038c2ecf20Sopenharmony_ci}; 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_cistatic const struct rpc_pipe_ops gss_upcall_ops_v1 = { 22068c2ecf20Sopenharmony_ci .upcall = gss_v1_upcall, 22078c2ecf20Sopenharmony_ci .downcall = gss_pipe_downcall, 22088c2ecf20Sopenharmony_ci .destroy_msg = gss_pipe_destroy_msg, 22098c2ecf20Sopenharmony_ci .open_pipe = gss_pipe_open_v1, 22108c2ecf20Sopenharmony_ci .release_pipe = gss_pipe_release, 22118c2ecf20Sopenharmony_ci}; 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_cistatic __net_init int rpcsec_gss_init_net(struct net *net) 22148c2ecf20Sopenharmony_ci{ 22158c2ecf20Sopenharmony_ci return gss_svc_init_net(net); 22168c2ecf20Sopenharmony_ci} 22178c2ecf20Sopenharmony_ci 22188c2ecf20Sopenharmony_cistatic __net_exit void rpcsec_gss_exit_net(struct net *net) 22198c2ecf20Sopenharmony_ci{ 22208c2ecf20Sopenharmony_ci gss_svc_shutdown_net(net); 22218c2ecf20Sopenharmony_ci} 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_cistatic struct pernet_operations rpcsec_gss_net_ops = { 22248c2ecf20Sopenharmony_ci .init = rpcsec_gss_init_net, 22258c2ecf20Sopenharmony_ci .exit = rpcsec_gss_exit_net, 22268c2ecf20Sopenharmony_ci}; 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_ci/* 22298c2ecf20Sopenharmony_ci * Initialize RPCSEC_GSS module 22308c2ecf20Sopenharmony_ci */ 22318c2ecf20Sopenharmony_cistatic int __init init_rpcsec_gss(void) 22328c2ecf20Sopenharmony_ci{ 22338c2ecf20Sopenharmony_ci int err = 0; 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_ci err = rpcauth_register(&authgss_ops); 22368c2ecf20Sopenharmony_ci if (err) 22378c2ecf20Sopenharmony_ci goto out; 22388c2ecf20Sopenharmony_ci err = gss_svc_init(); 22398c2ecf20Sopenharmony_ci if (err) 22408c2ecf20Sopenharmony_ci goto out_unregister; 22418c2ecf20Sopenharmony_ci err = register_pernet_subsys(&rpcsec_gss_net_ops); 22428c2ecf20Sopenharmony_ci if (err) 22438c2ecf20Sopenharmony_ci goto out_svc_exit; 22448c2ecf20Sopenharmony_ci rpc_init_wait_queue(&pipe_version_rpc_waitqueue, "gss pipe version"); 22458c2ecf20Sopenharmony_ci return 0; 22468c2ecf20Sopenharmony_ciout_svc_exit: 22478c2ecf20Sopenharmony_ci gss_svc_shutdown(); 22488c2ecf20Sopenharmony_ciout_unregister: 22498c2ecf20Sopenharmony_ci rpcauth_unregister(&authgss_ops); 22508c2ecf20Sopenharmony_ciout: 22518c2ecf20Sopenharmony_ci return err; 22528c2ecf20Sopenharmony_ci} 22538c2ecf20Sopenharmony_ci 22548c2ecf20Sopenharmony_cistatic void __exit exit_rpcsec_gss(void) 22558c2ecf20Sopenharmony_ci{ 22568c2ecf20Sopenharmony_ci unregister_pernet_subsys(&rpcsec_gss_net_ops); 22578c2ecf20Sopenharmony_ci gss_svc_shutdown(); 22588c2ecf20Sopenharmony_ci rpcauth_unregister(&authgss_ops); 22598c2ecf20Sopenharmony_ci rcu_barrier(); /* Wait for completion of call_rcu()'s */ 22608c2ecf20Sopenharmony_ci} 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ciMODULE_ALIAS("rpc-auth-6"); 22638c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 22648c2ecf20Sopenharmony_cimodule_param_named(expired_cred_retry_delay, 22658c2ecf20Sopenharmony_ci gss_expired_cred_retry_delay, 22668c2ecf20Sopenharmony_ci uint, 0644); 22678c2ecf20Sopenharmony_ciMODULE_PARM_DESC(expired_cred_retry_delay, "Timeout (in seconds) until " 22688c2ecf20Sopenharmony_ci "the RPC engine retries an expired credential"); 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_cimodule_param_named(key_expire_timeo, 22718c2ecf20Sopenharmony_ci gss_key_expire_timeo, 22728c2ecf20Sopenharmony_ci uint, 0644); 22738c2ecf20Sopenharmony_ciMODULE_PARM_DESC(key_expire_timeo, "Time (in seconds) at the end of a " 22748c2ecf20Sopenharmony_ci "credential keys lifetime where the NFS layer cleans up " 22758c2ecf20Sopenharmony_ci "prior to key expiration"); 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_cimodule_init(init_rpcsec_gss) 22788c2ecf20Sopenharmony_cimodule_exit(exit_rpcsec_gss) 2279