12573a464SChuck Lever // SPDX-License-Identifier: GPL-2.0 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * Neil Brown <neilb@cse.unsw.edu.au> 41da177e4SLinus Torvalds * J. Bruce Fields <bfields@umich.edu> 51da177e4SLinus Torvalds * Andy Adamson <andros@umich.edu> 61da177e4SLinus Torvalds * Dug Song <dugsong@monkey.org> 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * RPCSEC_GSS server authentication. 91da177e4SLinus Torvalds * This implements RPCSEC_GSS as defined in rfc2203 (rpcsec_gss) and rfc2078 101da177e4SLinus Torvalds * (gssapi) 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * The RPCSEC_GSS involves three stages: 131da177e4SLinus Torvalds * 1/ context creation 141da177e4SLinus Torvalds * 2/ data exchange 151da177e4SLinus Torvalds * 3/ context destruction 161da177e4SLinus Torvalds * 171da177e4SLinus Torvalds * Context creation is handled largely by upcalls to user-space. 181da177e4SLinus Torvalds * In particular, GSS_Accept_sec_context is handled by an upcall 191da177e4SLinus Torvalds * Data exchange is handled entirely within the kernel 201da177e4SLinus Torvalds * In particular, GSS_GetMIC, GSS_VerifyMIC, GSS_Seal, GSS_Unseal are in-kernel. 211da177e4SLinus Torvalds * Context destruction is handled in-kernel 221da177e4SLinus Torvalds * GSS_Delete_sec_context is in-kernel 231da177e4SLinus Torvalds * 241da177e4SLinus Torvalds * Context creation is initiated by a RPCSEC_GSS_INIT request arriving. 251da177e4SLinus Torvalds * The context handle and gss_token are used as a key into the rpcsec_init cache. 261da177e4SLinus Torvalds * The content of this cache includes some of the outputs of GSS_Accept_sec_context, 271da177e4SLinus Torvalds * being major_status, minor_status, context_handle, reply_token. 281da177e4SLinus Torvalds * These are sent back to the client. 291da177e4SLinus Torvalds * Sequence window management is handled by the kernel. The window size if currently 301da177e4SLinus Torvalds * a compile time constant. 311da177e4SLinus Torvalds * 321da177e4SLinus Torvalds * When user-space is happy that a context is established, it places an entry 331da177e4SLinus Torvalds * in the rpcsec_context cache. The key for this cache is the context_handle. 341da177e4SLinus Torvalds * The content includes: 351da177e4SLinus Torvalds * uid/gidlist - for determining access rights 361da177e4SLinus Torvalds * mechanism type 371da177e4SLinus Torvalds * mechanism specific information, such as a key 381da177e4SLinus Torvalds * 391da177e4SLinus Torvalds */ 401da177e4SLinus Torvalds 415a0e3ad6STejun Heo #include <linux/slab.h> 421da177e4SLinus Torvalds #include <linux/types.h> 431da177e4SLinus Torvalds #include <linux/module.h> 441da177e4SLinus Torvalds #include <linux/pagemap.h> 45ae2975bcSEric W. Biederman #include <linux/user_namespace.h> 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds #include <linux/sunrpc/auth_gss.h> 481da177e4SLinus Torvalds #include <linux/sunrpc/gss_err.h> 491da177e4SLinus Torvalds #include <linux/sunrpc/svcauth.h> 501da177e4SLinus Torvalds #include <linux/sunrpc/svcauth_gss.h> 511da177e4SLinus Torvalds #include <linux/sunrpc/cache.h> 52ff27e9f7SChuck Lever 53ff27e9f7SChuck Lever #include <trace/events/rpcgss.h> 54ff27e9f7SChuck Lever 55030d794bSSimo Sorce #include "gss_rpc_upcall.h" 561da177e4SLinus Torvalds 57a1db410dSStanislav Kinsbursky 581da177e4SLinus Torvalds /* The rpcsec_init cache is used for mapping RPCSEC_GSS_{,CONT_}INIT requests 591da177e4SLinus Torvalds * into replies. 601da177e4SLinus Torvalds * 611da177e4SLinus Torvalds * Key is context handle (\x if empty) and gss_token. 621da177e4SLinus Torvalds * Content is major_status minor_status (integers) context_handle, reply_token. 631da177e4SLinus Torvalds * 641da177e4SLinus Torvalds */ 651da177e4SLinus Torvalds 661da177e4SLinus Torvalds static int netobj_equal(struct xdr_netobj *a, struct xdr_netobj *b) 671da177e4SLinus Torvalds { 681da177e4SLinus Torvalds return a->len == b->len && 0 == memcmp(a->data, b->data, a->len); 691da177e4SLinus Torvalds } 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds #define RSI_HASHBITS 6 721da177e4SLinus Torvalds #define RSI_HASHMAX (1<<RSI_HASHBITS) 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds struct rsi { 751da177e4SLinus Torvalds struct cache_head h; 761da177e4SLinus Torvalds struct xdr_netobj in_handle, in_token; 771da177e4SLinus Torvalds struct xdr_netobj out_handle, out_token; 781da177e4SLinus Torvalds int major_status, minor_status; 796d1616b2STrond Myklebust struct rcu_head rcu_head; 801da177e4SLinus Torvalds }; 811da177e4SLinus Torvalds 82a1db410dSStanislav Kinsbursky static struct rsi *rsi_update(struct cache_detail *cd, struct rsi *new, struct rsi *old); 83a1db410dSStanislav Kinsbursky static struct rsi *rsi_lookup(struct cache_detail *cd, struct rsi *item); 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds static void rsi_free(struct rsi *rsii) 861da177e4SLinus Torvalds { 871da177e4SLinus Torvalds kfree(rsii->in_handle.data); 881da177e4SLinus Torvalds kfree(rsii->in_token.data); 891da177e4SLinus Torvalds kfree(rsii->out_handle.data); 901da177e4SLinus Torvalds kfree(rsii->out_token.data); 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds 936d1616b2STrond Myklebust static void rsi_free_rcu(struct rcu_head *head) 946d1616b2STrond Myklebust { 956d1616b2STrond Myklebust struct rsi *rsii = container_of(head, struct rsi, rcu_head); 966d1616b2STrond Myklebust 976d1616b2STrond Myklebust rsi_free(rsii); 986d1616b2STrond Myklebust kfree(rsii); 996d1616b2STrond Myklebust } 1006d1616b2STrond Myklebust 101baab935fSNeilBrown static void rsi_put(struct kref *ref) 1021da177e4SLinus Torvalds { 103baab935fSNeilBrown struct rsi *rsii = container_of(ref, struct rsi, h.ref); 1046d1616b2STrond Myklebust 1056d1616b2STrond Myklebust call_rcu(&rsii->rcu_head, rsi_free_rcu); 1061da177e4SLinus Torvalds } 1071da177e4SLinus Torvalds 1081da177e4SLinus Torvalds static inline int rsi_hash(struct rsi *item) 1091da177e4SLinus Torvalds { 1101da177e4SLinus Torvalds return hash_mem(item->in_handle.data, item->in_handle.len, RSI_HASHBITS) 1111da177e4SLinus Torvalds ^ hash_mem(item->in_token.data, item->in_token.len, RSI_HASHBITS); 1121da177e4SLinus Torvalds } 1131da177e4SLinus Torvalds 114d4d11ea9SNeilBrown static int rsi_match(struct cache_head *a, struct cache_head *b) 1151da177e4SLinus Torvalds { 116d4d11ea9SNeilBrown struct rsi *item = container_of(a, struct rsi, h); 117d4d11ea9SNeilBrown struct rsi *tmp = container_of(b, struct rsi, h); 118f64f9e71SJoe Perches return netobj_equal(&item->in_handle, &tmp->in_handle) && 119f64f9e71SJoe Perches netobj_equal(&item->in_token, &tmp->in_token); 1201da177e4SLinus Torvalds } 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds static int dup_to_netobj(struct xdr_netobj *dst, char *src, int len) 1231da177e4SLinus Torvalds { 1241da177e4SLinus Torvalds dst->len = len; 125e69062b4SArnaldo Carvalho de Melo dst->data = (len ? kmemdup(src, len, GFP_KERNEL) : NULL); 1261da177e4SLinus Torvalds if (len && !dst->data) 1271da177e4SLinus Torvalds return -ENOMEM; 1281da177e4SLinus Torvalds return 0; 1291da177e4SLinus Torvalds } 1301da177e4SLinus Torvalds 1311da177e4SLinus Torvalds static inline int dup_netobj(struct xdr_netobj *dst, struct xdr_netobj *src) 1321da177e4SLinus Torvalds { 1331da177e4SLinus Torvalds return dup_to_netobj(dst, src->data, src->len); 1341da177e4SLinus Torvalds } 1351da177e4SLinus Torvalds 136d4d11ea9SNeilBrown static void rsi_init(struct cache_head *cnew, struct cache_head *citem) 1371da177e4SLinus Torvalds { 138d4d11ea9SNeilBrown struct rsi *new = container_of(cnew, struct rsi, h); 139d4d11ea9SNeilBrown struct rsi *item = container_of(citem, struct rsi, h); 140d4d11ea9SNeilBrown 1411da177e4SLinus Torvalds new->out_handle.data = NULL; 1421da177e4SLinus Torvalds new->out_handle.len = 0; 1431da177e4SLinus Torvalds new->out_token.data = NULL; 1441da177e4SLinus Torvalds new->out_token.len = 0; 1451da177e4SLinus Torvalds new->in_handle.len = item->in_handle.len; 1461da177e4SLinus Torvalds item->in_handle.len = 0; 1471da177e4SLinus Torvalds new->in_token.len = item->in_token.len; 1481da177e4SLinus Torvalds item->in_token.len = 0; 1491da177e4SLinus Torvalds new->in_handle.data = item->in_handle.data; 1501da177e4SLinus Torvalds item->in_handle.data = NULL; 1511da177e4SLinus Torvalds new->in_token.data = item->in_token.data; 1521da177e4SLinus Torvalds item->in_token.data = NULL; 1531da177e4SLinus Torvalds } 1541da177e4SLinus Torvalds 155d4d11ea9SNeilBrown static void update_rsi(struct cache_head *cnew, struct cache_head *citem) 1561da177e4SLinus Torvalds { 157d4d11ea9SNeilBrown struct rsi *new = container_of(cnew, struct rsi, h); 158d4d11ea9SNeilBrown struct rsi *item = container_of(citem, struct rsi, h); 159d4d11ea9SNeilBrown 1601da177e4SLinus Torvalds BUG_ON(new->out_handle.data || new->out_token.data); 1611da177e4SLinus Torvalds new->out_handle.len = item->out_handle.len; 1621da177e4SLinus Torvalds item->out_handle.len = 0; 1631da177e4SLinus Torvalds new->out_token.len = item->out_token.len; 1641da177e4SLinus Torvalds item->out_token.len = 0; 1651da177e4SLinus Torvalds new->out_handle.data = item->out_handle.data; 1661da177e4SLinus Torvalds item->out_handle.data = NULL; 1671da177e4SLinus Torvalds new->out_token.data = item->out_token.data; 1681da177e4SLinus Torvalds item->out_token.data = NULL; 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds new->major_status = item->major_status; 1711da177e4SLinus Torvalds new->minor_status = item->minor_status; 1721da177e4SLinus Torvalds } 1731da177e4SLinus Torvalds 174d4d11ea9SNeilBrown static struct cache_head *rsi_alloc(void) 175d4d11ea9SNeilBrown { 176d4d11ea9SNeilBrown struct rsi *rsii = kmalloc(sizeof(*rsii), GFP_KERNEL); 177d4d11ea9SNeilBrown if (rsii) 178d4d11ea9SNeilBrown return &rsii->h; 179d4d11ea9SNeilBrown else 180d4d11ea9SNeilBrown return NULL; 181d4d11ea9SNeilBrown } 182d4d11ea9SNeilBrown 18365286b88STrond Myklebust static int rsi_upcall(struct cache_detail *cd, struct cache_head *h) 18465286b88STrond Myklebust { 18565286b88STrond Myklebust return sunrpc_cache_pipe_upcall_timeout(cd, h); 18665286b88STrond Myklebust } 18765286b88STrond Myklebust 1881da177e4SLinus Torvalds static void rsi_request(struct cache_detail *cd, 1891da177e4SLinus Torvalds struct cache_head *h, 1901da177e4SLinus Torvalds char **bpp, int *blen) 1911da177e4SLinus Torvalds { 1921da177e4SLinus Torvalds struct rsi *rsii = container_of(h, struct rsi, h); 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds qword_addhex(bpp, blen, rsii->in_handle.data, rsii->in_handle.len); 1951da177e4SLinus Torvalds qword_addhex(bpp, blen, rsii->in_token.data, rsii->in_token.len); 1961da177e4SLinus Torvalds (*bpp)[-1] = '\n'; 1970c217d50SNeilBrown WARN_ONCE(*blen < 0, 1980c217d50SNeilBrown "RPCSEC/GSS credential too large - please use gssproxy\n"); 1991da177e4SLinus Torvalds } 2001da177e4SLinus Torvalds 2011da177e4SLinus Torvalds static int rsi_parse(struct cache_detail *cd, 2021da177e4SLinus Torvalds char *mesg, int mlen) 2031da177e4SLinus Torvalds { 2041da177e4SLinus Torvalds /* context token expiry major minor context token */ 2051da177e4SLinus Torvalds char *buf = mesg; 2061da177e4SLinus Torvalds char *ep; 2071da177e4SLinus Torvalds int len; 2081da177e4SLinus Torvalds struct rsi rsii, *rsip = NULL; 209f559935eSArnd Bergmann time64_t expiry; 2101da177e4SLinus Torvalds int status = -EINVAL; 2111da177e4SLinus Torvalds 2121da177e4SLinus Torvalds memset(&rsii, 0, sizeof(rsii)); 2131da177e4SLinus Torvalds /* handle */ 2141da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2151da177e4SLinus Torvalds if (len < 0) 2161da177e4SLinus Torvalds goto out; 2171da177e4SLinus Torvalds status = -ENOMEM; 2181da177e4SLinus Torvalds if (dup_to_netobj(&rsii.in_handle, buf, len)) 2191da177e4SLinus Torvalds goto out; 2201da177e4SLinus Torvalds 2211da177e4SLinus Torvalds /* token */ 2221da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2231da177e4SLinus Torvalds status = -EINVAL; 2241da177e4SLinus Torvalds if (len < 0) 2251da177e4SLinus Torvalds goto out; 2261da177e4SLinus Torvalds status = -ENOMEM; 2271da177e4SLinus Torvalds if (dup_to_netobj(&rsii.in_token, buf, len)) 2281da177e4SLinus Torvalds goto out; 2291da177e4SLinus Torvalds 230a1db410dSStanislav Kinsbursky rsip = rsi_lookup(cd, &rsii); 231d4d11ea9SNeilBrown if (!rsip) 232d4d11ea9SNeilBrown goto out; 233d4d11ea9SNeilBrown 2341da177e4SLinus Torvalds rsii.h.flags = 0; 2351da177e4SLinus Torvalds /* expiry */ 2361da177e4SLinus Torvalds expiry = get_expiry(&mesg); 2371da177e4SLinus Torvalds status = -EINVAL; 2381da177e4SLinus Torvalds if (expiry == 0) 2391da177e4SLinus Torvalds goto out; 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds /* major/minor */ 2421da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 243b39c18fcSJ. Bruce Fields if (len <= 0) 2441da177e4SLinus Torvalds goto out; 2451da177e4SLinus Torvalds rsii.major_status = simple_strtoul(buf, &ep, 10); 2461da177e4SLinus Torvalds if (*ep) 2471da177e4SLinus Torvalds goto out; 2481da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2491da177e4SLinus Torvalds if (len <= 0) 2501da177e4SLinus Torvalds goto out; 2511da177e4SLinus Torvalds rsii.minor_status = simple_strtoul(buf, &ep, 10); 2521da177e4SLinus Torvalds if (*ep) 2531da177e4SLinus Torvalds goto out; 2541da177e4SLinus Torvalds 2551da177e4SLinus Torvalds /* out_handle */ 2561da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2571da177e4SLinus Torvalds if (len < 0) 2581da177e4SLinus Torvalds goto out; 2591da177e4SLinus Torvalds status = -ENOMEM; 2601da177e4SLinus Torvalds if (dup_to_netobj(&rsii.out_handle, buf, len)) 2611da177e4SLinus Torvalds goto out; 2621da177e4SLinus Torvalds 2631da177e4SLinus Torvalds /* out_token */ 2641da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2651da177e4SLinus Torvalds status = -EINVAL; 2661da177e4SLinus Torvalds if (len < 0) 2671da177e4SLinus Torvalds goto out; 2681da177e4SLinus Torvalds status = -ENOMEM; 2691da177e4SLinus Torvalds if (dup_to_netobj(&rsii.out_token, buf, len)) 2701da177e4SLinus Torvalds goto out; 2711da177e4SLinus Torvalds rsii.h.expiry_time = expiry; 272a1db410dSStanislav Kinsbursky rsip = rsi_update(cd, &rsii, rsip); 2731da177e4SLinus Torvalds status = 0; 2741da177e4SLinus Torvalds out: 2751da177e4SLinus Torvalds rsi_free(&rsii); 2761da177e4SLinus Torvalds if (rsip) 277a1db410dSStanislav Kinsbursky cache_put(&rsip->h, cd); 278d4d11ea9SNeilBrown else 279d4d11ea9SNeilBrown status = -ENOMEM; 2801da177e4SLinus Torvalds return status; 2811da177e4SLinus Torvalds } 2821da177e4SLinus Torvalds 283ee24eac3SBhumika Goyal static const struct cache_detail rsi_cache_template = { 284f35279d3SBruce Allan .owner = THIS_MODULE, 2851da177e4SLinus Torvalds .hash_size = RSI_HASHMAX, 2861da177e4SLinus Torvalds .name = "auth.rpcsec.init", 2871da177e4SLinus Torvalds .cache_put = rsi_put, 28865286b88STrond Myklebust .cache_upcall = rsi_upcall, 28973fb847aSStanislav Kinsbursky .cache_request = rsi_request, 2901da177e4SLinus Torvalds .cache_parse = rsi_parse, 291d4d11ea9SNeilBrown .match = rsi_match, 292d4d11ea9SNeilBrown .init = rsi_init, 293d4d11ea9SNeilBrown .update = update_rsi, 294d4d11ea9SNeilBrown .alloc = rsi_alloc, 2951da177e4SLinus Torvalds }; 2961da177e4SLinus Torvalds 297a1db410dSStanislav Kinsbursky static struct rsi *rsi_lookup(struct cache_detail *cd, struct rsi *item) 298d4d11ea9SNeilBrown { 299d4d11ea9SNeilBrown struct cache_head *ch; 300d4d11ea9SNeilBrown int hash = rsi_hash(item); 301d4d11ea9SNeilBrown 3026d1616b2STrond Myklebust ch = sunrpc_cache_lookup_rcu(cd, &item->h, hash); 303d4d11ea9SNeilBrown if (ch) 304d4d11ea9SNeilBrown return container_of(ch, struct rsi, h); 305d4d11ea9SNeilBrown else 306d4d11ea9SNeilBrown return NULL; 307d4d11ea9SNeilBrown } 308d4d11ea9SNeilBrown 309a1db410dSStanislav Kinsbursky static struct rsi *rsi_update(struct cache_detail *cd, struct rsi *new, struct rsi *old) 310d4d11ea9SNeilBrown { 311d4d11ea9SNeilBrown struct cache_head *ch; 312d4d11ea9SNeilBrown int hash = rsi_hash(new); 313d4d11ea9SNeilBrown 314a1db410dSStanislav Kinsbursky ch = sunrpc_cache_update(cd, &new->h, 315d4d11ea9SNeilBrown &old->h, hash); 316d4d11ea9SNeilBrown if (ch) 317d4d11ea9SNeilBrown return container_of(ch, struct rsi, h); 318d4d11ea9SNeilBrown else 319d4d11ea9SNeilBrown return NULL; 320d4d11ea9SNeilBrown } 321d4d11ea9SNeilBrown 3221da177e4SLinus Torvalds 3231da177e4SLinus Torvalds /* 3241da177e4SLinus Torvalds * The rpcsec_context cache is used to store a context that is 3251da177e4SLinus Torvalds * used in data exchange. 3261da177e4SLinus Torvalds * The key is a context handle. The content is: 3271da177e4SLinus Torvalds * uid, gidlist, mechanism, service-set, mech-specific-data 3281da177e4SLinus Torvalds */ 3291da177e4SLinus Torvalds 3301da177e4SLinus Torvalds #define RSC_HASHBITS 10 3311da177e4SLinus Torvalds #define RSC_HASHMAX (1<<RSC_HASHBITS) 3321da177e4SLinus Torvalds 3331da177e4SLinus Torvalds #define GSS_SEQ_WIN 128 3341da177e4SLinus Torvalds 3351da177e4SLinus Torvalds struct gss_svc_seq_data { 3361da177e4SLinus Torvalds /* highest seq number seen so far: */ 33710b9d99aSChuck Lever u32 sd_max; 3381da177e4SLinus Torvalds /* for i such that sd_max-GSS_SEQ_WIN < i <= sd_max, the i-th bit of 3391da177e4SLinus Torvalds * sd_win is nonzero iff sequence number i has been seen already: */ 3401da177e4SLinus Torvalds unsigned long sd_win[GSS_SEQ_WIN/BITS_PER_LONG]; 3411da177e4SLinus Torvalds spinlock_t sd_lock; 3421da177e4SLinus Torvalds }; 3431da177e4SLinus Torvalds 3441da177e4SLinus Torvalds struct rsc { 3451da177e4SLinus Torvalds struct cache_head h; 3461da177e4SLinus Torvalds struct xdr_netobj handle; 3471da177e4SLinus Torvalds struct svc_cred cred; 3481da177e4SLinus Torvalds struct gss_svc_seq_data seqdata; 3491da177e4SLinus Torvalds struct gss_ctx *mechctx; 3506d1616b2STrond Myklebust struct rcu_head rcu_head; 3511da177e4SLinus Torvalds }; 3521da177e4SLinus Torvalds 353a1db410dSStanislav Kinsbursky static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old); 354a1db410dSStanislav Kinsbursky static struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item); 3551da177e4SLinus Torvalds 3561da177e4SLinus Torvalds static void rsc_free(struct rsc *rsci) 3571da177e4SLinus Torvalds { 3581da177e4SLinus Torvalds kfree(rsci->handle.data); 3591da177e4SLinus Torvalds if (rsci->mechctx) 3601da177e4SLinus Torvalds gss_delete_sec_context(&rsci->mechctx); 36103a4e1f6SJ. Bruce Fields free_svc_cred(&rsci->cred); 3621da177e4SLinus Torvalds } 3631da177e4SLinus Torvalds 3646d1616b2STrond Myklebust static void rsc_free_rcu(struct rcu_head *head) 3656d1616b2STrond Myklebust { 3666d1616b2STrond Myklebust struct rsc *rsci = container_of(head, struct rsc, rcu_head); 3676d1616b2STrond Myklebust 3686d1616b2STrond Myklebust kfree(rsci->handle.data); 3696d1616b2STrond Myklebust kfree(rsci); 3706d1616b2STrond Myklebust } 3716d1616b2STrond Myklebust 372baab935fSNeilBrown static void rsc_put(struct kref *ref) 3731da177e4SLinus Torvalds { 374baab935fSNeilBrown struct rsc *rsci = container_of(ref, struct rsc, h.ref); 3751da177e4SLinus Torvalds 3766d1616b2STrond Myklebust if (rsci->mechctx) 3776d1616b2STrond Myklebust gss_delete_sec_context(&rsci->mechctx); 3786d1616b2STrond Myklebust free_svc_cred(&rsci->cred); 3796d1616b2STrond Myklebust call_rcu(&rsci->rcu_head, rsc_free_rcu); 3801da177e4SLinus Torvalds } 3811da177e4SLinus Torvalds 3821da177e4SLinus Torvalds static inline int 3831da177e4SLinus Torvalds rsc_hash(struct rsc *rsci) 3841da177e4SLinus Torvalds { 3851da177e4SLinus Torvalds return hash_mem(rsci->handle.data, rsci->handle.len, RSC_HASHBITS); 3861da177e4SLinus Torvalds } 3871da177e4SLinus Torvalds 38817f834b6SNeilBrown static int 38917f834b6SNeilBrown rsc_match(struct cache_head *a, struct cache_head *b) 3901da177e4SLinus Torvalds { 39117f834b6SNeilBrown struct rsc *new = container_of(a, struct rsc, h); 39217f834b6SNeilBrown struct rsc *tmp = container_of(b, struct rsc, h); 39317f834b6SNeilBrown 3941da177e4SLinus Torvalds return netobj_equal(&new->handle, &tmp->handle); 3951da177e4SLinus Torvalds } 3961da177e4SLinus Torvalds 39717f834b6SNeilBrown static void 39817f834b6SNeilBrown rsc_init(struct cache_head *cnew, struct cache_head *ctmp) 3991da177e4SLinus Torvalds { 40017f834b6SNeilBrown struct rsc *new = container_of(cnew, struct rsc, h); 40117f834b6SNeilBrown struct rsc *tmp = container_of(ctmp, struct rsc, h); 40217f834b6SNeilBrown 4031da177e4SLinus Torvalds new->handle.len = tmp->handle.len; 4041da177e4SLinus Torvalds tmp->handle.len = 0; 4051da177e4SLinus Torvalds new->handle.data = tmp->handle.data; 4061da177e4SLinus Torvalds tmp->handle.data = NULL; 4071da177e4SLinus Torvalds new->mechctx = NULL; 40844234063SJ. Bruce Fields init_svc_cred(&new->cred); 4091da177e4SLinus Torvalds } 4101da177e4SLinus Torvalds 41117f834b6SNeilBrown static void 41217f834b6SNeilBrown update_rsc(struct cache_head *cnew, struct cache_head *ctmp) 4131da177e4SLinus Torvalds { 41417f834b6SNeilBrown struct rsc *new = container_of(cnew, struct rsc, h); 41517f834b6SNeilBrown struct rsc *tmp = container_of(ctmp, struct rsc, h); 41617f834b6SNeilBrown 4171da177e4SLinus Torvalds new->mechctx = tmp->mechctx; 4181da177e4SLinus Torvalds tmp->mechctx = NULL; 4191da177e4SLinus Torvalds memset(&new->seqdata, 0, sizeof(new->seqdata)); 4201da177e4SLinus Torvalds spin_lock_init(&new->seqdata.sd_lock); 4211da177e4SLinus Torvalds new->cred = tmp->cred; 42244234063SJ. Bruce Fields init_svc_cred(&tmp->cred); 4231da177e4SLinus Torvalds } 4241da177e4SLinus Torvalds 42517f834b6SNeilBrown static struct cache_head * 42617f834b6SNeilBrown rsc_alloc(void) 42717f834b6SNeilBrown { 42817f834b6SNeilBrown struct rsc *rsci = kmalloc(sizeof(*rsci), GFP_KERNEL); 42917f834b6SNeilBrown if (rsci) 43017f834b6SNeilBrown return &rsci->h; 43117f834b6SNeilBrown else 43217f834b6SNeilBrown return NULL; 43317f834b6SNeilBrown } 43417f834b6SNeilBrown 43565286b88STrond Myklebust static int rsc_upcall(struct cache_detail *cd, struct cache_head *h) 43665286b88STrond Myklebust { 43765286b88STrond Myklebust return -EINVAL; 43865286b88STrond Myklebust } 43965286b88STrond Myklebust 4401da177e4SLinus Torvalds static int rsc_parse(struct cache_detail *cd, 4411da177e4SLinus Torvalds char *mesg, int mlen) 4421da177e4SLinus Torvalds { 4431da177e4SLinus Torvalds /* contexthandle expiry [ uid gid N <n gids> mechname ...mechdata... ] */ 4441da177e4SLinus Torvalds char *buf = mesg; 445683428faSEric W. Biederman int id; 4461da177e4SLinus Torvalds int len, rv; 4471da177e4SLinus Torvalds struct rsc rsci, *rscp = NULL; 448294ec5b8SArnd Bergmann time64_t expiry; 4491da177e4SLinus Torvalds int status = -EINVAL; 4501df0cadaSJ. Bruce Fields struct gss_api_mech *gm = NULL; 4511da177e4SLinus Torvalds 4521da177e4SLinus Torvalds memset(&rsci, 0, sizeof(rsci)); 4531da177e4SLinus Torvalds /* context handle */ 4541da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 4551da177e4SLinus Torvalds if (len < 0) goto out; 4561da177e4SLinus Torvalds status = -ENOMEM; 4571da177e4SLinus Torvalds if (dup_to_netobj(&rsci.handle, buf, len)) 4581da177e4SLinus Torvalds goto out; 4591da177e4SLinus Torvalds 4601da177e4SLinus Torvalds rsci.h.flags = 0; 4611da177e4SLinus Torvalds /* expiry */ 4621da177e4SLinus Torvalds expiry = get_expiry(&mesg); 4631da177e4SLinus Torvalds status = -EINVAL; 4641da177e4SLinus Torvalds if (expiry == 0) 4651da177e4SLinus Torvalds goto out; 4661da177e4SLinus Torvalds 467a1db410dSStanislav Kinsbursky rscp = rsc_lookup(cd, &rsci); 46817f834b6SNeilBrown if (!rscp) 46917f834b6SNeilBrown goto out; 47017f834b6SNeilBrown 4711da177e4SLinus Torvalds /* uid, or NEGATIVE */ 472683428faSEric W. Biederman rv = get_int(&mesg, &id); 4731da177e4SLinus Torvalds if (rv == -EINVAL) 4741da177e4SLinus Torvalds goto out; 4751da177e4SLinus Torvalds if (rv == -ENOENT) 4761da177e4SLinus Torvalds set_bit(CACHE_NEGATIVE, &rsci.h.flags); 4771da177e4SLinus Torvalds else { 4781da177e4SLinus Torvalds int N, i; 4791da177e4SLinus Torvalds 4803c34ae11SJ. Bruce Fields /* 4813c34ae11SJ. Bruce Fields * NOTE: we skip uid_valid()/gid_valid() checks here: 4823c34ae11SJ. Bruce Fields * instead, * -1 id's are later mapped to the 4833c34ae11SJ. Bruce Fields * (export-specific) anonymous id by nfsd_setuser. 4843c34ae11SJ. Bruce Fields * 4853c34ae11SJ. Bruce Fields * (But supplementary gid's get no such special 4863c34ae11SJ. Bruce Fields * treatment so are checked for validity here.) 4873c34ae11SJ. Bruce Fields */ 488683428faSEric W. Biederman /* uid */ 489e6667c73STrond Myklebust rsci.cred.cr_uid = make_kuid(current_user_ns(), id); 490683428faSEric W. Biederman 4911da177e4SLinus Torvalds /* gid */ 492683428faSEric W. Biederman if (get_int(&mesg, &id)) 493683428faSEric W. Biederman goto out; 494e6667c73STrond Myklebust rsci.cred.cr_gid = make_kgid(current_user_ns(), id); 4951da177e4SLinus Torvalds 4961da177e4SLinus Torvalds /* number of additional gid's */ 4971da177e4SLinus Torvalds if (get_int(&mesg, &N)) 4981da177e4SLinus Torvalds goto out; 49976cb4be9SDan Carpenter if (N < 0 || N > NGROUPS_MAX) 50076cb4be9SDan Carpenter goto out; 5011da177e4SLinus Torvalds status = -ENOMEM; 5021da177e4SLinus Torvalds rsci.cred.cr_group_info = groups_alloc(N); 5031da177e4SLinus Torvalds if (rsci.cred.cr_group_info == NULL) 5041da177e4SLinus Torvalds goto out; 5051da177e4SLinus Torvalds 5061da177e4SLinus Torvalds /* gid's */ 5071da177e4SLinus Torvalds status = -EINVAL; 5081da177e4SLinus Torvalds for (i=0; i<N; i++) { 509ae2975bcSEric W. Biederman kgid_t kgid; 510683428faSEric W. Biederman if (get_int(&mesg, &id)) 5111da177e4SLinus Torvalds goto out; 512e6667c73STrond Myklebust kgid = make_kgid(current_user_ns(), id); 513ae2975bcSEric W. Biederman if (!gid_valid(kgid)) 514ae2975bcSEric W. Biederman goto out; 51581243eacSAlexey Dobriyan rsci.cred.cr_group_info->gid[i] = kgid; 5161da177e4SLinus Torvalds } 517bdcf0a42SThiago Rafael Becker groups_sort(rsci.cred.cr_group_info); 5181da177e4SLinus Torvalds 5191da177e4SLinus Torvalds /* mech name */ 5201da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 5211da177e4SLinus Torvalds if (len < 0) 5221da177e4SLinus Torvalds goto out; 5230dc1531aSJ. Bruce Fields gm = rsci.cred.cr_gss_mech = gss_mech_get_by_name(buf); 5241da177e4SLinus Torvalds status = -EOPNOTSUPP; 5251da177e4SLinus Torvalds if (!gm) 5261da177e4SLinus Torvalds goto out; 5271da177e4SLinus Torvalds 5281da177e4SLinus Torvalds status = -EINVAL; 5291da177e4SLinus Torvalds /* mech-specific data: */ 5301da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 5311df0cadaSJ. Bruce Fields if (len < 0) 5321da177e4SLinus Torvalds goto out; 533400f26b5SSimo Sorce status = gss_import_sec_context(buf, len, gm, &rsci.mechctx, 534400f26b5SSimo Sorce NULL, GFP_KERNEL); 5351df0cadaSJ. Bruce Fields if (status) 5361da177e4SLinus Torvalds goto out; 53768e76ad0SOlga Kornievskaia 53868e76ad0SOlga Kornievskaia /* get client name */ 53968e76ad0SOlga Kornievskaia len = qword_get(&mesg, buf, mlen); 54068e76ad0SOlga Kornievskaia if (len > 0) { 54103a4e1f6SJ. Bruce Fields rsci.cred.cr_principal = kstrdup(buf, GFP_KERNEL); 5421eb6d622SWei Yongjun if (!rsci.cred.cr_principal) { 5431eb6d622SWei Yongjun status = -ENOMEM; 54468e76ad0SOlga Kornievskaia goto out; 54568e76ad0SOlga Kornievskaia } 5461eb6d622SWei Yongjun } 54768e76ad0SOlga Kornievskaia 5481da177e4SLinus Torvalds } 5491da177e4SLinus Torvalds rsci.h.expiry_time = expiry; 550a1db410dSStanislav Kinsbursky rscp = rsc_update(cd, &rsci, rscp); 5511da177e4SLinus Torvalds status = 0; 5521da177e4SLinus Torvalds out: 5531da177e4SLinus Torvalds rsc_free(&rsci); 5541da177e4SLinus Torvalds if (rscp) 555a1db410dSStanislav Kinsbursky cache_put(&rscp->h, cd); 55617f834b6SNeilBrown else 55717f834b6SNeilBrown status = -ENOMEM; 5581da177e4SLinus Torvalds return status; 5591da177e4SLinus Torvalds } 5601da177e4SLinus Torvalds 561ee24eac3SBhumika Goyal static const struct cache_detail rsc_cache_template = { 562f35279d3SBruce Allan .owner = THIS_MODULE, 5631da177e4SLinus Torvalds .hash_size = RSC_HASHMAX, 5641da177e4SLinus Torvalds .name = "auth.rpcsec.context", 5651da177e4SLinus Torvalds .cache_put = rsc_put, 56665286b88STrond Myklebust .cache_upcall = rsc_upcall, 5671da177e4SLinus Torvalds .cache_parse = rsc_parse, 56817f834b6SNeilBrown .match = rsc_match, 56917f834b6SNeilBrown .init = rsc_init, 57017f834b6SNeilBrown .update = update_rsc, 57117f834b6SNeilBrown .alloc = rsc_alloc, 5721da177e4SLinus Torvalds }; 5731da177e4SLinus Torvalds 574a1db410dSStanislav Kinsbursky static struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item) 57517f834b6SNeilBrown { 57617f834b6SNeilBrown struct cache_head *ch; 57717f834b6SNeilBrown int hash = rsc_hash(item); 57817f834b6SNeilBrown 5796d1616b2STrond Myklebust ch = sunrpc_cache_lookup_rcu(cd, &item->h, hash); 58017f834b6SNeilBrown if (ch) 58117f834b6SNeilBrown return container_of(ch, struct rsc, h); 58217f834b6SNeilBrown else 58317f834b6SNeilBrown return NULL; 58417f834b6SNeilBrown } 58517f834b6SNeilBrown 586a1db410dSStanislav Kinsbursky static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old) 58717f834b6SNeilBrown { 58817f834b6SNeilBrown struct cache_head *ch; 58917f834b6SNeilBrown int hash = rsc_hash(new); 59017f834b6SNeilBrown 591a1db410dSStanislav Kinsbursky ch = sunrpc_cache_update(cd, &new->h, 59217f834b6SNeilBrown &old->h, hash); 59317f834b6SNeilBrown if (ch) 59417f834b6SNeilBrown return container_of(ch, struct rsc, h); 59517f834b6SNeilBrown else 59617f834b6SNeilBrown return NULL; 59717f834b6SNeilBrown } 59817f834b6SNeilBrown 5991da177e4SLinus Torvalds 6001da177e4SLinus Torvalds static struct rsc * 601a1db410dSStanislav Kinsbursky gss_svc_searchbyctx(struct cache_detail *cd, struct xdr_netobj *handle) 6021da177e4SLinus Torvalds { 6031da177e4SLinus Torvalds struct rsc rsci; 6041da177e4SLinus Torvalds struct rsc *found; 6051da177e4SLinus Torvalds 6061da177e4SLinus Torvalds memset(&rsci, 0, sizeof(rsci)); 607bf2c4b6fSChuck Lever if (dup_to_netobj(&rsci.handle, handle->data, handle->len)) 608bf2c4b6fSChuck Lever return NULL; 609a1db410dSStanislav Kinsbursky found = rsc_lookup(cd, &rsci); 610bf2c4b6fSChuck Lever rsc_free(&rsci); 6111da177e4SLinus Torvalds if (!found) 6121da177e4SLinus Torvalds return NULL; 613a1db410dSStanislav Kinsbursky if (cache_check(cd, &found->h, NULL)) 6141da177e4SLinus Torvalds return NULL; 6151da177e4SLinus Torvalds return found; 6161da177e4SLinus Torvalds } 6171da177e4SLinus Torvalds 61810b9d99aSChuck Lever /** 61910b9d99aSChuck Lever * gss_check_seq_num - GSS sequence number window check 62010b9d99aSChuck Lever * @rqstp: RPC Call to use when reporting errors 62110b9d99aSChuck Lever * @rsci: cached GSS context state (updated on return) 62210b9d99aSChuck Lever * @seq_num: sequence number to check 62310b9d99aSChuck Lever * 62410b9d99aSChuck Lever * Implements sequence number algorithm as specified in 62510b9d99aSChuck Lever * RFC 2203, Section 5.3.3.1. "Context Management". 62610b9d99aSChuck Lever * 62710b9d99aSChuck Lever * Return values: 62810b9d99aSChuck Lever * %true: @rqstp's GSS sequence number is inside the window 62910b9d99aSChuck Lever * %false: @rqstp's GSS sequence number is outside the window 63010b9d99aSChuck Lever */ 63110b9d99aSChuck Lever static bool gss_check_seq_num(const struct svc_rqst *rqstp, struct rsc *rsci, 63210b9d99aSChuck Lever u32 seq_num) 6331da177e4SLinus Torvalds { 6341da177e4SLinus Torvalds struct gss_svc_seq_data *sd = &rsci->seqdata; 63510b9d99aSChuck Lever bool result = false; 6361da177e4SLinus Torvalds 6371da177e4SLinus Torvalds spin_lock(&sd->sd_lock); 6381da177e4SLinus Torvalds if (seq_num > sd->sd_max) { 6391da177e4SLinus Torvalds if (seq_num >= sd->sd_max + GSS_SEQ_WIN) { 6401da177e4SLinus Torvalds memset(sd->sd_win, 0, sizeof(sd->sd_win)); 6411da177e4SLinus Torvalds sd->sd_max = seq_num; 6421da177e4SLinus Torvalds } else while (sd->sd_max < seq_num) { 6431da177e4SLinus Torvalds sd->sd_max++; 6441da177e4SLinus Torvalds __clear_bit(sd->sd_max % GSS_SEQ_WIN, sd->sd_win); 6451da177e4SLinus Torvalds } 6461da177e4SLinus Torvalds __set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win); 6471da177e4SLinus Torvalds goto ok; 6482ba5acfbSJ. Bruce Fields } else if (seq_num + GSS_SEQ_WIN <= sd->sd_max) { 64910b9d99aSChuck Lever goto toolow; 6501da177e4SLinus Torvalds } 6511da177e4SLinus Torvalds if (__test_and_set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win)) 65210b9d99aSChuck Lever goto alreadyseen; 65310b9d99aSChuck Lever 6541da177e4SLinus Torvalds ok: 65510b9d99aSChuck Lever result = true; 65610b9d99aSChuck Lever out: 6571da177e4SLinus Torvalds spin_unlock(&sd->sd_lock); 65810b9d99aSChuck Lever return result; 65910b9d99aSChuck Lever 66010b9d99aSChuck Lever toolow: 66110b9d99aSChuck Lever trace_rpcgss_svc_seqno_low(rqstp, seq_num, 66210b9d99aSChuck Lever sd->sd_max - GSS_SEQ_WIN, 66310b9d99aSChuck Lever sd->sd_max); 66410b9d99aSChuck Lever goto out; 66510b9d99aSChuck Lever alreadyseen: 66610b9d99aSChuck Lever trace_rpcgss_svc_seqno_seen(rqstp, seq_num); 66710b9d99aSChuck Lever goto out; 6681da177e4SLinus Torvalds } 6691da177e4SLinus Torvalds 6701da177e4SLinus Torvalds static inline u32 round_up_to_quad(u32 i) 6711da177e4SLinus Torvalds { 6721da177e4SLinus Torvalds return (i + 3 ) & ~3; 6731da177e4SLinus Torvalds } 6741da177e4SLinus Torvalds 6751da177e4SLinus Torvalds static inline int 6761da177e4SLinus Torvalds svc_safe_getnetobj(struct kvec *argv, struct xdr_netobj *o) 6771da177e4SLinus Torvalds { 6781da177e4SLinus Torvalds int l; 6791da177e4SLinus Torvalds 6801da177e4SLinus Torvalds if (argv->iov_len < 4) 6811da177e4SLinus Torvalds return -1; 68276994313SAlexey Dobriyan o->len = svc_getnl(argv); 6831da177e4SLinus Torvalds l = round_up_to_quad(o->len); 6841da177e4SLinus Torvalds if (argv->iov_len < l) 6851da177e4SLinus Torvalds return -1; 6861da177e4SLinus Torvalds o->data = argv->iov_base; 6871da177e4SLinus Torvalds argv->iov_base += l; 6881da177e4SLinus Torvalds argv->iov_len -= l; 6891da177e4SLinus Torvalds return 0; 6901da177e4SLinus Torvalds } 6911da177e4SLinus Torvalds 6921da177e4SLinus Torvalds static inline int 6931da177e4SLinus Torvalds svc_safe_putnetobj(struct kvec *resv, struct xdr_netobj *o) 6941da177e4SLinus Torvalds { 695753ed90dSAl Viro u8 *p; 6961da177e4SLinus Torvalds 6971da177e4SLinus Torvalds if (resv->iov_len + 4 > PAGE_SIZE) 6981da177e4SLinus Torvalds return -1; 69976994313SAlexey Dobriyan svc_putnl(resv, o->len); 7001da177e4SLinus Torvalds p = resv->iov_base + resv->iov_len; 7011da177e4SLinus Torvalds resv->iov_len += round_up_to_quad(o->len); 7021da177e4SLinus Torvalds if (resv->iov_len > PAGE_SIZE) 7031da177e4SLinus Torvalds return -1; 7041da177e4SLinus Torvalds memcpy(p, o->data, o->len); 705753ed90dSAl Viro memset(p + o->len, 0, round_up_to_quad(o->len) - o->len); 7061da177e4SLinus Torvalds return 0; 7071da177e4SLinus Torvalds } 7081da177e4SLinus Torvalds 70921fcd02bSJ. Bruce Fields /* 71021fcd02bSJ. Bruce Fields * Verify the checksum on the header and return SVC_OK on success. 7111da177e4SLinus Torvalds * Otherwise, return SVC_DROP (in the case of a bad sequence number) 712438623a0SChuck Lever * or return SVC_DENIED and indicate error in rqstp->rq_auth_stat. 7131da177e4SLinus Torvalds */ 7141da177e4SLinus Torvalds static int 7151da177e4SLinus Torvalds gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci, 716438623a0SChuck Lever __be32 *rpcstart, struct rpc_gss_wire_cred *gc) 7171da177e4SLinus Torvalds { 7181da177e4SLinus Torvalds struct gss_ctx *ctx_id = rsci->mechctx; 7191da177e4SLinus Torvalds struct xdr_buf rpchdr; 7201da177e4SLinus Torvalds struct xdr_netobj checksum; 7211da177e4SLinus Torvalds u32 flavor = 0; 7221da177e4SLinus Torvalds struct kvec *argv = &rqstp->rq_arg.head[0]; 7231da177e4SLinus Torvalds struct kvec iov; 7241da177e4SLinus Torvalds 7251da177e4SLinus Torvalds /* data to compute the checksum over: */ 7261da177e4SLinus Torvalds iov.iov_base = rpcstart; 7271da177e4SLinus Torvalds iov.iov_len = (u8 *)argv->iov_base - (u8 *)rpcstart; 7281da177e4SLinus Torvalds xdr_buf_from_iov(&iov, &rpchdr); 7291da177e4SLinus Torvalds 730438623a0SChuck Lever rqstp->rq_auth_stat = rpc_autherr_badverf; 7311da177e4SLinus Torvalds if (argv->iov_len < 4) 7321da177e4SLinus Torvalds return SVC_DENIED; 73376994313SAlexey Dobriyan flavor = svc_getnl(argv); 7341da177e4SLinus Torvalds if (flavor != RPC_AUTH_GSS) 7351da177e4SLinus Torvalds return SVC_DENIED; 7361da177e4SLinus Torvalds if (svc_safe_getnetobj(argv, &checksum)) 7371da177e4SLinus Torvalds return SVC_DENIED; 7381da177e4SLinus Torvalds 7391da177e4SLinus Torvalds if (rqstp->rq_deferred) /* skip verification of revisited request */ 7401da177e4SLinus Torvalds return SVC_OK; 74100fd6e14SJ. Bruce Fields if (gss_verify_mic(ctx_id, &rpchdr, &checksum) != GSS_S_COMPLETE) { 742438623a0SChuck Lever rqstp->rq_auth_stat = rpcsec_gsserr_credproblem; 7431da177e4SLinus Torvalds return SVC_DENIED; 7441da177e4SLinus Torvalds } 7451da177e4SLinus Torvalds 7461da177e4SLinus Torvalds if (gc->gc_seq > MAXSEQ) { 74710b9d99aSChuck Lever trace_rpcgss_svc_seqno_large(rqstp, gc->gc_seq); 748438623a0SChuck Lever rqstp->rq_auth_stat = rpcsec_gsserr_ctxproblem; 7491da177e4SLinus Torvalds return SVC_DENIED; 7501da177e4SLinus Torvalds } 75110b9d99aSChuck Lever if (!gss_check_seq_num(rqstp, rsci, gc->gc_seq)) 7521da177e4SLinus Torvalds return SVC_DROP; 7531da177e4SLinus Torvalds return SVC_OK; 7541da177e4SLinus Torvalds } 7551da177e4SLinus Torvalds 7561da177e4SLinus Torvalds static int 757822f1005SAndy Adamson gss_write_null_verf(struct svc_rqst *rqstp) 758822f1005SAndy Adamson { 759d8ed029dSAlexey Dobriyan __be32 *p; 760822f1005SAndy Adamson 76176994313SAlexey Dobriyan svc_putnl(rqstp->rq_res.head, RPC_AUTH_NULL); 762822f1005SAndy Adamson p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len; 763822f1005SAndy Adamson /* don't really need to check if head->iov_len > PAGE_SIZE ... */ 764822f1005SAndy Adamson *p++ = 0; 765822f1005SAndy Adamson if (!xdr_ressize_check(rqstp, p)) 766822f1005SAndy Adamson return -1; 767822f1005SAndy Adamson return 0; 768822f1005SAndy Adamson } 769822f1005SAndy Adamson 770822f1005SAndy Adamson static int 7711da177e4SLinus Torvalds gss_write_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id, u32 seq) 7721da177e4SLinus Torvalds { 7732876a344SJ. Bruce Fields __be32 *xdr_seq; 7741da177e4SLinus Torvalds u32 maj_stat; 7751da177e4SLinus Torvalds struct xdr_buf verf_data; 7761da177e4SLinus Torvalds struct xdr_netobj mic; 777d8ed029dSAlexey Dobriyan __be32 *p; 7781da177e4SLinus Torvalds struct kvec iov; 7792876a344SJ. Bruce Fields int err = -1; 7801da177e4SLinus Torvalds 78176994313SAlexey Dobriyan svc_putnl(rqstp->rq_res.head, RPC_AUTH_GSS); 7822876a344SJ. Bruce Fields xdr_seq = kmalloc(4, GFP_KERNEL); 7832876a344SJ. Bruce Fields if (!xdr_seq) 784458032fcSYang Li return -ENOMEM; 7852876a344SJ. Bruce Fields *xdr_seq = htonl(seq); 7861da177e4SLinus Torvalds 7872876a344SJ. Bruce Fields iov.iov_base = xdr_seq; 7882876a344SJ. Bruce Fields iov.iov_len = 4; 7891da177e4SLinus Torvalds xdr_buf_from_iov(&iov, &verf_data); 7901da177e4SLinus Torvalds p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len; 7911da177e4SLinus Torvalds mic.data = (u8 *)(p + 1); 79200fd6e14SJ. Bruce Fields maj_stat = gss_get_mic(ctx_id, &verf_data, &mic); 7931da177e4SLinus Torvalds if (maj_stat != GSS_S_COMPLETE) 7942876a344SJ. Bruce Fields goto out; 7951da177e4SLinus Torvalds *p++ = htonl(mic.len); 7961da177e4SLinus Torvalds memset((u8 *)p + mic.len, 0, round_up_to_quad(mic.len) - mic.len); 7971da177e4SLinus Torvalds p += XDR_QUADLEN(mic.len); 7981da177e4SLinus Torvalds if (!xdr_ressize_check(rqstp, p)) 7992876a344SJ. Bruce Fields goto out; 8002876a344SJ. Bruce Fields err = 0; 8012876a344SJ. Bruce Fields out: 8022876a344SJ. Bruce Fields kfree(xdr_seq); 8032876a344SJ. Bruce Fields return err; 8041da177e4SLinus Torvalds } 8051da177e4SLinus Torvalds 8061da177e4SLinus Torvalds struct gss_domain { 8071da177e4SLinus Torvalds struct auth_domain h; 8081da177e4SLinus Torvalds u32 pseudoflavor; 8091da177e4SLinus Torvalds }; 8101da177e4SLinus Torvalds 8111da177e4SLinus Torvalds static struct auth_domain * 8121da177e4SLinus Torvalds find_gss_auth_domain(struct gss_ctx *ctx, u32 svc) 8131da177e4SLinus Torvalds { 8141da177e4SLinus Torvalds char *name; 8151da177e4SLinus Torvalds 8161da177e4SLinus Torvalds name = gss_service_to_auth_domain_name(ctx->mech_type, svc); 8171da177e4SLinus Torvalds if (!name) 8181da177e4SLinus Torvalds return NULL; 8191da177e4SLinus Torvalds return auth_domain_find(name); 8201da177e4SLinus Torvalds } 8211da177e4SLinus Torvalds 822efc36aa5SNeilBrown static struct auth_ops svcauthops_gss; 823efc36aa5SNeilBrown 8244796f457SJ. Bruce Fields u32 svcauth_gss_flavor(struct auth_domain *dom) 8254796f457SJ. Bruce Fields { 8264796f457SJ. Bruce Fields struct gss_domain *gd = container_of(dom, struct gss_domain, h); 8274796f457SJ. Bruce Fields 8284796f457SJ. Bruce Fields return gd->pseudoflavor; 8294796f457SJ. Bruce Fields } 8304796f457SJ. Bruce Fields 8317bd88269STrond Myklebust EXPORT_SYMBOL_GPL(svcauth_gss_flavor); 8324796f457SJ. Bruce Fields 83324c5efe4SNeilBrown struct auth_domain * 8341da177e4SLinus Torvalds svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name) 8351da177e4SLinus Torvalds { 8361da177e4SLinus Torvalds struct gss_domain *new; 8371da177e4SLinus Torvalds struct auth_domain *test; 8381da177e4SLinus Torvalds int stat = -ENOMEM; 8391da177e4SLinus Torvalds 8401da177e4SLinus Torvalds new = kmalloc(sizeof(*new), GFP_KERNEL); 8411da177e4SLinus Torvalds if (!new) 8421da177e4SLinus Torvalds goto out; 843efc36aa5SNeilBrown kref_init(&new->h.ref); 844e69062b4SArnaldo Carvalho de Melo new->h.name = kstrdup(name, GFP_KERNEL); 8451da177e4SLinus Torvalds if (!new->h.name) 8461da177e4SLinus Torvalds goto out_free_dom; 847efc36aa5SNeilBrown new->h.flavour = &svcauthops_gss; 8481da177e4SLinus Torvalds new->pseudoflavor = pseudoflavor; 8491da177e4SLinus Torvalds 850efc36aa5SNeilBrown test = auth_domain_lookup(name, &new->h); 851d47a5dc2SNeilBrown if (test != &new->h) { 852d47a5dc2SNeilBrown pr_warn("svc: duplicate registration of gss pseudo flavour %s.\n", 853d47a5dc2SNeilBrown name); 854d47a5dc2SNeilBrown stat = -EADDRINUSE; 855cb276805SJ. Bruce Fields auth_domain_put(test); 85624c5efe4SNeilBrown goto out_free_name; 8571da177e4SLinus Torvalds } 85824c5efe4SNeilBrown return test; 8591da177e4SLinus Torvalds 86024c5efe4SNeilBrown out_free_name: 86124c5efe4SNeilBrown kfree(new->h.name); 8621da177e4SLinus Torvalds out_free_dom: 8631da177e4SLinus Torvalds kfree(new); 8641da177e4SLinus Torvalds out: 86524c5efe4SNeilBrown return ERR_PTR(stat); 8661da177e4SLinus Torvalds } 8677bd88269STrond Myklebust EXPORT_SYMBOL_GPL(svcauth_gss_register_pseudoflavor); 8681da177e4SLinus Torvalds 8691da177e4SLinus Torvalds static inline int 8701da177e4SLinus Torvalds read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj) 8711da177e4SLinus Torvalds { 872d8ed029dSAlexey Dobriyan __be32 raw; 8731da177e4SLinus Torvalds int status; 8741da177e4SLinus Torvalds 8751da177e4SLinus Torvalds status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj)); 8761da177e4SLinus Torvalds if (status) 8771da177e4SLinus Torvalds return status; 8781da177e4SLinus Torvalds *obj = ntohl(raw); 8791da177e4SLinus Torvalds return 0; 8801da177e4SLinus Torvalds } 8811da177e4SLinus Torvalds 8821da177e4SLinus Torvalds /* It would be nice if this bit of code could be shared with the client. 8831da177e4SLinus Torvalds * Obstacles: 8841da177e4SLinus Torvalds * The client shouldn't malloc(), would have to pass in own memory. 8851da177e4SLinus Torvalds * The server uses base of head iovec as read pointer, while the 8861da177e4SLinus Torvalds * client uses separate pointer. */ 8871da177e4SLinus Torvalds static int 8884c190e2fSJeff Layton unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx) 8891da177e4SLinus Torvalds { 89010b9d99aSChuck Lever u32 integ_len, rseqno, maj_stat; 8911da177e4SLinus Torvalds int stat = -EINVAL; 8921da177e4SLinus Torvalds struct xdr_netobj mic; 8931da177e4SLinus Torvalds struct xdr_buf integ_buf; 8941da177e4SLinus Torvalds 89510b9d99aSChuck Lever mic.data = NULL; 89610b9d99aSChuck Lever 89706eb8a56SChuck Lever /* NFS READ normally uses splice to send data in-place. However 89806eb8a56SChuck Lever * the data in cache can change after the reply's MIC is computed 89906eb8a56SChuck Lever * but before the RPC reply is sent. To prevent the client from 90006eb8a56SChuck Lever * rejecting the server-computed MIC in this somewhat rare case, 90106eb8a56SChuck Lever * do not use splice with the GSS integrity service. 90206eb8a56SChuck Lever */ 90328df0988SChuck Lever __clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags); 90406eb8a56SChuck Lever 9054c190e2fSJeff Layton /* Did we already verify the signature on the original pass through? */ 9064c190e2fSJeff Layton if (rqstp->rq_deferred) 9074c190e2fSJeff Layton return 0; 9084c190e2fSJeff Layton 90976994313SAlexey Dobriyan integ_len = svc_getnl(&buf->head[0]); 9101da177e4SLinus Torvalds if (integ_len & 3) 91110b9d99aSChuck Lever goto unwrap_failed; 9121da177e4SLinus Torvalds if (integ_len > buf->len) 91310b9d99aSChuck Lever goto unwrap_failed; 91410b9d99aSChuck Lever if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len)) 91510b9d99aSChuck Lever goto unwrap_failed; 91610b9d99aSChuck Lever 9171da177e4SLinus Torvalds /* copy out mic... */ 9181da177e4SLinus Torvalds if (read_u32_from_xdr_buf(buf, integ_len, &mic.len)) 91910b9d99aSChuck Lever goto unwrap_failed; 9201da177e4SLinus Torvalds if (mic.len > RPC_MAX_AUTH_SIZE) 92110b9d99aSChuck Lever goto unwrap_failed; 9221da177e4SLinus Torvalds mic.data = kmalloc(mic.len, GFP_KERNEL); 9231da177e4SLinus Torvalds if (!mic.data) 92410b9d99aSChuck Lever goto unwrap_failed; 9251da177e4SLinus Torvalds if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len)) 92610b9d99aSChuck Lever goto unwrap_failed; 92700fd6e14SJ. Bruce Fields maj_stat = gss_verify_mic(ctx, &integ_buf, &mic); 9281da177e4SLinus Torvalds if (maj_stat != GSS_S_COMPLETE) 92910b9d99aSChuck Lever goto bad_mic; 93010b9d99aSChuck Lever rseqno = svc_getnl(&buf->head[0]); 93110b9d99aSChuck Lever if (rseqno != seq) 93210b9d99aSChuck Lever goto bad_seqno; 933c0cb8bf3STomáš Trnka /* trim off the mic and padding at the end before returning */ 9340a8e7b7dSChuck Lever xdr_buf_trim(buf, round_up_to_quad(mic.len) + 4); 9351da177e4SLinus Torvalds stat = 0; 9361da177e4SLinus Torvalds out: 937b797b5beSJ.Bruce Fields kfree(mic.data); 9381da177e4SLinus Torvalds return stat; 93910b9d99aSChuck Lever 94010b9d99aSChuck Lever unwrap_failed: 94110b9d99aSChuck Lever trace_rpcgss_svc_unwrap_failed(rqstp); 94210b9d99aSChuck Lever goto out; 94310b9d99aSChuck Lever bad_seqno: 94410b9d99aSChuck Lever trace_rpcgss_svc_seqno_bad(rqstp, seq, rseqno); 94510b9d99aSChuck Lever goto out; 94610b9d99aSChuck Lever bad_mic: 94710b9d99aSChuck Lever trace_rpcgss_svc_mic(rqstp, maj_stat); 94810b9d99aSChuck Lever goto out; 9491da177e4SLinus Torvalds } 9501da177e4SLinus Torvalds 9517c9fdcfbSJ. Bruce Fields static inline int 9527c9fdcfbSJ. Bruce Fields total_buf_len(struct xdr_buf *buf) 9537c9fdcfbSJ. Bruce Fields { 9547c9fdcfbSJ. Bruce Fields return buf->head[0].iov_len + buf->page_len + buf->tail[0].iov_len; 9557c9fdcfbSJ. Bruce Fields } 9567c9fdcfbSJ. Bruce Fields 9577c9fdcfbSJ. Bruce Fields static void 9587c9fdcfbSJ. Bruce Fields fix_priv_head(struct xdr_buf *buf, int pad) 9597c9fdcfbSJ. Bruce Fields { 9607c9fdcfbSJ. Bruce Fields if (buf->page_len == 0) { 9617c9fdcfbSJ. Bruce Fields /* We need to adjust head and buf->len in tandem in this 9627c9fdcfbSJ. Bruce Fields * case to make svc_defer() work--it finds the original 9637c9fdcfbSJ. Bruce Fields * buffer start using buf->len - buf->head[0].iov_len. */ 9647c9fdcfbSJ. Bruce Fields buf->head[0].iov_len -= pad; 9657c9fdcfbSJ. Bruce Fields } 9667c9fdcfbSJ. Bruce Fields } 9677c9fdcfbSJ. Bruce Fields 9687c9fdcfbSJ. Bruce Fields static int 9697c9fdcfbSJ. Bruce Fields unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx) 9707c9fdcfbSJ. Bruce Fields { 9717c9fdcfbSJ. Bruce Fields u32 priv_len, maj_stat; 97231c9590aSChuck Lever int pad, remaining_len, offset; 97310b9d99aSChuck Lever u32 rseqno; 9747c9fdcfbSJ. Bruce Fields 97528df0988SChuck Lever __clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags); 9767c9fdcfbSJ. Bruce Fields 97776994313SAlexey Dobriyan priv_len = svc_getnl(&buf->head[0]); 9787c9fdcfbSJ. Bruce Fields if (rqstp->rq_deferred) { 9797c9fdcfbSJ. Bruce Fields /* Already decrypted last time through! The sequence number 9807c9fdcfbSJ. Bruce Fields * check at out_seq is unnecessary but harmless: */ 9817c9fdcfbSJ. Bruce Fields goto out_seq; 9827c9fdcfbSJ. Bruce Fields } 9837c9fdcfbSJ. Bruce Fields /* buf->len is the number of bytes from the original start of the 9847c9fdcfbSJ. Bruce Fields * request to the end, where head[0].iov_len is just the bytes 9857c9fdcfbSJ. Bruce Fields * not yet read from the head, so these two values are different: */ 9867c9fdcfbSJ. Bruce Fields remaining_len = total_buf_len(buf); 9877c9fdcfbSJ. Bruce Fields if (priv_len > remaining_len) 98810b9d99aSChuck Lever goto unwrap_failed; 9897c9fdcfbSJ. Bruce Fields pad = remaining_len - priv_len; 9907c9fdcfbSJ. Bruce Fields buf->len -= pad; 9917c9fdcfbSJ. Bruce Fields fix_priv_head(buf, pad); 9927c9fdcfbSJ. Bruce Fields 99331c9590aSChuck Lever maj_stat = gss_unwrap(ctx, 0, priv_len, buf); 9947c9fdcfbSJ. Bruce Fields pad = priv_len - buf->len; 9957c9fdcfbSJ. Bruce Fields /* The upper layers assume the buffer is aligned on 4-byte boundaries. 9967c9fdcfbSJ. Bruce Fields * In the krb5p case, at least, the data ends up offset, so we need to 9977c9fdcfbSJ. Bruce Fields * move it around. */ 9987c9fdcfbSJ. Bruce Fields /* XXX: This is very inefficient. It would be better to either do 9997c9fdcfbSJ. Bruce Fields * this while we encrypt, or maybe in the receive code, if we can peak 10007c9fdcfbSJ. Bruce Fields * ahead and work out the service and mechanism there. */ 100196f194b7SChuck Lever offset = xdr_pad_size(buf->head[0].iov_len); 10027c9fdcfbSJ. Bruce Fields if (offset) { 10037c9fdcfbSJ. Bruce Fields buf->buflen = RPCSVC_MAXPAYLOAD; 10047c9fdcfbSJ. Bruce Fields xdr_shift_buf(buf, offset); 10057c9fdcfbSJ. Bruce Fields fix_priv_head(buf, pad); 10067c9fdcfbSJ. Bruce Fields } 10077c9fdcfbSJ. Bruce Fields if (maj_stat != GSS_S_COMPLETE) 100810b9d99aSChuck Lever goto bad_unwrap; 10097c9fdcfbSJ. Bruce Fields out_seq: 101010b9d99aSChuck Lever rseqno = svc_getnl(&buf->head[0]); 101110b9d99aSChuck Lever if (rseqno != seq) 101210b9d99aSChuck Lever goto bad_seqno; 10137c9fdcfbSJ. Bruce Fields return 0; 101410b9d99aSChuck Lever 101510b9d99aSChuck Lever unwrap_failed: 101610b9d99aSChuck Lever trace_rpcgss_svc_unwrap_failed(rqstp); 101710b9d99aSChuck Lever return -EINVAL; 101810b9d99aSChuck Lever bad_seqno: 101910b9d99aSChuck Lever trace_rpcgss_svc_seqno_bad(rqstp, seq, rseqno); 102010b9d99aSChuck Lever return -EINVAL; 102110b9d99aSChuck Lever bad_unwrap: 102210b9d99aSChuck Lever trace_rpcgss_svc_unwrap(rqstp, maj_stat); 102310b9d99aSChuck Lever return -EINVAL; 10247c9fdcfbSJ. Bruce Fields } 10257c9fdcfbSJ. Bruce Fields 10261da177e4SLinus Torvalds struct gss_svc_data { 10271da177e4SLinus Torvalds /* decoded gss client cred: */ 10281da177e4SLinus Torvalds struct rpc_gss_wire_cred clcred; 10295b304bc5SJ.Bruce Fields /* save a pointer to the beginning of the encoded verifier, 10305b304bc5SJ.Bruce Fields * for use in encryption/checksumming in svcauth_gss_release: */ 10315b304bc5SJ.Bruce Fields __be32 *verf_start; 10321da177e4SLinus Torvalds struct rsc *rsci; 10331da177e4SLinus Torvalds }; 10341da177e4SLinus Torvalds 10351da177e4SLinus Torvalds static int 10361da177e4SLinus Torvalds svcauth_gss_set_client(struct svc_rqst *rqstp) 10371da177e4SLinus Torvalds { 10381da177e4SLinus Torvalds struct gss_svc_data *svcdata = rqstp->rq_auth_data; 10391da177e4SLinus Torvalds struct rsc *rsci = svcdata->rsci; 10401da177e4SLinus Torvalds struct rpc_gss_wire_cred *gc = &svcdata->clcred; 10413ab4d8b1SJ. Bruce Fields int stat; 10421da177e4SLinus Torvalds 10435c2465dfSChuck Lever rqstp->rq_auth_stat = rpc_autherr_badcred; 10445c2465dfSChuck Lever 10453ab4d8b1SJ. Bruce Fields /* 10463ab4d8b1SJ. Bruce Fields * A gss export can be specified either by: 10473ab4d8b1SJ. Bruce Fields * export *(sec=krb5,rw) 10483ab4d8b1SJ. Bruce Fields * or by 10493ab4d8b1SJ. Bruce Fields * export gss/krb5(rw) 10503ab4d8b1SJ. Bruce Fields * The latter is deprecated; but for backwards compatibility reasons 10513ab4d8b1SJ. Bruce Fields * the nfsd code will still fall back on trying it if the former 10523ab4d8b1SJ. Bruce Fields * doesn't work; so we try to make both available to nfsd, below. 10533ab4d8b1SJ. Bruce Fields */ 10543ab4d8b1SJ. Bruce Fields rqstp->rq_gssclient = find_gss_auth_domain(rsci->mechctx, gc->gc_svc); 10553ab4d8b1SJ. Bruce Fields if (rqstp->rq_gssclient == NULL) 10561da177e4SLinus Torvalds return SVC_DENIED; 10573ab4d8b1SJ. Bruce Fields stat = svcauth_unix_set_client(rqstp); 10581ebede86SNeilBrown if (stat == SVC_DROP || stat == SVC_CLOSE) 10593ab4d8b1SJ. Bruce Fields return stat; 10605c2465dfSChuck Lever 10615c2465dfSChuck Lever rqstp->rq_auth_stat = rpc_auth_ok; 10621da177e4SLinus Torvalds return SVC_OK; 10631da177e4SLinus Torvalds } 10641da177e4SLinus Torvalds 106591a4762eSKevin Coffman static inline int 1066fc2952a2SSimo Sorce gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp, 1067fc2952a2SSimo Sorce struct xdr_netobj *out_handle, int *major_status) 106891a4762eSKevin Coffman { 106991a4762eSKevin Coffman struct rsc *rsci; 107054f9247bSFrank Filz int rc; 107191a4762eSKevin Coffman 1072fc2952a2SSimo Sorce if (*major_status != GSS_S_COMPLETE) 107391a4762eSKevin Coffman return gss_write_null_verf(rqstp); 1074fc2952a2SSimo Sorce rsci = gss_svc_searchbyctx(cd, out_handle); 107591a4762eSKevin Coffman if (rsci == NULL) { 1076fc2952a2SSimo Sorce *major_status = GSS_S_NO_CONTEXT; 107791a4762eSKevin Coffman return gss_write_null_verf(rqstp); 107891a4762eSKevin Coffman } 107954f9247bSFrank Filz rc = gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN); 1080a1db410dSStanislav Kinsbursky cache_put(&rsci->h, cd); 108154f9247bSFrank Filz return rc; 108291a4762eSKevin Coffman } 108391a4762eSKevin Coffman 1084fc2952a2SSimo Sorce static inline int 1085030d794bSSimo Sorce gss_read_common_verf(struct rpc_gss_wire_cred *gc, 1086fc2952a2SSimo Sorce struct kvec *argv, __be32 *authp, 1087030d794bSSimo Sorce struct xdr_netobj *in_handle) 1088fc2952a2SSimo Sorce { 1089fc2952a2SSimo Sorce /* Read the verifier; should be NULL: */ 1090fc2952a2SSimo Sorce *authp = rpc_autherr_badverf; 1091fc2952a2SSimo Sorce if (argv->iov_len < 2 * 4) 1092fc2952a2SSimo Sorce return SVC_DENIED; 1093fc2952a2SSimo Sorce if (svc_getnl(argv) != RPC_AUTH_NULL) 1094fc2952a2SSimo Sorce return SVC_DENIED; 1095fc2952a2SSimo Sorce if (svc_getnl(argv) != 0) 1096fc2952a2SSimo Sorce return SVC_DENIED; 1097fc2952a2SSimo Sorce /* Martial context handle and token for upcall: */ 1098fc2952a2SSimo Sorce *authp = rpc_autherr_badcred; 1099fc2952a2SSimo Sorce if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0) 1100fc2952a2SSimo Sorce return SVC_DENIED; 1101fc2952a2SSimo Sorce if (dup_netobj(in_handle, &gc->gc_ctx)) 1102fc2952a2SSimo Sorce return SVC_CLOSE; 1103fc2952a2SSimo Sorce *authp = rpc_autherr_badverf; 1104030d794bSSimo Sorce 1105030d794bSSimo Sorce return 0; 1106030d794bSSimo Sorce } 1107030d794bSSimo Sorce 1108030d794bSSimo Sorce static inline int 1109030d794bSSimo Sorce gss_read_verf(struct rpc_gss_wire_cred *gc, 1110030d794bSSimo Sorce struct kvec *argv, __be32 *authp, 1111030d794bSSimo Sorce struct xdr_netobj *in_handle, 1112030d794bSSimo Sorce struct xdr_netobj *in_token) 1113030d794bSSimo Sorce { 1114030d794bSSimo Sorce struct xdr_netobj tmpobj; 1115030d794bSSimo Sorce int res; 1116030d794bSSimo Sorce 1117030d794bSSimo Sorce res = gss_read_common_verf(gc, argv, authp, in_handle); 1118030d794bSSimo Sorce if (res) 1119030d794bSSimo Sorce return res; 1120030d794bSSimo Sorce 1121fc2952a2SSimo Sorce if (svc_safe_getnetobj(argv, &tmpobj)) { 1122fc2952a2SSimo Sorce kfree(in_handle->data); 1123fc2952a2SSimo Sorce return SVC_DENIED; 1124fc2952a2SSimo Sorce } 1125fc2952a2SSimo Sorce if (dup_netobj(in_token, &tmpobj)) { 1126fc2952a2SSimo Sorce kfree(in_handle->data); 1127fc2952a2SSimo Sorce return SVC_CLOSE; 1128fc2952a2SSimo Sorce } 1129fc2952a2SSimo Sorce 1130fc2952a2SSimo Sorce return 0; 1131fc2952a2SSimo Sorce } 1132fc2952a2SSimo Sorce 11335866efa8SChuck Lever static void gss_free_in_token_pages(struct gssp_in_token *in_token) 11345866efa8SChuck Lever { 11355866efa8SChuck Lever u32 inlen; 11365866efa8SChuck Lever int i; 11375866efa8SChuck Lever 11385866efa8SChuck Lever i = 0; 11395866efa8SChuck Lever inlen = in_token->page_len; 11405866efa8SChuck Lever while (inlen) { 11415866efa8SChuck Lever if (in_token->pages[i]) 11425866efa8SChuck Lever put_page(in_token->pages[i]); 11435866efa8SChuck Lever inlen -= inlen > PAGE_SIZE ? PAGE_SIZE : inlen; 11445866efa8SChuck Lever } 11455866efa8SChuck Lever 11465866efa8SChuck Lever kfree(in_token->pages); 11475866efa8SChuck Lever in_token->pages = NULL; 11485866efa8SChuck Lever } 11495866efa8SChuck Lever 11505866efa8SChuck Lever static int gss_read_proxy_verf(struct svc_rqst *rqstp, 1151438623a0SChuck Lever struct rpc_gss_wire_cred *gc, 1152030d794bSSimo Sorce struct xdr_netobj *in_handle, 1153030d794bSSimo Sorce struct gssp_in_token *in_token) 1154030d794bSSimo Sorce { 1155030d794bSSimo Sorce struct kvec *argv = &rqstp->rq_arg.head[0]; 1156d48c8124SMartijn de Gouw unsigned int length, pgto_offs, pgfrom_offs; 1157d48c8124SMartijn de Gouw int pages, i, res, pgto, pgfrom; 1158d48c8124SMartijn de Gouw size_t inlen, to_offs, from_offs; 1159030d794bSSimo Sorce 1160438623a0SChuck Lever res = gss_read_common_verf(gc, argv, &rqstp->rq_auth_stat, in_handle); 1161030d794bSSimo Sorce if (res) 1162030d794bSSimo Sorce return res; 1163030d794bSSimo Sorce 1164030d794bSSimo Sorce inlen = svc_getnl(argv); 1165*da522b5fSChuck Lever if (inlen > (argv->iov_len + rqstp->rq_arg.page_len)) { 1166*da522b5fSChuck Lever kfree(in_handle->data); 1167030d794bSSimo Sorce return SVC_DENIED; 1168*da522b5fSChuck Lever } 1169030d794bSSimo Sorce 11705866efa8SChuck Lever pages = DIV_ROUND_UP(inlen, PAGE_SIZE); 11715866efa8SChuck Lever in_token->pages = kcalloc(pages, sizeof(struct page *), GFP_KERNEL); 1172*da522b5fSChuck Lever if (!in_token->pages) { 1173*da522b5fSChuck Lever kfree(in_handle->data); 11745866efa8SChuck Lever return SVC_DENIED; 1175*da522b5fSChuck Lever } 11765866efa8SChuck Lever in_token->page_base = 0; 1177030d794bSSimo Sorce in_token->page_len = inlen; 11785866efa8SChuck Lever for (i = 0; i < pages; i++) { 11795866efa8SChuck Lever in_token->pages[i] = alloc_page(GFP_KERNEL); 11805866efa8SChuck Lever if (!in_token->pages[i]) { 1181*da522b5fSChuck Lever kfree(in_handle->data); 11825866efa8SChuck Lever gss_free_in_token_pages(in_token); 11835866efa8SChuck Lever return SVC_DENIED; 11845866efa8SChuck Lever } 11855866efa8SChuck Lever } 1186030d794bSSimo Sorce 11875866efa8SChuck Lever length = min_t(unsigned int, inlen, argv->iov_len); 11885866efa8SChuck Lever memcpy(page_address(in_token->pages[0]), argv->iov_base, length); 11895866efa8SChuck Lever inlen -= length; 11905866efa8SChuck Lever 1191d48c8124SMartijn de Gouw to_offs = length; 1192d48c8124SMartijn de Gouw from_offs = rqstp->rq_arg.page_base; 11935866efa8SChuck Lever while (inlen) { 1194d48c8124SMartijn de Gouw pgto = to_offs >> PAGE_SHIFT; 1195d48c8124SMartijn de Gouw pgfrom = from_offs >> PAGE_SHIFT; 1196d48c8124SMartijn de Gouw pgto_offs = to_offs & ~PAGE_MASK; 1197d48c8124SMartijn de Gouw pgfrom_offs = from_offs & ~PAGE_MASK; 1198d48c8124SMartijn de Gouw 1199d48c8124SMartijn de Gouw length = min_t(unsigned int, inlen, 1200d48c8124SMartijn de Gouw min_t(unsigned int, PAGE_SIZE - pgto_offs, 1201d48c8124SMartijn de Gouw PAGE_SIZE - pgfrom_offs)); 1202d48c8124SMartijn de Gouw memcpy(page_address(in_token->pages[pgto]) + pgto_offs, 1203d48c8124SMartijn de Gouw page_address(rqstp->rq_arg.pages[pgfrom]) + pgfrom_offs, 12045866efa8SChuck Lever length); 12055866efa8SChuck Lever 1206d48c8124SMartijn de Gouw to_offs += length; 1207d48c8124SMartijn de Gouw from_offs += length; 12085866efa8SChuck Lever inlen -= length; 12095866efa8SChuck Lever } 1210030d794bSSimo Sorce return 0; 1211030d794bSSimo Sorce } 1212030d794bSSimo Sorce 1213fc2952a2SSimo Sorce static inline int 1214fc2952a2SSimo Sorce gss_write_resv(struct kvec *resv, size_t size_limit, 1215fc2952a2SSimo Sorce struct xdr_netobj *out_handle, struct xdr_netobj *out_token, 1216fc2952a2SSimo Sorce int major_status, int minor_status) 1217fc2952a2SSimo Sorce { 1218fc2952a2SSimo Sorce if (resv->iov_len + 4 > size_limit) 1219fc2952a2SSimo Sorce return -1; 1220fc2952a2SSimo Sorce svc_putnl(resv, RPC_SUCCESS); 1221fc2952a2SSimo Sorce if (svc_safe_putnetobj(resv, out_handle)) 1222fc2952a2SSimo Sorce return -1; 1223fc2952a2SSimo Sorce if (resv->iov_len + 3 * 4 > size_limit) 1224fc2952a2SSimo Sorce return -1; 1225fc2952a2SSimo Sorce svc_putnl(resv, major_status); 1226fc2952a2SSimo Sorce svc_putnl(resv, minor_status); 1227fc2952a2SSimo Sorce svc_putnl(resv, GSS_SEQ_WIN); 1228fc2952a2SSimo Sorce if (svc_safe_putnetobj(resv, out_token)) 1229fc2952a2SSimo Sorce return -1; 1230fc2952a2SSimo Sorce return 0; 1231fc2952a2SSimo Sorce } 1232fc2952a2SSimo Sorce 12331da177e4SLinus Torvalds /* 123421fcd02bSJ. Bruce Fields * Having read the cred already and found we're in the context 123521fcd02bSJ. Bruce Fields * initiation case, read the verifier and initiate (or check the results 123621fcd02bSJ. Bruce Fields * of) upcalls to userspace for help with context initiation. If 123721fcd02bSJ. Bruce Fields * the upcall results are available, write the verifier and result. 123821fcd02bSJ. Bruce Fields * Otherwise, drop the request pending an answer to the upcall. 123921fcd02bSJ. Bruce Fields */ 1240030d794bSSimo Sorce static int svcauth_gss_legacy_init(struct svc_rqst *rqstp, 1241438623a0SChuck Lever struct rpc_gss_wire_cred *gc) 124221fcd02bSJ. Bruce Fields { 124321fcd02bSJ. Bruce Fields struct kvec *argv = &rqstp->rq_arg.head[0]; 124421fcd02bSJ. Bruce Fields struct kvec *resv = &rqstp->rq_res.head[0]; 124521fcd02bSJ. Bruce Fields struct rsi *rsip, rsikey; 1246980e5a40SJ. Bruce Fields int ret; 1247b8be5674SVasily Averin struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id); 124821fcd02bSJ. Bruce Fields 124921fcd02bSJ. Bruce Fields memset(&rsikey, 0, sizeof(rsikey)); 1250438623a0SChuck Lever ret = gss_read_verf(gc, argv, &rqstp->rq_auth_stat, 1251fc2952a2SSimo Sorce &rsikey.in_handle, &rsikey.in_token); 1252fc2952a2SSimo Sorce if (ret) 1253fc2952a2SSimo Sorce return ret; 125421fcd02bSJ. Bruce Fields 125521fcd02bSJ. Bruce Fields /* Perform upcall, or find upcall result: */ 1256a1db410dSStanislav Kinsbursky rsip = rsi_lookup(sn->rsi_cache, &rsikey); 125721fcd02bSJ. Bruce Fields rsi_free(&rsikey); 125821fcd02bSJ. Bruce Fields if (!rsip) 12591ebede86SNeilBrown return SVC_CLOSE; 1260a1db410dSStanislav Kinsbursky if (cache_check(sn->rsi_cache, &rsip->h, &rqstp->rq_chandle) < 0) 126121fcd02bSJ. Bruce Fields /* No upcall result: */ 12621ebede86SNeilBrown return SVC_CLOSE; 12632ed5282cSNeilBrown 12641ebede86SNeilBrown ret = SVC_CLOSE; 126521fcd02bSJ. Bruce Fields /* Got an answer to the upcall; use it: */ 1266fc2952a2SSimo Sorce if (gss_write_init_verf(sn->rsc_cache, rqstp, 1267fc2952a2SSimo Sorce &rsip->out_handle, &rsip->major_status)) 1268980e5a40SJ. Bruce Fields goto out; 1269fc2952a2SSimo Sorce if (gss_write_resv(resv, PAGE_SIZE, 1270fc2952a2SSimo Sorce &rsip->out_handle, &rsip->out_token, 1271fc2952a2SSimo Sorce rsip->major_status, rsip->minor_status)) 1272980e5a40SJ. Bruce Fields goto out; 12732ed5282cSNeilBrown 1274980e5a40SJ. Bruce Fields ret = SVC_COMPLETE; 1275980e5a40SJ. Bruce Fields out: 1276a1db410dSStanislav Kinsbursky cache_put(&rsip->h, sn->rsi_cache); 1277980e5a40SJ. Bruce Fields return ret; 127821fcd02bSJ. Bruce Fields } 127921fcd02bSJ. Bruce Fields 1280030d794bSSimo Sorce static int gss_proxy_save_rsc(struct cache_detail *cd, 1281030d794bSSimo Sorce struct gssp_upcall_data *ud, 1282030d794bSSimo Sorce uint64_t *handle) 1283030d794bSSimo Sorce { 1284030d794bSSimo Sorce struct rsc rsci, *rscp = NULL; 1285030d794bSSimo Sorce static atomic64_t ctxhctr; 1286030d794bSSimo Sorce long long ctxh; 1287030d794bSSimo Sorce struct gss_api_mech *gm = NULL; 1288294ec5b8SArnd Bergmann time64_t expiry; 1289f6260b98SColin Ian King int status; 1290030d794bSSimo Sorce 1291030d794bSSimo Sorce memset(&rsci, 0, sizeof(rsci)); 1292030d794bSSimo Sorce /* context handle */ 1293030d794bSSimo Sorce status = -ENOMEM; 1294030d794bSSimo Sorce /* the handle needs to be just a unique id, 1295030d794bSSimo Sorce * use a static counter */ 1296030d794bSSimo Sorce ctxh = atomic64_inc_return(&ctxhctr); 1297030d794bSSimo Sorce 1298030d794bSSimo Sorce /* make a copy for the caller */ 1299030d794bSSimo Sorce *handle = ctxh; 1300030d794bSSimo Sorce 1301030d794bSSimo Sorce /* make a copy for the rsc cache */ 1302030d794bSSimo Sorce if (dup_to_netobj(&rsci.handle, (char *)handle, sizeof(uint64_t))) 1303030d794bSSimo Sorce goto out; 1304030d794bSSimo Sorce rscp = rsc_lookup(cd, &rsci); 1305030d794bSSimo Sorce if (!rscp) 1306030d794bSSimo Sorce goto out; 1307030d794bSSimo Sorce 1308030d794bSSimo Sorce /* creds */ 1309030d794bSSimo Sorce if (!ud->found_creds) { 1310030d794bSSimo Sorce /* userspace seem buggy, we should always get at least a 1311030d794bSSimo Sorce * mapping to nobody */ 13123be34555SJ. Bruce Fields goto out; 1313030d794bSSimo Sorce } else { 13143d96208cSRoberto Bergantinos Corpas struct timespec64 boot; 1315030d794bSSimo Sorce 1316030d794bSSimo Sorce /* steal creds */ 1317030d794bSSimo Sorce rsci.cred = ud->creds; 1318030d794bSSimo Sorce memset(&ud->creds, 0, sizeof(struct svc_cred)); 1319030d794bSSimo Sorce 1320030d794bSSimo Sorce status = -EOPNOTSUPP; 1321030d794bSSimo Sorce /* get mech handle from OID */ 1322030d794bSSimo Sorce gm = gss_mech_get_by_OID(&ud->mech_oid); 1323030d794bSSimo Sorce if (!gm) 1324030d794bSSimo Sorce goto out; 13257193bd17SJ. Bruce Fields rsci.cred.cr_gss_mech = gm; 1326030d794bSSimo Sorce 1327030d794bSSimo Sorce status = -EINVAL; 1328030d794bSSimo Sorce /* mech-specific data: */ 1329030d794bSSimo Sorce status = gss_import_sec_context(ud->out_handle.data, 1330030d794bSSimo Sorce ud->out_handle.len, 1331030d794bSSimo Sorce gm, &rsci.mechctx, 1332030d794bSSimo Sorce &expiry, GFP_KERNEL); 1333030d794bSSimo Sorce if (status) 1334030d794bSSimo Sorce goto out; 13353d96208cSRoberto Bergantinos Corpas 13363d96208cSRoberto Bergantinos Corpas getboottime64(&boot); 13373d96208cSRoberto Bergantinos Corpas expiry -= boot.tv_sec; 1338030d794bSSimo Sorce } 1339030d794bSSimo Sorce 1340030d794bSSimo Sorce rsci.h.expiry_time = expiry; 1341030d794bSSimo Sorce rscp = rsc_update(cd, &rsci, rscp); 1342030d794bSSimo Sorce status = 0; 1343030d794bSSimo Sorce out: 1344030d794bSSimo Sorce rsc_free(&rsci); 1345030d794bSSimo Sorce if (rscp) 1346030d794bSSimo Sorce cache_put(&rscp->h, cd); 1347030d794bSSimo Sorce else 1348030d794bSSimo Sorce status = -ENOMEM; 1349030d794bSSimo Sorce return status; 1350030d794bSSimo Sorce } 1351030d794bSSimo Sorce 1352030d794bSSimo Sorce static int svcauth_gss_proxy_init(struct svc_rqst *rqstp, 1353438623a0SChuck Lever struct rpc_gss_wire_cred *gc) 1354030d794bSSimo Sorce { 1355030d794bSSimo Sorce struct kvec *resv = &rqstp->rq_res.head[0]; 1356030d794bSSimo Sorce struct xdr_netobj cli_handle; 1357030d794bSSimo Sorce struct gssp_upcall_data ud; 1358030d794bSSimo Sorce uint64_t handle; 1359030d794bSSimo Sorce int status; 1360030d794bSSimo Sorce int ret; 1361b8be5674SVasily Averin struct net *net = SVC_NET(rqstp); 1362030d794bSSimo Sorce struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1363030d794bSSimo Sorce 1364030d794bSSimo Sorce memset(&ud, 0, sizeof(ud)); 1365438623a0SChuck Lever ret = gss_read_proxy_verf(rqstp, gc, &ud.in_handle, &ud.in_token); 1366030d794bSSimo Sorce if (ret) 1367030d794bSSimo Sorce return ret; 1368030d794bSSimo Sorce 1369030d794bSSimo Sorce ret = SVC_CLOSE; 1370030d794bSSimo Sorce 1371030d794bSSimo Sorce /* Perform synchronous upcall to gss-proxy */ 1372030d794bSSimo Sorce status = gssp_accept_sec_context_upcall(net, &ud); 1373030d794bSSimo Sorce if (status) 1374030d794bSSimo Sorce goto out; 1375030d794bSSimo Sorce 137610b9d99aSChuck Lever trace_rpcgss_svc_accept_upcall(rqstp, ud.major_status, ud.minor_status); 1377030d794bSSimo Sorce 1378030d794bSSimo Sorce switch (ud.major_status) { 1379030d794bSSimo Sorce case GSS_S_CONTINUE_NEEDED: 1380030d794bSSimo Sorce cli_handle = ud.out_handle; 1381030d794bSSimo Sorce break; 1382030d794bSSimo Sorce case GSS_S_COMPLETE: 1383030d794bSSimo Sorce status = gss_proxy_save_rsc(sn->rsc_cache, &ud, &handle); 138428155524SChuck Lever if (status) 1385030d794bSSimo Sorce goto out; 1386030d794bSSimo Sorce cli_handle.data = (u8 *)&handle; 1387030d794bSSimo Sorce cli_handle.len = sizeof(handle); 1388030d794bSSimo Sorce break; 1389030d794bSSimo Sorce default: 1390030d794bSSimo Sorce goto out; 1391030d794bSSimo Sorce } 1392030d794bSSimo Sorce 1393030d794bSSimo Sorce /* Got an answer to the upcall; use it: */ 1394030d794bSSimo Sorce if (gss_write_init_verf(sn->rsc_cache, rqstp, 139528155524SChuck Lever &cli_handle, &ud.major_status)) 1396030d794bSSimo Sorce goto out; 1397030d794bSSimo Sorce if (gss_write_resv(resv, PAGE_SIZE, 1398030d794bSSimo Sorce &cli_handle, &ud.out_token, 139928155524SChuck Lever ud.major_status, ud.minor_status)) 1400030d794bSSimo Sorce goto out; 1401030d794bSSimo Sorce 1402030d794bSSimo Sorce ret = SVC_COMPLETE; 1403030d794bSSimo Sorce out: 14045866efa8SChuck Lever gss_free_in_token_pages(&ud.in_token); 1405030d794bSSimo Sorce gssp_free_upcall_data(&ud); 1406030d794bSSimo Sorce return ret; 1407030d794bSSimo Sorce } 1408030d794bSSimo Sorce 14090fdc2678SJeff Layton /* 14100fdc2678SJeff Layton * Try to set the sn->use_gss_proxy variable to a new value. We only allow 14110fdc2678SJeff Layton * it to be changed if it's currently undefined (-1). If it's any other value 14120fdc2678SJeff Layton * then return -EBUSY unless the type wouldn't have changed anyway. 14130fdc2678SJeff Layton */ 14140fdc2678SJeff Layton static int set_gss_proxy(struct net *net, int type) 14150fdc2678SJeff Layton { 14160fdc2678SJeff Layton struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 14170fdc2678SJeff Layton int ret; 14180fdc2678SJeff Layton 14190fdc2678SJeff Layton WARN_ON_ONCE(type != 0 && type != 1); 14200fdc2678SJeff Layton ret = cmpxchg(&sn->use_gss_proxy, -1, type); 14210fdc2678SJeff Layton if (ret != -1 && ret != type) 14220fdc2678SJeff Layton return -EBUSY; 14230fdc2678SJeff Layton return 0; 14240fdc2678SJeff Layton } 1425030d794bSSimo Sorce 1426030d794bSSimo Sorce static bool use_gss_proxy(struct net *net) 1427030d794bSSimo Sorce { 1428030d794bSSimo Sorce struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1429030d794bSSimo Sorce 14300fdc2678SJeff Layton /* If use_gss_proxy is still undefined, then try to disable it */ 14310fdc2678SJeff Layton if (sn->use_gss_proxy == -1) 14320fdc2678SJeff Layton set_gss_proxy(net, 0); 1433030d794bSSimo Sorce return sn->use_gss_proxy; 1434030d794bSSimo Sorce } 1435030d794bSSimo Sorce 14360ff3bab5SJ. Bruce Fields #ifdef CONFIG_PROC_FS 14370ff3bab5SJ. Bruce Fields 1438030d794bSSimo Sorce static ssize_t write_gssp(struct file *file, const char __user *buf, 1439030d794bSSimo Sorce size_t count, loff_t *ppos) 1440030d794bSSimo Sorce { 1441359745d7SMuchun Song struct net *net = pde_data(file_inode(file)); 1442030d794bSSimo Sorce char tbuf[20]; 1443030d794bSSimo Sorce unsigned long i; 1444030d794bSSimo Sorce int res; 1445030d794bSSimo Sorce 1446030d794bSSimo Sorce if (*ppos || count > sizeof(tbuf)-1) 1447030d794bSSimo Sorce return -EINVAL; 1448030d794bSSimo Sorce if (copy_from_user(tbuf, buf, count)) 1449030d794bSSimo Sorce return -EFAULT; 1450030d794bSSimo Sorce 1451030d794bSSimo Sorce tbuf[count] = 0; 1452030d794bSSimo Sorce res = kstrtoul(tbuf, 0, &i); 1453030d794bSSimo Sorce if (res) 1454030d794bSSimo Sorce return res; 1455030d794bSSimo Sorce if (i != 1) 1456030d794bSSimo Sorce return -EINVAL; 1457a92e5eb1SJeff Layton res = set_gssp_clnt(net); 1458030d794bSSimo Sorce if (res) 1459030d794bSSimo Sorce return res; 1460a92e5eb1SJeff Layton res = set_gss_proxy(net, 1); 1461030d794bSSimo Sorce if (res) 1462030d794bSSimo Sorce return res; 1463030d794bSSimo Sorce return count; 1464030d794bSSimo Sorce } 1465030d794bSSimo Sorce 1466030d794bSSimo Sorce static ssize_t read_gssp(struct file *file, char __user *buf, 1467030d794bSSimo Sorce size_t count, loff_t *ppos) 1468030d794bSSimo Sorce { 1469359745d7SMuchun Song struct net *net = pde_data(file_inode(file)); 14701654a04cSJeff Layton struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1471030d794bSSimo Sorce unsigned long p = *ppos; 1472030d794bSSimo Sorce char tbuf[10]; 1473030d794bSSimo Sorce size_t len; 1474030d794bSSimo Sorce 14751654a04cSJeff Layton snprintf(tbuf, sizeof(tbuf), "%d\n", sn->use_gss_proxy); 1476030d794bSSimo Sorce len = strlen(tbuf); 1477030d794bSSimo Sorce if (p >= len) 1478030d794bSSimo Sorce return 0; 1479030d794bSSimo Sorce len -= p; 1480030d794bSSimo Sorce if (len > count) 1481030d794bSSimo Sorce len = count; 1482030d794bSSimo Sorce if (copy_to_user(buf, (void *)(tbuf+p), len)) 1483030d794bSSimo Sorce return -EFAULT; 1484030d794bSSimo Sorce *ppos += len; 1485030d794bSSimo Sorce return len; 1486030d794bSSimo Sorce } 1487030d794bSSimo Sorce 148897a32539SAlexey Dobriyan static const struct proc_ops use_gss_proxy_proc_ops = { 148997a32539SAlexey Dobriyan .proc_open = nonseekable_open, 149097a32539SAlexey Dobriyan .proc_write = write_gssp, 149197a32539SAlexey Dobriyan .proc_read = read_gssp, 1492030d794bSSimo Sorce }; 1493030d794bSSimo Sorce 1494030d794bSSimo Sorce static int create_use_gss_proxy_proc_entry(struct net *net) 1495030d794bSSimo Sorce { 1496030d794bSSimo Sorce struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1497030d794bSSimo Sorce struct proc_dir_entry **p = &sn->use_gssp_proc; 1498030d794bSSimo Sorce 1499030d794bSSimo Sorce sn->use_gss_proxy = -1; 1500d6444062SJoe Perches *p = proc_create_data("use-gss-proxy", S_IFREG | 0600, 1501030d794bSSimo Sorce sn->proc_net_rpc, 150297a32539SAlexey Dobriyan &use_gss_proxy_proc_ops, net); 1503030d794bSSimo Sorce if (!*p) 1504030d794bSSimo Sorce return -ENOMEM; 1505030d794bSSimo Sorce init_gssp_clnt(sn); 1506030d794bSSimo Sorce return 0; 1507030d794bSSimo Sorce } 1508030d794bSSimo Sorce 1509030d794bSSimo Sorce static void destroy_use_gss_proxy_proc_entry(struct net *net) 1510030d794bSSimo Sorce { 1511030d794bSSimo Sorce struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1512030d794bSSimo Sorce 1513030d794bSSimo Sorce if (sn->use_gssp_proc) { 1514030d794bSSimo Sorce remove_proc_entry("use-gss-proxy", sn->proc_net_rpc); 1515030d794bSSimo Sorce clear_gssp_clnt(sn); 1516030d794bSSimo Sorce } 1517030d794bSSimo Sorce } 15180ff3bab5SJ. Bruce Fields #else /* CONFIG_PROC_FS */ 15190ff3bab5SJ. Bruce Fields 15200ff3bab5SJ. Bruce Fields static int create_use_gss_proxy_proc_entry(struct net *net) 15210ff3bab5SJ. Bruce Fields { 15220ff3bab5SJ. Bruce Fields return 0; 15230ff3bab5SJ. Bruce Fields } 15240ff3bab5SJ. Bruce Fields 15250ff3bab5SJ. Bruce Fields static void destroy_use_gss_proxy_proc_entry(struct net *net) {} 1526030d794bSSimo Sorce 1527030d794bSSimo Sorce #endif /* CONFIG_PROC_FS */ 1528030d794bSSimo Sorce 152921fcd02bSJ. Bruce Fields /* 15301da177e4SLinus Torvalds * Accept an rpcsec packet. 15311da177e4SLinus Torvalds * If context establishment, punt to user space 15321da177e4SLinus Torvalds * If data exchange, verify/decrypt 15331da177e4SLinus Torvalds * If context destruction, handle here 15341da177e4SLinus Torvalds * In the context establishment and destruction case we encode 15351da177e4SLinus Torvalds * response here and return SVC_COMPLETE. 15361da177e4SLinus Torvalds */ 15371da177e4SLinus Torvalds static int 1538438623a0SChuck Lever svcauth_gss_accept(struct svc_rqst *rqstp) 15391da177e4SLinus Torvalds { 15401da177e4SLinus Torvalds struct kvec *argv = &rqstp->rq_arg.head[0]; 15411da177e4SLinus Torvalds struct kvec *resv = &rqstp->rq_res.head[0]; 15421da177e4SLinus Torvalds u32 crlen; 15431da177e4SLinus Torvalds struct gss_svc_data *svcdata = rqstp->rq_auth_data; 15441da177e4SLinus Torvalds struct rpc_gss_wire_cred *gc; 15451da177e4SLinus Torvalds struct rsc *rsci = NULL; 1546d8ed029dSAlexey Dobriyan __be32 *rpcstart; 1547d8ed029dSAlexey Dobriyan __be32 *reject_stat = resv->iov_base + resv->iov_len; 15481da177e4SLinus Torvalds int ret; 1549b8be5674SVasily Averin struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id); 15501da177e4SLinus Torvalds 1551438623a0SChuck Lever rqstp->rq_auth_stat = rpc_autherr_badcred; 15521da177e4SLinus Torvalds if (!svcdata) 15531da177e4SLinus Torvalds svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL); 15541da177e4SLinus Torvalds if (!svcdata) 15551da177e4SLinus Torvalds goto auth_err; 15561da177e4SLinus Torvalds rqstp->rq_auth_data = svcdata; 15575b304bc5SJ.Bruce Fields svcdata->verf_start = NULL; 15581da177e4SLinus Torvalds svcdata->rsci = NULL; 15591da177e4SLinus Torvalds gc = &svcdata->clcred; 15601da177e4SLinus Torvalds 15611da177e4SLinus Torvalds /* start of rpc packet is 7 u32's back from here: 15621da177e4SLinus Torvalds * xid direction rpcversion prog vers proc flavour 15631da177e4SLinus Torvalds */ 15641da177e4SLinus Torvalds rpcstart = argv->iov_base; 15651da177e4SLinus Torvalds rpcstart -= 7; 15661da177e4SLinus Torvalds 15671da177e4SLinus Torvalds /* credential is: 15681da177e4SLinus Torvalds * version(==1), proc(0,1,2,3), seq, service (1,2,3), handle 156925985edcSLucas De Marchi * at least 5 u32s, and is preceded by length, so that makes 6. 15701da177e4SLinus Torvalds */ 15711da177e4SLinus Torvalds 15721da177e4SLinus Torvalds if (argv->iov_len < 5 * 4) 15731da177e4SLinus Torvalds goto auth_err; 157476994313SAlexey Dobriyan crlen = svc_getnl(argv); 157576994313SAlexey Dobriyan if (svc_getnl(argv) != RPC_GSS_VERSION) 15761da177e4SLinus Torvalds goto auth_err; 157776994313SAlexey Dobriyan gc->gc_proc = svc_getnl(argv); 157876994313SAlexey Dobriyan gc->gc_seq = svc_getnl(argv); 157976994313SAlexey Dobriyan gc->gc_svc = svc_getnl(argv); 15801da177e4SLinus Torvalds if (svc_safe_getnetobj(argv, &gc->gc_ctx)) 15811da177e4SLinus Torvalds goto auth_err; 15821da177e4SLinus Torvalds if (crlen != round_up_to_quad(gc->gc_ctx.len) + 5 * 4) 15831da177e4SLinus Torvalds goto auth_err; 15841da177e4SLinus Torvalds 15851da177e4SLinus Torvalds if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0)) 15861da177e4SLinus Torvalds goto auth_err; 15871da177e4SLinus Torvalds 1588438623a0SChuck Lever rqstp->rq_auth_stat = rpc_autherr_badverf; 15891da177e4SLinus Torvalds switch (gc->gc_proc) { 15901da177e4SLinus Torvalds case RPC_GSS_PROC_INIT: 15911da177e4SLinus Torvalds case RPC_GSS_PROC_CONTINUE_INIT: 1592030d794bSSimo Sorce if (use_gss_proxy(SVC_NET(rqstp))) 1593438623a0SChuck Lever return svcauth_gss_proxy_init(rqstp, gc); 1594030d794bSSimo Sorce else 1595438623a0SChuck Lever return svcauth_gss_legacy_init(rqstp, gc); 15961da177e4SLinus Torvalds case RPC_GSS_PROC_DATA: 15971da177e4SLinus Torvalds case RPC_GSS_PROC_DESTROY: 159821fcd02bSJ. Bruce Fields /* Look up the context, and check the verifier: */ 1599438623a0SChuck Lever rqstp->rq_auth_stat = rpcsec_gsserr_credproblem; 1600a1db410dSStanislav Kinsbursky rsci = gss_svc_searchbyctx(sn->rsc_cache, &gc->gc_ctx); 16011da177e4SLinus Torvalds if (!rsci) 16021da177e4SLinus Torvalds goto auth_err; 1603438623a0SChuck Lever switch (gss_verify_header(rqstp, rsci, rpcstart, gc)) { 16041da177e4SLinus Torvalds case SVC_OK: 16051da177e4SLinus Torvalds break; 16061da177e4SLinus Torvalds case SVC_DENIED: 16071da177e4SLinus Torvalds goto auth_err; 16081da177e4SLinus Torvalds case SVC_DROP: 16091da177e4SLinus Torvalds goto drop; 16101da177e4SLinus Torvalds } 16111da177e4SLinus Torvalds break; 16121da177e4SLinus Torvalds default: 1613438623a0SChuck Lever rqstp->rq_auth_stat = rpc_autherr_rejectedcred; 16141da177e4SLinus Torvalds goto auth_err; 16151da177e4SLinus Torvalds } 16161da177e4SLinus Torvalds 16171da177e4SLinus Torvalds /* now act upon the command: */ 16181da177e4SLinus Torvalds switch (gc->gc_proc) { 16191da177e4SLinus Torvalds case RPC_GSS_PROC_DESTROY: 1620c5e434c9SWei Yongjun if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq)) 1621c5e434c9SWei Yongjun goto auth_err; 16222b477c00SNeil Brown /* Delete the entry from the cache_list and call cache_put */ 16232b477c00SNeil Brown sunrpc_cache_unhash(sn->rsc_cache, &rsci->h); 16241da177e4SLinus Torvalds if (resv->iov_len + 4 > PAGE_SIZE) 16251da177e4SLinus Torvalds goto drop; 162676994313SAlexey Dobriyan svc_putnl(resv, RPC_SUCCESS); 16271da177e4SLinus Torvalds goto complete; 16281da177e4SLinus Torvalds case RPC_GSS_PROC_DATA: 1629438623a0SChuck Lever rqstp->rq_auth_stat = rpcsec_gsserr_ctxproblem; 16305b304bc5SJ.Bruce Fields svcdata->verf_start = resv->iov_base + resv->iov_len; 16311da177e4SLinus Torvalds if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq)) 16321da177e4SLinus Torvalds goto auth_err; 16331da177e4SLinus Torvalds rqstp->rq_cred = rsci->cred; 16341da177e4SLinus Torvalds get_group_info(rsci->cred.cr_group_info); 1635438623a0SChuck Lever rqstp->rq_auth_stat = rpc_autherr_badcred; 16361da177e4SLinus Torvalds switch (gc->gc_svc) { 16371da177e4SLinus Torvalds case RPC_GSS_SVC_NONE: 16381da177e4SLinus Torvalds break; 16391da177e4SLinus Torvalds case RPC_GSS_SVC_INTEGRITY: 1640b620754bSJ. Bruce Fields /* placeholders for length and seq. number: */ 1641b620754bSJ. Bruce Fields svc_putnl(resv, 0); 1642b620754bSJ. Bruce Fields svc_putnl(resv, 0); 16434c190e2fSJeff Layton if (unwrap_integ_data(rqstp, &rqstp->rq_arg, 16441da177e4SLinus Torvalds gc->gc_seq, rsci->mechctx)) 1645dd35210eSHarshula Jayasuriya goto garbage_args; 1646a5cddc88SJ. Bruce Fields rqstp->rq_auth_slack = RPC_MAX_AUTH_SIZE; 1647b620754bSJ. Bruce Fields break; 1648b620754bSJ. Bruce Fields case RPC_GSS_SVC_PRIVACY: 16491da177e4SLinus Torvalds /* placeholders for length and seq. number: */ 165076994313SAlexey Dobriyan svc_putnl(resv, 0); 165176994313SAlexey Dobriyan svc_putnl(resv, 0); 16527c9fdcfbSJ. Bruce Fields if (unwrap_priv_data(rqstp, &rqstp->rq_arg, 16537c9fdcfbSJ. Bruce Fields gc->gc_seq, rsci->mechctx)) 1654dd35210eSHarshula Jayasuriya goto garbage_args; 1655a5cddc88SJ. Bruce Fields rqstp->rq_auth_slack = RPC_MAX_AUTH_SIZE * 2; 16567c9fdcfbSJ. Bruce Fields break; 16571da177e4SLinus Torvalds default: 16581da177e4SLinus Torvalds goto auth_err; 16591da177e4SLinus Torvalds } 16601da177e4SLinus Torvalds svcdata->rsci = rsci; 16611da177e4SLinus Torvalds cache_get(&rsci->h); 1662d5497fc6SJ. Bruce Fields rqstp->rq_cred.cr_flavor = gss_svc_to_pseudoflavor( 166383523d08SChuck Lever rsci->mechctx->mech_type, 166483523d08SChuck Lever GSS_C_QOP_DEFAULT, 166583523d08SChuck Lever gc->gc_svc); 16661da177e4SLinus Torvalds ret = SVC_OK; 166710b9d99aSChuck Lever trace_rpcgss_svc_authenticate(rqstp, gc); 16681da177e4SLinus Torvalds goto out; 16691da177e4SLinus Torvalds } 1670dd35210eSHarshula Jayasuriya garbage_args: 1671dd35210eSHarshula Jayasuriya ret = SVC_GARBAGE; 1672dd35210eSHarshula Jayasuriya goto out; 16731da177e4SLinus Torvalds auth_err: 167421fcd02bSJ. Bruce Fields /* Restore write pointer to its original value: */ 16751da177e4SLinus Torvalds xdr_ressize_check(rqstp, reject_stat); 16761da177e4SLinus Torvalds ret = SVC_DENIED; 16771da177e4SLinus Torvalds goto out; 16781da177e4SLinus Torvalds complete: 16791da177e4SLinus Torvalds ret = SVC_COMPLETE; 16801da177e4SLinus Torvalds goto out; 16811da177e4SLinus Torvalds drop: 16824d712ef1SChuck Lever ret = SVC_CLOSE; 16831da177e4SLinus Torvalds out: 16841da177e4SLinus Torvalds if (rsci) 1685a1db410dSStanislav Kinsbursky cache_put(&rsci->h, sn->rsc_cache); 16861da177e4SLinus Torvalds return ret; 16871da177e4SLinus Torvalds } 16881da177e4SLinus Torvalds 1689cfbdbab0SAl Viro static __be32 * 16903c15a486SJ.Bruce Fields svcauth_gss_prepare_to_wrap(struct xdr_buf *resbuf, struct gss_svc_data *gsd) 16913c15a486SJ.Bruce Fields { 1692cfbdbab0SAl Viro __be32 *p; 1693cfbdbab0SAl Viro u32 verf_len; 16943c15a486SJ.Bruce Fields 16955b304bc5SJ.Bruce Fields p = gsd->verf_start; 16965b304bc5SJ.Bruce Fields gsd->verf_start = NULL; 16975b304bc5SJ.Bruce Fields 16985b304bc5SJ.Bruce Fields /* If the reply stat is nonzero, don't wrap: */ 16995b304bc5SJ.Bruce Fields if (*(p-1) != rpc_success) 17005b304bc5SJ.Bruce Fields return NULL; 17015b304bc5SJ.Bruce Fields /* Skip the verifier: */ 17025b304bc5SJ.Bruce Fields p += 1; 17035b304bc5SJ.Bruce Fields verf_len = ntohl(*p++); 17045b304bc5SJ.Bruce Fields p += XDR_QUADLEN(verf_len); 17053c15a486SJ.Bruce Fields /* move accept_stat to right place: */ 17063c15a486SJ.Bruce Fields memcpy(p, p + 2, 4); 17075b304bc5SJ.Bruce Fields /* Also don't wrap if the accept stat is nonzero: */ 17083c15a486SJ.Bruce Fields if (*p != rpc_success) { 17093c15a486SJ.Bruce Fields resbuf->head[0].iov_len -= 2 * 4; 17103c15a486SJ.Bruce Fields return NULL; 17113c15a486SJ.Bruce Fields } 17123c15a486SJ.Bruce Fields p++; 17133c15a486SJ.Bruce Fields return p; 17143c15a486SJ.Bruce Fields } 17153c15a486SJ.Bruce Fields 1716e142ede8SJ. Bruce Fields static inline int 1717e142ede8SJ. Bruce Fields svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp) 17181da177e4SLinus Torvalds { 17191da177e4SLinus Torvalds struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data; 17201da177e4SLinus Torvalds struct rpc_gss_wire_cred *gc = &gsd->clcred; 17211da177e4SLinus Torvalds struct xdr_buf *resbuf = &rqstp->rq_res; 17221da177e4SLinus Torvalds struct xdr_buf integ_buf; 17231da177e4SLinus Torvalds struct xdr_netobj mic; 17241da177e4SLinus Torvalds struct kvec *resv; 1725d8ed029dSAlexey Dobriyan __be32 *p; 17261da177e4SLinus Torvalds int integ_offset, integ_len; 17271da177e4SLinus Torvalds int stat = -EINVAL; 17281da177e4SLinus Torvalds 17293c15a486SJ.Bruce Fields p = svcauth_gss_prepare_to_wrap(resbuf, gsd); 17303c15a486SJ.Bruce Fields if (p == NULL) 17311da177e4SLinus Torvalds goto out; 17321da177e4SLinus Torvalds integ_offset = (u8 *)(p + 1) - (u8 *)resbuf->head[0].iov_base; 17331da177e4SLinus Torvalds integ_len = resbuf->len - integ_offset; 173496f194b7SChuck Lever if (integ_len & 3) 173596f194b7SChuck Lever goto out; 17361da177e4SLinus Torvalds *p++ = htonl(integ_len); 17371da177e4SLinus Torvalds *p++ = htonl(gc->gc_seq); 17381754eb2bSJ. Bruce Fields if (xdr_buf_subsegment(resbuf, &integ_buf, integ_offset, integ_len)) { 17391754eb2bSJ. Bruce Fields WARN_ON_ONCE(1); 17401754eb2bSJ. Bruce Fields goto out_err; 17411754eb2bSJ. Bruce Fields } 1742153e44d2SNeilBrown if (resbuf->tail[0].iov_base == NULL) { 1743e142ede8SJ. Bruce Fields if (resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE > PAGE_SIZE) 1744dfee55f0SNeilBrown goto out_err; 1745e142ede8SJ. Bruce Fields resbuf->tail[0].iov_base = resbuf->head[0].iov_base 1746dfee55f0SNeilBrown + resbuf->head[0].iov_len; 17471da177e4SLinus Torvalds resbuf->tail[0].iov_len = 0; 17481da177e4SLinus Torvalds } 1749bba0f88bSJ. Bruce Fields resv = &resbuf->tail[0]; 17501da177e4SLinus Torvalds mic.data = (u8 *)resv->iov_base + resv->iov_len + 4; 175100fd6e14SJ. Bruce Fields if (gss_get_mic(gsd->rsci->mechctx, &integ_buf, &mic)) 17521da177e4SLinus Torvalds goto out_err; 175376994313SAlexey Dobriyan svc_putnl(resv, mic.len); 17541da177e4SLinus Torvalds memset(mic.data + mic.len, 0, 17551da177e4SLinus Torvalds round_up_to_quad(mic.len) - mic.len); 17561da177e4SLinus Torvalds resv->iov_len += XDR_QUADLEN(mic.len) << 2; 17571da177e4SLinus Torvalds /* not strictly required: */ 17581da177e4SLinus Torvalds resbuf->len += XDR_QUADLEN(mic.len) << 2; 175928155524SChuck Lever if (resv->iov_len > PAGE_SIZE) 176028155524SChuck Lever goto out_err; 1761e142ede8SJ. Bruce Fields out: 1762e142ede8SJ. Bruce Fields stat = 0; 1763e142ede8SJ. Bruce Fields out_err: 1764e142ede8SJ. Bruce Fields return stat; 1765e142ede8SJ. Bruce Fields } 1766e142ede8SJ. Bruce Fields 17677c9fdcfbSJ. Bruce Fields static inline int 17687c9fdcfbSJ. Bruce Fields svcauth_gss_wrap_resp_priv(struct svc_rqst *rqstp) 17697c9fdcfbSJ. Bruce Fields { 17707c9fdcfbSJ. Bruce Fields struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data; 17717c9fdcfbSJ. Bruce Fields struct rpc_gss_wire_cred *gc = &gsd->clcred; 17727c9fdcfbSJ. Bruce Fields struct xdr_buf *resbuf = &rqstp->rq_res; 17737c9fdcfbSJ. Bruce Fields struct page **inpages = NULL; 1774753ed90dSAl Viro __be32 *p, *len; 1775753ed90dSAl Viro int offset; 17767c9fdcfbSJ. Bruce Fields int pad; 17777c9fdcfbSJ. Bruce Fields 17783c15a486SJ.Bruce Fields p = svcauth_gss_prepare_to_wrap(resbuf, gsd); 17793c15a486SJ.Bruce Fields if (p == NULL) 17807c9fdcfbSJ. Bruce Fields return 0; 17817c9fdcfbSJ. Bruce Fields len = p++; 17827c9fdcfbSJ. Bruce Fields offset = (u8 *)p - (u8 *)resbuf->head[0].iov_base; 17837c9fdcfbSJ. Bruce Fields *p++ = htonl(gc->gc_seq); 17847c9fdcfbSJ. Bruce Fields inpages = resbuf->pages; 17857c9fdcfbSJ. Bruce Fields /* XXX: Would be better to write some xdr helper functions for 17867c9fdcfbSJ. Bruce Fields * nfs{2,3,4}xdr.c that place the data right, instead of copying: */ 17877561042fSKevin Coffman 17887561042fSKevin Coffman /* 17897561042fSKevin Coffman * If there is currently tail data, make sure there is 17907561042fSKevin Coffman * room for the head, tail, and 2 * RPC_MAX_AUTH_SIZE in 17917561042fSKevin Coffman * the page, and move the current tail data such that 17927561042fSKevin Coffman * there is RPC_MAX_AUTH_SIZE slack space available in 17937561042fSKevin Coffman * both the head and tail. 17947561042fSKevin Coffman */ 179544524359SNeilBrown if (resbuf->tail[0].iov_base) { 179628155524SChuck Lever if (resbuf->tail[0].iov_base >= 179728155524SChuck Lever resbuf->head[0].iov_base + PAGE_SIZE) 179828155524SChuck Lever return -EINVAL; 179928155524SChuck Lever if (resbuf->tail[0].iov_base < resbuf->head[0].iov_base) 180028155524SChuck Lever return -EINVAL; 18017c9fdcfbSJ. Bruce Fields if (resbuf->tail[0].iov_len + resbuf->head[0].iov_len 18027c9fdcfbSJ. Bruce Fields + 2 * RPC_MAX_AUTH_SIZE > PAGE_SIZE) 18037c9fdcfbSJ. Bruce Fields return -ENOMEM; 18047c9fdcfbSJ. Bruce Fields memmove(resbuf->tail[0].iov_base + RPC_MAX_AUTH_SIZE, 18057c9fdcfbSJ. Bruce Fields resbuf->tail[0].iov_base, 18067c9fdcfbSJ. Bruce Fields resbuf->tail[0].iov_len); 18077c9fdcfbSJ. Bruce Fields resbuf->tail[0].iov_base += RPC_MAX_AUTH_SIZE; 18087c9fdcfbSJ. Bruce Fields } 18097561042fSKevin Coffman /* 18107561042fSKevin Coffman * If there is no current tail data, make sure there is 18117561042fSKevin Coffman * room for the head data, and 2 * RPC_MAX_AUTH_SIZE in the 18127561042fSKevin Coffman * allotted page, and set up tail information such that there 18137561042fSKevin Coffman * is RPC_MAX_AUTH_SIZE slack space available in both the 18147561042fSKevin Coffman * head and tail. 18157561042fSKevin Coffman */ 18167c9fdcfbSJ. Bruce Fields if (resbuf->tail[0].iov_base == NULL) { 18177c9fdcfbSJ. Bruce Fields if (resbuf->head[0].iov_len + 2*RPC_MAX_AUTH_SIZE > PAGE_SIZE) 18187c9fdcfbSJ. Bruce Fields return -ENOMEM; 18197c9fdcfbSJ. Bruce Fields resbuf->tail[0].iov_base = resbuf->head[0].iov_base 18207c9fdcfbSJ. Bruce Fields + resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE; 18217c9fdcfbSJ. Bruce Fields resbuf->tail[0].iov_len = 0; 18227c9fdcfbSJ. Bruce Fields } 18237c9fdcfbSJ. Bruce Fields if (gss_wrap(gsd->rsci->mechctx, offset, resbuf, inpages)) 18247c9fdcfbSJ. Bruce Fields return -ENOMEM; 18257c9fdcfbSJ. Bruce Fields *len = htonl(resbuf->len - offset); 18267c9fdcfbSJ. Bruce Fields pad = 3 - ((resbuf->len - offset - 1)&3); 1827d8ed029dSAlexey Dobriyan p = (__be32 *)(resbuf->tail[0].iov_base + resbuf->tail[0].iov_len); 18287c9fdcfbSJ. Bruce Fields memset(p, 0, pad); 18297c9fdcfbSJ. Bruce Fields resbuf->tail[0].iov_len += pad; 18307c9fdcfbSJ. Bruce Fields resbuf->len += pad; 18317c9fdcfbSJ. Bruce Fields return 0; 18327c9fdcfbSJ. Bruce Fields } 18337c9fdcfbSJ. Bruce Fields 1834e142ede8SJ. Bruce Fields static int 1835e142ede8SJ. Bruce Fields svcauth_gss_release(struct svc_rqst *rqstp) 1836e142ede8SJ. Bruce Fields { 1837e142ede8SJ. Bruce Fields struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data; 18380ddc9423SJ. Bruce Fields struct rpc_gss_wire_cred *gc; 1839e142ede8SJ. Bruce Fields struct xdr_buf *resbuf = &rqstp->rq_res; 1840e142ede8SJ. Bruce Fields int stat = -EINVAL; 1841b8be5674SVasily Averin struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id); 1842e142ede8SJ. Bruce Fields 18430ddc9423SJ. Bruce Fields if (!gsd) 18440ddc9423SJ. Bruce Fields goto out; 18450ddc9423SJ. Bruce Fields gc = &gsd->clcred; 1846e142ede8SJ. Bruce Fields if (gc->gc_proc != RPC_GSS_PROC_DATA) 1847e142ede8SJ. Bruce Fields goto out; 1848e142ede8SJ. Bruce Fields /* Release can be called twice, but we only wrap once. */ 18495b304bc5SJ.Bruce Fields if (gsd->verf_start == NULL) 1850e142ede8SJ. Bruce Fields goto out; 1851e142ede8SJ. Bruce Fields /* normally not set till svc_send, but we need it here: */ 18527c9fdcfbSJ. Bruce Fields /* XXX: what for? Do we mess it up the moment we call svc_putu32 18537c9fdcfbSJ. Bruce Fields * or whatever? */ 18547c9fdcfbSJ. Bruce Fields resbuf->len = total_buf_len(resbuf); 1855e142ede8SJ. Bruce Fields switch (gc->gc_svc) { 1856e142ede8SJ. Bruce Fields case RPC_GSS_SVC_NONE: 1857e142ede8SJ. Bruce Fields break; 1858e142ede8SJ. Bruce Fields case RPC_GSS_SVC_INTEGRITY: 18597c9fdcfbSJ. Bruce Fields stat = svcauth_gss_wrap_resp_integ(rqstp); 18607c9fdcfbSJ. Bruce Fields if (stat) 18617c9fdcfbSJ. Bruce Fields goto out_err; 18621da177e4SLinus Torvalds break; 18631da177e4SLinus Torvalds case RPC_GSS_SVC_PRIVACY: 18647c9fdcfbSJ. Bruce Fields stat = svcauth_gss_wrap_resp_priv(rqstp); 18657c9fdcfbSJ. Bruce Fields if (stat) 18667c9fdcfbSJ. Bruce Fields goto out_err; 18677c9fdcfbSJ. Bruce Fields break; 1868eac81736SWei Yongjun /* 1869eac81736SWei Yongjun * For any other gc_svc value, svcauth_gss_accept() already set 1870eac81736SWei Yongjun * the auth_error appropriately; just fall through: 1871eac81736SWei Yongjun */ 18721da177e4SLinus Torvalds } 18731da177e4SLinus Torvalds 18741da177e4SLinus Torvalds out: 18751da177e4SLinus Torvalds stat = 0; 18761da177e4SLinus Torvalds out_err: 18771da177e4SLinus Torvalds if (rqstp->rq_client) 18781da177e4SLinus Torvalds auth_domain_put(rqstp->rq_client); 18791da177e4SLinus Torvalds rqstp->rq_client = NULL; 18803ab4d8b1SJ. Bruce Fields if (rqstp->rq_gssclient) 18813ab4d8b1SJ. Bruce Fields auth_domain_put(rqstp->rq_gssclient); 18823ab4d8b1SJ. Bruce Fields rqstp->rq_gssclient = NULL; 18831da177e4SLinus Torvalds if (rqstp->rq_cred.cr_group_info) 18841da177e4SLinus Torvalds put_group_info(rqstp->rq_cred.cr_group_info); 18851da177e4SLinus Torvalds rqstp->rq_cred.cr_group_info = NULL; 18860ddc9423SJ. Bruce Fields if (gsd && gsd->rsci) { 1887a1db410dSStanislav Kinsbursky cache_put(&gsd->rsci->h, sn->rsc_cache); 18881da177e4SLinus Torvalds gsd->rsci = NULL; 18890ddc9423SJ. Bruce Fields } 18901da177e4SLinus Torvalds return stat; 18911da177e4SLinus Torvalds } 18921da177e4SLinus Torvalds 18931da177e4SLinus Torvalds static void 1894608a0ab2STrond Myklebust svcauth_gss_domain_release_rcu(struct rcu_head *head) 18951da177e4SLinus Torvalds { 1896608a0ab2STrond Myklebust struct auth_domain *dom = container_of(head, struct auth_domain, rcu_head); 18971da177e4SLinus Torvalds struct gss_domain *gd = container_of(dom, struct gss_domain, h); 18981da177e4SLinus Torvalds 18991da177e4SLinus Torvalds kfree(dom->name); 19001da177e4SLinus Torvalds kfree(gd); 19011da177e4SLinus Torvalds } 19021da177e4SLinus Torvalds 1903608a0ab2STrond Myklebust static void 1904608a0ab2STrond Myklebust svcauth_gss_domain_release(struct auth_domain *dom) 1905608a0ab2STrond Myklebust { 1906608a0ab2STrond Myklebust call_rcu(&dom->rcu_head, svcauth_gss_domain_release_rcu); 1907608a0ab2STrond Myklebust } 1908608a0ab2STrond Myklebust 19091da177e4SLinus Torvalds static struct auth_ops svcauthops_gss = { 19101da177e4SLinus Torvalds .name = "rpcsec_gss", 19111da177e4SLinus Torvalds .owner = THIS_MODULE, 19121da177e4SLinus Torvalds .flavour = RPC_AUTH_GSS, 19131da177e4SLinus Torvalds .accept = svcauth_gss_accept, 19141da177e4SLinus Torvalds .release = svcauth_gss_release, 19151da177e4SLinus Torvalds .domain_release = svcauth_gss_domain_release, 19161da177e4SLinus Torvalds .set_client = svcauth_gss_set_client, 19171da177e4SLinus Torvalds }; 19181da177e4SLinus Torvalds 1919a1db410dSStanislav Kinsbursky static int rsi_cache_create_net(struct net *net) 1920a1db410dSStanislav Kinsbursky { 1921a1db410dSStanislav Kinsbursky struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1922a1db410dSStanislav Kinsbursky struct cache_detail *cd; 1923a1db410dSStanislav Kinsbursky int err; 1924a1db410dSStanislav Kinsbursky 1925a1db410dSStanislav Kinsbursky cd = cache_create_net(&rsi_cache_template, net); 1926a1db410dSStanislav Kinsbursky if (IS_ERR(cd)) 1927a1db410dSStanislav Kinsbursky return PTR_ERR(cd); 1928a1db410dSStanislav Kinsbursky err = cache_register_net(cd, net); 1929a1db410dSStanislav Kinsbursky if (err) { 1930a1db410dSStanislav Kinsbursky cache_destroy_net(cd, net); 1931a1db410dSStanislav Kinsbursky return err; 1932a1db410dSStanislav Kinsbursky } 1933a1db410dSStanislav Kinsbursky sn->rsi_cache = cd; 1934a1db410dSStanislav Kinsbursky return 0; 1935a1db410dSStanislav Kinsbursky } 1936a1db410dSStanislav Kinsbursky 1937a1db410dSStanislav Kinsbursky static void rsi_cache_destroy_net(struct net *net) 1938a1db410dSStanislav Kinsbursky { 1939a1db410dSStanislav Kinsbursky struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1940a1db410dSStanislav Kinsbursky struct cache_detail *cd = sn->rsi_cache; 1941a1db410dSStanislav Kinsbursky 1942a1db410dSStanislav Kinsbursky sn->rsi_cache = NULL; 1943a1db410dSStanislav Kinsbursky cache_purge(cd); 1944a1db410dSStanislav Kinsbursky cache_unregister_net(cd, net); 1945a1db410dSStanislav Kinsbursky cache_destroy_net(cd, net); 1946a1db410dSStanislav Kinsbursky } 1947a1db410dSStanislav Kinsbursky 1948a1db410dSStanislav Kinsbursky static int rsc_cache_create_net(struct net *net) 1949a1db410dSStanislav Kinsbursky { 1950a1db410dSStanislav Kinsbursky struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1951a1db410dSStanislav Kinsbursky struct cache_detail *cd; 1952a1db410dSStanislav Kinsbursky int err; 1953a1db410dSStanislav Kinsbursky 1954a1db410dSStanislav Kinsbursky cd = cache_create_net(&rsc_cache_template, net); 1955a1db410dSStanislav Kinsbursky if (IS_ERR(cd)) 1956a1db410dSStanislav Kinsbursky return PTR_ERR(cd); 1957a1db410dSStanislav Kinsbursky err = cache_register_net(cd, net); 1958a1db410dSStanislav Kinsbursky if (err) { 1959a1db410dSStanislav Kinsbursky cache_destroy_net(cd, net); 1960a1db410dSStanislav Kinsbursky return err; 1961a1db410dSStanislav Kinsbursky } 1962a1db410dSStanislav Kinsbursky sn->rsc_cache = cd; 1963a1db410dSStanislav Kinsbursky return 0; 1964a1db410dSStanislav Kinsbursky } 1965a1db410dSStanislav Kinsbursky 1966a1db410dSStanislav Kinsbursky static void rsc_cache_destroy_net(struct net *net) 1967a1db410dSStanislav Kinsbursky { 1968a1db410dSStanislav Kinsbursky struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1969a1db410dSStanislav Kinsbursky struct cache_detail *cd = sn->rsc_cache; 1970a1db410dSStanislav Kinsbursky 1971a1db410dSStanislav Kinsbursky sn->rsc_cache = NULL; 1972a1db410dSStanislav Kinsbursky cache_purge(cd); 1973a1db410dSStanislav Kinsbursky cache_unregister_net(cd, net); 1974a1db410dSStanislav Kinsbursky cache_destroy_net(cd, net); 1975a1db410dSStanislav Kinsbursky } 1976a1db410dSStanislav Kinsbursky 1977a1db410dSStanislav Kinsbursky int 1978a1db410dSStanislav Kinsbursky gss_svc_init_net(struct net *net) 1979a1db410dSStanislav Kinsbursky { 1980a1db410dSStanislav Kinsbursky int rv; 1981a1db410dSStanislav Kinsbursky 1982a1db410dSStanislav Kinsbursky rv = rsc_cache_create_net(net); 1983a1db410dSStanislav Kinsbursky if (rv) 1984a1db410dSStanislav Kinsbursky return rv; 1985a1db410dSStanislav Kinsbursky rv = rsi_cache_create_net(net); 1986a1db410dSStanislav Kinsbursky if (rv) 1987a1db410dSStanislav Kinsbursky goto out1; 1988030d794bSSimo Sorce rv = create_use_gss_proxy_proc_entry(net); 1989030d794bSSimo Sorce if (rv) 1990030d794bSSimo Sorce goto out2; 1991a1db410dSStanislav Kinsbursky return 0; 1992030d794bSSimo Sorce out2: 19935a475344SJ. Bruce Fields rsi_cache_destroy_net(net); 1994a1db410dSStanislav Kinsbursky out1: 1995a1db410dSStanislav Kinsbursky rsc_cache_destroy_net(net); 1996a1db410dSStanislav Kinsbursky return rv; 1997a1db410dSStanislav Kinsbursky } 1998a1db410dSStanislav Kinsbursky 1999a1db410dSStanislav Kinsbursky void 2000a1db410dSStanislav Kinsbursky gss_svc_shutdown_net(struct net *net) 2001a1db410dSStanislav Kinsbursky { 2002030d794bSSimo Sorce destroy_use_gss_proxy_proc_entry(net); 2003a1db410dSStanislav Kinsbursky rsi_cache_destroy_net(net); 2004a1db410dSStanislav Kinsbursky rsc_cache_destroy_net(net); 2005a1db410dSStanislav Kinsbursky } 2006a1db410dSStanislav Kinsbursky 20071da177e4SLinus Torvalds int 20081da177e4SLinus Torvalds gss_svc_init(void) 20091da177e4SLinus Torvalds { 2010a1db410dSStanislav Kinsbursky return svc_auth_register(RPC_AUTH_GSS, &svcauthops_gss); 20111da177e4SLinus Torvalds } 20121da177e4SLinus Torvalds 20131da177e4SLinus Torvalds void 20141da177e4SLinus Torvalds gss_svc_shutdown(void) 20151da177e4SLinus Torvalds { 20161da177e4SLinus Torvalds svc_auth_unregister(RPC_AUTH_GSS); 20171da177e4SLinus Torvalds } 2018