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> 524dd9daa9SChuck Lever #include <linux/sunrpc/gss_krb5.h> 53ff27e9f7SChuck Lever 54ff27e9f7SChuck Lever #include <trace/events/rpcgss.h> 55ff27e9f7SChuck Lever 56030d794bSSimo Sorce #include "gss_rpc_upcall.h" 571da177e4SLinus Torvalds 584dd9daa9SChuck Lever /* 594dd9daa9SChuck Lever * Unfortunately there isn't a maximum checksum size exported via the 604dd9daa9SChuck Lever * GSS API. Manufacture one based on GSS mechanisms supported by this 614dd9daa9SChuck Lever * implementation. 624dd9daa9SChuck Lever */ 634dd9daa9SChuck Lever #define GSS_MAX_CKSUMSIZE (GSS_KRB5_TOK_HDR_LEN + GSS_KRB5_MAX_CKSUM_LEN) 644dd9daa9SChuck Lever 654dd9daa9SChuck Lever /* 664dd9daa9SChuck Lever * This value may be increased in the future to accommodate other 674dd9daa9SChuck Lever * usage of the scratch buffer. 684dd9daa9SChuck Lever */ 694dd9daa9SChuck Lever #define GSS_SCRATCH_SIZE GSS_MAX_CKSUMSIZE 704dd9daa9SChuck Lever 714dd9daa9SChuck Lever struct gss_svc_data { 724dd9daa9SChuck Lever /* decoded gss client cred: */ 734dd9daa9SChuck Lever struct rpc_gss_wire_cred clcred; 744dd9daa9SChuck Lever /* save a pointer to the beginning of the encoded verifier, 754dd9daa9SChuck Lever * for use in encryption/checksumming in svcauth_gss_release: */ 764dd9daa9SChuck Lever __be32 *verf_start; 774dd9daa9SChuck Lever struct rsc *rsci; 784dd9daa9SChuck Lever 794dd9daa9SChuck Lever /* for temporary results */ 804dd9daa9SChuck Lever u8 gsd_scratch[GSS_SCRATCH_SIZE]; 814dd9daa9SChuck Lever }; 82a1db410dSStanislav Kinsbursky 831da177e4SLinus Torvalds /* The rpcsec_init cache is used for mapping RPCSEC_GSS_{,CONT_}INIT requests 841da177e4SLinus Torvalds * into replies. 851da177e4SLinus Torvalds * 861da177e4SLinus Torvalds * Key is context handle (\x if empty) and gss_token. 871da177e4SLinus Torvalds * Content is major_status minor_status (integers) context_handle, reply_token. 881da177e4SLinus Torvalds * 891da177e4SLinus Torvalds */ 901da177e4SLinus Torvalds 911da177e4SLinus Torvalds static int netobj_equal(struct xdr_netobj *a, struct xdr_netobj *b) 921da177e4SLinus Torvalds { 931da177e4SLinus Torvalds return a->len == b->len && 0 == memcmp(a->data, b->data, a->len); 941da177e4SLinus Torvalds } 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds #define RSI_HASHBITS 6 971da177e4SLinus Torvalds #define RSI_HASHMAX (1<<RSI_HASHBITS) 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds struct rsi { 1001da177e4SLinus Torvalds struct cache_head h; 1011da177e4SLinus Torvalds struct xdr_netobj in_handle, in_token; 1021da177e4SLinus Torvalds struct xdr_netobj out_handle, out_token; 1031da177e4SLinus Torvalds int major_status, minor_status; 1046d1616b2STrond Myklebust struct rcu_head rcu_head; 1051da177e4SLinus Torvalds }; 1061da177e4SLinus Torvalds 107a1db410dSStanislav Kinsbursky static struct rsi *rsi_update(struct cache_detail *cd, struct rsi *new, struct rsi *old); 108a1db410dSStanislav Kinsbursky static struct rsi *rsi_lookup(struct cache_detail *cd, struct rsi *item); 1091da177e4SLinus Torvalds 1101da177e4SLinus Torvalds static void rsi_free(struct rsi *rsii) 1111da177e4SLinus Torvalds { 1121da177e4SLinus Torvalds kfree(rsii->in_handle.data); 1131da177e4SLinus Torvalds kfree(rsii->in_token.data); 1141da177e4SLinus Torvalds kfree(rsii->out_handle.data); 1151da177e4SLinus Torvalds kfree(rsii->out_token.data); 1161da177e4SLinus Torvalds } 1171da177e4SLinus Torvalds 1186d1616b2STrond Myklebust static void rsi_free_rcu(struct rcu_head *head) 1196d1616b2STrond Myklebust { 1206d1616b2STrond Myklebust struct rsi *rsii = container_of(head, struct rsi, rcu_head); 1216d1616b2STrond Myklebust 1226d1616b2STrond Myklebust rsi_free(rsii); 1236d1616b2STrond Myklebust kfree(rsii); 1246d1616b2STrond Myklebust } 1256d1616b2STrond Myklebust 126baab935fSNeilBrown static void rsi_put(struct kref *ref) 1271da177e4SLinus Torvalds { 128baab935fSNeilBrown struct rsi *rsii = container_of(ref, struct rsi, h.ref); 1296d1616b2STrond Myklebust 1306d1616b2STrond Myklebust call_rcu(&rsii->rcu_head, rsi_free_rcu); 1311da177e4SLinus Torvalds } 1321da177e4SLinus Torvalds 1331da177e4SLinus Torvalds static inline int rsi_hash(struct rsi *item) 1341da177e4SLinus Torvalds { 1351da177e4SLinus Torvalds return hash_mem(item->in_handle.data, item->in_handle.len, RSI_HASHBITS) 1361da177e4SLinus Torvalds ^ hash_mem(item->in_token.data, item->in_token.len, RSI_HASHBITS); 1371da177e4SLinus Torvalds } 1381da177e4SLinus Torvalds 139d4d11ea9SNeilBrown static int rsi_match(struct cache_head *a, struct cache_head *b) 1401da177e4SLinus Torvalds { 141d4d11ea9SNeilBrown struct rsi *item = container_of(a, struct rsi, h); 142d4d11ea9SNeilBrown struct rsi *tmp = container_of(b, struct rsi, h); 143f64f9e71SJoe Perches return netobj_equal(&item->in_handle, &tmp->in_handle) && 144f64f9e71SJoe Perches netobj_equal(&item->in_token, &tmp->in_token); 1451da177e4SLinus Torvalds } 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds static int dup_to_netobj(struct xdr_netobj *dst, char *src, int len) 1481da177e4SLinus Torvalds { 1491da177e4SLinus Torvalds dst->len = len; 150e69062b4SArnaldo Carvalho de Melo dst->data = (len ? kmemdup(src, len, GFP_KERNEL) : NULL); 1511da177e4SLinus Torvalds if (len && !dst->data) 1521da177e4SLinus Torvalds return -ENOMEM; 1531da177e4SLinus Torvalds return 0; 1541da177e4SLinus Torvalds } 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds static inline int dup_netobj(struct xdr_netobj *dst, struct xdr_netobj *src) 1571da177e4SLinus Torvalds { 1581da177e4SLinus Torvalds return dup_to_netobj(dst, src->data, src->len); 1591da177e4SLinus Torvalds } 1601da177e4SLinus Torvalds 161d4d11ea9SNeilBrown static void rsi_init(struct cache_head *cnew, struct cache_head *citem) 1621da177e4SLinus Torvalds { 163d4d11ea9SNeilBrown struct rsi *new = container_of(cnew, struct rsi, h); 164d4d11ea9SNeilBrown struct rsi *item = container_of(citem, struct rsi, h); 165d4d11ea9SNeilBrown 1661da177e4SLinus Torvalds new->out_handle.data = NULL; 1671da177e4SLinus Torvalds new->out_handle.len = 0; 1681da177e4SLinus Torvalds new->out_token.data = NULL; 1691da177e4SLinus Torvalds new->out_token.len = 0; 1701da177e4SLinus Torvalds new->in_handle.len = item->in_handle.len; 1711da177e4SLinus Torvalds item->in_handle.len = 0; 1721da177e4SLinus Torvalds new->in_token.len = item->in_token.len; 1731da177e4SLinus Torvalds item->in_token.len = 0; 1741da177e4SLinus Torvalds new->in_handle.data = item->in_handle.data; 1751da177e4SLinus Torvalds item->in_handle.data = NULL; 1761da177e4SLinus Torvalds new->in_token.data = item->in_token.data; 1771da177e4SLinus Torvalds item->in_token.data = NULL; 1781da177e4SLinus Torvalds } 1791da177e4SLinus Torvalds 180d4d11ea9SNeilBrown static void update_rsi(struct cache_head *cnew, struct cache_head *citem) 1811da177e4SLinus Torvalds { 182d4d11ea9SNeilBrown struct rsi *new = container_of(cnew, struct rsi, h); 183d4d11ea9SNeilBrown struct rsi *item = container_of(citem, struct rsi, h); 184d4d11ea9SNeilBrown 1851da177e4SLinus Torvalds BUG_ON(new->out_handle.data || new->out_token.data); 1861da177e4SLinus Torvalds new->out_handle.len = item->out_handle.len; 1871da177e4SLinus Torvalds item->out_handle.len = 0; 1881da177e4SLinus Torvalds new->out_token.len = item->out_token.len; 1891da177e4SLinus Torvalds item->out_token.len = 0; 1901da177e4SLinus Torvalds new->out_handle.data = item->out_handle.data; 1911da177e4SLinus Torvalds item->out_handle.data = NULL; 1921da177e4SLinus Torvalds new->out_token.data = item->out_token.data; 1931da177e4SLinus Torvalds item->out_token.data = NULL; 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds new->major_status = item->major_status; 1961da177e4SLinus Torvalds new->minor_status = item->minor_status; 1971da177e4SLinus Torvalds } 1981da177e4SLinus Torvalds 199d4d11ea9SNeilBrown static struct cache_head *rsi_alloc(void) 200d4d11ea9SNeilBrown { 201d4d11ea9SNeilBrown struct rsi *rsii = kmalloc(sizeof(*rsii), GFP_KERNEL); 202d4d11ea9SNeilBrown if (rsii) 203d4d11ea9SNeilBrown return &rsii->h; 204d4d11ea9SNeilBrown else 205d4d11ea9SNeilBrown return NULL; 206d4d11ea9SNeilBrown } 207d4d11ea9SNeilBrown 20865286b88STrond Myklebust static int rsi_upcall(struct cache_detail *cd, struct cache_head *h) 20965286b88STrond Myklebust { 21065286b88STrond Myklebust return sunrpc_cache_pipe_upcall_timeout(cd, h); 21165286b88STrond Myklebust } 21265286b88STrond Myklebust 2131da177e4SLinus Torvalds static void rsi_request(struct cache_detail *cd, 2141da177e4SLinus Torvalds struct cache_head *h, 2151da177e4SLinus Torvalds char **bpp, int *blen) 2161da177e4SLinus Torvalds { 2171da177e4SLinus Torvalds struct rsi *rsii = container_of(h, struct rsi, h); 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds qword_addhex(bpp, blen, rsii->in_handle.data, rsii->in_handle.len); 2201da177e4SLinus Torvalds qword_addhex(bpp, blen, rsii->in_token.data, rsii->in_token.len); 2211da177e4SLinus Torvalds (*bpp)[-1] = '\n'; 2220c217d50SNeilBrown WARN_ONCE(*blen < 0, 2230c217d50SNeilBrown "RPCSEC/GSS credential too large - please use gssproxy\n"); 2241da177e4SLinus Torvalds } 2251da177e4SLinus Torvalds 2261da177e4SLinus Torvalds static int rsi_parse(struct cache_detail *cd, 2271da177e4SLinus Torvalds char *mesg, int mlen) 2281da177e4SLinus Torvalds { 2291da177e4SLinus Torvalds /* context token expiry major minor context token */ 2301da177e4SLinus Torvalds char *buf = mesg; 2311da177e4SLinus Torvalds char *ep; 2321da177e4SLinus Torvalds int len; 2331da177e4SLinus Torvalds struct rsi rsii, *rsip = NULL; 234f559935eSArnd Bergmann time64_t expiry; 2351da177e4SLinus Torvalds int status = -EINVAL; 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds memset(&rsii, 0, sizeof(rsii)); 2381da177e4SLinus Torvalds /* handle */ 2391da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2401da177e4SLinus Torvalds if (len < 0) 2411da177e4SLinus Torvalds goto out; 2421da177e4SLinus Torvalds status = -ENOMEM; 2431da177e4SLinus Torvalds if (dup_to_netobj(&rsii.in_handle, buf, len)) 2441da177e4SLinus Torvalds goto out; 2451da177e4SLinus Torvalds 2461da177e4SLinus Torvalds /* token */ 2471da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2481da177e4SLinus Torvalds status = -EINVAL; 2491da177e4SLinus Torvalds if (len < 0) 2501da177e4SLinus Torvalds goto out; 2511da177e4SLinus Torvalds status = -ENOMEM; 2521da177e4SLinus Torvalds if (dup_to_netobj(&rsii.in_token, buf, len)) 2531da177e4SLinus Torvalds goto out; 2541da177e4SLinus Torvalds 255a1db410dSStanislav Kinsbursky rsip = rsi_lookup(cd, &rsii); 256d4d11ea9SNeilBrown if (!rsip) 257d4d11ea9SNeilBrown goto out; 258d4d11ea9SNeilBrown 2591da177e4SLinus Torvalds rsii.h.flags = 0; 2601da177e4SLinus Torvalds /* expiry */ 2611da177e4SLinus Torvalds expiry = get_expiry(&mesg); 2621da177e4SLinus Torvalds status = -EINVAL; 2631da177e4SLinus Torvalds if (expiry == 0) 2641da177e4SLinus Torvalds goto out; 2651da177e4SLinus Torvalds 2661da177e4SLinus Torvalds /* major/minor */ 2671da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 268b39c18fcSJ. Bruce Fields if (len <= 0) 2691da177e4SLinus Torvalds goto out; 2701da177e4SLinus Torvalds rsii.major_status = simple_strtoul(buf, &ep, 10); 2711da177e4SLinus Torvalds if (*ep) 2721da177e4SLinus Torvalds goto out; 2731da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2741da177e4SLinus Torvalds if (len <= 0) 2751da177e4SLinus Torvalds goto out; 2761da177e4SLinus Torvalds rsii.minor_status = simple_strtoul(buf, &ep, 10); 2771da177e4SLinus Torvalds if (*ep) 2781da177e4SLinus Torvalds goto out; 2791da177e4SLinus Torvalds 2801da177e4SLinus Torvalds /* out_handle */ 2811da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2821da177e4SLinus Torvalds if (len < 0) 2831da177e4SLinus Torvalds goto out; 2841da177e4SLinus Torvalds status = -ENOMEM; 2851da177e4SLinus Torvalds if (dup_to_netobj(&rsii.out_handle, buf, len)) 2861da177e4SLinus Torvalds goto out; 2871da177e4SLinus Torvalds 2881da177e4SLinus Torvalds /* out_token */ 2891da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2901da177e4SLinus Torvalds status = -EINVAL; 2911da177e4SLinus Torvalds if (len < 0) 2921da177e4SLinus Torvalds goto out; 2931da177e4SLinus Torvalds status = -ENOMEM; 2941da177e4SLinus Torvalds if (dup_to_netobj(&rsii.out_token, buf, len)) 2951da177e4SLinus Torvalds goto out; 2961da177e4SLinus Torvalds rsii.h.expiry_time = expiry; 297a1db410dSStanislav Kinsbursky rsip = rsi_update(cd, &rsii, rsip); 2981da177e4SLinus Torvalds status = 0; 2991da177e4SLinus Torvalds out: 3001da177e4SLinus Torvalds rsi_free(&rsii); 3011da177e4SLinus Torvalds if (rsip) 302a1db410dSStanislav Kinsbursky cache_put(&rsip->h, cd); 303d4d11ea9SNeilBrown else 304d4d11ea9SNeilBrown status = -ENOMEM; 3051da177e4SLinus Torvalds return status; 3061da177e4SLinus Torvalds } 3071da177e4SLinus Torvalds 308ee24eac3SBhumika Goyal static const struct cache_detail rsi_cache_template = { 309f35279d3SBruce Allan .owner = THIS_MODULE, 3101da177e4SLinus Torvalds .hash_size = RSI_HASHMAX, 3111da177e4SLinus Torvalds .name = "auth.rpcsec.init", 3121da177e4SLinus Torvalds .cache_put = rsi_put, 31365286b88STrond Myklebust .cache_upcall = rsi_upcall, 31473fb847aSStanislav Kinsbursky .cache_request = rsi_request, 3151da177e4SLinus Torvalds .cache_parse = rsi_parse, 316d4d11ea9SNeilBrown .match = rsi_match, 317d4d11ea9SNeilBrown .init = rsi_init, 318d4d11ea9SNeilBrown .update = update_rsi, 319d4d11ea9SNeilBrown .alloc = rsi_alloc, 3201da177e4SLinus Torvalds }; 3211da177e4SLinus Torvalds 322a1db410dSStanislav Kinsbursky static struct rsi *rsi_lookup(struct cache_detail *cd, struct rsi *item) 323d4d11ea9SNeilBrown { 324d4d11ea9SNeilBrown struct cache_head *ch; 325d4d11ea9SNeilBrown int hash = rsi_hash(item); 326d4d11ea9SNeilBrown 3276d1616b2STrond Myklebust ch = sunrpc_cache_lookup_rcu(cd, &item->h, hash); 328d4d11ea9SNeilBrown if (ch) 329d4d11ea9SNeilBrown return container_of(ch, struct rsi, h); 330d4d11ea9SNeilBrown else 331d4d11ea9SNeilBrown return NULL; 332d4d11ea9SNeilBrown } 333d4d11ea9SNeilBrown 334a1db410dSStanislav Kinsbursky static struct rsi *rsi_update(struct cache_detail *cd, struct rsi *new, struct rsi *old) 335d4d11ea9SNeilBrown { 336d4d11ea9SNeilBrown struct cache_head *ch; 337d4d11ea9SNeilBrown int hash = rsi_hash(new); 338d4d11ea9SNeilBrown 339a1db410dSStanislav Kinsbursky ch = sunrpc_cache_update(cd, &new->h, 340d4d11ea9SNeilBrown &old->h, hash); 341d4d11ea9SNeilBrown if (ch) 342d4d11ea9SNeilBrown return container_of(ch, struct rsi, h); 343d4d11ea9SNeilBrown else 344d4d11ea9SNeilBrown return NULL; 345d4d11ea9SNeilBrown } 346d4d11ea9SNeilBrown 3471da177e4SLinus Torvalds 3481da177e4SLinus Torvalds /* 3491da177e4SLinus Torvalds * The rpcsec_context cache is used to store a context that is 3501da177e4SLinus Torvalds * used in data exchange. 3511da177e4SLinus Torvalds * The key is a context handle. The content is: 3521da177e4SLinus Torvalds * uid, gidlist, mechanism, service-set, mech-specific-data 3531da177e4SLinus Torvalds */ 3541da177e4SLinus Torvalds 3551da177e4SLinus Torvalds #define RSC_HASHBITS 10 3561da177e4SLinus Torvalds #define RSC_HASHMAX (1<<RSC_HASHBITS) 3571da177e4SLinus Torvalds 3581da177e4SLinus Torvalds #define GSS_SEQ_WIN 128 3591da177e4SLinus Torvalds 3601da177e4SLinus Torvalds struct gss_svc_seq_data { 3611da177e4SLinus Torvalds /* highest seq number seen so far: */ 36210b9d99aSChuck Lever u32 sd_max; 3631da177e4SLinus Torvalds /* for i such that sd_max-GSS_SEQ_WIN < i <= sd_max, the i-th bit of 3641da177e4SLinus Torvalds * sd_win is nonzero iff sequence number i has been seen already: */ 3651da177e4SLinus Torvalds unsigned long sd_win[GSS_SEQ_WIN/BITS_PER_LONG]; 3661da177e4SLinus Torvalds spinlock_t sd_lock; 3671da177e4SLinus Torvalds }; 3681da177e4SLinus Torvalds 3691da177e4SLinus Torvalds struct rsc { 3701da177e4SLinus Torvalds struct cache_head h; 3711da177e4SLinus Torvalds struct xdr_netobj handle; 3721da177e4SLinus Torvalds struct svc_cred cred; 3731da177e4SLinus Torvalds struct gss_svc_seq_data seqdata; 3741da177e4SLinus Torvalds struct gss_ctx *mechctx; 3756d1616b2STrond Myklebust struct rcu_head rcu_head; 3761da177e4SLinus Torvalds }; 3771da177e4SLinus Torvalds 378a1db410dSStanislav Kinsbursky static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old); 379a1db410dSStanislav Kinsbursky static struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item); 3801da177e4SLinus Torvalds 3811da177e4SLinus Torvalds static void rsc_free(struct rsc *rsci) 3821da177e4SLinus Torvalds { 3831da177e4SLinus Torvalds kfree(rsci->handle.data); 3841da177e4SLinus Torvalds if (rsci->mechctx) 3851da177e4SLinus Torvalds gss_delete_sec_context(&rsci->mechctx); 38603a4e1f6SJ. Bruce Fields free_svc_cred(&rsci->cred); 3871da177e4SLinus Torvalds } 3881da177e4SLinus Torvalds 3896d1616b2STrond Myklebust static void rsc_free_rcu(struct rcu_head *head) 3906d1616b2STrond Myklebust { 3916d1616b2STrond Myklebust struct rsc *rsci = container_of(head, struct rsc, rcu_head); 3926d1616b2STrond Myklebust 3936d1616b2STrond Myklebust kfree(rsci->handle.data); 3946d1616b2STrond Myklebust kfree(rsci); 3956d1616b2STrond Myklebust } 3966d1616b2STrond Myklebust 397baab935fSNeilBrown static void rsc_put(struct kref *ref) 3981da177e4SLinus Torvalds { 399baab935fSNeilBrown struct rsc *rsci = container_of(ref, struct rsc, h.ref); 4001da177e4SLinus Torvalds 4016d1616b2STrond Myklebust if (rsci->mechctx) 4026d1616b2STrond Myklebust gss_delete_sec_context(&rsci->mechctx); 4036d1616b2STrond Myklebust free_svc_cred(&rsci->cred); 4046d1616b2STrond Myklebust call_rcu(&rsci->rcu_head, rsc_free_rcu); 4051da177e4SLinus Torvalds } 4061da177e4SLinus Torvalds 4071da177e4SLinus Torvalds static inline int 4081da177e4SLinus Torvalds rsc_hash(struct rsc *rsci) 4091da177e4SLinus Torvalds { 4101da177e4SLinus Torvalds return hash_mem(rsci->handle.data, rsci->handle.len, RSC_HASHBITS); 4111da177e4SLinus Torvalds } 4121da177e4SLinus Torvalds 41317f834b6SNeilBrown static int 41417f834b6SNeilBrown rsc_match(struct cache_head *a, struct cache_head *b) 4151da177e4SLinus Torvalds { 41617f834b6SNeilBrown struct rsc *new = container_of(a, struct rsc, h); 41717f834b6SNeilBrown struct rsc *tmp = container_of(b, struct rsc, h); 41817f834b6SNeilBrown 4191da177e4SLinus Torvalds return netobj_equal(&new->handle, &tmp->handle); 4201da177e4SLinus Torvalds } 4211da177e4SLinus Torvalds 42217f834b6SNeilBrown static void 42317f834b6SNeilBrown rsc_init(struct cache_head *cnew, struct cache_head *ctmp) 4241da177e4SLinus Torvalds { 42517f834b6SNeilBrown struct rsc *new = container_of(cnew, struct rsc, h); 42617f834b6SNeilBrown struct rsc *tmp = container_of(ctmp, struct rsc, h); 42717f834b6SNeilBrown 4281da177e4SLinus Torvalds new->handle.len = tmp->handle.len; 4291da177e4SLinus Torvalds tmp->handle.len = 0; 4301da177e4SLinus Torvalds new->handle.data = tmp->handle.data; 4311da177e4SLinus Torvalds tmp->handle.data = NULL; 4321da177e4SLinus Torvalds new->mechctx = NULL; 43344234063SJ. Bruce Fields init_svc_cred(&new->cred); 4341da177e4SLinus Torvalds } 4351da177e4SLinus Torvalds 43617f834b6SNeilBrown static void 43717f834b6SNeilBrown update_rsc(struct cache_head *cnew, struct cache_head *ctmp) 4381da177e4SLinus Torvalds { 43917f834b6SNeilBrown struct rsc *new = container_of(cnew, struct rsc, h); 44017f834b6SNeilBrown struct rsc *tmp = container_of(ctmp, struct rsc, h); 44117f834b6SNeilBrown 4421da177e4SLinus Torvalds new->mechctx = tmp->mechctx; 4431da177e4SLinus Torvalds tmp->mechctx = NULL; 4441da177e4SLinus Torvalds memset(&new->seqdata, 0, sizeof(new->seqdata)); 4451da177e4SLinus Torvalds spin_lock_init(&new->seqdata.sd_lock); 4461da177e4SLinus Torvalds new->cred = tmp->cred; 44744234063SJ. Bruce Fields init_svc_cred(&tmp->cred); 4481da177e4SLinus Torvalds } 4491da177e4SLinus Torvalds 45017f834b6SNeilBrown static struct cache_head * 45117f834b6SNeilBrown rsc_alloc(void) 45217f834b6SNeilBrown { 45317f834b6SNeilBrown struct rsc *rsci = kmalloc(sizeof(*rsci), GFP_KERNEL); 45417f834b6SNeilBrown if (rsci) 45517f834b6SNeilBrown return &rsci->h; 45617f834b6SNeilBrown else 45717f834b6SNeilBrown return NULL; 45817f834b6SNeilBrown } 45917f834b6SNeilBrown 46065286b88STrond Myklebust static int rsc_upcall(struct cache_detail *cd, struct cache_head *h) 46165286b88STrond Myklebust { 46265286b88STrond Myklebust return -EINVAL; 46365286b88STrond Myklebust } 46465286b88STrond Myklebust 4651da177e4SLinus Torvalds static int rsc_parse(struct cache_detail *cd, 4661da177e4SLinus Torvalds char *mesg, int mlen) 4671da177e4SLinus Torvalds { 4681da177e4SLinus Torvalds /* contexthandle expiry [ uid gid N <n gids> mechname ...mechdata... ] */ 4691da177e4SLinus Torvalds char *buf = mesg; 470683428faSEric W. Biederman int id; 4711da177e4SLinus Torvalds int len, rv; 4721da177e4SLinus Torvalds struct rsc rsci, *rscp = NULL; 473294ec5b8SArnd Bergmann time64_t expiry; 4741da177e4SLinus Torvalds int status = -EINVAL; 4751df0cadaSJ. Bruce Fields struct gss_api_mech *gm = NULL; 4761da177e4SLinus Torvalds 4771da177e4SLinus Torvalds memset(&rsci, 0, sizeof(rsci)); 4781da177e4SLinus Torvalds /* context handle */ 4791da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 4801da177e4SLinus Torvalds if (len < 0) goto out; 4811da177e4SLinus Torvalds status = -ENOMEM; 4821da177e4SLinus Torvalds if (dup_to_netobj(&rsci.handle, buf, len)) 4831da177e4SLinus Torvalds goto out; 4841da177e4SLinus Torvalds 4851da177e4SLinus Torvalds rsci.h.flags = 0; 4861da177e4SLinus Torvalds /* expiry */ 4871da177e4SLinus Torvalds expiry = get_expiry(&mesg); 4881da177e4SLinus Torvalds status = -EINVAL; 4891da177e4SLinus Torvalds if (expiry == 0) 4901da177e4SLinus Torvalds goto out; 4911da177e4SLinus Torvalds 492a1db410dSStanislav Kinsbursky rscp = rsc_lookup(cd, &rsci); 49317f834b6SNeilBrown if (!rscp) 49417f834b6SNeilBrown goto out; 49517f834b6SNeilBrown 4961da177e4SLinus Torvalds /* uid, or NEGATIVE */ 497683428faSEric W. Biederman rv = get_int(&mesg, &id); 4981da177e4SLinus Torvalds if (rv == -EINVAL) 4991da177e4SLinus Torvalds goto out; 5001da177e4SLinus Torvalds if (rv == -ENOENT) 5011da177e4SLinus Torvalds set_bit(CACHE_NEGATIVE, &rsci.h.flags); 5021da177e4SLinus Torvalds else { 5031da177e4SLinus Torvalds int N, i; 5041da177e4SLinus Torvalds 5053c34ae11SJ. Bruce Fields /* 5063c34ae11SJ. Bruce Fields * NOTE: we skip uid_valid()/gid_valid() checks here: 5073c34ae11SJ. Bruce Fields * instead, * -1 id's are later mapped to the 5083c34ae11SJ. Bruce Fields * (export-specific) anonymous id by nfsd_setuser. 5093c34ae11SJ. Bruce Fields * 5103c34ae11SJ. Bruce Fields * (But supplementary gid's get no such special 5113c34ae11SJ. Bruce Fields * treatment so are checked for validity here.) 5123c34ae11SJ. Bruce Fields */ 513683428faSEric W. Biederman /* uid */ 514e6667c73STrond Myklebust rsci.cred.cr_uid = make_kuid(current_user_ns(), id); 515683428faSEric W. Biederman 5161da177e4SLinus Torvalds /* gid */ 517683428faSEric W. Biederman if (get_int(&mesg, &id)) 518683428faSEric W. Biederman goto out; 519e6667c73STrond Myklebust rsci.cred.cr_gid = make_kgid(current_user_ns(), id); 5201da177e4SLinus Torvalds 5211da177e4SLinus Torvalds /* number of additional gid's */ 5221da177e4SLinus Torvalds if (get_int(&mesg, &N)) 5231da177e4SLinus Torvalds goto out; 52476cb4be9SDan Carpenter if (N < 0 || N > NGROUPS_MAX) 52576cb4be9SDan Carpenter goto out; 5261da177e4SLinus Torvalds status = -ENOMEM; 5271da177e4SLinus Torvalds rsci.cred.cr_group_info = groups_alloc(N); 5281da177e4SLinus Torvalds if (rsci.cred.cr_group_info == NULL) 5291da177e4SLinus Torvalds goto out; 5301da177e4SLinus Torvalds 5311da177e4SLinus Torvalds /* gid's */ 5321da177e4SLinus Torvalds status = -EINVAL; 5331da177e4SLinus Torvalds for (i=0; i<N; i++) { 534ae2975bcSEric W. Biederman kgid_t kgid; 535683428faSEric W. Biederman if (get_int(&mesg, &id)) 5361da177e4SLinus Torvalds goto out; 537e6667c73STrond Myklebust kgid = make_kgid(current_user_ns(), id); 538ae2975bcSEric W. Biederman if (!gid_valid(kgid)) 539ae2975bcSEric W. Biederman goto out; 54081243eacSAlexey Dobriyan rsci.cred.cr_group_info->gid[i] = kgid; 5411da177e4SLinus Torvalds } 542bdcf0a42SThiago Rafael Becker groups_sort(rsci.cred.cr_group_info); 5431da177e4SLinus Torvalds 5441da177e4SLinus Torvalds /* mech name */ 5451da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 5461da177e4SLinus Torvalds if (len < 0) 5471da177e4SLinus Torvalds goto out; 5480dc1531aSJ. Bruce Fields gm = rsci.cred.cr_gss_mech = gss_mech_get_by_name(buf); 5491da177e4SLinus Torvalds status = -EOPNOTSUPP; 5501da177e4SLinus Torvalds if (!gm) 5511da177e4SLinus Torvalds goto out; 5521da177e4SLinus Torvalds 5531da177e4SLinus Torvalds status = -EINVAL; 5541da177e4SLinus Torvalds /* mech-specific data: */ 5551da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 5561df0cadaSJ. Bruce Fields if (len < 0) 5571da177e4SLinus Torvalds goto out; 558400f26b5SSimo Sorce status = gss_import_sec_context(buf, len, gm, &rsci.mechctx, 559400f26b5SSimo Sorce NULL, GFP_KERNEL); 5601df0cadaSJ. Bruce Fields if (status) 5611da177e4SLinus Torvalds goto out; 56268e76ad0SOlga Kornievskaia 56368e76ad0SOlga Kornievskaia /* get client name */ 56468e76ad0SOlga Kornievskaia len = qword_get(&mesg, buf, mlen); 56568e76ad0SOlga Kornievskaia if (len > 0) { 56603a4e1f6SJ. Bruce Fields rsci.cred.cr_principal = kstrdup(buf, GFP_KERNEL); 5671eb6d622SWei Yongjun if (!rsci.cred.cr_principal) { 5681eb6d622SWei Yongjun status = -ENOMEM; 56968e76ad0SOlga Kornievskaia goto out; 57068e76ad0SOlga Kornievskaia } 5711eb6d622SWei Yongjun } 57268e76ad0SOlga Kornievskaia 5731da177e4SLinus Torvalds } 5741da177e4SLinus Torvalds rsci.h.expiry_time = expiry; 575a1db410dSStanislav Kinsbursky rscp = rsc_update(cd, &rsci, rscp); 5761da177e4SLinus Torvalds status = 0; 5771da177e4SLinus Torvalds out: 5781da177e4SLinus Torvalds rsc_free(&rsci); 5791da177e4SLinus Torvalds if (rscp) 580a1db410dSStanislav Kinsbursky cache_put(&rscp->h, cd); 58117f834b6SNeilBrown else 58217f834b6SNeilBrown status = -ENOMEM; 5831da177e4SLinus Torvalds return status; 5841da177e4SLinus Torvalds } 5851da177e4SLinus Torvalds 586ee24eac3SBhumika Goyal static const struct cache_detail rsc_cache_template = { 587f35279d3SBruce Allan .owner = THIS_MODULE, 5881da177e4SLinus Torvalds .hash_size = RSC_HASHMAX, 5891da177e4SLinus Torvalds .name = "auth.rpcsec.context", 5901da177e4SLinus Torvalds .cache_put = rsc_put, 59165286b88STrond Myklebust .cache_upcall = rsc_upcall, 5921da177e4SLinus Torvalds .cache_parse = rsc_parse, 59317f834b6SNeilBrown .match = rsc_match, 59417f834b6SNeilBrown .init = rsc_init, 59517f834b6SNeilBrown .update = update_rsc, 59617f834b6SNeilBrown .alloc = rsc_alloc, 5971da177e4SLinus Torvalds }; 5981da177e4SLinus Torvalds 599a1db410dSStanislav Kinsbursky static struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item) 60017f834b6SNeilBrown { 60117f834b6SNeilBrown struct cache_head *ch; 60217f834b6SNeilBrown int hash = rsc_hash(item); 60317f834b6SNeilBrown 6046d1616b2STrond Myklebust ch = sunrpc_cache_lookup_rcu(cd, &item->h, hash); 60517f834b6SNeilBrown if (ch) 60617f834b6SNeilBrown return container_of(ch, struct rsc, h); 60717f834b6SNeilBrown else 60817f834b6SNeilBrown return NULL; 60917f834b6SNeilBrown } 61017f834b6SNeilBrown 611a1db410dSStanislav Kinsbursky static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old) 61217f834b6SNeilBrown { 61317f834b6SNeilBrown struct cache_head *ch; 61417f834b6SNeilBrown int hash = rsc_hash(new); 61517f834b6SNeilBrown 616a1db410dSStanislav Kinsbursky ch = sunrpc_cache_update(cd, &new->h, 61717f834b6SNeilBrown &old->h, hash); 61817f834b6SNeilBrown if (ch) 61917f834b6SNeilBrown return container_of(ch, struct rsc, h); 62017f834b6SNeilBrown else 62117f834b6SNeilBrown return NULL; 62217f834b6SNeilBrown } 62317f834b6SNeilBrown 6241da177e4SLinus Torvalds 6251da177e4SLinus Torvalds static struct rsc * 626a1db410dSStanislav Kinsbursky gss_svc_searchbyctx(struct cache_detail *cd, struct xdr_netobj *handle) 6271da177e4SLinus Torvalds { 6281da177e4SLinus Torvalds struct rsc rsci; 6291da177e4SLinus Torvalds struct rsc *found; 6301da177e4SLinus Torvalds 6311da177e4SLinus Torvalds memset(&rsci, 0, sizeof(rsci)); 632bf2c4b6fSChuck Lever if (dup_to_netobj(&rsci.handle, handle->data, handle->len)) 633bf2c4b6fSChuck Lever return NULL; 634a1db410dSStanislav Kinsbursky found = rsc_lookup(cd, &rsci); 635bf2c4b6fSChuck Lever rsc_free(&rsci); 6361da177e4SLinus Torvalds if (!found) 6371da177e4SLinus Torvalds return NULL; 638a1db410dSStanislav Kinsbursky if (cache_check(cd, &found->h, NULL)) 6391da177e4SLinus Torvalds return NULL; 6401da177e4SLinus Torvalds return found; 6411da177e4SLinus Torvalds } 6421da177e4SLinus Torvalds 64310b9d99aSChuck Lever /** 64410b9d99aSChuck Lever * gss_check_seq_num - GSS sequence number window check 64510b9d99aSChuck Lever * @rqstp: RPC Call to use when reporting errors 64610b9d99aSChuck Lever * @rsci: cached GSS context state (updated on return) 64710b9d99aSChuck Lever * @seq_num: sequence number to check 64810b9d99aSChuck Lever * 64910b9d99aSChuck Lever * Implements sequence number algorithm as specified in 65010b9d99aSChuck Lever * RFC 2203, Section 5.3.3.1. "Context Management". 65110b9d99aSChuck Lever * 65210b9d99aSChuck Lever * Return values: 65310b9d99aSChuck Lever * %true: @rqstp's GSS sequence number is inside the window 65410b9d99aSChuck Lever * %false: @rqstp's GSS sequence number is outside the window 65510b9d99aSChuck Lever */ 65610b9d99aSChuck Lever static bool gss_check_seq_num(const struct svc_rqst *rqstp, struct rsc *rsci, 65710b9d99aSChuck Lever u32 seq_num) 6581da177e4SLinus Torvalds { 6591da177e4SLinus Torvalds struct gss_svc_seq_data *sd = &rsci->seqdata; 66010b9d99aSChuck Lever bool result = false; 6611da177e4SLinus Torvalds 6621da177e4SLinus Torvalds spin_lock(&sd->sd_lock); 6631da177e4SLinus Torvalds if (seq_num > sd->sd_max) { 6641da177e4SLinus Torvalds if (seq_num >= sd->sd_max + GSS_SEQ_WIN) { 6651da177e4SLinus Torvalds memset(sd->sd_win, 0, sizeof(sd->sd_win)); 6661da177e4SLinus Torvalds sd->sd_max = seq_num; 6671da177e4SLinus Torvalds } else while (sd->sd_max < seq_num) { 6681da177e4SLinus Torvalds sd->sd_max++; 6691da177e4SLinus Torvalds __clear_bit(sd->sd_max % GSS_SEQ_WIN, sd->sd_win); 6701da177e4SLinus Torvalds } 6711da177e4SLinus Torvalds __set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win); 6721da177e4SLinus Torvalds goto ok; 6732ba5acfbSJ. Bruce Fields } else if (seq_num + GSS_SEQ_WIN <= sd->sd_max) { 67410b9d99aSChuck Lever goto toolow; 6751da177e4SLinus Torvalds } 6761da177e4SLinus Torvalds if (__test_and_set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win)) 67710b9d99aSChuck Lever goto alreadyseen; 67810b9d99aSChuck Lever 6791da177e4SLinus Torvalds ok: 68010b9d99aSChuck Lever result = true; 68110b9d99aSChuck Lever out: 6821da177e4SLinus Torvalds spin_unlock(&sd->sd_lock); 68310b9d99aSChuck Lever return result; 68410b9d99aSChuck Lever 68510b9d99aSChuck Lever toolow: 68610b9d99aSChuck Lever trace_rpcgss_svc_seqno_low(rqstp, seq_num, 68710b9d99aSChuck Lever sd->sd_max - GSS_SEQ_WIN, 68810b9d99aSChuck Lever sd->sd_max); 68910b9d99aSChuck Lever goto out; 69010b9d99aSChuck Lever alreadyseen: 69110b9d99aSChuck Lever trace_rpcgss_svc_seqno_seen(rqstp, seq_num); 69210b9d99aSChuck Lever goto out; 6931da177e4SLinus Torvalds } 6941da177e4SLinus Torvalds 6951da177e4SLinus Torvalds static inline u32 round_up_to_quad(u32 i) 6961da177e4SLinus Torvalds { 6971da177e4SLinus Torvalds return (i + 3 ) & ~3; 6981da177e4SLinus Torvalds } 6991da177e4SLinus Torvalds 7001da177e4SLinus Torvalds static inline int 7011da177e4SLinus Torvalds svc_safe_putnetobj(struct kvec *resv, struct xdr_netobj *o) 7021da177e4SLinus Torvalds { 703753ed90dSAl Viro u8 *p; 7041da177e4SLinus Torvalds 7051da177e4SLinus Torvalds if (resv->iov_len + 4 > PAGE_SIZE) 7061da177e4SLinus Torvalds return -1; 70776994313SAlexey Dobriyan svc_putnl(resv, o->len); 7081da177e4SLinus Torvalds p = resv->iov_base + resv->iov_len; 7091da177e4SLinus Torvalds resv->iov_len += round_up_to_quad(o->len); 7101da177e4SLinus Torvalds if (resv->iov_len > PAGE_SIZE) 7111da177e4SLinus Torvalds return -1; 7121da177e4SLinus Torvalds memcpy(p, o->data, o->len); 713753ed90dSAl Viro memset(p + o->len, 0, round_up_to_quad(o->len) - o->len); 7141da177e4SLinus Torvalds return 0; 7151da177e4SLinus Torvalds } 7161da177e4SLinus Torvalds 71721fcd02bSJ. Bruce Fields /* 7180653028eSChuck Lever * Decode and verify a Call's verifier field. For RPC_AUTH_GSS Calls, 7190653028eSChuck Lever * the body of this field contains a variable length checksum. 7200653028eSChuck Lever * 7210653028eSChuck Lever * GSS-specific auth_stat values are mandated by RFC 2203 Section 7220653028eSChuck Lever * 5.3.3.3. 7231da177e4SLinus Torvalds */ 7241da177e4SLinus Torvalds static int 7250653028eSChuck Lever svcauth_gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci, 726438623a0SChuck Lever __be32 *rpcstart, struct rpc_gss_wire_cred *gc) 7271da177e4SLinus Torvalds { 7280653028eSChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 7291da177e4SLinus Torvalds struct gss_ctx *ctx_id = rsci->mechctx; 7300653028eSChuck Lever u32 flavor, maj_stat; 7311da177e4SLinus Torvalds struct xdr_buf rpchdr; 7321da177e4SLinus Torvalds struct xdr_netobj checksum; 7331da177e4SLinus Torvalds struct kvec iov; 7341da177e4SLinus Torvalds 7350653028eSChuck Lever /* 7360653028eSChuck Lever * Compute the checksum of the incoming Call from the 7370653028eSChuck Lever * XID field to credential field: 7380653028eSChuck Lever */ 7391da177e4SLinus Torvalds iov.iov_base = rpcstart; 7400653028eSChuck Lever iov.iov_len = (u8 *)xdr->p - (u8 *)rpcstart; 7411da177e4SLinus Torvalds xdr_buf_from_iov(&iov, &rpchdr); 7421da177e4SLinus Torvalds 7430653028eSChuck Lever /* Call's verf field: */ 7440653028eSChuck Lever if (xdr_stream_decode_opaque_auth(xdr, &flavor, 7450653028eSChuck Lever (void **)&checksum.data, 7460653028eSChuck Lever &checksum.len) < 0) { 747438623a0SChuck Lever rqstp->rq_auth_stat = rpc_autherr_badverf; 7481da177e4SLinus Torvalds return SVC_DENIED; 7490653028eSChuck Lever } 7500653028eSChuck Lever if (flavor != RPC_AUTH_GSS) { 7510653028eSChuck Lever rqstp->rq_auth_stat = rpc_autherr_badverf; 7521da177e4SLinus Torvalds return SVC_DENIED; 7530653028eSChuck Lever } 7541da177e4SLinus Torvalds 7550653028eSChuck Lever if (rqstp->rq_deferred) 7561da177e4SLinus Torvalds return SVC_OK; 7570653028eSChuck Lever maj_stat = gss_verify_mic(ctx_id, &rpchdr, &checksum); 7580653028eSChuck Lever if (maj_stat != GSS_S_COMPLETE) { 7590653028eSChuck Lever trace_rpcgss_svc_mic(rqstp, maj_stat); 760438623a0SChuck Lever rqstp->rq_auth_stat = rpcsec_gsserr_credproblem; 7611da177e4SLinus Torvalds return SVC_DENIED; 7621da177e4SLinus Torvalds } 7631da177e4SLinus Torvalds 7641da177e4SLinus Torvalds if (gc->gc_seq > MAXSEQ) { 76510b9d99aSChuck Lever trace_rpcgss_svc_seqno_large(rqstp, gc->gc_seq); 766438623a0SChuck Lever rqstp->rq_auth_stat = rpcsec_gsserr_ctxproblem; 7671da177e4SLinus Torvalds return SVC_DENIED; 7681da177e4SLinus Torvalds } 76910b9d99aSChuck Lever if (!gss_check_seq_num(rqstp, rsci, gc->gc_seq)) 7701da177e4SLinus Torvalds return SVC_DROP; 7711da177e4SLinus Torvalds return SVC_OK; 7721da177e4SLinus Torvalds } 7731da177e4SLinus Torvalds 7741da177e4SLinus Torvalds static int 775822f1005SAndy Adamson gss_write_null_verf(struct svc_rqst *rqstp) 776822f1005SAndy Adamson { 777d8ed029dSAlexey Dobriyan __be32 *p; 778822f1005SAndy Adamson 77976994313SAlexey Dobriyan svc_putnl(rqstp->rq_res.head, RPC_AUTH_NULL); 780822f1005SAndy Adamson p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len; 781822f1005SAndy Adamson /* don't really need to check if head->iov_len > PAGE_SIZE ... */ 782822f1005SAndy Adamson *p++ = 0; 783822f1005SAndy Adamson if (!xdr_ressize_check(rqstp, p)) 784822f1005SAndy Adamson return -1; 785822f1005SAndy Adamson return 0; 786822f1005SAndy Adamson } 787822f1005SAndy Adamson 788822f1005SAndy Adamson static int 7891da177e4SLinus Torvalds gss_write_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id, u32 seq) 7901da177e4SLinus Torvalds { 7912876a344SJ. Bruce Fields __be32 *xdr_seq; 7921da177e4SLinus Torvalds u32 maj_stat; 7931da177e4SLinus Torvalds struct xdr_buf verf_data; 7941da177e4SLinus Torvalds struct xdr_netobj mic; 795d8ed029dSAlexey Dobriyan __be32 *p; 7961da177e4SLinus Torvalds struct kvec iov; 7972876a344SJ. Bruce Fields int err = -1; 7981da177e4SLinus Torvalds 79976994313SAlexey Dobriyan svc_putnl(rqstp->rq_res.head, RPC_AUTH_GSS); 8002876a344SJ. Bruce Fields xdr_seq = kmalloc(4, GFP_KERNEL); 8012876a344SJ. Bruce Fields if (!xdr_seq) 802458032fcSYang Li return -ENOMEM; 8032876a344SJ. Bruce Fields *xdr_seq = htonl(seq); 8041da177e4SLinus Torvalds 8052876a344SJ. Bruce Fields iov.iov_base = xdr_seq; 8062876a344SJ. Bruce Fields iov.iov_len = 4; 8071da177e4SLinus Torvalds xdr_buf_from_iov(&iov, &verf_data); 8081da177e4SLinus Torvalds p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len; 8091da177e4SLinus Torvalds mic.data = (u8 *)(p + 1); 81000fd6e14SJ. Bruce Fields maj_stat = gss_get_mic(ctx_id, &verf_data, &mic); 8111da177e4SLinus Torvalds if (maj_stat != GSS_S_COMPLETE) 8122876a344SJ. Bruce Fields goto out; 8131da177e4SLinus Torvalds *p++ = htonl(mic.len); 8141da177e4SLinus Torvalds memset((u8 *)p + mic.len, 0, round_up_to_quad(mic.len) - mic.len); 8151da177e4SLinus Torvalds p += XDR_QUADLEN(mic.len); 8161da177e4SLinus Torvalds if (!xdr_ressize_check(rqstp, p)) 8172876a344SJ. Bruce Fields goto out; 8182876a344SJ. Bruce Fields err = 0; 8192876a344SJ. Bruce Fields out: 8202876a344SJ. Bruce Fields kfree(xdr_seq); 8212876a344SJ. Bruce Fields return err; 8221da177e4SLinus Torvalds } 8231da177e4SLinus Torvalds 8241da177e4SLinus Torvalds struct gss_domain { 8251da177e4SLinus Torvalds struct auth_domain h; 8261da177e4SLinus Torvalds u32 pseudoflavor; 8271da177e4SLinus Torvalds }; 8281da177e4SLinus Torvalds 8291da177e4SLinus Torvalds static struct auth_domain * 8301da177e4SLinus Torvalds find_gss_auth_domain(struct gss_ctx *ctx, u32 svc) 8311da177e4SLinus Torvalds { 8321da177e4SLinus Torvalds char *name; 8331da177e4SLinus Torvalds 8341da177e4SLinus Torvalds name = gss_service_to_auth_domain_name(ctx->mech_type, svc); 8351da177e4SLinus Torvalds if (!name) 8361da177e4SLinus Torvalds return NULL; 8371da177e4SLinus Torvalds return auth_domain_find(name); 8381da177e4SLinus Torvalds } 8391da177e4SLinus Torvalds 840efc36aa5SNeilBrown static struct auth_ops svcauthops_gss; 841efc36aa5SNeilBrown 8424796f457SJ. Bruce Fields u32 svcauth_gss_flavor(struct auth_domain *dom) 8434796f457SJ. Bruce Fields { 8444796f457SJ. Bruce Fields struct gss_domain *gd = container_of(dom, struct gss_domain, h); 8454796f457SJ. Bruce Fields 8464796f457SJ. Bruce Fields return gd->pseudoflavor; 8474796f457SJ. Bruce Fields } 8484796f457SJ. Bruce Fields 8497bd88269STrond Myklebust EXPORT_SYMBOL_GPL(svcauth_gss_flavor); 8504796f457SJ. Bruce Fields 85124c5efe4SNeilBrown struct auth_domain * 8521da177e4SLinus Torvalds svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name) 8531da177e4SLinus Torvalds { 8541da177e4SLinus Torvalds struct gss_domain *new; 8551da177e4SLinus Torvalds struct auth_domain *test; 8561da177e4SLinus Torvalds int stat = -ENOMEM; 8571da177e4SLinus Torvalds 8581da177e4SLinus Torvalds new = kmalloc(sizeof(*new), GFP_KERNEL); 8591da177e4SLinus Torvalds if (!new) 8601da177e4SLinus Torvalds goto out; 861efc36aa5SNeilBrown kref_init(&new->h.ref); 862e69062b4SArnaldo Carvalho de Melo new->h.name = kstrdup(name, GFP_KERNEL); 8631da177e4SLinus Torvalds if (!new->h.name) 8641da177e4SLinus Torvalds goto out_free_dom; 865efc36aa5SNeilBrown new->h.flavour = &svcauthops_gss; 8661da177e4SLinus Torvalds new->pseudoflavor = pseudoflavor; 8671da177e4SLinus Torvalds 868efc36aa5SNeilBrown test = auth_domain_lookup(name, &new->h); 869d47a5dc2SNeilBrown if (test != &new->h) { 870d47a5dc2SNeilBrown pr_warn("svc: duplicate registration of gss pseudo flavour %s.\n", 871d47a5dc2SNeilBrown name); 872d47a5dc2SNeilBrown stat = -EADDRINUSE; 873cb276805SJ. Bruce Fields auth_domain_put(test); 87424c5efe4SNeilBrown goto out_free_name; 8751da177e4SLinus Torvalds } 87624c5efe4SNeilBrown return test; 8771da177e4SLinus Torvalds 87824c5efe4SNeilBrown out_free_name: 87924c5efe4SNeilBrown kfree(new->h.name); 8801da177e4SLinus Torvalds out_free_dom: 8811da177e4SLinus Torvalds kfree(new); 8821da177e4SLinus Torvalds out: 88324c5efe4SNeilBrown return ERR_PTR(stat); 8841da177e4SLinus Torvalds } 8857bd88269STrond Myklebust EXPORT_SYMBOL_GPL(svcauth_gss_register_pseudoflavor); 8861da177e4SLinus Torvalds 887e14673c9SChuck Lever /* 888e14673c9SChuck Lever * RFC 2203, Section 5.3.2.2 889e14673c9SChuck Lever * 890e14673c9SChuck Lever * struct rpc_gss_integ_data { 891e14673c9SChuck Lever * opaque databody_integ<>; 892e14673c9SChuck Lever * opaque checksum<>; 893e14673c9SChuck Lever * }; 894e14673c9SChuck Lever * 895e14673c9SChuck Lever * struct rpc_gss_data_t { 896e14673c9SChuck Lever * unsigned int seq_num; 897e14673c9SChuck Lever * proc_req_arg_t arg; 898e14673c9SChuck Lever * }; 899e14673c9SChuck Lever */ 900b68e4c5cSChuck Lever static noinline_for_stack int 901b68e4c5cSChuck Lever svcauth_gss_unwrap_integ(struct svc_rqst *rqstp, u32 seq, struct gss_ctx *ctx) 9021da177e4SLinus Torvalds { 9034dd9daa9SChuck Lever struct gss_svc_data *gsd = rqstp->rq_auth_data; 904b68e4c5cSChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 905b68e4c5cSChuck Lever u32 len, offset, seq_num, maj_stat; 906b68e4c5cSChuck Lever struct xdr_buf *buf = xdr->buf; 907e14673c9SChuck Lever struct xdr_buf databody_integ; 908e14673c9SChuck Lever struct xdr_netobj checksum; 9091da177e4SLinus Torvalds 91006eb8a56SChuck Lever /* NFS READ normally uses splice to send data in-place. However 91106eb8a56SChuck Lever * the data in cache can change after the reply's MIC is computed 91206eb8a56SChuck Lever * but before the RPC reply is sent. To prevent the client from 91306eb8a56SChuck Lever * rejecting the server-computed MIC in this somewhat rare case, 91406eb8a56SChuck Lever * do not use splice with the GSS integrity service. 91506eb8a56SChuck Lever */ 9167827c81fSChuck Lever clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags); 91706eb8a56SChuck Lever 9184c190e2fSJeff Layton /* Did we already verify the signature on the original pass through? */ 9194c190e2fSJeff Layton if (rqstp->rq_deferred) 9204c190e2fSJeff Layton return 0; 9214c190e2fSJeff Layton 922b68e4c5cSChuck Lever if (xdr_stream_decode_u32(xdr, &len) < 0) 923b68e4c5cSChuck Lever goto unwrap_failed; 924e14673c9SChuck Lever if (len & 3) 92510b9d99aSChuck Lever goto unwrap_failed; 926b68e4c5cSChuck Lever offset = xdr_stream_pos(xdr); 927b68e4c5cSChuck Lever if (xdr_buf_subsegment(buf, &databody_integ, offset, len)) 92810b9d99aSChuck Lever goto unwrap_failed; 92910b9d99aSChuck Lever 930b68e4c5cSChuck Lever /* 931b68e4c5cSChuck Lever * The xdr_stream now points to the @seq_num field. The next 932b68e4c5cSChuck Lever * XDR data item is the @arg field, which contains the clear 933b68e4c5cSChuck Lever * text RPC program payload. The checksum, which follows the 934b68e4c5cSChuck Lever * @arg field, is located and decoded without updating the 935b68e4c5cSChuck Lever * xdr_stream. 936b68e4c5cSChuck Lever */ 937b68e4c5cSChuck Lever 938b68e4c5cSChuck Lever offset += len; 939b68e4c5cSChuck Lever if (xdr_decode_word(buf, offset, &checksum.len)) 94010b9d99aSChuck Lever goto unwrap_failed; 941e14673c9SChuck Lever if (checksum.len > sizeof(gsd->gsd_scratch)) 94210b9d99aSChuck Lever goto unwrap_failed; 943e14673c9SChuck Lever checksum.data = gsd->gsd_scratch; 944b68e4c5cSChuck Lever if (read_bytes_from_xdr_buf(buf, offset + XDR_UNIT, checksum.data, 945b68e4c5cSChuck Lever checksum.len)) 94610b9d99aSChuck Lever goto unwrap_failed; 947b68e4c5cSChuck Lever 948e14673c9SChuck Lever maj_stat = gss_verify_mic(ctx, &databody_integ, &checksum); 9491da177e4SLinus Torvalds if (maj_stat != GSS_S_COMPLETE) 95010b9d99aSChuck Lever goto bad_mic; 951b68e4c5cSChuck Lever 952b68e4c5cSChuck Lever /* The received seqno is protected by the checksum. */ 953b68e4c5cSChuck Lever if (xdr_stream_decode_u32(xdr, &seq_num) < 0) 954b68e4c5cSChuck Lever goto unwrap_failed; 955e14673c9SChuck Lever if (seq_num != seq) 95610b9d99aSChuck Lever goto bad_seqno; 957b68e4c5cSChuck Lever 958b68e4c5cSChuck Lever xdr_truncate_decode(xdr, XDR_UNIT + checksum.len); 9594dd9daa9SChuck Lever return 0; 96010b9d99aSChuck Lever 96110b9d99aSChuck Lever unwrap_failed: 96210b9d99aSChuck Lever trace_rpcgss_svc_unwrap_failed(rqstp); 9634dd9daa9SChuck Lever return -EINVAL; 96410b9d99aSChuck Lever bad_seqno: 965e14673c9SChuck Lever trace_rpcgss_svc_seqno_bad(rqstp, seq, seq_num); 9664dd9daa9SChuck Lever return -EINVAL; 96710b9d99aSChuck Lever bad_mic: 96810b9d99aSChuck Lever trace_rpcgss_svc_mic(rqstp, maj_stat); 9694dd9daa9SChuck Lever return -EINVAL; 9701da177e4SLinus Torvalds } 9711da177e4SLinus Torvalds 972f4a59e82SChuck Lever /* 973f4a59e82SChuck Lever * RFC 2203, Section 5.3.2.3 974f4a59e82SChuck Lever * 975f4a59e82SChuck Lever * struct rpc_gss_priv_data { 976f4a59e82SChuck Lever * opaque databody_priv<> 977f4a59e82SChuck Lever * }; 978f4a59e82SChuck Lever * 979f4a59e82SChuck Lever * struct rpc_gss_data_t { 980f4a59e82SChuck Lever * unsigned int seq_num; 981f4a59e82SChuck Lever * proc_req_arg_t arg; 982f4a59e82SChuck Lever * }; 983f4a59e82SChuck Lever */ 98442140718SChuck Lever static noinline_for_stack int 98542140718SChuck Lever svcauth_gss_unwrap_priv(struct svc_rqst *rqstp, u32 seq, struct gss_ctx *ctx) 9867c9fdcfbSJ. Bruce Fields { 98742140718SChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 98842140718SChuck Lever u32 len, maj_stat, seq_num, offset; 98942140718SChuck Lever struct xdr_buf *buf = xdr->buf; 99042140718SChuck Lever unsigned int saved_len; 9917c9fdcfbSJ. Bruce Fields 9927827c81fSChuck Lever clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags); 9937c9fdcfbSJ. Bruce Fields 99442140718SChuck Lever if (xdr_stream_decode_u32(xdr, &len) < 0) 99542140718SChuck Lever goto unwrap_failed; 9967c9fdcfbSJ. Bruce Fields if (rqstp->rq_deferred) { 9977c9fdcfbSJ. Bruce Fields /* Already decrypted last time through! The sequence number 9987c9fdcfbSJ. Bruce Fields * check at out_seq is unnecessary but harmless: */ 9997c9fdcfbSJ. Bruce Fields goto out_seq; 10007c9fdcfbSJ. Bruce Fields } 100142140718SChuck Lever if (len > xdr_stream_remaining(xdr)) 100210b9d99aSChuck Lever goto unwrap_failed; 100342140718SChuck Lever offset = xdr_stream_pos(xdr); 10047c9fdcfbSJ. Bruce Fields 100542140718SChuck Lever saved_len = buf->len; 100642140718SChuck Lever maj_stat = gss_unwrap(ctx, offset, offset + len, buf); 10077c9fdcfbSJ. Bruce Fields if (maj_stat != GSS_S_COMPLETE) 100810b9d99aSChuck Lever goto bad_unwrap; 100942140718SChuck Lever xdr->nwords -= XDR_QUADLEN(saved_len - buf->len); 101042140718SChuck Lever 10117c9fdcfbSJ. Bruce Fields out_seq: 101242140718SChuck Lever /* gss_unwrap() decrypted the sequence number. */ 101342140718SChuck Lever if (xdr_stream_decode_u32(xdr, &seq_num) < 0) 101442140718SChuck Lever goto unwrap_failed; 1015f4a59e82SChuck Lever if (seq_num != seq) 101610b9d99aSChuck Lever goto bad_seqno; 10177c9fdcfbSJ. Bruce Fields return 0; 101810b9d99aSChuck Lever 101910b9d99aSChuck Lever unwrap_failed: 102010b9d99aSChuck Lever trace_rpcgss_svc_unwrap_failed(rqstp); 102110b9d99aSChuck Lever return -EINVAL; 102210b9d99aSChuck Lever bad_seqno: 1023f4a59e82SChuck Lever trace_rpcgss_svc_seqno_bad(rqstp, seq, seq_num); 102410b9d99aSChuck Lever return -EINVAL; 102510b9d99aSChuck Lever bad_unwrap: 102610b9d99aSChuck Lever trace_rpcgss_svc_unwrap(rqstp, maj_stat); 102710b9d99aSChuck Lever return -EINVAL; 10287c9fdcfbSJ. Bruce Fields } 10297c9fdcfbSJ. Bruce Fields 10301da177e4SLinus Torvalds static int 10311da177e4SLinus Torvalds svcauth_gss_set_client(struct svc_rqst *rqstp) 10321da177e4SLinus Torvalds { 10331da177e4SLinus Torvalds struct gss_svc_data *svcdata = rqstp->rq_auth_data; 10341da177e4SLinus Torvalds struct rsc *rsci = svcdata->rsci; 10351da177e4SLinus Torvalds struct rpc_gss_wire_cred *gc = &svcdata->clcred; 10363ab4d8b1SJ. Bruce Fields int stat; 10371da177e4SLinus Torvalds 10385c2465dfSChuck Lever rqstp->rq_auth_stat = rpc_autherr_badcred; 10395c2465dfSChuck Lever 10403ab4d8b1SJ. Bruce Fields /* 10413ab4d8b1SJ. Bruce Fields * A gss export can be specified either by: 10423ab4d8b1SJ. Bruce Fields * export *(sec=krb5,rw) 10433ab4d8b1SJ. Bruce Fields * or by 10443ab4d8b1SJ. Bruce Fields * export gss/krb5(rw) 10453ab4d8b1SJ. Bruce Fields * The latter is deprecated; but for backwards compatibility reasons 10463ab4d8b1SJ. Bruce Fields * the nfsd code will still fall back on trying it if the former 10473ab4d8b1SJ. Bruce Fields * doesn't work; so we try to make both available to nfsd, below. 10483ab4d8b1SJ. Bruce Fields */ 10493ab4d8b1SJ. Bruce Fields rqstp->rq_gssclient = find_gss_auth_domain(rsci->mechctx, gc->gc_svc); 10503ab4d8b1SJ. Bruce Fields if (rqstp->rq_gssclient == NULL) 10511da177e4SLinus Torvalds return SVC_DENIED; 10523ab4d8b1SJ. Bruce Fields stat = svcauth_unix_set_client(rqstp); 10531ebede86SNeilBrown if (stat == SVC_DROP || stat == SVC_CLOSE) 10543ab4d8b1SJ. Bruce Fields return stat; 10555c2465dfSChuck Lever 10565c2465dfSChuck Lever rqstp->rq_auth_stat = rpc_auth_ok; 10571da177e4SLinus Torvalds return SVC_OK; 10581da177e4SLinus Torvalds } 10591da177e4SLinus Torvalds 106091a4762eSKevin Coffman static inline int 1061fc2952a2SSimo Sorce gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp, 1062fc2952a2SSimo Sorce struct xdr_netobj *out_handle, int *major_status) 106391a4762eSKevin Coffman { 106491a4762eSKevin Coffman struct rsc *rsci; 106554f9247bSFrank Filz int rc; 106691a4762eSKevin Coffman 1067fc2952a2SSimo Sorce if (*major_status != GSS_S_COMPLETE) 106891a4762eSKevin Coffman return gss_write_null_verf(rqstp); 1069fc2952a2SSimo Sorce rsci = gss_svc_searchbyctx(cd, out_handle); 107091a4762eSKevin Coffman if (rsci == NULL) { 1071fc2952a2SSimo Sorce *major_status = GSS_S_NO_CONTEXT; 107291a4762eSKevin Coffman return gss_write_null_verf(rqstp); 107391a4762eSKevin Coffman } 107454f9247bSFrank Filz rc = gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN); 1075a1db410dSStanislav Kinsbursky cache_put(&rsci->h, cd); 107654f9247bSFrank Filz return rc; 107791a4762eSKevin Coffman } 107891a4762eSKevin Coffman 10795866efa8SChuck Lever static void gss_free_in_token_pages(struct gssp_in_token *in_token) 10805866efa8SChuck Lever { 10815866efa8SChuck Lever u32 inlen; 10825866efa8SChuck Lever int i; 10835866efa8SChuck Lever 10845866efa8SChuck Lever i = 0; 10855866efa8SChuck Lever inlen = in_token->page_len; 10865866efa8SChuck Lever while (inlen) { 10875866efa8SChuck Lever if (in_token->pages[i]) 10885866efa8SChuck Lever put_page(in_token->pages[i]); 10895866efa8SChuck Lever inlen -= inlen > PAGE_SIZE ? PAGE_SIZE : inlen; 10905866efa8SChuck Lever } 10915866efa8SChuck Lever 10925866efa8SChuck Lever kfree(in_token->pages); 10935866efa8SChuck Lever in_token->pages = NULL; 10945866efa8SChuck Lever } 10955866efa8SChuck Lever 10965866efa8SChuck Lever static int gss_read_proxy_verf(struct svc_rqst *rqstp, 1097438623a0SChuck Lever struct rpc_gss_wire_cred *gc, 1098030d794bSSimo Sorce struct xdr_netobj *in_handle, 1099030d794bSSimo Sorce struct gssp_in_token *in_token) 1100030d794bSSimo Sorce { 1101c020fa69SChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 1102d48c8124SMartijn de Gouw unsigned int length, pgto_offs, pgfrom_offs; 11034d51366dSChuck Lever int pages, i, pgto, pgfrom; 1104c020fa69SChuck Lever size_t to_offs, from_offs; 1105c020fa69SChuck Lever u32 inlen; 1106030d794bSSimo Sorce 11074d51366dSChuck Lever if (dup_netobj(in_handle, &gc->gc_ctx)) 11084d51366dSChuck Lever return SVC_CLOSE; 1109030d794bSSimo Sorce 1110c020fa69SChuck Lever /* 1111c020fa69SChuck Lever * RFC 2203 Section 5.2.2 1112c020fa69SChuck Lever * 1113c020fa69SChuck Lever * struct rpc_gss_init_arg { 1114c020fa69SChuck Lever * opaque gss_token<>; 1115c020fa69SChuck Lever * }; 1116c020fa69SChuck Lever */ 1117c020fa69SChuck Lever if (xdr_stream_decode_u32(xdr, &inlen) < 0) 1118c020fa69SChuck Lever goto out_denied_free; 1119c020fa69SChuck Lever if (inlen > xdr_stream_remaining(xdr)) 1120c020fa69SChuck Lever goto out_denied_free; 1121030d794bSSimo Sorce 11225866efa8SChuck Lever pages = DIV_ROUND_UP(inlen, PAGE_SIZE); 11235866efa8SChuck Lever in_token->pages = kcalloc(pages, sizeof(struct page *), GFP_KERNEL); 1124c020fa69SChuck Lever if (!in_token->pages) 1125c020fa69SChuck Lever goto out_denied_free; 11265866efa8SChuck Lever in_token->page_base = 0; 1127030d794bSSimo Sorce in_token->page_len = inlen; 11285866efa8SChuck Lever for (i = 0; i < pages; i++) { 11295866efa8SChuck Lever in_token->pages[i] = alloc_page(GFP_KERNEL); 11305866efa8SChuck Lever if (!in_token->pages[i]) { 11315866efa8SChuck Lever gss_free_in_token_pages(in_token); 1132c020fa69SChuck Lever goto out_denied_free; 11335866efa8SChuck Lever } 11345866efa8SChuck Lever } 1135030d794bSSimo Sorce 1136c020fa69SChuck Lever length = min_t(unsigned int, inlen, (char *)xdr->end - (char *)xdr->p); 1137c020fa69SChuck Lever memcpy(page_address(in_token->pages[0]), xdr->p, length); 11385866efa8SChuck Lever inlen -= length; 11395866efa8SChuck Lever 1140d48c8124SMartijn de Gouw to_offs = length; 1141d48c8124SMartijn de Gouw from_offs = rqstp->rq_arg.page_base; 11425866efa8SChuck Lever while (inlen) { 1143d48c8124SMartijn de Gouw pgto = to_offs >> PAGE_SHIFT; 1144d48c8124SMartijn de Gouw pgfrom = from_offs >> PAGE_SHIFT; 1145d48c8124SMartijn de Gouw pgto_offs = to_offs & ~PAGE_MASK; 1146d48c8124SMartijn de Gouw pgfrom_offs = from_offs & ~PAGE_MASK; 1147d48c8124SMartijn de Gouw 1148d48c8124SMartijn de Gouw length = min_t(unsigned int, inlen, 1149d48c8124SMartijn de Gouw min_t(unsigned int, PAGE_SIZE - pgto_offs, 1150d48c8124SMartijn de Gouw PAGE_SIZE - pgfrom_offs)); 1151d48c8124SMartijn de Gouw memcpy(page_address(in_token->pages[pgto]) + pgto_offs, 1152d48c8124SMartijn de Gouw page_address(rqstp->rq_arg.pages[pgfrom]) + pgfrom_offs, 11535866efa8SChuck Lever length); 11545866efa8SChuck Lever 1155d48c8124SMartijn de Gouw to_offs += length; 1156d48c8124SMartijn de Gouw from_offs += length; 11575866efa8SChuck Lever inlen -= length; 11585866efa8SChuck Lever } 1159030d794bSSimo Sorce return 0; 1160c020fa69SChuck Lever 1161c020fa69SChuck Lever out_denied_free: 1162c020fa69SChuck Lever kfree(in_handle->data); 1163c020fa69SChuck Lever return SVC_DENIED; 1164030d794bSSimo Sorce } 1165030d794bSSimo Sorce 1166fc2952a2SSimo Sorce static inline int 1167fc2952a2SSimo Sorce gss_write_resv(struct kvec *resv, size_t size_limit, 1168fc2952a2SSimo Sorce struct xdr_netobj *out_handle, struct xdr_netobj *out_token, 1169fc2952a2SSimo Sorce int major_status, int minor_status) 1170fc2952a2SSimo Sorce { 1171fc2952a2SSimo Sorce if (resv->iov_len + 4 > size_limit) 1172fc2952a2SSimo Sorce return -1; 1173fc2952a2SSimo Sorce svc_putnl(resv, RPC_SUCCESS); 1174fc2952a2SSimo Sorce if (svc_safe_putnetobj(resv, out_handle)) 1175fc2952a2SSimo Sorce return -1; 1176fc2952a2SSimo Sorce if (resv->iov_len + 3 * 4 > size_limit) 1177fc2952a2SSimo Sorce return -1; 1178fc2952a2SSimo Sorce svc_putnl(resv, major_status); 1179fc2952a2SSimo Sorce svc_putnl(resv, minor_status); 1180fc2952a2SSimo Sorce svc_putnl(resv, GSS_SEQ_WIN); 1181fc2952a2SSimo Sorce if (svc_safe_putnetobj(resv, out_token)) 1182fc2952a2SSimo Sorce return -1; 1183fc2952a2SSimo Sorce return 0; 1184fc2952a2SSimo Sorce } 1185fc2952a2SSimo Sorce 11861da177e4SLinus Torvalds /* 118721fcd02bSJ. Bruce Fields * Having read the cred already and found we're in the context 118821fcd02bSJ. Bruce Fields * initiation case, read the verifier and initiate (or check the results 118921fcd02bSJ. Bruce Fields * of) upcalls to userspace for help with context initiation. If 119021fcd02bSJ. Bruce Fields * the upcall results are available, write the verifier and result. 119121fcd02bSJ. Bruce Fields * Otherwise, drop the request pending an answer to the upcall. 119221fcd02bSJ. Bruce Fields */ 1193c020fa69SChuck Lever static int 1194c020fa69SChuck Lever svcauth_gss_legacy_init(struct svc_rqst *rqstp, 1195438623a0SChuck Lever struct rpc_gss_wire_cred *gc) 119621fcd02bSJ. Bruce Fields { 1197c020fa69SChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 119821fcd02bSJ. Bruce Fields struct kvec *resv = &rqstp->rq_res.head[0]; 119921fcd02bSJ. Bruce Fields struct rsi *rsip, rsikey; 1200c020fa69SChuck Lever __be32 *p; 1201c020fa69SChuck Lever u32 len; 1202980e5a40SJ. Bruce Fields int ret; 1203b8be5674SVasily Averin struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id); 120421fcd02bSJ. Bruce Fields 120521fcd02bSJ. Bruce Fields memset(&rsikey, 0, sizeof(rsikey)); 12061cbfb921SChuck Lever if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx)) 12071cbfb921SChuck Lever return SVC_CLOSE; 1208c020fa69SChuck Lever 1209c020fa69SChuck Lever /* 1210c020fa69SChuck Lever * RFC 2203 Section 5.2.2 1211c020fa69SChuck Lever * 1212c020fa69SChuck Lever * struct rpc_gss_init_arg { 1213c020fa69SChuck Lever * opaque gss_token<>; 1214c020fa69SChuck Lever * }; 1215c020fa69SChuck Lever */ 1216c020fa69SChuck Lever if (xdr_stream_decode_u32(xdr, &len) < 0) { 12171cbfb921SChuck Lever kfree(rsikey.in_handle.data); 12181cbfb921SChuck Lever return SVC_DENIED; 12191cbfb921SChuck Lever } 1220c020fa69SChuck Lever p = xdr_inline_decode(xdr, len); 1221c020fa69SChuck Lever if (!p) { 1222c020fa69SChuck Lever kfree(rsikey.in_handle.data); 1223c020fa69SChuck Lever return SVC_DENIED; 1224c020fa69SChuck Lever } 1225c020fa69SChuck Lever rsikey.in_token.data = kmalloc(len, GFP_KERNEL); 1226c020fa69SChuck Lever if (ZERO_OR_NULL_PTR(rsikey.in_token.data)) { 12271cbfb921SChuck Lever kfree(rsikey.in_handle.data); 12281cbfb921SChuck Lever return SVC_CLOSE; 12291cbfb921SChuck Lever } 1230c020fa69SChuck Lever memcpy(rsikey.in_token.data, p, len); 1231c020fa69SChuck Lever rsikey.in_token.len = len; 123221fcd02bSJ. Bruce Fields 123321fcd02bSJ. Bruce Fields /* Perform upcall, or find upcall result: */ 1234a1db410dSStanislav Kinsbursky rsip = rsi_lookup(sn->rsi_cache, &rsikey); 123521fcd02bSJ. Bruce Fields rsi_free(&rsikey); 123621fcd02bSJ. Bruce Fields if (!rsip) 12371ebede86SNeilBrown return SVC_CLOSE; 1238a1db410dSStanislav Kinsbursky if (cache_check(sn->rsi_cache, &rsip->h, &rqstp->rq_chandle) < 0) 123921fcd02bSJ. Bruce Fields /* No upcall result: */ 12401ebede86SNeilBrown return SVC_CLOSE; 12412ed5282cSNeilBrown 12421ebede86SNeilBrown ret = SVC_CLOSE; 124321fcd02bSJ. Bruce Fields /* Got an answer to the upcall; use it: */ 1244fc2952a2SSimo Sorce if (gss_write_init_verf(sn->rsc_cache, rqstp, 1245fc2952a2SSimo Sorce &rsip->out_handle, &rsip->major_status)) 1246980e5a40SJ. Bruce Fields goto out; 1247fc2952a2SSimo Sorce if (gss_write_resv(resv, PAGE_SIZE, 1248fc2952a2SSimo Sorce &rsip->out_handle, &rsip->out_token, 1249fc2952a2SSimo Sorce rsip->major_status, rsip->minor_status)) 1250980e5a40SJ. Bruce Fields goto out; 12512ed5282cSNeilBrown 1252980e5a40SJ. Bruce Fields ret = SVC_COMPLETE; 1253faca8978SChuck Lever svcxdr_init_encode(rqstp); 1254980e5a40SJ. Bruce Fields out: 1255a1db410dSStanislav Kinsbursky cache_put(&rsip->h, sn->rsi_cache); 1256980e5a40SJ. Bruce Fields return ret; 125721fcd02bSJ. Bruce Fields } 125821fcd02bSJ. Bruce Fields 1259030d794bSSimo Sorce static int gss_proxy_save_rsc(struct cache_detail *cd, 1260030d794bSSimo Sorce struct gssp_upcall_data *ud, 1261030d794bSSimo Sorce uint64_t *handle) 1262030d794bSSimo Sorce { 1263030d794bSSimo Sorce struct rsc rsci, *rscp = NULL; 1264030d794bSSimo Sorce static atomic64_t ctxhctr; 1265030d794bSSimo Sorce long long ctxh; 1266030d794bSSimo Sorce struct gss_api_mech *gm = NULL; 1267294ec5b8SArnd Bergmann time64_t expiry; 1268f6260b98SColin Ian King int status; 1269030d794bSSimo Sorce 1270030d794bSSimo Sorce memset(&rsci, 0, sizeof(rsci)); 1271030d794bSSimo Sorce /* context handle */ 1272030d794bSSimo Sorce status = -ENOMEM; 1273030d794bSSimo Sorce /* the handle needs to be just a unique id, 1274030d794bSSimo Sorce * use a static counter */ 1275030d794bSSimo Sorce ctxh = atomic64_inc_return(&ctxhctr); 1276030d794bSSimo Sorce 1277030d794bSSimo Sorce /* make a copy for the caller */ 1278030d794bSSimo Sorce *handle = ctxh; 1279030d794bSSimo Sorce 1280030d794bSSimo Sorce /* make a copy for the rsc cache */ 1281030d794bSSimo Sorce if (dup_to_netobj(&rsci.handle, (char *)handle, sizeof(uint64_t))) 1282030d794bSSimo Sorce goto out; 1283030d794bSSimo Sorce rscp = rsc_lookup(cd, &rsci); 1284030d794bSSimo Sorce if (!rscp) 1285030d794bSSimo Sorce goto out; 1286030d794bSSimo Sorce 1287030d794bSSimo Sorce /* creds */ 1288030d794bSSimo Sorce if (!ud->found_creds) { 1289030d794bSSimo Sorce /* userspace seem buggy, we should always get at least a 1290030d794bSSimo Sorce * mapping to nobody */ 12913be34555SJ. Bruce Fields goto out; 1292030d794bSSimo Sorce } else { 12933d96208cSRoberto Bergantinos Corpas struct timespec64 boot; 1294030d794bSSimo Sorce 1295030d794bSSimo Sorce /* steal creds */ 1296030d794bSSimo Sorce rsci.cred = ud->creds; 1297030d794bSSimo Sorce memset(&ud->creds, 0, sizeof(struct svc_cred)); 1298030d794bSSimo Sorce 1299030d794bSSimo Sorce status = -EOPNOTSUPP; 1300030d794bSSimo Sorce /* get mech handle from OID */ 1301030d794bSSimo Sorce gm = gss_mech_get_by_OID(&ud->mech_oid); 1302030d794bSSimo Sorce if (!gm) 1303030d794bSSimo Sorce goto out; 13047193bd17SJ. Bruce Fields rsci.cred.cr_gss_mech = gm; 1305030d794bSSimo Sorce 1306030d794bSSimo Sorce status = -EINVAL; 1307030d794bSSimo Sorce /* mech-specific data: */ 1308030d794bSSimo Sorce status = gss_import_sec_context(ud->out_handle.data, 1309030d794bSSimo Sorce ud->out_handle.len, 1310030d794bSSimo Sorce gm, &rsci.mechctx, 1311030d794bSSimo Sorce &expiry, GFP_KERNEL); 1312030d794bSSimo Sorce if (status) 1313030d794bSSimo Sorce goto out; 13143d96208cSRoberto Bergantinos Corpas 13153d96208cSRoberto Bergantinos Corpas getboottime64(&boot); 13163d96208cSRoberto Bergantinos Corpas expiry -= boot.tv_sec; 1317030d794bSSimo Sorce } 1318030d794bSSimo Sorce 1319030d794bSSimo Sorce rsci.h.expiry_time = expiry; 1320030d794bSSimo Sorce rscp = rsc_update(cd, &rsci, rscp); 1321030d794bSSimo Sorce status = 0; 1322030d794bSSimo Sorce out: 1323030d794bSSimo Sorce rsc_free(&rsci); 1324030d794bSSimo Sorce if (rscp) 1325030d794bSSimo Sorce cache_put(&rscp->h, cd); 1326030d794bSSimo Sorce else 1327030d794bSSimo Sorce status = -ENOMEM; 1328030d794bSSimo Sorce return status; 1329030d794bSSimo Sorce } 1330030d794bSSimo Sorce 1331030d794bSSimo Sorce static int svcauth_gss_proxy_init(struct svc_rqst *rqstp, 1332438623a0SChuck Lever struct rpc_gss_wire_cred *gc) 1333030d794bSSimo Sorce { 1334030d794bSSimo Sorce struct kvec *resv = &rqstp->rq_res.head[0]; 1335030d794bSSimo Sorce struct xdr_netobj cli_handle; 1336030d794bSSimo Sorce struct gssp_upcall_data ud; 1337030d794bSSimo Sorce uint64_t handle; 1338030d794bSSimo Sorce int status; 1339030d794bSSimo Sorce int ret; 1340b8be5674SVasily Averin struct net *net = SVC_NET(rqstp); 1341030d794bSSimo Sorce struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1342030d794bSSimo Sorce 1343030d794bSSimo Sorce memset(&ud, 0, sizeof(ud)); 1344438623a0SChuck Lever ret = gss_read_proxy_verf(rqstp, gc, &ud.in_handle, &ud.in_token); 1345030d794bSSimo Sorce if (ret) 1346030d794bSSimo Sorce return ret; 1347030d794bSSimo Sorce 1348030d794bSSimo Sorce ret = SVC_CLOSE; 1349030d794bSSimo Sorce 1350030d794bSSimo Sorce /* Perform synchronous upcall to gss-proxy */ 1351030d794bSSimo Sorce status = gssp_accept_sec_context_upcall(net, &ud); 1352030d794bSSimo Sorce if (status) 1353030d794bSSimo Sorce goto out; 1354030d794bSSimo Sorce 135510b9d99aSChuck Lever trace_rpcgss_svc_accept_upcall(rqstp, ud.major_status, ud.minor_status); 1356030d794bSSimo Sorce 1357030d794bSSimo Sorce switch (ud.major_status) { 1358030d794bSSimo Sorce case GSS_S_CONTINUE_NEEDED: 1359030d794bSSimo Sorce cli_handle = ud.out_handle; 1360030d794bSSimo Sorce break; 1361030d794bSSimo Sorce case GSS_S_COMPLETE: 1362030d794bSSimo Sorce status = gss_proxy_save_rsc(sn->rsc_cache, &ud, &handle); 136328155524SChuck Lever if (status) 1364030d794bSSimo Sorce goto out; 1365030d794bSSimo Sorce cli_handle.data = (u8 *)&handle; 1366030d794bSSimo Sorce cli_handle.len = sizeof(handle); 1367030d794bSSimo Sorce break; 1368030d794bSSimo Sorce default: 1369030d794bSSimo Sorce goto out; 1370030d794bSSimo Sorce } 1371030d794bSSimo Sorce 1372030d794bSSimo Sorce /* Got an answer to the upcall; use it: */ 1373030d794bSSimo Sorce if (gss_write_init_verf(sn->rsc_cache, rqstp, 137428155524SChuck Lever &cli_handle, &ud.major_status)) 1375030d794bSSimo Sorce goto out; 1376030d794bSSimo Sorce if (gss_write_resv(resv, PAGE_SIZE, 1377030d794bSSimo Sorce &cli_handle, &ud.out_token, 137828155524SChuck Lever ud.major_status, ud.minor_status)) 1379030d794bSSimo Sorce goto out; 1380030d794bSSimo Sorce 1381030d794bSSimo Sorce ret = SVC_COMPLETE; 1382faca8978SChuck Lever svcxdr_init_encode(rqstp); 1383030d794bSSimo Sorce out: 13845866efa8SChuck Lever gss_free_in_token_pages(&ud.in_token); 1385030d794bSSimo Sorce gssp_free_upcall_data(&ud); 1386030d794bSSimo Sorce return ret; 1387030d794bSSimo Sorce } 1388030d794bSSimo Sorce 13890fdc2678SJeff Layton /* 13900fdc2678SJeff Layton * Try to set the sn->use_gss_proxy variable to a new value. We only allow 13910fdc2678SJeff Layton * it to be changed if it's currently undefined (-1). If it's any other value 13920fdc2678SJeff Layton * then return -EBUSY unless the type wouldn't have changed anyway. 13930fdc2678SJeff Layton */ 13940fdc2678SJeff Layton static int set_gss_proxy(struct net *net, int type) 13950fdc2678SJeff Layton { 13960fdc2678SJeff Layton struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 13970fdc2678SJeff Layton int ret; 13980fdc2678SJeff Layton 13990fdc2678SJeff Layton WARN_ON_ONCE(type != 0 && type != 1); 14000fdc2678SJeff Layton ret = cmpxchg(&sn->use_gss_proxy, -1, type); 14010fdc2678SJeff Layton if (ret != -1 && ret != type) 14020fdc2678SJeff Layton return -EBUSY; 14030fdc2678SJeff Layton return 0; 14040fdc2678SJeff Layton } 1405030d794bSSimo Sorce 1406030d794bSSimo Sorce static bool use_gss_proxy(struct net *net) 1407030d794bSSimo Sorce { 1408030d794bSSimo Sorce struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1409030d794bSSimo Sorce 14100fdc2678SJeff Layton /* If use_gss_proxy is still undefined, then try to disable it */ 14110fdc2678SJeff Layton if (sn->use_gss_proxy == -1) 14120fdc2678SJeff Layton set_gss_proxy(net, 0); 1413030d794bSSimo Sorce return sn->use_gss_proxy; 1414030d794bSSimo Sorce } 1415030d794bSSimo Sorce 14164ac5e7a6SChuck Lever static noinline_for_stack int 14174ac5e7a6SChuck Lever svcauth_gss_proc_init(struct svc_rqst *rqstp, struct rpc_gss_wire_cred *gc) 14184ac5e7a6SChuck Lever { 1419c020fa69SChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 1420c020fa69SChuck Lever u32 flavor, len; 1421c020fa69SChuck Lever void *body; 142220ebe927SChuck Lever 1423c020fa69SChuck Lever /* Call's verf field: */ 1424c020fa69SChuck Lever if (xdr_stream_decode_opaque_auth(xdr, &flavor, &body, &len) < 0) 1425c020fa69SChuck Lever return SVC_GARBAGE; 1426c020fa69SChuck Lever if (flavor != RPC_AUTH_NULL || len != 0) { 1427c020fa69SChuck Lever rqstp->rq_auth_stat = rpc_autherr_badverf; 142820ebe927SChuck Lever return SVC_DENIED; 1429c020fa69SChuck Lever } 143020ebe927SChuck Lever 143120ebe927SChuck Lever if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0) { 143220ebe927SChuck Lever rqstp->rq_auth_stat = rpc_autherr_badcred; 143320ebe927SChuck Lever return SVC_DENIED; 143420ebe927SChuck Lever } 143520ebe927SChuck Lever 14364ac5e7a6SChuck Lever if (!use_gss_proxy(SVC_NET(rqstp))) 14374ac5e7a6SChuck Lever return svcauth_gss_legacy_init(rqstp, gc); 14384ac5e7a6SChuck Lever return svcauth_gss_proxy_init(rqstp, gc); 14394ac5e7a6SChuck Lever } 14404ac5e7a6SChuck Lever 14410ff3bab5SJ. Bruce Fields #ifdef CONFIG_PROC_FS 14420ff3bab5SJ. Bruce Fields 1443030d794bSSimo Sorce static ssize_t write_gssp(struct file *file, const char __user *buf, 1444030d794bSSimo Sorce size_t count, loff_t *ppos) 1445030d794bSSimo Sorce { 1446359745d7SMuchun Song struct net *net = pde_data(file_inode(file)); 1447030d794bSSimo Sorce char tbuf[20]; 1448030d794bSSimo Sorce unsigned long i; 1449030d794bSSimo Sorce int res; 1450030d794bSSimo Sorce 1451030d794bSSimo Sorce if (*ppos || count > sizeof(tbuf)-1) 1452030d794bSSimo Sorce return -EINVAL; 1453030d794bSSimo Sorce if (copy_from_user(tbuf, buf, count)) 1454030d794bSSimo Sorce return -EFAULT; 1455030d794bSSimo Sorce 1456030d794bSSimo Sorce tbuf[count] = 0; 1457030d794bSSimo Sorce res = kstrtoul(tbuf, 0, &i); 1458030d794bSSimo Sorce if (res) 1459030d794bSSimo Sorce return res; 1460030d794bSSimo Sorce if (i != 1) 1461030d794bSSimo Sorce return -EINVAL; 1462a92e5eb1SJeff Layton res = set_gssp_clnt(net); 1463030d794bSSimo Sorce if (res) 1464030d794bSSimo Sorce return res; 1465a92e5eb1SJeff Layton res = set_gss_proxy(net, 1); 1466030d794bSSimo Sorce if (res) 1467030d794bSSimo Sorce return res; 1468030d794bSSimo Sorce return count; 1469030d794bSSimo Sorce } 1470030d794bSSimo Sorce 1471030d794bSSimo Sorce static ssize_t read_gssp(struct file *file, char __user *buf, 1472030d794bSSimo Sorce size_t count, loff_t *ppos) 1473030d794bSSimo Sorce { 1474359745d7SMuchun Song struct net *net = pde_data(file_inode(file)); 14751654a04cSJeff Layton struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1476030d794bSSimo Sorce unsigned long p = *ppos; 1477030d794bSSimo Sorce char tbuf[10]; 1478030d794bSSimo Sorce size_t len; 1479030d794bSSimo Sorce 14801654a04cSJeff Layton snprintf(tbuf, sizeof(tbuf), "%d\n", sn->use_gss_proxy); 1481030d794bSSimo Sorce len = strlen(tbuf); 1482030d794bSSimo Sorce if (p >= len) 1483030d794bSSimo Sorce return 0; 1484030d794bSSimo Sorce len -= p; 1485030d794bSSimo Sorce if (len > count) 1486030d794bSSimo Sorce len = count; 1487030d794bSSimo Sorce if (copy_to_user(buf, (void *)(tbuf+p), len)) 1488030d794bSSimo Sorce return -EFAULT; 1489030d794bSSimo Sorce *ppos += len; 1490030d794bSSimo Sorce return len; 1491030d794bSSimo Sorce } 1492030d794bSSimo Sorce 149397a32539SAlexey Dobriyan static const struct proc_ops use_gss_proxy_proc_ops = { 149497a32539SAlexey Dobriyan .proc_open = nonseekable_open, 149597a32539SAlexey Dobriyan .proc_write = write_gssp, 149697a32539SAlexey Dobriyan .proc_read = read_gssp, 1497030d794bSSimo Sorce }; 1498030d794bSSimo Sorce 1499030d794bSSimo Sorce static int create_use_gss_proxy_proc_entry(struct net *net) 1500030d794bSSimo Sorce { 1501030d794bSSimo Sorce struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1502030d794bSSimo Sorce struct proc_dir_entry **p = &sn->use_gssp_proc; 1503030d794bSSimo Sorce 1504030d794bSSimo Sorce sn->use_gss_proxy = -1; 1505d6444062SJoe Perches *p = proc_create_data("use-gss-proxy", S_IFREG | 0600, 1506030d794bSSimo Sorce sn->proc_net_rpc, 150797a32539SAlexey Dobriyan &use_gss_proxy_proc_ops, net); 1508030d794bSSimo Sorce if (!*p) 1509030d794bSSimo Sorce return -ENOMEM; 1510030d794bSSimo Sorce init_gssp_clnt(sn); 1511030d794bSSimo Sorce return 0; 1512030d794bSSimo Sorce } 1513030d794bSSimo Sorce 1514030d794bSSimo Sorce static void destroy_use_gss_proxy_proc_entry(struct net *net) 1515030d794bSSimo Sorce { 1516030d794bSSimo Sorce struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1517030d794bSSimo Sorce 1518030d794bSSimo Sorce if (sn->use_gssp_proc) { 1519030d794bSSimo Sorce remove_proc_entry("use-gss-proxy", sn->proc_net_rpc); 1520030d794bSSimo Sorce clear_gssp_clnt(sn); 1521030d794bSSimo Sorce } 1522030d794bSSimo Sorce } 15230ff3bab5SJ. Bruce Fields #else /* CONFIG_PROC_FS */ 15240ff3bab5SJ. Bruce Fields 15250ff3bab5SJ. Bruce Fields static int create_use_gss_proxy_proc_entry(struct net *net) 15260ff3bab5SJ. Bruce Fields { 15270ff3bab5SJ. Bruce Fields return 0; 15280ff3bab5SJ. Bruce Fields } 15290ff3bab5SJ. Bruce Fields 15300ff3bab5SJ. Bruce Fields static void destroy_use_gss_proxy_proc_entry(struct net *net) {} 1531030d794bSSimo Sorce 1532030d794bSSimo Sorce #endif /* CONFIG_PROC_FS */ 1533030d794bSSimo Sorce 153421fcd02bSJ. Bruce Fields /* 1535b0bc5347SChuck Lever * The Call's credential body should contain a struct rpc_gss_cred_t. 1536b0bc5347SChuck Lever * 1537b0bc5347SChuck Lever * RFC 2203 Section 5 1538b0bc5347SChuck Lever * 1539b0bc5347SChuck Lever * struct rpc_gss_cred_t { 1540b0bc5347SChuck Lever * union switch (unsigned int version) { 1541b0bc5347SChuck Lever * case RPCSEC_GSS_VERS_1: 1542b0bc5347SChuck Lever * struct { 1543b0bc5347SChuck Lever * rpc_gss_proc_t gss_proc; 1544b0bc5347SChuck Lever * unsigned int seq_num; 1545b0bc5347SChuck Lever * rpc_gss_service_t service; 1546b0bc5347SChuck Lever * opaque handle<>; 1547b0bc5347SChuck Lever * } rpc_gss_cred_vers_1_t; 1548b0bc5347SChuck Lever * } 1549b0bc5347SChuck Lever * }; 1550b0bc5347SChuck Lever */ 1551b0bc5347SChuck Lever static bool 1552b0bc5347SChuck Lever svcauth_gss_decode_credbody(struct xdr_stream *xdr, 1553b0bc5347SChuck Lever struct rpc_gss_wire_cred *gc, 1554b0bc5347SChuck Lever __be32 **rpcstart) 1555b0bc5347SChuck Lever { 1556b0bc5347SChuck Lever ssize_t handle_len; 1557b0bc5347SChuck Lever u32 body_len; 1558b0bc5347SChuck Lever __be32 *p; 1559b0bc5347SChuck Lever 1560b0bc5347SChuck Lever p = xdr_inline_decode(xdr, XDR_UNIT); 1561b0bc5347SChuck Lever if (!p) 1562b0bc5347SChuck Lever return false; 1563b0bc5347SChuck Lever /* 1564b0bc5347SChuck Lever * start of rpc packet is 7 u32's back from here: 1565b0bc5347SChuck Lever * xid direction rpcversion prog vers proc flavour 1566b0bc5347SChuck Lever */ 1567b0bc5347SChuck Lever *rpcstart = p - 7; 1568b0bc5347SChuck Lever body_len = be32_to_cpup(p); 1569b0bc5347SChuck Lever if (body_len > RPC_MAX_AUTH_SIZE) 1570b0bc5347SChuck Lever return false; 1571b0bc5347SChuck Lever 1572b0bc5347SChuck Lever /* struct rpc_gss_cred_t */ 1573b0bc5347SChuck Lever if (xdr_stream_decode_u32(xdr, &gc->gc_v) < 0) 1574b0bc5347SChuck Lever return false; 1575b0bc5347SChuck Lever if (xdr_stream_decode_u32(xdr, &gc->gc_proc) < 0) 1576b0bc5347SChuck Lever return false; 1577b0bc5347SChuck Lever if (xdr_stream_decode_u32(xdr, &gc->gc_seq) < 0) 1578b0bc5347SChuck Lever return false; 1579b0bc5347SChuck Lever if (xdr_stream_decode_u32(xdr, &gc->gc_svc) < 0) 1580b0bc5347SChuck Lever return false; 1581b0bc5347SChuck Lever handle_len = xdr_stream_decode_opaque_inline(xdr, 1582b0bc5347SChuck Lever (void **)&gc->gc_ctx.data, 1583b0bc5347SChuck Lever body_len); 1584b0bc5347SChuck Lever if (handle_len < 0) 1585b0bc5347SChuck Lever return false; 1586b0bc5347SChuck Lever if (body_len != XDR_UNIT * 5 + xdr_align_size(handle_len)) 1587b0bc5347SChuck Lever return false; 1588b0bc5347SChuck Lever 1589b0bc5347SChuck Lever gc->gc_ctx.len = handle_len; 1590b0bc5347SChuck Lever return true; 1591b0bc5347SChuck Lever } 1592b0bc5347SChuck Lever 1593b0bc5347SChuck Lever /** 1594b0bc5347SChuck Lever * svcauth_gss_accept - Decode and validate incoming RPC_AUTH_GSS credential 1595b0bc5347SChuck Lever * @rqstp: RPC transaction 1596b0bc5347SChuck Lever * 1597b0bc5347SChuck Lever * Return values: 1598b0bc5347SChuck Lever * %SVC_OK: Success 1599b0bc5347SChuck Lever * %SVC_COMPLETE: GSS context lifetime event 1600b0bc5347SChuck Lever * %SVC_DENIED: Credential or verifier is not valid 1601b0bc5347SChuck Lever * %SVC_GARBAGE: Failed to decode credential or verifier 1602b0bc5347SChuck Lever * %SVC_CLOSE: Temporary failure 1603b0bc5347SChuck Lever * 1604b0bc5347SChuck Lever * The rqstp->rq_auth_stat field is also set (see RFCs 2203 and 5531). 16051da177e4SLinus Torvalds */ 16061da177e4SLinus Torvalds static int 1607438623a0SChuck Lever svcauth_gss_accept(struct svc_rqst *rqstp) 16081da177e4SLinus Torvalds { 16091da177e4SLinus Torvalds struct kvec *resv = &rqstp->rq_res.head[0]; 16101da177e4SLinus Torvalds struct gss_svc_data *svcdata = rqstp->rq_auth_data; 1611b0bc5347SChuck Lever __be32 *rpcstart; 16121da177e4SLinus Torvalds struct rpc_gss_wire_cred *gc; 16131da177e4SLinus Torvalds struct rsc *rsci = NULL; 1614d8ed029dSAlexey Dobriyan __be32 *reject_stat = resv->iov_base + resv->iov_len; 16151da177e4SLinus Torvalds int ret; 1616b8be5674SVasily Averin struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id); 16171da177e4SLinus Torvalds 1618438623a0SChuck Lever rqstp->rq_auth_stat = rpc_autherr_badcred; 16191da177e4SLinus Torvalds if (!svcdata) 16201da177e4SLinus Torvalds svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL); 16211da177e4SLinus Torvalds if (!svcdata) 16221da177e4SLinus Torvalds goto auth_err; 16231da177e4SLinus Torvalds rqstp->rq_auth_data = svcdata; 16245b304bc5SJ.Bruce Fields svcdata->verf_start = NULL; 16251da177e4SLinus Torvalds svcdata->rsci = NULL; 16261da177e4SLinus Torvalds gc = &svcdata->clcred; 16271da177e4SLinus Torvalds 1628b0bc5347SChuck Lever if (!svcauth_gss_decode_credbody(&rqstp->rq_arg_stream, gc, &rpcstart)) 16291da177e4SLinus Torvalds goto auth_err; 1630b0bc5347SChuck Lever if (gc->gc_v != RPC_GSS_VERSION) 16311da177e4SLinus Torvalds goto auth_err; 16326734706bSChuck Lever 16331da177e4SLinus Torvalds switch (gc->gc_proc) { 16341da177e4SLinus Torvalds case RPC_GSS_PROC_INIT: 16351da177e4SLinus Torvalds case RPC_GSS_PROC_CONTINUE_INIT: 16366734706bSChuck Lever if (rqstp->rq_proc != 0) 16376734706bSChuck Lever goto auth_err; 16384ac5e7a6SChuck Lever return svcauth_gss_proc_init(rqstp, gc); 16391da177e4SLinus Torvalds case RPC_GSS_PROC_DESTROY: 16406734706bSChuck Lever if (rqstp->rq_proc != 0) 16416734706bSChuck Lever goto auth_err; 16426734706bSChuck Lever fallthrough; 16436734706bSChuck Lever case RPC_GSS_PROC_DATA: 1644438623a0SChuck Lever rqstp->rq_auth_stat = rpcsec_gsserr_credproblem; 1645a1db410dSStanislav Kinsbursky rsci = gss_svc_searchbyctx(sn->rsc_cache, &gc->gc_ctx); 16461da177e4SLinus Torvalds if (!rsci) 16471da177e4SLinus Torvalds goto auth_err; 16480653028eSChuck Lever switch (svcauth_gss_verify_header(rqstp, rsci, rpcstart, gc)) { 16491da177e4SLinus Torvalds case SVC_OK: 16501da177e4SLinus Torvalds break; 16511da177e4SLinus Torvalds case SVC_DENIED: 16521da177e4SLinus Torvalds goto auth_err; 16531da177e4SLinus Torvalds case SVC_DROP: 16541da177e4SLinus Torvalds goto drop; 16551da177e4SLinus Torvalds } 16561da177e4SLinus Torvalds break; 16571da177e4SLinus Torvalds default: 16586734706bSChuck Lever if (rqstp->rq_proc != 0) 16596734706bSChuck Lever goto auth_err; 1660438623a0SChuck Lever rqstp->rq_auth_stat = rpc_autherr_rejectedcred; 16611da177e4SLinus Torvalds goto auth_err; 16621da177e4SLinus Torvalds } 16631da177e4SLinus Torvalds 16641da177e4SLinus Torvalds /* now act upon the command: */ 16651da177e4SLinus Torvalds switch (gc->gc_proc) { 16661da177e4SLinus Torvalds case RPC_GSS_PROC_DESTROY: 1667c5e434c9SWei Yongjun if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq)) 1668c5e434c9SWei Yongjun goto auth_err; 16692b477c00SNeil Brown /* Delete the entry from the cache_list and call cache_put */ 16702b477c00SNeil Brown sunrpc_cache_unhash(sn->rsc_cache, &rsci->h); 16711da177e4SLinus Torvalds if (resv->iov_len + 4 > PAGE_SIZE) 16721da177e4SLinus Torvalds goto drop; 167376994313SAlexey Dobriyan svc_putnl(resv, RPC_SUCCESS); 16741da177e4SLinus Torvalds goto complete; 16751da177e4SLinus Torvalds case RPC_GSS_PROC_DATA: 1676438623a0SChuck Lever rqstp->rq_auth_stat = rpcsec_gsserr_ctxproblem; 16775b304bc5SJ.Bruce Fields svcdata->verf_start = resv->iov_base + resv->iov_len; 16781da177e4SLinus Torvalds if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq)) 16791da177e4SLinus Torvalds goto auth_err; 16801da177e4SLinus Torvalds rqstp->rq_cred = rsci->cred; 16811da177e4SLinus Torvalds get_group_info(rsci->cred.cr_group_info); 1682438623a0SChuck Lever rqstp->rq_auth_stat = rpc_autherr_badcred; 16831da177e4SLinus Torvalds switch (gc->gc_svc) { 16841da177e4SLinus Torvalds case RPC_GSS_SVC_NONE: 1685faca8978SChuck Lever svcxdr_init_encode(rqstp); 16861da177e4SLinus Torvalds break; 16871da177e4SLinus Torvalds case RPC_GSS_SVC_INTEGRITY: 1688*7bb0dfb2SChuck Lever svcxdr_init_encode(rqstp); 1689*7bb0dfb2SChuck Lever /* placeholders for body length and seq. number: */ 1690*7bb0dfb2SChuck Lever xdr_reserve_space(&rqstp->rq_res_stream, XDR_UNIT * 2); 1691b68e4c5cSChuck Lever if (svcauth_gss_unwrap_integ(rqstp, gc->gc_seq, 1692b68e4c5cSChuck Lever rsci->mechctx)) 1693dd35210eSHarshula Jayasuriya goto garbage_args; 1694*7bb0dfb2SChuck Lever svcxdr_set_auth_slack(rqstp, RPC_MAX_AUTH_SIZE); 1695b620754bSJ. Bruce Fields break; 1696b620754bSJ. Bruce Fields case RPC_GSS_SVC_PRIVACY: 1697*7bb0dfb2SChuck Lever svcxdr_init_encode(rqstp); 1698*7bb0dfb2SChuck Lever /* placeholders for body length and seq. number: */ 1699*7bb0dfb2SChuck Lever xdr_reserve_space(&rqstp->rq_res_stream, XDR_UNIT * 2); 170042140718SChuck Lever if (svcauth_gss_unwrap_priv(rqstp, gc->gc_seq, 170142140718SChuck Lever rsci->mechctx)) 1702dd35210eSHarshula Jayasuriya goto garbage_args; 1703*7bb0dfb2SChuck Lever svcxdr_set_auth_slack(rqstp, RPC_MAX_AUTH_SIZE * 2); 17047c9fdcfbSJ. Bruce Fields break; 17051da177e4SLinus Torvalds default: 17061da177e4SLinus Torvalds goto auth_err; 17071da177e4SLinus Torvalds } 17081da177e4SLinus Torvalds svcdata->rsci = rsci; 17091da177e4SLinus Torvalds cache_get(&rsci->h); 1710d5497fc6SJ. Bruce Fields rqstp->rq_cred.cr_flavor = gss_svc_to_pseudoflavor( 171183523d08SChuck Lever rsci->mechctx->mech_type, 171283523d08SChuck Lever GSS_C_QOP_DEFAULT, 171383523d08SChuck Lever gc->gc_svc); 17141da177e4SLinus Torvalds ret = SVC_OK; 171510b9d99aSChuck Lever trace_rpcgss_svc_authenticate(rqstp, gc); 17161da177e4SLinus Torvalds goto out; 17171da177e4SLinus Torvalds } 1718dd35210eSHarshula Jayasuriya garbage_args: 1719dd35210eSHarshula Jayasuriya ret = SVC_GARBAGE; 1720dd35210eSHarshula Jayasuriya goto out; 17211da177e4SLinus Torvalds auth_err: 172221fcd02bSJ. Bruce Fields /* Restore write pointer to its original value: */ 17231da177e4SLinus Torvalds xdr_ressize_check(rqstp, reject_stat); 17241da177e4SLinus Torvalds ret = SVC_DENIED; 17251da177e4SLinus Torvalds goto out; 17261da177e4SLinus Torvalds complete: 17271da177e4SLinus Torvalds ret = SVC_COMPLETE; 17281da177e4SLinus Torvalds goto out; 17291da177e4SLinus Torvalds drop: 17304d712ef1SChuck Lever ret = SVC_CLOSE; 17311da177e4SLinus Torvalds out: 17321da177e4SLinus Torvalds if (rsci) 1733a1db410dSStanislav Kinsbursky cache_put(&rsci->h, sn->rsc_cache); 17341da177e4SLinus Torvalds return ret; 17351da177e4SLinus Torvalds } 17361da177e4SLinus Torvalds 1737cfbdbab0SAl Viro static __be32 * 173899d074d6SChuck Lever svcauth_gss_prepare_to_wrap(struct svc_rqst *rqstp, struct gss_svc_data *gsd) 17393c15a486SJ.Bruce Fields { 174099d074d6SChuck Lever struct xdr_buf *resbuf = &rqstp->rq_res; 1741cfbdbab0SAl Viro __be32 *p; 1742cfbdbab0SAl Viro u32 verf_len; 17433c15a486SJ.Bruce Fields 17445b304bc5SJ.Bruce Fields p = gsd->verf_start; 17455b304bc5SJ.Bruce Fields gsd->verf_start = NULL; 17465b304bc5SJ.Bruce Fields 174799d074d6SChuck Lever /* AUTH_ERROR replies are not wrapped. */ 174899d074d6SChuck Lever if (rqstp->rq_auth_stat != rpc_auth_ok) 17495b304bc5SJ.Bruce Fields return NULL; 175099d074d6SChuck Lever 17515b304bc5SJ.Bruce Fields /* Skip the verifier: */ 17525b304bc5SJ.Bruce Fields p += 1; 17535b304bc5SJ.Bruce Fields verf_len = ntohl(*p++); 17545b304bc5SJ.Bruce Fields p += XDR_QUADLEN(verf_len); 17553c15a486SJ.Bruce Fields /* move accept_stat to right place: */ 17563c15a486SJ.Bruce Fields memcpy(p, p + 2, 4); 17575b304bc5SJ.Bruce Fields /* Also don't wrap if the accept stat is nonzero: */ 17583c15a486SJ.Bruce Fields if (*p != rpc_success) { 17593c15a486SJ.Bruce Fields resbuf->head[0].iov_len -= 2 * 4; 17603c15a486SJ.Bruce Fields return NULL; 17613c15a486SJ.Bruce Fields } 17623c15a486SJ.Bruce Fields p++; 17633c15a486SJ.Bruce Fields return p; 17643c15a486SJ.Bruce Fields } 17653c15a486SJ.Bruce Fields 17660adaddd3SChuck Lever /* 17670adaddd3SChuck Lever * RFC 2203, Section 5.3.2.2 17680adaddd3SChuck Lever * 17690adaddd3SChuck Lever * struct rpc_gss_integ_data { 17700adaddd3SChuck Lever * opaque databody_integ<>; 17710adaddd3SChuck Lever * opaque checksum<>; 17720adaddd3SChuck Lever * }; 17730adaddd3SChuck Lever * 17740adaddd3SChuck Lever * struct rpc_gss_data_t { 17750adaddd3SChuck Lever * unsigned int seq_num; 17760adaddd3SChuck Lever * proc_req_arg_t arg; 17770adaddd3SChuck Lever * }; 17780adaddd3SChuck Lever * 17790adaddd3SChuck Lever * The RPC Reply message has already been XDR-encoded. rq_res_stream 17800adaddd3SChuck Lever * is now positioned so that the checksum can be written just past 17810adaddd3SChuck Lever * the RPC Reply message. 17820adaddd3SChuck Lever */ 17830adaddd3SChuck Lever static int svcauth_gss_wrap_integ(struct svc_rqst *rqstp) 17841da177e4SLinus Torvalds { 17850adaddd3SChuck Lever struct gss_svc_data *gsd = rqstp->rq_auth_data; 17867702378aSChuck Lever struct xdr_stream *xdr = &rqstp->rq_res_stream; 17871da177e4SLinus Torvalds struct rpc_gss_wire_cred *gc = &gsd->clcred; 17887702378aSChuck Lever struct xdr_buf *buf = xdr->buf; 17890adaddd3SChuck Lever struct xdr_buf databody_integ; 17900adaddd3SChuck Lever struct xdr_netobj checksum; 179115d8f808SChuck Lever u32 offset, len, maj_stat; 1792d8ed029dSAlexey Dobriyan __be32 *p; 17931da177e4SLinus Torvalds 179499d074d6SChuck Lever p = svcauth_gss_prepare_to_wrap(rqstp, gsd); 17953c15a486SJ.Bruce Fields if (p == NULL) 17961da177e4SLinus Torvalds goto out; 17977702378aSChuck Lever 17980adaddd3SChuck Lever offset = (u8 *)(p + 1) - (u8 *)buf->head[0].iov_base; 17990adaddd3SChuck Lever len = buf->len - offset; 180015d8f808SChuck Lever if (xdr_buf_subsegment(buf, &databody_integ, offset, len)) 180115d8f808SChuck Lever goto wrap_failed; 18027702378aSChuck Lever /* Buffer space for these has already been reserved in 18037702378aSChuck Lever * svcauth_gss_accept(). */ 18047702378aSChuck Lever *p++ = cpu_to_be32(len); 18057702378aSChuck Lever *p = cpu_to_be32(gc->gc_seq); 1806d91f0323SChuck Lever 1807d91f0323SChuck Lever checksum.data = gsd->gsd_scratch; 180815d8f808SChuck Lever maj_stat = gss_get_mic(gsd->rsci->mechctx, &databody_integ, &checksum); 180915d8f808SChuck Lever if (maj_stat != GSS_S_COMPLETE) 181015d8f808SChuck Lever goto bad_mic; 18117702378aSChuck Lever 18127702378aSChuck Lever if (xdr_stream_encode_opaque(xdr, checksum.data, checksum.len) < 0) 181315d8f808SChuck Lever goto wrap_failed; 18147702378aSChuck Lever xdr_commit_encode(xdr); 18157702378aSChuck Lever 1816e142ede8SJ. Bruce Fields out: 181715d8f808SChuck Lever return 0; 18187702378aSChuck Lever 181915d8f808SChuck Lever bad_mic: 182015d8f808SChuck Lever trace_rpcgss_svc_get_mic(rqstp, maj_stat); 1821ba8b13e5SChuck Lever return -EINVAL; 182215d8f808SChuck Lever wrap_failed: 1823ba8b13e5SChuck Lever trace_rpcgss_svc_wrap_failed(rqstp); 182415d8f808SChuck Lever return -EINVAL; 1825e142ede8SJ. Bruce Fields } 1826e142ede8SJ. Bruce Fields 18277b135c65SChuck Lever /* 18287b135c65SChuck Lever * RFC 2203, Section 5.3.2.3 18297b135c65SChuck Lever * 18307b135c65SChuck Lever * struct rpc_gss_priv_data { 18317b135c65SChuck Lever * opaque databody_priv<> 18327b135c65SChuck Lever * }; 18337b135c65SChuck Lever * 18347b135c65SChuck Lever * struct rpc_gss_data_t { 18357b135c65SChuck Lever * unsigned int seq_num; 18367b135c65SChuck Lever * proc_req_arg_t arg; 18377b135c65SChuck Lever * }; 1838eb1b780fSChuck Lever * 1839eb1b780fSChuck Lever * gss_wrap() expands the size of the RPC message payload in the 1840eb1b780fSChuck Lever * response buffer. The main purpose of svcauth_gss_wrap_priv() 1841eb1b780fSChuck Lever * is to ensure there is adequate space in the response buffer to 1842eb1b780fSChuck Lever * avoid overflow during the wrap. 18437b135c65SChuck Lever */ 18447b135c65SChuck Lever static int svcauth_gss_wrap_priv(struct svc_rqst *rqstp) 18457c9fdcfbSJ. Bruce Fields { 18467b135c65SChuck Lever struct gss_svc_data *gsd = rqstp->rq_auth_data; 18477c9fdcfbSJ. Bruce Fields struct rpc_gss_wire_cred *gc = &gsd->clcred; 18487b135c65SChuck Lever struct xdr_buf *buf = &rqstp->rq_res; 1849a84cfbcdSChuck Lever struct kvec *head = buf->head; 1850a84cfbcdSChuck Lever struct kvec *tail = buf->tail; 1851ba8b13e5SChuck Lever u32 offset, pad, maj_stat; 18527b135c65SChuck Lever __be32 *p, *lenp; 18537c9fdcfbSJ. Bruce Fields 185499d074d6SChuck Lever p = svcauth_gss_prepare_to_wrap(rqstp, gsd); 18553c15a486SJ.Bruce Fields if (p == NULL) 18567c9fdcfbSJ. Bruce Fields return 0; 1857ba8b13e5SChuck Lever 18587b135c65SChuck Lever lenp = p++; 1859a84cfbcdSChuck Lever offset = (u8 *)p - (u8 *)head->iov_base; 1860eb1b780fSChuck Lever /* Buffer space for this field has already been reserved 1861eb1b780fSChuck Lever * in svcauth_gss_accept(). */ 1862eb1b780fSChuck Lever *p = cpu_to_be32(gc->gc_seq); 18637561042fSKevin Coffman 18647561042fSKevin Coffman /* 18657561042fSKevin Coffman * If there is currently tail data, make sure there is 18667561042fSKevin Coffman * room for the head, tail, and 2 * RPC_MAX_AUTH_SIZE in 18677561042fSKevin Coffman * the page, and move the current tail data such that 18687561042fSKevin Coffman * there is RPC_MAX_AUTH_SIZE slack space available in 18697561042fSKevin Coffman * both the head and tail. 18707561042fSKevin Coffman */ 1871a84cfbcdSChuck Lever if (tail->iov_base) { 1872a84cfbcdSChuck Lever if (tail->iov_base >= head->iov_base + PAGE_SIZE) 1873ba8b13e5SChuck Lever goto wrap_failed; 1874a84cfbcdSChuck Lever if (tail->iov_base < head->iov_base) 1875ba8b13e5SChuck Lever goto wrap_failed; 1876a84cfbcdSChuck Lever if (tail->iov_len + head->iov_len 18777c9fdcfbSJ. Bruce Fields + 2 * RPC_MAX_AUTH_SIZE > PAGE_SIZE) 1878ba8b13e5SChuck Lever goto wrap_failed; 1879a84cfbcdSChuck Lever memmove(tail->iov_base + RPC_MAX_AUTH_SIZE, tail->iov_base, 1880a84cfbcdSChuck Lever tail->iov_len); 1881a84cfbcdSChuck Lever tail->iov_base += RPC_MAX_AUTH_SIZE; 18827c9fdcfbSJ. Bruce Fields } 18837561042fSKevin Coffman /* 18847561042fSKevin Coffman * If there is no current tail data, make sure there is 18857561042fSKevin Coffman * room for the head data, and 2 * RPC_MAX_AUTH_SIZE in the 18867561042fSKevin Coffman * allotted page, and set up tail information such that there 18877561042fSKevin Coffman * is RPC_MAX_AUTH_SIZE slack space available in both the 18887561042fSKevin Coffman * head and tail. 18897561042fSKevin Coffman */ 1890a84cfbcdSChuck Lever if (!tail->iov_base) { 1891a84cfbcdSChuck Lever if (head->iov_len + 2 * RPC_MAX_AUTH_SIZE > PAGE_SIZE) 1892ba8b13e5SChuck Lever goto wrap_failed; 1893a84cfbcdSChuck Lever tail->iov_base = head->iov_base 1894a84cfbcdSChuck Lever + head->iov_len + RPC_MAX_AUTH_SIZE; 1895a84cfbcdSChuck Lever tail->iov_len = 0; 18967c9fdcfbSJ. Bruce Fields } 1897ba8b13e5SChuck Lever 1898ba8b13e5SChuck Lever maj_stat = gss_wrap(gsd->rsci->mechctx, offset, buf, buf->pages); 1899ba8b13e5SChuck Lever if (maj_stat != GSS_S_COMPLETE) 1900ba8b13e5SChuck Lever goto bad_wrap; 1901ba8b13e5SChuck Lever 1902eb1b780fSChuck Lever *lenp = cpu_to_be32(buf->len - offset); 1903eb1b780fSChuck Lever pad = xdr_pad_size(buf->len - offset); 1904a84cfbcdSChuck Lever p = (__be32 *)(tail->iov_base + tail->iov_len); 19057c9fdcfbSJ. Bruce Fields memset(p, 0, pad); 1906a84cfbcdSChuck Lever tail->iov_len += pad; 19077b135c65SChuck Lever buf->len += pad; 19087b135c65SChuck Lever 19097c9fdcfbSJ. Bruce Fields return 0; 1910ba8b13e5SChuck Lever wrap_failed: 1911ba8b13e5SChuck Lever trace_rpcgss_svc_wrap_failed(rqstp); 1912ba8b13e5SChuck Lever return -EINVAL; 1913ba8b13e5SChuck Lever bad_wrap: 1914ba8b13e5SChuck Lever trace_rpcgss_svc_wrap(rqstp, maj_stat); 1915ba8b13e5SChuck Lever return -ENOMEM; 19167c9fdcfbSJ. Bruce Fields } 19177c9fdcfbSJ. Bruce Fields 19185a929383SChuck Lever /** 19195a929383SChuck Lever * svcauth_gss_release - Wrap payload and release resources 19205a929383SChuck Lever * @rqstp: RPC transaction context 19215a929383SChuck Lever * 19225a929383SChuck Lever * Return values: 19235a929383SChuck Lever * %0: the Reply is ready to be sent 19245a929383SChuck Lever * %-ENOMEM: failed to allocate memory 19255a929383SChuck Lever * %-EINVAL: encoding error 19265a929383SChuck Lever * 19275a929383SChuck Lever * XXX: These return values do not match the return values documented 19285a929383SChuck Lever * for the auth_ops ->release method in linux/sunrpc/svcauth.h. 19295a929383SChuck Lever */ 1930e142ede8SJ. Bruce Fields static int 1931e142ede8SJ. Bruce Fields svcauth_gss_release(struct svc_rqst *rqstp) 1932e142ede8SJ. Bruce Fields { 1933b8be5674SVasily Averin struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id); 19345a929383SChuck Lever struct gss_svc_data *gsd = rqstp->rq_auth_data; 19355a929383SChuck Lever struct rpc_gss_wire_cred *gc; 19365a929383SChuck Lever int stat; 1937e142ede8SJ. Bruce Fields 19380ddc9423SJ. Bruce Fields if (!gsd) 19390ddc9423SJ. Bruce Fields goto out; 19400ddc9423SJ. Bruce Fields gc = &gsd->clcred; 1941e142ede8SJ. Bruce Fields if (gc->gc_proc != RPC_GSS_PROC_DATA) 1942e142ede8SJ. Bruce Fields goto out; 1943e142ede8SJ. Bruce Fields /* Release can be called twice, but we only wrap once. */ 19445b304bc5SJ.Bruce Fields if (gsd->verf_start == NULL) 1945e142ede8SJ. Bruce Fields goto out; 19465a929383SChuck Lever 1947e142ede8SJ. Bruce Fields switch (gc->gc_svc) { 1948e142ede8SJ. Bruce Fields case RPC_GSS_SVC_NONE: 1949e142ede8SJ. Bruce Fields break; 1950e142ede8SJ. Bruce Fields case RPC_GSS_SVC_INTEGRITY: 19510adaddd3SChuck Lever stat = svcauth_gss_wrap_integ(rqstp); 19527c9fdcfbSJ. Bruce Fields if (stat) 19537c9fdcfbSJ. Bruce Fields goto out_err; 19541da177e4SLinus Torvalds break; 19551da177e4SLinus Torvalds case RPC_GSS_SVC_PRIVACY: 19567b135c65SChuck Lever stat = svcauth_gss_wrap_priv(rqstp); 19577c9fdcfbSJ. Bruce Fields if (stat) 19587c9fdcfbSJ. Bruce Fields goto out_err; 19597c9fdcfbSJ. Bruce Fields break; 1960eac81736SWei Yongjun /* 1961eac81736SWei Yongjun * For any other gc_svc value, svcauth_gss_accept() already set 1962eac81736SWei Yongjun * the auth_error appropriately; just fall through: 1963eac81736SWei Yongjun */ 19641da177e4SLinus Torvalds } 19651da177e4SLinus Torvalds 19661da177e4SLinus Torvalds out: 19671da177e4SLinus Torvalds stat = 0; 19681da177e4SLinus Torvalds out_err: 19691da177e4SLinus Torvalds if (rqstp->rq_client) 19701da177e4SLinus Torvalds auth_domain_put(rqstp->rq_client); 19711da177e4SLinus Torvalds rqstp->rq_client = NULL; 19723ab4d8b1SJ. Bruce Fields if (rqstp->rq_gssclient) 19733ab4d8b1SJ. Bruce Fields auth_domain_put(rqstp->rq_gssclient); 19743ab4d8b1SJ. Bruce Fields rqstp->rq_gssclient = NULL; 19751da177e4SLinus Torvalds if (rqstp->rq_cred.cr_group_info) 19761da177e4SLinus Torvalds put_group_info(rqstp->rq_cred.cr_group_info); 19771da177e4SLinus Torvalds rqstp->rq_cred.cr_group_info = NULL; 19780ddc9423SJ. Bruce Fields if (gsd && gsd->rsci) { 1979a1db410dSStanislav Kinsbursky cache_put(&gsd->rsci->h, sn->rsc_cache); 19801da177e4SLinus Torvalds gsd->rsci = NULL; 19810ddc9423SJ. Bruce Fields } 19821da177e4SLinus Torvalds return stat; 19831da177e4SLinus Torvalds } 19841da177e4SLinus Torvalds 19851da177e4SLinus Torvalds static void 1986608a0ab2STrond Myklebust svcauth_gss_domain_release_rcu(struct rcu_head *head) 19871da177e4SLinus Torvalds { 1988608a0ab2STrond Myklebust struct auth_domain *dom = container_of(head, struct auth_domain, rcu_head); 19891da177e4SLinus Torvalds struct gss_domain *gd = container_of(dom, struct gss_domain, h); 19901da177e4SLinus Torvalds 19911da177e4SLinus Torvalds kfree(dom->name); 19921da177e4SLinus Torvalds kfree(gd); 19931da177e4SLinus Torvalds } 19941da177e4SLinus Torvalds 1995608a0ab2STrond Myklebust static void 1996608a0ab2STrond Myklebust svcauth_gss_domain_release(struct auth_domain *dom) 1997608a0ab2STrond Myklebust { 1998608a0ab2STrond Myklebust call_rcu(&dom->rcu_head, svcauth_gss_domain_release_rcu); 1999608a0ab2STrond Myklebust } 2000608a0ab2STrond Myklebust 20011da177e4SLinus Torvalds static struct auth_ops svcauthops_gss = { 20021da177e4SLinus Torvalds .name = "rpcsec_gss", 20031da177e4SLinus Torvalds .owner = THIS_MODULE, 20041da177e4SLinus Torvalds .flavour = RPC_AUTH_GSS, 20051da177e4SLinus Torvalds .accept = svcauth_gss_accept, 20061da177e4SLinus Torvalds .release = svcauth_gss_release, 20071da177e4SLinus Torvalds .domain_release = svcauth_gss_domain_release, 20081da177e4SLinus Torvalds .set_client = svcauth_gss_set_client, 20091da177e4SLinus Torvalds }; 20101da177e4SLinus Torvalds 2011a1db410dSStanislav Kinsbursky static int rsi_cache_create_net(struct net *net) 2012a1db410dSStanislav Kinsbursky { 2013a1db410dSStanislav Kinsbursky struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 2014a1db410dSStanislav Kinsbursky struct cache_detail *cd; 2015a1db410dSStanislav Kinsbursky int err; 2016a1db410dSStanislav Kinsbursky 2017a1db410dSStanislav Kinsbursky cd = cache_create_net(&rsi_cache_template, net); 2018a1db410dSStanislav Kinsbursky if (IS_ERR(cd)) 2019a1db410dSStanislav Kinsbursky return PTR_ERR(cd); 2020a1db410dSStanislav Kinsbursky err = cache_register_net(cd, net); 2021a1db410dSStanislav Kinsbursky if (err) { 2022a1db410dSStanislav Kinsbursky cache_destroy_net(cd, net); 2023a1db410dSStanislav Kinsbursky return err; 2024a1db410dSStanislav Kinsbursky } 2025a1db410dSStanislav Kinsbursky sn->rsi_cache = cd; 2026a1db410dSStanislav Kinsbursky return 0; 2027a1db410dSStanislav Kinsbursky } 2028a1db410dSStanislav Kinsbursky 2029a1db410dSStanislav Kinsbursky static void rsi_cache_destroy_net(struct net *net) 2030a1db410dSStanislav Kinsbursky { 2031a1db410dSStanislav Kinsbursky struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 2032a1db410dSStanislav Kinsbursky struct cache_detail *cd = sn->rsi_cache; 2033a1db410dSStanislav Kinsbursky 2034a1db410dSStanislav Kinsbursky sn->rsi_cache = NULL; 2035a1db410dSStanislav Kinsbursky cache_purge(cd); 2036a1db410dSStanislav Kinsbursky cache_unregister_net(cd, net); 2037a1db410dSStanislav Kinsbursky cache_destroy_net(cd, net); 2038a1db410dSStanislav Kinsbursky } 2039a1db410dSStanislav Kinsbursky 2040a1db410dSStanislav Kinsbursky static int rsc_cache_create_net(struct net *net) 2041a1db410dSStanislav Kinsbursky { 2042a1db410dSStanislav Kinsbursky struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 2043a1db410dSStanislav Kinsbursky struct cache_detail *cd; 2044a1db410dSStanislav Kinsbursky int err; 2045a1db410dSStanislav Kinsbursky 2046a1db410dSStanislav Kinsbursky cd = cache_create_net(&rsc_cache_template, net); 2047a1db410dSStanislav Kinsbursky if (IS_ERR(cd)) 2048a1db410dSStanislav Kinsbursky return PTR_ERR(cd); 2049a1db410dSStanislav Kinsbursky err = cache_register_net(cd, net); 2050a1db410dSStanislav Kinsbursky if (err) { 2051a1db410dSStanislav Kinsbursky cache_destroy_net(cd, net); 2052a1db410dSStanislav Kinsbursky return err; 2053a1db410dSStanislav Kinsbursky } 2054a1db410dSStanislav Kinsbursky sn->rsc_cache = cd; 2055a1db410dSStanislav Kinsbursky return 0; 2056a1db410dSStanislav Kinsbursky } 2057a1db410dSStanislav Kinsbursky 2058a1db410dSStanislav Kinsbursky static void rsc_cache_destroy_net(struct net *net) 2059a1db410dSStanislav Kinsbursky { 2060a1db410dSStanislav Kinsbursky struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 2061a1db410dSStanislav Kinsbursky struct cache_detail *cd = sn->rsc_cache; 2062a1db410dSStanislav Kinsbursky 2063a1db410dSStanislav Kinsbursky sn->rsc_cache = NULL; 2064a1db410dSStanislav Kinsbursky cache_purge(cd); 2065a1db410dSStanislav Kinsbursky cache_unregister_net(cd, net); 2066a1db410dSStanislav Kinsbursky cache_destroy_net(cd, net); 2067a1db410dSStanislav Kinsbursky } 2068a1db410dSStanislav Kinsbursky 2069a1db410dSStanislav Kinsbursky int 2070a1db410dSStanislav Kinsbursky gss_svc_init_net(struct net *net) 2071a1db410dSStanislav Kinsbursky { 2072a1db410dSStanislav Kinsbursky int rv; 2073a1db410dSStanislav Kinsbursky 2074a1db410dSStanislav Kinsbursky rv = rsc_cache_create_net(net); 2075a1db410dSStanislav Kinsbursky if (rv) 2076a1db410dSStanislav Kinsbursky return rv; 2077a1db410dSStanislav Kinsbursky rv = rsi_cache_create_net(net); 2078a1db410dSStanislav Kinsbursky if (rv) 2079a1db410dSStanislav Kinsbursky goto out1; 2080030d794bSSimo Sorce rv = create_use_gss_proxy_proc_entry(net); 2081030d794bSSimo Sorce if (rv) 2082030d794bSSimo Sorce goto out2; 2083a1db410dSStanislav Kinsbursky return 0; 2084030d794bSSimo Sorce out2: 20855a475344SJ. Bruce Fields rsi_cache_destroy_net(net); 2086a1db410dSStanislav Kinsbursky out1: 2087a1db410dSStanislav Kinsbursky rsc_cache_destroy_net(net); 2088a1db410dSStanislav Kinsbursky return rv; 2089a1db410dSStanislav Kinsbursky } 2090a1db410dSStanislav Kinsbursky 2091a1db410dSStanislav Kinsbursky void 2092a1db410dSStanislav Kinsbursky gss_svc_shutdown_net(struct net *net) 2093a1db410dSStanislav Kinsbursky { 2094030d794bSSimo Sorce destroy_use_gss_proxy_proc_entry(net); 2095a1db410dSStanislav Kinsbursky rsi_cache_destroy_net(net); 2096a1db410dSStanislav Kinsbursky rsc_cache_destroy_net(net); 2097a1db410dSStanislav Kinsbursky } 2098a1db410dSStanislav Kinsbursky 20991da177e4SLinus Torvalds int 21001da177e4SLinus Torvalds gss_svc_init(void) 21011da177e4SLinus Torvalds { 2102a1db410dSStanislav Kinsbursky return svc_auth_register(RPC_AUTH_GSS, &svcauthops_gss); 21031da177e4SLinus Torvalds } 21041da177e4SLinus Torvalds 21051da177e4SLinus Torvalds void 21061da177e4SLinus Torvalds gss_svc_shutdown(void) 21071da177e4SLinus Torvalds { 21081da177e4SLinus Torvalds svc_auth_unregister(RPC_AUTH_GSS); 21091da177e4SLinus Torvalds } 2110