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; 74db1d6165SChuck Lever u32 gsd_databody_offset; 754dd9daa9SChuck Lever struct rsc *rsci; 764dd9daa9SChuck Lever 774dd9daa9SChuck Lever /* for temporary results */ 78b2f42f1dSChuck Lever __be32 gsd_seq_num; 794dd9daa9SChuck Lever u8 gsd_scratch[GSS_SCRATCH_SIZE]; 804dd9daa9SChuck Lever }; 81a1db410dSStanislav Kinsbursky 821da177e4SLinus Torvalds /* The rpcsec_init cache is used for mapping RPCSEC_GSS_{,CONT_}INIT requests 831da177e4SLinus Torvalds * into replies. 841da177e4SLinus Torvalds * 851da177e4SLinus Torvalds * Key is context handle (\x if empty) and gss_token. 861da177e4SLinus Torvalds * Content is major_status minor_status (integers) context_handle, reply_token. 871da177e4SLinus Torvalds * 881da177e4SLinus Torvalds */ 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds static int netobj_equal(struct xdr_netobj *a, struct xdr_netobj *b) 911da177e4SLinus Torvalds { 921da177e4SLinus Torvalds return a->len == b->len && 0 == memcmp(a->data, b->data, a->len); 931da177e4SLinus Torvalds } 941da177e4SLinus Torvalds 951da177e4SLinus Torvalds #define RSI_HASHBITS 6 961da177e4SLinus Torvalds #define RSI_HASHMAX (1<<RSI_HASHBITS) 971da177e4SLinus Torvalds 981da177e4SLinus Torvalds struct rsi { 991da177e4SLinus Torvalds struct cache_head h; 1001da177e4SLinus Torvalds struct xdr_netobj in_handle, in_token; 1011da177e4SLinus Torvalds struct xdr_netobj out_handle, out_token; 1021da177e4SLinus Torvalds int major_status, minor_status; 1036d1616b2STrond Myklebust struct rcu_head rcu_head; 1041da177e4SLinus Torvalds }; 1051da177e4SLinus Torvalds 106a1db410dSStanislav Kinsbursky static struct rsi *rsi_update(struct cache_detail *cd, struct rsi *new, struct rsi *old); 107a1db410dSStanislav Kinsbursky static struct rsi *rsi_lookup(struct cache_detail *cd, struct rsi *item); 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds static void rsi_free(struct rsi *rsii) 1101da177e4SLinus Torvalds { 1111da177e4SLinus Torvalds kfree(rsii->in_handle.data); 1121da177e4SLinus Torvalds kfree(rsii->in_token.data); 1131da177e4SLinus Torvalds kfree(rsii->out_handle.data); 1141da177e4SLinus Torvalds kfree(rsii->out_token.data); 1151da177e4SLinus Torvalds } 1161da177e4SLinus Torvalds 1176d1616b2STrond Myklebust static void rsi_free_rcu(struct rcu_head *head) 1186d1616b2STrond Myklebust { 1196d1616b2STrond Myklebust struct rsi *rsii = container_of(head, struct rsi, rcu_head); 1206d1616b2STrond Myklebust 1216d1616b2STrond Myklebust rsi_free(rsii); 1226d1616b2STrond Myklebust kfree(rsii); 1236d1616b2STrond Myklebust } 1246d1616b2STrond Myklebust 125baab935fSNeilBrown static void rsi_put(struct kref *ref) 1261da177e4SLinus Torvalds { 127baab935fSNeilBrown struct rsi *rsii = container_of(ref, struct rsi, h.ref); 1286d1616b2STrond Myklebust 1296d1616b2STrond Myklebust call_rcu(&rsii->rcu_head, rsi_free_rcu); 1301da177e4SLinus Torvalds } 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds static inline int rsi_hash(struct rsi *item) 1331da177e4SLinus Torvalds { 1341da177e4SLinus Torvalds return hash_mem(item->in_handle.data, item->in_handle.len, RSI_HASHBITS) 1351da177e4SLinus Torvalds ^ hash_mem(item->in_token.data, item->in_token.len, RSI_HASHBITS); 1361da177e4SLinus Torvalds } 1371da177e4SLinus Torvalds 138d4d11ea9SNeilBrown static int rsi_match(struct cache_head *a, struct cache_head *b) 1391da177e4SLinus Torvalds { 140d4d11ea9SNeilBrown struct rsi *item = container_of(a, struct rsi, h); 141d4d11ea9SNeilBrown struct rsi *tmp = container_of(b, struct rsi, h); 142f64f9e71SJoe Perches return netobj_equal(&item->in_handle, &tmp->in_handle) && 143f64f9e71SJoe Perches netobj_equal(&item->in_token, &tmp->in_token); 1441da177e4SLinus Torvalds } 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds static int dup_to_netobj(struct xdr_netobj *dst, char *src, int len) 1471da177e4SLinus Torvalds { 1481da177e4SLinus Torvalds dst->len = len; 149e69062b4SArnaldo Carvalho de Melo dst->data = (len ? kmemdup(src, len, GFP_KERNEL) : NULL); 1501da177e4SLinus Torvalds if (len && !dst->data) 1511da177e4SLinus Torvalds return -ENOMEM; 1521da177e4SLinus Torvalds return 0; 1531da177e4SLinus Torvalds } 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds static inline int dup_netobj(struct xdr_netobj *dst, struct xdr_netobj *src) 1561da177e4SLinus Torvalds { 1571da177e4SLinus Torvalds return dup_to_netobj(dst, src->data, src->len); 1581da177e4SLinus Torvalds } 1591da177e4SLinus Torvalds 160d4d11ea9SNeilBrown static void rsi_init(struct cache_head *cnew, struct cache_head *citem) 1611da177e4SLinus Torvalds { 162d4d11ea9SNeilBrown struct rsi *new = container_of(cnew, struct rsi, h); 163d4d11ea9SNeilBrown struct rsi *item = container_of(citem, struct rsi, h); 164d4d11ea9SNeilBrown 1651da177e4SLinus Torvalds new->out_handle.data = NULL; 1661da177e4SLinus Torvalds new->out_handle.len = 0; 1671da177e4SLinus Torvalds new->out_token.data = NULL; 1681da177e4SLinus Torvalds new->out_token.len = 0; 1691da177e4SLinus Torvalds new->in_handle.len = item->in_handle.len; 1701da177e4SLinus Torvalds item->in_handle.len = 0; 1711da177e4SLinus Torvalds new->in_token.len = item->in_token.len; 1721da177e4SLinus Torvalds item->in_token.len = 0; 1731da177e4SLinus Torvalds new->in_handle.data = item->in_handle.data; 1741da177e4SLinus Torvalds item->in_handle.data = NULL; 1751da177e4SLinus Torvalds new->in_token.data = item->in_token.data; 1761da177e4SLinus Torvalds item->in_token.data = NULL; 1771da177e4SLinus Torvalds } 1781da177e4SLinus Torvalds 179d4d11ea9SNeilBrown static void update_rsi(struct cache_head *cnew, struct cache_head *citem) 1801da177e4SLinus Torvalds { 181d4d11ea9SNeilBrown struct rsi *new = container_of(cnew, struct rsi, h); 182d4d11ea9SNeilBrown struct rsi *item = container_of(citem, struct rsi, h); 183d4d11ea9SNeilBrown 1841da177e4SLinus Torvalds BUG_ON(new->out_handle.data || new->out_token.data); 1851da177e4SLinus Torvalds new->out_handle.len = item->out_handle.len; 1861da177e4SLinus Torvalds item->out_handle.len = 0; 1871da177e4SLinus Torvalds new->out_token.len = item->out_token.len; 1881da177e4SLinus Torvalds item->out_token.len = 0; 1891da177e4SLinus Torvalds new->out_handle.data = item->out_handle.data; 1901da177e4SLinus Torvalds item->out_handle.data = NULL; 1911da177e4SLinus Torvalds new->out_token.data = item->out_token.data; 1921da177e4SLinus Torvalds item->out_token.data = NULL; 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds new->major_status = item->major_status; 1951da177e4SLinus Torvalds new->minor_status = item->minor_status; 1961da177e4SLinus Torvalds } 1971da177e4SLinus Torvalds 198d4d11ea9SNeilBrown static struct cache_head *rsi_alloc(void) 199d4d11ea9SNeilBrown { 200d4d11ea9SNeilBrown struct rsi *rsii = kmalloc(sizeof(*rsii), GFP_KERNEL); 201d4d11ea9SNeilBrown if (rsii) 202d4d11ea9SNeilBrown return &rsii->h; 203d4d11ea9SNeilBrown else 204d4d11ea9SNeilBrown return NULL; 205d4d11ea9SNeilBrown } 206d4d11ea9SNeilBrown 20765286b88STrond Myklebust static int rsi_upcall(struct cache_detail *cd, struct cache_head *h) 20865286b88STrond Myklebust { 20965286b88STrond Myklebust return sunrpc_cache_pipe_upcall_timeout(cd, h); 21065286b88STrond Myklebust } 21165286b88STrond Myklebust 2121da177e4SLinus Torvalds static void rsi_request(struct cache_detail *cd, 2131da177e4SLinus Torvalds struct cache_head *h, 2141da177e4SLinus Torvalds char **bpp, int *blen) 2151da177e4SLinus Torvalds { 2161da177e4SLinus Torvalds struct rsi *rsii = container_of(h, struct rsi, h); 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds qword_addhex(bpp, blen, rsii->in_handle.data, rsii->in_handle.len); 2191da177e4SLinus Torvalds qword_addhex(bpp, blen, rsii->in_token.data, rsii->in_token.len); 2201da177e4SLinus Torvalds (*bpp)[-1] = '\n'; 2210c217d50SNeilBrown WARN_ONCE(*blen < 0, 2220c217d50SNeilBrown "RPCSEC/GSS credential too large - please use gssproxy\n"); 2231da177e4SLinus Torvalds } 2241da177e4SLinus Torvalds 2251da177e4SLinus Torvalds static int rsi_parse(struct cache_detail *cd, 2261da177e4SLinus Torvalds char *mesg, int mlen) 2271da177e4SLinus Torvalds { 2281da177e4SLinus Torvalds /* context token expiry major minor context token */ 2291da177e4SLinus Torvalds char *buf = mesg; 2301da177e4SLinus Torvalds char *ep; 2311da177e4SLinus Torvalds int len; 2321da177e4SLinus Torvalds struct rsi rsii, *rsip = NULL; 233f559935eSArnd Bergmann time64_t expiry; 2341da177e4SLinus Torvalds int status = -EINVAL; 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds memset(&rsii, 0, sizeof(rsii)); 2371da177e4SLinus Torvalds /* handle */ 2381da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2391da177e4SLinus Torvalds if (len < 0) 2401da177e4SLinus Torvalds goto out; 2411da177e4SLinus Torvalds status = -ENOMEM; 2421da177e4SLinus Torvalds if (dup_to_netobj(&rsii.in_handle, buf, len)) 2431da177e4SLinus Torvalds goto out; 2441da177e4SLinus Torvalds 2451da177e4SLinus Torvalds /* token */ 2461da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2471da177e4SLinus Torvalds status = -EINVAL; 2481da177e4SLinus Torvalds if (len < 0) 2491da177e4SLinus Torvalds goto out; 2501da177e4SLinus Torvalds status = -ENOMEM; 2511da177e4SLinus Torvalds if (dup_to_netobj(&rsii.in_token, buf, len)) 2521da177e4SLinus Torvalds goto out; 2531da177e4SLinus Torvalds 254a1db410dSStanislav Kinsbursky rsip = rsi_lookup(cd, &rsii); 255d4d11ea9SNeilBrown if (!rsip) 256d4d11ea9SNeilBrown goto out; 257d4d11ea9SNeilBrown 2581da177e4SLinus Torvalds rsii.h.flags = 0; 2591da177e4SLinus Torvalds /* expiry */ 260cf64b9bcSNeilBrown status = get_expiry(&mesg, &expiry); 261cf64b9bcSNeilBrown if (status) 2621da177e4SLinus Torvalds goto out; 2631da177e4SLinus Torvalds 264cf64b9bcSNeilBrown status = -EINVAL; 2651da177e4SLinus Torvalds /* major/minor */ 2661da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 267b39c18fcSJ. Bruce Fields if (len <= 0) 2681da177e4SLinus Torvalds goto out; 2691da177e4SLinus Torvalds rsii.major_status = simple_strtoul(buf, &ep, 10); 2701da177e4SLinus Torvalds if (*ep) 2711da177e4SLinus Torvalds goto out; 2721da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2731da177e4SLinus Torvalds if (len <= 0) 2741da177e4SLinus Torvalds goto out; 2751da177e4SLinus Torvalds rsii.minor_status = simple_strtoul(buf, &ep, 10); 2761da177e4SLinus Torvalds if (*ep) 2771da177e4SLinus Torvalds goto out; 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds /* out_handle */ 2801da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2811da177e4SLinus Torvalds if (len < 0) 2821da177e4SLinus Torvalds goto out; 2831da177e4SLinus Torvalds status = -ENOMEM; 2841da177e4SLinus Torvalds if (dup_to_netobj(&rsii.out_handle, buf, len)) 2851da177e4SLinus Torvalds goto out; 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds /* out_token */ 2881da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2891da177e4SLinus Torvalds status = -EINVAL; 2901da177e4SLinus Torvalds if (len < 0) 2911da177e4SLinus Torvalds goto out; 2921da177e4SLinus Torvalds status = -ENOMEM; 2931da177e4SLinus Torvalds if (dup_to_netobj(&rsii.out_token, buf, len)) 2941da177e4SLinus Torvalds goto out; 2951da177e4SLinus Torvalds rsii.h.expiry_time = expiry; 296a1db410dSStanislav Kinsbursky rsip = rsi_update(cd, &rsii, rsip); 2971da177e4SLinus Torvalds status = 0; 2981da177e4SLinus Torvalds out: 2991da177e4SLinus Torvalds rsi_free(&rsii); 3001da177e4SLinus Torvalds if (rsip) 301a1db410dSStanislav Kinsbursky cache_put(&rsip->h, cd); 302d4d11ea9SNeilBrown else 303d4d11ea9SNeilBrown status = -ENOMEM; 3041da177e4SLinus Torvalds return status; 3051da177e4SLinus Torvalds } 3061da177e4SLinus Torvalds 307ee24eac3SBhumika Goyal static const struct cache_detail rsi_cache_template = { 308f35279d3SBruce Allan .owner = THIS_MODULE, 3091da177e4SLinus Torvalds .hash_size = RSI_HASHMAX, 3101da177e4SLinus Torvalds .name = "auth.rpcsec.init", 3111da177e4SLinus Torvalds .cache_put = rsi_put, 31265286b88STrond Myklebust .cache_upcall = rsi_upcall, 31373fb847aSStanislav Kinsbursky .cache_request = rsi_request, 3141da177e4SLinus Torvalds .cache_parse = rsi_parse, 315d4d11ea9SNeilBrown .match = rsi_match, 316d4d11ea9SNeilBrown .init = rsi_init, 317d4d11ea9SNeilBrown .update = update_rsi, 318d4d11ea9SNeilBrown .alloc = rsi_alloc, 3191da177e4SLinus Torvalds }; 3201da177e4SLinus Torvalds 321a1db410dSStanislav Kinsbursky static struct rsi *rsi_lookup(struct cache_detail *cd, struct rsi *item) 322d4d11ea9SNeilBrown { 323d4d11ea9SNeilBrown struct cache_head *ch; 324d4d11ea9SNeilBrown int hash = rsi_hash(item); 325d4d11ea9SNeilBrown 3266d1616b2STrond Myklebust ch = sunrpc_cache_lookup_rcu(cd, &item->h, hash); 327d4d11ea9SNeilBrown if (ch) 328d4d11ea9SNeilBrown return container_of(ch, struct rsi, h); 329d4d11ea9SNeilBrown else 330d4d11ea9SNeilBrown return NULL; 331d4d11ea9SNeilBrown } 332d4d11ea9SNeilBrown 333a1db410dSStanislav Kinsbursky static struct rsi *rsi_update(struct cache_detail *cd, struct rsi *new, struct rsi *old) 334d4d11ea9SNeilBrown { 335d4d11ea9SNeilBrown struct cache_head *ch; 336d4d11ea9SNeilBrown int hash = rsi_hash(new); 337d4d11ea9SNeilBrown 338a1db410dSStanislav Kinsbursky ch = sunrpc_cache_update(cd, &new->h, 339d4d11ea9SNeilBrown &old->h, hash); 340d4d11ea9SNeilBrown if (ch) 341d4d11ea9SNeilBrown return container_of(ch, struct rsi, h); 342d4d11ea9SNeilBrown else 343d4d11ea9SNeilBrown return NULL; 344d4d11ea9SNeilBrown } 345d4d11ea9SNeilBrown 3461da177e4SLinus Torvalds 3471da177e4SLinus Torvalds /* 3481da177e4SLinus Torvalds * The rpcsec_context cache is used to store a context that is 3491da177e4SLinus Torvalds * used in data exchange. 3501da177e4SLinus Torvalds * The key is a context handle. The content is: 3511da177e4SLinus Torvalds * uid, gidlist, mechanism, service-set, mech-specific-data 3521da177e4SLinus Torvalds */ 3531da177e4SLinus Torvalds 3541da177e4SLinus Torvalds #define RSC_HASHBITS 10 3551da177e4SLinus Torvalds #define RSC_HASHMAX (1<<RSC_HASHBITS) 3561da177e4SLinus Torvalds 3571da177e4SLinus Torvalds #define GSS_SEQ_WIN 128 3581da177e4SLinus Torvalds 3591da177e4SLinus Torvalds struct gss_svc_seq_data { 3601da177e4SLinus Torvalds /* highest seq number seen so far: */ 36110b9d99aSChuck Lever u32 sd_max; 3621da177e4SLinus Torvalds /* for i such that sd_max-GSS_SEQ_WIN < i <= sd_max, the i-th bit of 3631da177e4SLinus Torvalds * sd_win is nonzero iff sequence number i has been seen already: */ 3641da177e4SLinus Torvalds unsigned long sd_win[GSS_SEQ_WIN/BITS_PER_LONG]; 3651da177e4SLinus Torvalds spinlock_t sd_lock; 3661da177e4SLinus Torvalds }; 3671da177e4SLinus Torvalds 3681da177e4SLinus Torvalds struct rsc { 3691da177e4SLinus Torvalds struct cache_head h; 3701da177e4SLinus Torvalds struct xdr_netobj handle; 3711da177e4SLinus Torvalds struct svc_cred cred; 3721da177e4SLinus Torvalds struct gss_svc_seq_data seqdata; 3731da177e4SLinus Torvalds struct gss_ctx *mechctx; 3746d1616b2STrond Myklebust struct rcu_head rcu_head; 3751da177e4SLinus Torvalds }; 3761da177e4SLinus Torvalds 377a1db410dSStanislav Kinsbursky static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old); 378a1db410dSStanislav Kinsbursky static struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item); 3791da177e4SLinus Torvalds 3801da177e4SLinus Torvalds static void rsc_free(struct rsc *rsci) 3811da177e4SLinus Torvalds { 3821da177e4SLinus Torvalds kfree(rsci->handle.data); 3831da177e4SLinus Torvalds if (rsci->mechctx) 3841da177e4SLinus Torvalds gss_delete_sec_context(&rsci->mechctx); 38503a4e1f6SJ. Bruce Fields free_svc_cred(&rsci->cred); 3861da177e4SLinus Torvalds } 3871da177e4SLinus Torvalds 3886d1616b2STrond Myklebust static void rsc_free_rcu(struct rcu_head *head) 3896d1616b2STrond Myklebust { 3906d1616b2STrond Myklebust struct rsc *rsci = container_of(head, struct rsc, rcu_head); 3916d1616b2STrond Myklebust 3926d1616b2STrond Myklebust kfree(rsci->handle.data); 3936d1616b2STrond Myklebust kfree(rsci); 3946d1616b2STrond Myklebust } 3956d1616b2STrond Myklebust 396baab935fSNeilBrown static void rsc_put(struct kref *ref) 3971da177e4SLinus Torvalds { 398baab935fSNeilBrown struct rsc *rsci = container_of(ref, struct rsc, h.ref); 3991da177e4SLinus Torvalds 4006d1616b2STrond Myklebust if (rsci->mechctx) 4016d1616b2STrond Myklebust gss_delete_sec_context(&rsci->mechctx); 4026d1616b2STrond Myklebust free_svc_cred(&rsci->cred); 4036d1616b2STrond Myklebust call_rcu(&rsci->rcu_head, rsc_free_rcu); 4041da177e4SLinus Torvalds } 4051da177e4SLinus Torvalds 4061da177e4SLinus Torvalds static inline int 4071da177e4SLinus Torvalds rsc_hash(struct rsc *rsci) 4081da177e4SLinus Torvalds { 4091da177e4SLinus Torvalds return hash_mem(rsci->handle.data, rsci->handle.len, RSC_HASHBITS); 4101da177e4SLinus Torvalds } 4111da177e4SLinus Torvalds 41217f834b6SNeilBrown static int 41317f834b6SNeilBrown rsc_match(struct cache_head *a, struct cache_head *b) 4141da177e4SLinus Torvalds { 41517f834b6SNeilBrown struct rsc *new = container_of(a, struct rsc, h); 41617f834b6SNeilBrown struct rsc *tmp = container_of(b, struct rsc, h); 41717f834b6SNeilBrown 4181da177e4SLinus Torvalds return netobj_equal(&new->handle, &tmp->handle); 4191da177e4SLinus Torvalds } 4201da177e4SLinus Torvalds 42117f834b6SNeilBrown static void 42217f834b6SNeilBrown rsc_init(struct cache_head *cnew, struct cache_head *ctmp) 4231da177e4SLinus Torvalds { 42417f834b6SNeilBrown struct rsc *new = container_of(cnew, struct rsc, h); 42517f834b6SNeilBrown struct rsc *tmp = container_of(ctmp, struct rsc, h); 42617f834b6SNeilBrown 4271da177e4SLinus Torvalds new->handle.len = tmp->handle.len; 4281da177e4SLinus Torvalds tmp->handle.len = 0; 4291da177e4SLinus Torvalds new->handle.data = tmp->handle.data; 4301da177e4SLinus Torvalds tmp->handle.data = NULL; 4311da177e4SLinus Torvalds new->mechctx = NULL; 43244234063SJ. Bruce Fields init_svc_cred(&new->cred); 4331da177e4SLinus Torvalds } 4341da177e4SLinus Torvalds 43517f834b6SNeilBrown static void 43617f834b6SNeilBrown update_rsc(struct cache_head *cnew, struct cache_head *ctmp) 4371da177e4SLinus Torvalds { 43817f834b6SNeilBrown struct rsc *new = container_of(cnew, struct rsc, h); 43917f834b6SNeilBrown struct rsc *tmp = container_of(ctmp, struct rsc, h); 44017f834b6SNeilBrown 4411da177e4SLinus Torvalds new->mechctx = tmp->mechctx; 4421da177e4SLinus Torvalds tmp->mechctx = NULL; 4431da177e4SLinus Torvalds memset(&new->seqdata, 0, sizeof(new->seqdata)); 4441da177e4SLinus Torvalds spin_lock_init(&new->seqdata.sd_lock); 4451da177e4SLinus Torvalds new->cred = tmp->cred; 44644234063SJ. Bruce Fields init_svc_cred(&tmp->cred); 4471da177e4SLinus Torvalds } 4481da177e4SLinus Torvalds 44917f834b6SNeilBrown static struct cache_head * 45017f834b6SNeilBrown rsc_alloc(void) 45117f834b6SNeilBrown { 45217f834b6SNeilBrown struct rsc *rsci = kmalloc(sizeof(*rsci), GFP_KERNEL); 45317f834b6SNeilBrown if (rsci) 45417f834b6SNeilBrown return &rsci->h; 45517f834b6SNeilBrown else 45617f834b6SNeilBrown return NULL; 45717f834b6SNeilBrown } 45817f834b6SNeilBrown 45965286b88STrond Myklebust static int rsc_upcall(struct cache_detail *cd, struct cache_head *h) 46065286b88STrond Myklebust { 46165286b88STrond Myklebust return -EINVAL; 46265286b88STrond Myklebust } 46365286b88STrond Myklebust 4641da177e4SLinus Torvalds static int rsc_parse(struct cache_detail *cd, 4651da177e4SLinus Torvalds char *mesg, int mlen) 4661da177e4SLinus Torvalds { 4671da177e4SLinus Torvalds /* contexthandle expiry [ uid gid N <n gids> mechname ...mechdata... ] */ 4681da177e4SLinus Torvalds char *buf = mesg; 469683428faSEric W. Biederman int id; 4701da177e4SLinus Torvalds int len, rv; 4711da177e4SLinus Torvalds struct rsc rsci, *rscp = NULL; 472294ec5b8SArnd Bergmann time64_t expiry; 4731da177e4SLinus Torvalds int status = -EINVAL; 4741df0cadaSJ. Bruce Fields struct gss_api_mech *gm = NULL; 4751da177e4SLinus Torvalds 4761da177e4SLinus Torvalds memset(&rsci, 0, sizeof(rsci)); 4771da177e4SLinus Torvalds /* context handle */ 4781da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 4791da177e4SLinus Torvalds if (len < 0) goto out; 4801da177e4SLinus Torvalds status = -ENOMEM; 4811da177e4SLinus Torvalds if (dup_to_netobj(&rsci.handle, buf, len)) 4821da177e4SLinus Torvalds goto out; 4831da177e4SLinus Torvalds 4841da177e4SLinus Torvalds rsci.h.flags = 0; 4851da177e4SLinus Torvalds /* expiry */ 486cf64b9bcSNeilBrown status = get_expiry(&mesg, &expiry); 487cf64b9bcSNeilBrown if (status) 4881da177e4SLinus Torvalds goto out; 4891da177e4SLinus Torvalds 490cf64b9bcSNeilBrown status = -EINVAL; 491a1db410dSStanislav Kinsbursky rscp = rsc_lookup(cd, &rsci); 49217f834b6SNeilBrown if (!rscp) 49317f834b6SNeilBrown goto out; 49417f834b6SNeilBrown 4951da177e4SLinus Torvalds /* uid, or NEGATIVE */ 496683428faSEric W. Biederman rv = get_int(&mesg, &id); 4971da177e4SLinus Torvalds if (rv == -EINVAL) 4981da177e4SLinus Torvalds goto out; 4991da177e4SLinus Torvalds if (rv == -ENOENT) 5001da177e4SLinus Torvalds set_bit(CACHE_NEGATIVE, &rsci.h.flags); 5011da177e4SLinus Torvalds else { 5021da177e4SLinus Torvalds int N, i; 5031da177e4SLinus Torvalds 5043c34ae11SJ. Bruce Fields /* 5053c34ae11SJ. Bruce Fields * NOTE: we skip uid_valid()/gid_valid() checks here: 5063c34ae11SJ. Bruce Fields * instead, * -1 id's are later mapped to the 5073c34ae11SJ. Bruce Fields * (export-specific) anonymous id by nfsd_setuser. 5083c34ae11SJ. Bruce Fields * 5093c34ae11SJ. Bruce Fields * (But supplementary gid's get no such special 5103c34ae11SJ. Bruce Fields * treatment so are checked for validity here.) 5113c34ae11SJ. Bruce Fields */ 512683428faSEric W. Biederman /* uid */ 513e6667c73STrond Myklebust rsci.cred.cr_uid = make_kuid(current_user_ns(), id); 514683428faSEric W. Biederman 5151da177e4SLinus Torvalds /* gid */ 516683428faSEric W. Biederman if (get_int(&mesg, &id)) 517683428faSEric W. Biederman goto out; 518e6667c73STrond Myklebust rsci.cred.cr_gid = make_kgid(current_user_ns(), id); 5191da177e4SLinus Torvalds 5201da177e4SLinus Torvalds /* number of additional gid's */ 5211da177e4SLinus Torvalds if (get_int(&mesg, &N)) 5221da177e4SLinus Torvalds goto out; 52376cb4be9SDan Carpenter if (N < 0 || N > NGROUPS_MAX) 52476cb4be9SDan Carpenter goto out; 5251da177e4SLinus Torvalds status = -ENOMEM; 5261da177e4SLinus Torvalds rsci.cred.cr_group_info = groups_alloc(N); 5271da177e4SLinus Torvalds if (rsci.cred.cr_group_info == NULL) 5281da177e4SLinus Torvalds goto out; 5291da177e4SLinus Torvalds 5301da177e4SLinus Torvalds /* gid's */ 5311da177e4SLinus Torvalds status = -EINVAL; 5321da177e4SLinus Torvalds for (i=0; i<N; i++) { 533ae2975bcSEric W. Biederman kgid_t kgid; 534683428faSEric W. Biederman if (get_int(&mesg, &id)) 5351da177e4SLinus Torvalds goto out; 536e6667c73STrond Myklebust kgid = make_kgid(current_user_ns(), id); 537ae2975bcSEric W. Biederman if (!gid_valid(kgid)) 538ae2975bcSEric W. Biederman goto out; 53981243eacSAlexey Dobriyan rsci.cred.cr_group_info->gid[i] = kgid; 5401da177e4SLinus Torvalds } 541bdcf0a42SThiago Rafael Becker groups_sort(rsci.cred.cr_group_info); 5421da177e4SLinus Torvalds 5431da177e4SLinus Torvalds /* mech name */ 5441da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 5451da177e4SLinus Torvalds if (len < 0) 5461da177e4SLinus Torvalds goto out; 5470dc1531aSJ. Bruce Fields gm = rsci.cred.cr_gss_mech = gss_mech_get_by_name(buf); 5481da177e4SLinus Torvalds status = -EOPNOTSUPP; 5491da177e4SLinus Torvalds if (!gm) 5501da177e4SLinus Torvalds goto out; 5511da177e4SLinus Torvalds 5521da177e4SLinus Torvalds status = -EINVAL; 5531da177e4SLinus Torvalds /* mech-specific data: */ 5541da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 5551df0cadaSJ. Bruce Fields if (len < 0) 5561da177e4SLinus Torvalds goto out; 557400f26b5SSimo Sorce status = gss_import_sec_context(buf, len, gm, &rsci.mechctx, 558400f26b5SSimo Sorce NULL, GFP_KERNEL); 5591df0cadaSJ. Bruce Fields if (status) 5601da177e4SLinus Torvalds goto out; 56168e76ad0SOlga Kornievskaia 56268e76ad0SOlga Kornievskaia /* get client name */ 56368e76ad0SOlga Kornievskaia len = qword_get(&mesg, buf, mlen); 56468e76ad0SOlga Kornievskaia if (len > 0) { 56503a4e1f6SJ. Bruce Fields rsci.cred.cr_principal = kstrdup(buf, GFP_KERNEL); 5661eb6d622SWei Yongjun if (!rsci.cred.cr_principal) { 5671eb6d622SWei Yongjun status = -ENOMEM; 56868e76ad0SOlga Kornievskaia goto out; 56968e76ad0SOlga Kornievskaia } 5701eb6d622SWei Yongjun } 57168e76ad0SOlga Kornievskaia 5721da177e4SLinus Torvalds } 5731da177e4SLinus Torvalds rsci.h.expiry_time = expiry; 574a1db410dSStanislav Kinsbursky rscp = rsc_update(cd, &rsci, rscp); 5751da177e4SLinus Torvalds status = 0; 5761da177e4SLinus Torvalds out: 5771da177e4SLinus Torvalds rsc_free(&rsci); 5781da177e4SLinus Torvalds if (rscp) 579a1db410dSStanislav Kinsbursky cache_put(&rscp->h, cd); 58017f834b6SNeilBrown else 58117f834b6SNeilBrown status = -ENOMEM; 5821da177e4SLinus Torvalds return status; 5831da177e4SLinus Torvalds } 5841da177e4SLinus Torvalds 585ee24eac3SBhumika Goyal static const struct cache_detail rsc_cache_template = { 586f35279d3SBruce Allan .owner = THIS_MODULE, 5871da177e4SLinus Torvalds .hash_size = RSC_HASHMAX, 5881da177e4SLinus Torvalds .name = "auth.rpcsec.context", 5891da177e4SLinus Torvalds .cache_put = rsc_put, 59065286b88STrond Myklebust .cache_upcall = rsc_upcall, 5911da177e4SLinus Torvalds .cache_parse = rsc_parse, 59217f834b6SNeilBrown .match = rsc_match, 59317f834b6SNeilBrown .init = rsc_init, 59417f834b6SNeilBrown .update = update_rsc, 59517f834b6SNeilBrown .alloc = rsc_alloc, 5961da177e4SLinus Torvalds }; 5971da177e4SLinus Torvalds 598a1db410dSStanislav Kinsbursky static struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item) 59917f834b6SNeilBrown { 60017f834b6SNeilBrown struct cache_head *ch; 60117f834b6SNeilBrown int hash = rsc_hash(item); 60217f834b6SNeilBrown 6036d1616b2STrond Myklebust ch = sunrpc_cache_lookup_rcu(cd, &item->h, hash); 60417f834b6SNeilBrown if (ch) 60517f834b6SNeilBrown return container_of(ch, struct rsc, h); 60617f834b6SNeilBrown else 60717f834b6SNeilBrown return NULL; 60817f834b6SNeilBrown } 60917f834b6SNeilBrown 610a1db410dSStanislav Kinsbursky static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old) 61117f834b6SNeilBrown { 61217f834b6SNeilBrown struct cache_head *ch; 61317f834b6SNeilBrown int hash = rsc_hash(new); 61417f834b6SNeilBrown 615a1db410dSStanislav Kinsbursky ch = sunrpc_cache_update(cd, &new->h, 61617f834b6SNeilBrown &old->h, hash); 61717f834b6SNeilBrown if (ch) 61817f834b6SNeilBrown return container_of(ch, struct rsc, h); 61917f834b6SNeilBrown else 62017f834b6SNeilBrown return NULL; 62117f834b6SNeilBrown } 62217f834b6SNeilBrown 6231da177e4SLinus Torvalds 6241da177e4SLinus Torvalds static struct rsc * 625a1db410dSStanislav Kinsbursky gss_svc_searchbyctx(struct cache_detail *cd, struct xdr_netobj *handle) 6261da177e4SLinus Torvalds { 6271da177e4SLinus Torvalds struct rsc rsci; 6281da177e4SLinus Torvalds struct rsc *found; 6291da177e4SLinus Torvalds 6301da177e4SLinus Torvalds memset(&rsci, 0, sizeof(rsci)); 631bf2c4b6fSChuck Lever if (dup_to_netobj(&rsci.handle, handle->data, handle->len)) 632bf2c4b6fSChuck Lever return NULL; 633a1db410dSStanislav Kinsbursky found = rsc_lookup(cd, &rsci); 634bf2c4b6fSChuck Lever rsc_free(&rsci); 6351da177e4SLinus Torvalds if (!found) 6361da177e4SLinus Torvalds return NULL; 637a1db410dSStanislav Kinsbursky if (cache_check(cd, &found->h, NULL)) 6381da177e4SLinus Torvalds return NULL; 6391da177e4SLinus Torvalds return found; 6401da177e4SLinus Torvalds } 6411da177e4SLinus Torvalds 64210b9d99aSChuck Lever /** 64310b9d99aSChuck Lever * gss_check_seq_num - GSS sequence number window check 64410b9d99aSChuck Lever * @rqstp: RPC Call to use when reporting errors 64510b9d99aSChuck Lever * @rsci: cached GSS context state (updated on return) 64610b9d99aSChuck Lever * @seq_num: sequence number to check 64710b9d99aSChuck Lever * 64810b9d99aSChuck Lever * Implements sequence number algorithm as specified in 64910b9d99aSChuck Lever * RFC 2203, Section 5.3.3.1. "Context Management". 65010b9d99aSChuck Lever * 65110b9d99aSChuck Lever * Return values: 65210b9d99aSChuck Lever * %true: @rqstp's GSS sequence number is inside the window 65310b9d99aSChuck Lever * %false: @rqstp's GSS sequence number is outside the window 65410b9d99aSChuck Lever */ 65510b9d99aSChuck Lever static bool gss_check_seq_num(const struct svc_rqst *rqstp, struct rsc *rsci, 65610b9d99aSChuck Lever u32 seq_num) 6571da177e4SLinus Torvalds { 6581da177e4SLinus Torvalds struct gss_svc_seq_data *sd = &rsci->seqdata; 65910b9d99aSChuck Lever bool result = false; 6601da177e4SLinus Torvalds 6611da177e4SLinus Torvalds spin_lock(&sd->sd_lock); 6621da177e4SLinus Torvalds if (seq_num > sd->sd_max) { 6631da177e4SLinus Torvalds if (seq_num >= sd->sd_max + GSS_SEQ_WIN) { 6641da177e4SLinus Torvalds memset(sd->sd_win, 0, sizeof(sd->sd_win)); 6651da177e4SLinus Torvalds sd->sd_max = seq_num; 6661da177e4SLinus Torvalds } else while (sd->sd_max < seq_num) { 6671da177e4SLinus Torvalds sd->sd_max++; 6681da177e4SLinus Torvalds __clear_bit(sd->sd_max % GSS_SEQ_WIN, sd->sd_win); 6691da177e4SLinus Torvalds } 6701da177e4SLinus Torvalds __set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win); 6711da177e4SLinus Torvalds goto ok; 6722ba5acfbSJ. Bruce Fields } else if (seq_num + GSS_SEQ_WIN <= sd->sd_max) { 67310b9d99aSChuck Lever goto toolow; 6741da177e4SLinus Torvalds } 6751da177e4SLinus Torvalds if (__test_and_set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win)) 67610b9d99aSChuck Lever goto alreadyseen; 67710b9d99aSChuck Lever 6781da177e4SLinus Torvalds ok: 67910b9d99aSChuck Lever result = true; 68010b9d99aSChuck Lever out: 6811da177e4SLinus Torvalds spin_unlock(&sd->sd_lock); 68210b9d99aSChuck Lever return result; 68310b9d99aSChuck Lever 68410b9d99aSChuck Lever toolow: 68510b9d99aSChuck Lever trace_rpcgss_svc_seqno_low(rqstp, seq_num, 68610b9d99aSChuck Lever sd->sd_max - GSS_SEQ_WIN, 68710b9d99aSChuck Lever sd->sd_max); 68810b9d99aSChuck Lever goto out; 68910b9d99aSChuck Lever alreadyseen: 69010b9d99aSChuck Lever trace_rpcgss_svc_seqno_seen(rqstp, seq_num); 69110b9d99aSChuck Lever goto out; 6921da177e4SLinus Torvalds } 6931da177e4SLinus Torvalds 69421fcd02bSJ. Bruce Fields /* 6950653028eSChuck Lever * Decode and verify a Call's verifier field. For RPC_AUTH_GSS Calls, 6960653028eSChuck Lever * the body of this field contains a variable length checksum. 6970653028eSChuck Lever * 6980653028eSChuck Lever * GSS-specific auth_stat values are mandated by RFC 2203 Section 6990653028eSChuck Lever * 5.3.3.3. 7001da177e4SLinus Torvalds */ 7011da177e4SLinus Torvalds static int 7020653028eSChuck Lever svcauth_gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci, 703438623a0SChuck Lever __be32 *rpcstart, struct rpc_gss_wire_cred *gc) 7041da177e4SLinus Torvalds { 7050653028eSChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 7061da177e4SLinus Torvalds struct gss_ctx *ctx_id = rsci->mechctx; 7070653028eSChuck Lever u32 flavor, maj_stat; 7081da177e4SLinus Torvalds struct xdr_buf rpchdr; 7091da177e4SLinus Torvalds struct xdr_netobj checksum; 7101da177e4SLinus Torvalds struct kvec iov; 7111da177e4SLinus Torvalds 7120653028eSChuck Lever /* 7130653028eSChuck Lever * Compute the checksum of the incoming Call from the 7140653028eSChuck Lever * XID field to credential field: 7150653028eSChuck Lever */ 7161da177e4SLinus Torvalds iov.iov_base = rpcstart; 7170653028eSChuck Lever iov.iov_len = (u8 *)xdr->p - (u8 *)rpcstart; 7181da177e4SLinus Torvalds xdr_buf_from_iov(&iov, &rpchdr); 7191da177e4SLinus Torvalds 7200653028eSChuck Lever /* Call's verf field: */ 7210653028eSChuck Lever if (xdr_stream_decode_opaque_auth(xdr, &flavor, 7220653028eSChuck Lever (void **)&checksum.data, 7230653028eSChuck Lever &checksum.len) < 0) { 724438623a0SChuck Lever rqstp->rq_auth_stat = rpc_autherr_badverf; 7251da177e4SLinus Torvalds return SVC_DENIED; 7260653028eSChuck Lever } 7270653028eSChuck Lever if (flavor != RPC_AUTH_GSS) { 7280653028eSChuck Lever rqstp->rq_auth_stat = rpc_autherr_badverf; 7291da177e4SLinus Torvalds return SVC_DENIED; 7300653028eSChuck Lever } 7311da177e4SLinus Torvalds 7320653028eSChuck Lever if (rqstp->rq_deferred) 7331da177e4SLinus Torvalds return SVC_OK; 7340653028eSChuck Lever maj_stat = gss_verify_mic(ctx_id, &rpchdr, &checksum); 7350653028eSChuck Lever if (maj_stat != GSS_S_COMPLETE) { 7360653028eSChuck Lever trace_rpcgss_svc_mic(rqstp, maj_stat); 737438623a0SChuck Lever rqstp->rq_auth_stat = rpcsec_gsserr_credproblem; 7381da177e4SLinus Torvalds return SVC_DENIED; 7391da177e4SLinus Torvalds } 7401da177e4SLinus Torvalds 7411da177e4SLinus Torvalds if (gc->gc_seq > MAXSEQ) { 74210b9d99aSChuck Lever trace_rpcgss_svc_seqno_large(rqstp, gc->gc_seq); 743438623a0SChuck Lever rqstp->rq_auth_stat = rpcsec_gsserr_ctxproblem; 7441da177e4SLinus Torvalds return SVC_DENIED; 7451da177e4SLinus Torvalds } 74610b9d99aSChuck Lever if (!gss_check_seq_num(rqstp, rsci, gc->gc_seq)) 7471da177e4SLinus Torvalds return SVC_DROP; 7481da177e4SLinus Torvalds return SVC_OK; 7491da177e4SLinus Torvalds } 7501da177e4SLinus Torvalds 751b2f42f1dSChuck Lever /* 752b2f42f1dSChuck Lever * Construct and encode a Reply's verifier field. The verifier's body 753b2f42f1dSChuck Lever * field contains a variable-length checksum of the GSS sequence 754b2f42f1dSChuck Lever * number. 755b2f42f1dSChuck Lever */ 756b2f42f1dSChuck Lever static bool 757b2f42f1dSChuck Lever svcauth_gss_encode_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id, u32 seq) 758b2f42f1dSChuck Lever { 759b2f42f1dSChuck Lever struct gss_svc_data *gsd = rqstp->rq_auth_data; 760b2f42f1dSChuck Lever u32 maj_stat; 761b2f42f1dSChuck Lever struct xdr_buf verf_data; 762b2f42f1dSChuck Lever struct xdr_netobj checksum; 763b2f42f1dSChuck Lever struct kvec iov; 764b2f42f1dSChuck Lever 765b2f42f1dSChuck Lever gsd->gsd_seq_num = cpu_to_be32(seq); 766b2f42f1dSChuck Lever iov.iov_base = &gsd->gsd_seq_num; 767b2f42f1dSChuck Lever iov.iov_len = XDR_UNIT; 768b2f42f1dSChuck Lever xdr_buf_from_iov(&iov, &verf_data); 769b2f42f1dSChuck Lever 770b2f42f1dSChuck Lever checksum.data = gsd->gsd_scratch; 771b2f42f1dSChuck Lever maj_stat = gss_get_mic(ctx_id, &verf_data, &checksum); 772b2f42f1dSChuck Lever if (maj_stat != GSS_S_COMPLETE) 773b2f42f1dSChuck Lever goto bad_mic; 774b2f42f1dSChuck Lever 775b2f42f1dSChuck Lever return xdr_stream_encode_opaque_auth(&rqstp->rq_res_stream, RPC_AUTH_GSS, 776b2f42f1dSChuck Lever checksum.data, checksum.len) > 0; 777b2f42f1dSChuck Lever 778b2f42f1dSChuck Lever bad_mic: 779b2f42f1dSChuck Lever trace_rpcgss_svc_get_mic(rqstp, maj_stat); 780b2f42f1dSChuck Lever return false; 781b2f42f1dSChuck Lever } 782b2f42f1dSChuck Lever 7831da177e4SLinus Torvalds struct gss_domain { 7841da177e4SLinus Torvalds struct auth_domain h; 7851da177e4SLinus Torvalds u32 pseudoflavor; 7861da177e4SLinus Torvalds }; 7871da177e4SLinus Torvalds 7881da177e4SLinus Torvalds static struct auth_domain * 7891da177e4SLinus Torvalds find_gss_auth_domain(struct gss_ctx *ctx, u32 svc) 7901da177e4SLinus Torvalds { 7911da177e4SLinus Torvalds char *name; 7921da177e4SLinus Torvalds 7931da177e4SLinus Torvalds name = gss_service_to_auth_domain_name(ctx->mech_type, svc); 7941da177e4SLinus Torvalds if (!name) 7951da177e4SLinus Torvalds return NULL; 7961da177e4SLinus Torvalds return auth_domain_find(name); 7971da177e4SLinus Torvalds } 7981da177e4SLinus Torvalds 799efc36aa5SNeilBrown static struct auth_ops svcauthops_gss; 800efc36aa5SNeilBrown 8014796f457SJ. Bruce Fields u32 svcauth_gss_flavor(struct auth_domain *dom) 8024796f457SJ. Bruce Fields { 8034796f457SJ. Bruce Fields struct gss_domain *gd = container_of(dom, struct gss_domain, h); 8044796f457SJ. Bruce Fields 8054796f457SJ. Bruce Fields return gd->pseudoflavor; 8064796f457SJ. Bruce Fields } 8074796f457SJ. Bruce Fields 8087bd88269STrond Myklebust EXPORT_SYMBOL_GPL(svcauth_gss_flavor); 8094796f457SJ. Bruce Fields 81024c5efe4SNeilBrown struct auth_domain * 8111da177e4SLinus Torvalds svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name) 8121da177e4SLinus Torvalds { 8131da177e4SLinus Torvalds struct gss_domain *new; 8141da177e4SLinus Torvalds struct auth_domain *test; 8151da177e4SLinus Torvalds int stat = -ENOMEM; 8161da177e4SLinus Torvalds 8171da177e4SLinus Torvalds new = kmalloc(sizeof(*new), GFP_KERNEL); 8181da177e4SLinus Torvalds if (!new) 8191da177e4SLinus Torvalds goto out; 820efc36aa5SNeilBrown kref_init(&new->h.ref); 821e69062b4SArnaldo Carvalho de Melo new->h.name = kstrdup(name, GFP_KERNEL); 8221da177e4SLinus Torvalds if (!new->h.name) 8231da177e4SLinus Torvalds goto out_free_dom; 824efc36aa5SNeilBrown new->h.flavour = &svcauthops_gss; 8251da177e4SLinus Torvalds new->pseudoflavor = pseudoflavor; 8261da177e4SLinus Torvalds 827efc36aa5SNeilBrown test = auth_domain_lookup(name, &new->h); 828d47a5dc2SNeilBrown if (test != &new->h) { 829d47a5dc2SNeilBrown pr_warn("svc: duplicate registration of gss pseudo flavour %s.\n", 830d47a5dc2SNeilBrown name); 831d47a5dc2SNeilBrown stat = -EADDRINUSE; 832cb276805SJ. Bruce Fields auth_domain_put(test); 83324c5efe4SNeilBrown goto out_free_name; 8341da177e4SLinus Torvalds } 83524c5efe4SNeilBrown return test; 8361da177e4SLinus Torvalds 83724c5efe4SNeilBrown out_free_name: 83824c5efe4SNeilBrown kfree(new->h.name); 8391da177e4SLinus Torvalds out_free_dom: 8401da177e4SLinus Torvalds kfree(new); 8411da177e4SLinus Torvalds out: 84224c5efe4SNeilBrown return ERR_PTR(stat); 8431da177e4SLinus Torvalds } 8447bd88269STrond Myklebust EXPORT_SYMBOL_GPL(svcauth_gss_register_pseudoflavor); 8451da177e4SLinus Torvalds 846e14673c9SChuck Lever /* 847e14673c9SChuck Lever * RFC 2203, Section 5.3.2.2 848e14673c9SChuck Lever * 849e14673c9SChuck Lever * struct rpc_gss_integ_data { 850e14673c9SChuck Lever * opaque databody_integ<>; 851e14673c9SChuck Lever * opaque checksum<>; 852e14673c9SChuck Lever * }; 853e14673c9SChuck Lever * 854e14673c9SChuck Lever * struct rpc_gss_data_t { 855e14673c9SChuck Lever * unsigned int seq_num; 856e14673c9SChuck Lever * proc_req_arg_t arg; 857e14673c9SChuck Lever * }; 858e14673c9SChuck Lever */ 859b68e4c5cSChuck Lever static noinline_for_stack int 860b68e4c5cSChuck Lever svcauth_gss_unwrap_integ(struct svc_rqst *rqstp, u32 seq, struct gss_ctx *ctx) 8611da177e4SLinus Torvalds { 8624dd9daa9SChuck Lever struct gss_svc_data *gsd = rqstp->rq_auth_data; 863b68e4c5cSChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 864b68e4c5cSChuck Lever u32 len, offset, seq_num, maj_stat; 865b68e4c5cSChuck Lever struct xdr_buf *buf = xdr->buf; 866e14673c9SChuck Lever struct xdr_buf databody_integ; 867e14673c9SChuck Lever struct xdr_netobj checksum; 8681da177e4SLinus Torvalds 86906eb8a56SChuck Lever /* NFS READ normally uses splice to send data in-place. However 87006eb8a56SChuck Lever * the data in cache can change after the reply's MIC is computed 87106eb8a56SChuck Lever * but before the RPC reply is sent. To prevent the client from 87206eb8a56SChuck Lever * rejecting the server-computed MIC in this somewhat rare case, 87306eb8a56SChuck Lever * do not use splice with the GSS integrity service. 87406eb8a56SChuck Lever */ 8757827c81fSChuck Lever clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags); 87606eb8a56SChuck Lever 8774c190e2fSJeff Layton /* Did we already verify the signature on the original pass through? */ 8784c190e2fSJeff Layton if (rqstp->rq_deferred) 8794c190e2fSJeff Layton return 0; 8804c190e2fSJeff Layton 881b68e4c5cSChuck Lever if (xdr_stream_decode_u32(xdr, &len) < 0) 882b68e4c5cSChuck Lever goto unwrap_failed; 883e14673c9SChuck Lever if (len & 3) 88410b9d99aSChuck Lever goto unwrap_failed; 885b68e4c5cSChuck Lever offset = xdr_stream_pos(xdr); 886b68e4c5cSChuck Lever if (xdr_buf_subsegment(buf, &databody_integ, offset, len)) 88710b9d99aSChuck Lever goto unwrap_failed; 88810b9d99aSChuck Lever 889b68e4c5cSChuck Lever /* 890b68e4c5cSChuck Lever * The xdr_stream now points to the @seq_num field. The next 891b68e4c5cSChuck Lever * XDR data item is the @arg field, which contains the clear 892b68e4c5cSChuck Lever * text RPC program payload. The checksum, which follows the 893b68e4c5cSChuck Lever * @arg field, is located and decoded without updating the 894b68e4c5cSChuck Lever * xdr_stream. 895b68e4c5cSChuck Lever */ 896b68e4c5cSChuck Lever 897b68e4c5cSChuck Lever offset += len; 898b68e4c5cSChuck Lever if (xdr_decode_word(buf, offset, &checksum.len)) 89910b9d99aSChuck Lever goto unwrap_failed; 900e14673c9SChuck Lever if (checksum.len > sizeof(gsd->gsd_scratch)) 90110b9d99aSChuck Lever goto unwrap_failed; 902e14673c9SChuck Lever checksum.data = gsd->gsd_scratch; 903b68e4c5cSChuck Lever if (read_bytes_from_xdr_buf(buf, offset + XDR_UNIT, checksum.data, 904b68e4c5cSChuck Lever checksum.len)) 90510b9d99aSChuck Lever goto unwrap_failed; 906b68e4c5cSChuck Lever 907e14673c9SChuck Lever maj_stat = gss_verify_mic(ctx, &databody_integ, &checksum); 9081da177e4SLinus Torvalds if (maj_stat != GSS_S_COMPLETE) 90910b9d99aSChuck Lever goto bad_mic; 910b68e4c5cSChuck Lever 911b68e4c5cSChuck Lever /* The received seqno is protected by the checksum. */ 912b68e4c5cSChuck Lever if (xdr_stream_decode_u32(xdr, &seq_num) < 0) 913b68e4c5cSChuck Lever goto unwrap_failed; 914e14673c9SChuck Lever if (seq_num != seq) 91510b9d99aSChuck Lever goto bad_seqno; 916b68e4c5cSChuck Lever 917b68e4c5cSChuck Lever xdr_truncate_decode(xdr, XDR_UNIT + checksum.len); 9184dd9daa9SChuck Lever return 0; 91910b9d99aSChuck Lever 92010b9d99aSChuck Lever unwrap_failed: 92110b9d99aSChuck Lever trace_rpcgss_svc_unwrap_failed(rqstp); 9224dd9daa9SChuck Lever return -EINVAL; 92310b9d99aSChuck Lever bad_seqno: 924e14673c9SChuck Lever trace_rpcgss_svc_seqno_bad(rqstp, seq, seq_num); 9254dd9daa9SChuck Lever return -EINVAL; 92610b9d99aSChuck Lever bad_mic: 92710b9d99aSChuck Lever trace_rpcgss_svc_mic(rqstp, maj_stat); 9284dd9daa9SChuck Lever return -EINVAL; 9291da177e4SLinus Torvalds } 9301da177e4SLinus Torvalds 931f4a59e82SChuck Lever /* 932f4a59e82SChuck Lever * RFC 2203, Section 5.3.2.3 933f4a59e82SChuck Lever * 934f4a59e82SChuck Lever * struct rpc_gss_priv_data { 935f4a59e82SChuck Lever * opaque databody_priv<> 936f4a59e82SChuck Lever * }; 937f4a59e82SChuck Lever * 938f4a59e82SChuck Lever * struct rpc_gss_data_t { 939f4a59e82SChuck Lever * unsigned int seq_num; 940f4a59e82SChuck Lever * proc_req_arg_t arg; 941f4a59e82SChuck Lever * }; 942f4a59e82SChuck Lever */ 94342140718SChuck Lever static noinline_for_stack int 94442140718SChuck Lever svcauth_gss_unwrap_priv(struct svc_rqst *rqstp, u32 seq, struct gss_ctx *ctx) 9457c9fdcfbSJ. Bruce Fields { 94642140718SChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 94742140718SChuck Lever u32 len, maj_stat, seq_num, offset; 94842140718SChuck Lever struct xdr_buf *buf = xdr->buf; 94942140718SChuck Lever unsigned int saved_len; 9507c9fdcfbSJ. Bruce Fields 9517827c81fSChuck Lever clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags); 9527c9fdcfbSJ. Bruce Fields 95342140718SChuck Lever if (xdr_stream_decode_u32(xdr, &len) < 0) 95442140718SChuck Lever goto unwrap_failed; 9557c9fdcfbSJ. Bruce Fields if (rqstp->rq_deferred) { 9567c9fdcfbSJ. Bruce Fields /* Already decrypted last time through! The sequence number 9577c9fdcfbSJ. Bruce Fields * check at out_seq is unnecessary but harmless: */ 9587c9fdcfbSJ. Bruce Fields goto out_seq; 9597c9fdcfbSJ. Bruce Fields } 96042140718SChuck Lever if (len > xdr_stream_remaining(xdr)) 96110b9d99aSChuck Lever goto unwrap_failed; 96242140718SChuck Lever offset = xdr_stream_pos(xdr); 9637c9fdcfbSJ. Bruce Fields 96442140718SChuck Lever saved_len = buf->len; 96542140718SChuck Lever maj_stat = gss_unwrap(ctx, offset, offset + len, buf); 9667c9fdcfbSJ. Bruce Fields if (maj_stat != GSS_S_COMPLETE) 96710b9d99aSChuck Lever goto bad_unwrap; 96842140718SChuck Lever xdr->nwords -= XDR_QUADLEN(saved_len - buf->len); 96942140718SChuck Lever 9707c9fdcfbSJ. Bruce Fields out_seq: 97142140718SChuck Lever /* gss_unwrap() decrypted the sequence number. */ 97242140718SChuck Lever if (xdr_stream_decode_u32(xdr, &seq_num) < 0) 97342140718SChuck Lever goto unwrap_failed; 974f4a59e82SChuck Lever if (seq_num != seq) 97510b9d99aSChuck Lever goto bad_seqno; 9767c9fdcfbSJ. Bruce Fields return 0; 97710b9d99aSChuck Lever 97810b9d99aSChuck Lever unwrap_failed: 97910b9d99aSChuck Lever trace_rpcgss_svc_unwrap_failed(rqstp); 98010b9d99aSChuck Lever return -EINVAL; 98110b9d99aSChuck Lever bad_seqno: 982f4a59e82SChuck Lever trace_rpcgss_svc_seqno_bad(rqstp, seq, seq_num); 98310b9d99aSChuck Lever return -EINVAL; 98410b9d99aSChuck Lever bad_unwrap: 98510b9d99aSChuck Lever trace_rpcgss_svc_unwrap(rqstp, maj_stat); 98610b9d99aSChuck Lever return -EINVAL; 9877c9fdcfbSJ. Bruce Fields } 9887c9fdcfbSJ. Bruce Fields 989*78c542f9SChuck Lever static enum svc_auth_status 9901da177e4SLinus Torvalds svcauth_gss_set_client(struct svc_rqst *rqstp) 9911da177e4SLinus Torvalds { 9921da177e4SLinus Torvalds struct gss_svc_data *svcdata = rqstp->rq_auth_data; 9931da177e4SLinus Torvalds struct rsc *rsci = svcdata->rsci; 9941da177e4SLinus Torvalds struct rpc_gss_wire_cred *gc = &svcdata->clcred; 9953ab4d8b1SJ. Bruce Fields int stat; 9961da177e4SLinus Torvalds 9975c2465dfSChuck Lever rqstp->rq_auth_stat = rpc_autherr_badcred; 9985c2465dfSChuck Lever 9993ab4d8b1SJ. Bruce Fields /* 10003ab4d8b1SJ. Bruce Fields * A gss export can be specified either by: 10013ab4d8b1SJ. Bruce Fields * export *(sec=krb5,rw) 10023ab4d8b1SJ. Bruce Fields * or by 10033ab4d8b1SJ. Bruce Fields * export gss/krb5(rw) 10043ab4d8b1SJ. Bruce Fields * The latter is deprecated; but for backwards compatibility reasons 10053ab4d8b1SJ. Bruce Fields * the nfsd code will still fall back on trying it if the former 10063ab4d8b1SJ. Bruce Fields * doesn't work; so we try to make both available to nfsd, below. 10073ab4d8b1SJ. Bruce Fields */ 10083ab4d8b1SJ. Bruce Fields rqstp->rq_gssclient = find_gss_auth_domain(rsci->mechctx, gc->gc_svc); 10093ab4d8b1SJ. Bruce Fields if (rqstp->rq_gssclient == NULL) 10101da177e4SLinus Torvalds return SVC_DENIED; 10113ab4d8b1SJ. Bruce Fields stat = svcauth_unix_set_client(rqstp); 10121ebede86SNeilBrown if (stat == SVC_DROP || stat == SVC_CLOSE) 10133ab4d8b1SJ. Bruce Fields return stat; 10145c2465dfSChuck Lever 10155c2465dfSChuck Lever rqstp->rq_auth_stat = rpc_auth_ok; 10161da177e4SLinus Torvalds return SVC_OK; 10171da177e4SLinus Torvalds } 10181da177e4SLinus Torvalds 1019b2f42f1dSChuck Lever static bool 1020b2f42f1dSChuck Lever svcauth_gss_proc_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp, 1021b2f42f1dSChuck Lever struct xdr_netobj *out_handle, int *major_status, 1022b2f42f1dSChuck Lever u32 seq_num) 102391a4762eSKevin Coffman { 1024b2f42f1dSChuck Lever struct xdr_stream *xdr = &rqstp->rq_res_stream; 102591a4762eSKevin Coffman struct rsc *rsci; 1026b2f42f1dSChuck Lever bool rc; 102791a4762eSKevin Coffman 1028fc2952a2SSimo Sorce if (*major_status != GSS_S_COMPLETE) 1029b2f42f1dSChuck Lever goto null_verifier; 1030fc2952a2SSimo Sorce rsci = gss_svc_searchbyctx(cd, out_handle); 103191a4762eSKevin Coffman if (rsci == NULL) { 1032fc2952a2SSimo Sorce *major_status = GSS_S_NO_CONTEXT; 1033b2f42f1dSChuck Lever goto null_verifier; 103491a4762eSKevin Coffman } 1035b2f42f1dSChuck Lever 1036b2f42f1dSChuck Lever rc = svcauth_gss_encode_verf(rqstp, rsci->mechctx, seq_num); 1037a1db410dSStanislav Kinsbursky cache_put(&rsci->h, cd); 103854f9247bSFrank Filz return rc; 1039b2f42f1dSChuck Lever 1040b2f42f1dSChuck Lever null_verifier: 1041b2f42f1dSChuck Lever return xdr_stream_encode_opaque_auth(xdr, RPC_AUTH_NULL, NULL, 0) > 0; 104291a4762eSKevin Coffman } 104391a4762eSKevin Coffman 10445866efa8SChuck Lever static void gss_free_in_token_pages(struct gssp_in_token *in_token) 10455866efa8SChuck Lever { 10465866efa8SChuck Lever u32 inlen; 10475866efa8SChuck Lever int i; 10485866efa8SChuck Lever 10495866efa8SChuck Lever i = 0; 10505866efa8SChuck Lever inlen = in_token->page_len; 10515866efa8SChuck Lever while (inlen) { 10525866efa8SChuck Lever if (in_token->pages[i]) 10535866efa8SChuck Lever put_page(in_token->pages[i]); 10545866efa8SChuck Lever inlen -= inlen > PAGE_SIZE ? PAGE_SIZE : inlen; 10555866efa8SChuck Lever } 10565866efa8SChuck Lever 10575866efa8SChuck Lever kfree(in_token->pages); 10585866efa8SChuck Lever in_token->pages = NULL; 10595866efa8SChuck Lever } 10605866efa8SChuck Lever 10615866efa8SChuck Lever static int gss_read_proxy_verf(struct svc_rqst *rqstp, 1062438623a0SChuck Lever struct rpc_gss_wire_cred *gc, 1063030d794bSSimo Sorce struct xdr_netobj *in_handle, 1064030d794bSSimo Sorce struct gssp_in_token *in_token) 1065030d794bSSimo Sorce { 1066c020fa69SChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 1067d48c8124SMartijn de Gouw unsigned int length, pgto_offs, pgfrom_offs; 10684d51366dSChuck Lever int pages, i, pgto, pgfrom; 1069c020fa69SChuck Lever size_t to_offs, from_offs; 1070c020fa69SChuck Lever u32 inlen; 1071030d794bSSimo Sorce 10724d51366dSChuck Lever if (dup_netobj(in_handle, &gc->gc_ctx)) 10734d51366dSChuck Lever return SVC_CLOSE; 1074030d794bSSimo Sorce 1075c020fa69SChuck Lever /* 1076c020fa69SChuck Lever * RFC 2203 Section 5.2.2 1077c020fa69SChuck Lever * 1078c020fa69SChuck Lever * struct rpc_gss_init_arg { 1079c020fa69SChuck Lever * opaque gss_token<>; 1080c020fa69SChuck Lever * }; 1081c020fa69SChuck Lever */ 1082c020fa69SChuck Lever if (xdr_stream_decode_u32(xdr, &inlen) < 0) 1083c020fa69SChuck Lever goto out_denied_free; 1084c020fa69SChuck Lever if (inlen > xdr_stream_remaining(xdr)) 1085c020fa69SChuck Lever goto out_denied_free; 1086030d794bSSimo Sorce 10875866efa8SChuck Lever pages = DIV_ROUND_UP(inlen, PAGE_SIZE); 10885866efa8SChuck Lever in_token->pages = kcalloc(pages, sizeof(struct page *), GFP_KERNEL); 1089c020fa69SChuck Lever if (!in_token->pages) 1090c020fa69SChuck Lever goto out_denied_free; 10915866efa8SChuck Lever in_token->page_base = 0; 1092030d794bSSimo Sorce in_token->page_len = inlen; 10935866efa8SChuck Lever for (i = 0; i < pages; i++) { 10945866efa8SChuck Lever in_token->pages[i] = alloc_page(GFP_KERNEL); 10955866efa8SChuck Lever if (!in_token->pages[i]) { 10965866efa8SChuck Lever gss_free_in_token_pages(in_token); 1097c020fa69SChuck Lever goto out_denied_free; 10985866efa8SChuck Lever } 10995866efa8SChuck Lever } 1100030d794bSSimo Sorce 1101c020fa69SChuck Lever length = min_t(unsigned int, inlen, (char *)xdr->end - (char *)xdr->p); 1102c020fa69SChuck Lever memcpy(page_address(in_token->pages[0]), xdr->p, length); 11035866efa8SChuck Lever inlen -= length; 11045866efa8SChuck Lever 1105d48c8124SMartijn de Gouw to_offs = length; 1106d48c8124SMartijn de Gouw from_offs = rqstp->rq_arg.page_base; 11075866efa8SChuck Lever while (inlen) { 1108d48c8124SMartijn de Gouw pgto = to_offs >> PAGE_SHIFT; 1109d48c8124SMartijn de Gouw pgfrom = from_offs >> PAGE_SHIFT; 1110d48c8124SMartijn de Gouw pgto_offs = to_offs & ~PAGE_MASK; 1111d48c8124SMartijn de Gouw pgfrom_offs = from_offs & ~PAGE_MASK; 1112d48c8124SMartijn de Gouw 1113d48c8124SMartijn de Gouw length = min_t(unsigned int, inlen, 1114d48c8124SMartijn de Gouw min_t(unsigned int, PAGE_SIZE - pgto_offs, 1115d48c8124SMartijn de Gouw PAGE_SIZE - pgfrom_offs)); 1116d48c8124SMartijn de Gouw memcpy(page_address(in_token->pages[pgto]) + pgto_offs, 1117d48c8124SMartijn de Gouw page_address(rqstp->rq_arg.pages[pgfrom]) + pgfrom_offs, 11185866efa8SChuck Lever length); 11195866efa8SChuck Lever 1120d48c8124SMartijn de Gouw to_offs += length; 1121d48c8124SMartijn de Gouw from_offs += length; 11225866efa8SChuck Lever inlen -= length; 11235866efa8SChuck Lever } 1124030d794bSSimo Sorce return 0; 1125c020fa69SChuck Lever 1126c020fa69SChuck Lever out_denied_free: 1127c020fa69SChuck Lever kfree(in_handle->data); 1128c020fa69SChuck Lever return SVC_DENIED; 1129030d794bSSimo Sorce } 1130030d794bSSimo Sorce 1131b2f42f1dSChuck Lever /* 1132b2f42f1dSChuck Lever * RFC 2203, Section 5.2.3.1. 1133b2f42f1dSChuck Lever * 1134b2f42f1dSChuck Lever * struct rpc_gss_init_res { 1135b2f42f1dSChuck Lever * opaque handle<>; 1136b2f42f1dSChuck Lever * unsigned int gss_major; 1137b2f42f1dSChuck Lever * unsigned int gss_minor; 1138b2f42f1dSChuck Lever * unsigned int seq_window; 1139b2f42f1dSChuck Lever * opaque gss_token<>; 1140b2f42f1dSChuck Lever * }; 1141b2f42f1dSChuck Lever */ 1142b2f42f1dSChuck Lever static bool 1143b2f42f1dSChuck Lever svcxdr_encode_gss_init_res(struct xdr_stream *xdr, 1144b2f42f1dSChuck Lever struct xdr_netobj *handle, 1145b2f42f1dSChuck Lever struct xdr_netobj *gss_token, 1146b2f42f1dSChuck Lever unsigned int major_status, 1147b2f42f1dSChuck Lever unsigned int minor_status, u32 seq_num) 1148fc2952a2SSimo Sorce { 1149b2f42f1dSChuck Lever if (xdr_stream_encode_opaque(xdr, handle->data, handle->len) < 0) 1150b2f42f1dSChuck Lever return false; 1151b2f42f1dSChuck Lever if (xdr_stream_encode_u32(xdr, major_status) < 0) 1152b2f42f1dSChuck Lever return false; 1153b2f42f1dSChuck Lever if (xdr_stream_encode_u32(xdr, minor_status) < 0) 1154b2f42f1dSChuck Lever return false; 1155b2f42f1dSChuck Lever if (xdr_stream_encode_u32(xdr, seq_num) < 0) 1156b2f42f1dSChuck Lever return false; 1157b2f42f1dSChuck Lever if (xdr_stream_encode_opaque(xdr, gss_token->data, gss_token->len) < 0) 1158b2f42f1dSChuck Lever return false; 1159b2f42f1dSChuck Lever return true; 1160fc2952a2SSimo Sorce } 1161fc2952a2SSimo Sorce 11621da177e4SLinus Torvalds /* 116321fcd02bSJ. Bruce Fields * Having read the cred already and found we're in the context 116421fcd02bSJ. Bruce Fields * initiation case, read the verifier and initiate (or check the results 116521fcd02bSJ. Bruce Fields * of) upcalls to userspace for help with context initiation. If 116621fcd02bSJ. Bruce Fields * the upcall results are available, write the verifier and result. 116721fcd02bSJ. Bruce Fields * Otherwise, drop the request pending an answer to the upcall. 116821fcd02bSJ. Bruce Fields */ 1169c020fa69SChuck Lever static int 1170c020fa69SChuck Lever svcauth_gss_legacy_init(struct svc_rqst *rqstp, 1171438623a0SChuck Lever struct rpc_gss_wire_cred *gc) 117221fcd02bSJ. Bruce Fields { 1173c020fa69SChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 117421fcd02bSJ. Bruce Fields struct rsi *rsip, rsikey; 1175c020fa69SChuck Lever __be32 *p; 1176c020fa69SChuck Lever u32 len; 1177980e5a40SJ. Bruce Fields int ret; 1178b8be5674SVasily Averin struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id); 117921fcd02bSJ. Bruce Fields 118021fcd02bSJ. Bruce Fields memset(&rsikey, 0, sizeof(rsikey)); 11811cbfb921SChuck Lever if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx)) 11821cbfb921SChuck Lever return SVC_CLOSE; 1183c020fa69SChuck Lever 1184c020fa69SChuck Lever /* 1185c020fa69SChuck Lever * RFC 2203 Section 5.2.2 1186c020fa69SChuck Lever * 1187c020fa69SChuck Lever * struct rpc_gss_init_arg { 1188c020fa69SChuck Lever * opaque gss_token<>; 1189c020fa69SChuck Lever * }; 1190c020fa69SChuck Lever */ 1191c020fa69SChuck Lever if (xdr_stream_decode_u32(xdr, &len) < 0) { 11921cbfb921SChuck Lever kfree(rsikey.in_handle.data); 11931cbfb921SChuck Lever return SVC_DENIED; 11941cbfb921SChuck Lever } 1195c020fa69SChuck Lever p = xdr_inline_decode(xdr, len); 1196c020fa69SChuck Lever if (!p) { 1197c020fa69SChuck Lever kfree(rsikey.in_handle.data); 1198c020fa69SChuck Lever return SVC_DENIED; 1199c020fa69SChuck Lever } 1200c020fa69SChuck Lever rsikey.in_token.data = kmalloc(len, GFP_KERNEL); 1201c020fa69SChuck Lever if (ZERO_OR_NULL_PTR(rsikey.in_token.data)) { 12021cbfb921SChuck Lever kfree(rsikey.in_handle.data); 12031cbfb921SChuck Lever return SVC_CLOSE; 12041cbfb921SChuck Lever } 1205c020fa69SChuck Lever memcpy(rsikey.in_token.data, p, len); 1206c020fa69SChuck Lever rsikey.in_token.len = len; 120721fcd02bSJ. Bruce Fields 120821fcd02bSJ. Bruce Fields /* Perform upcall, or find upcall result: */ 1209a1db410dSStanislav Kinsbursky rsip = rsi_lookup(sn->rsi_cache, &rsikey); 121021fcd02bSJ. Bruce Fields rsi_free(&rsikey); 121121fcd02bSJ. Bruce Fields if (!rsip) 12121ebede86SNeilBrown return SVC_CLOSE; 1213a1db410dSStanislav Kinsbursky if (cache_check(sn->rsi_cache, &rsip->h, &rqstp->rq_chandle) < 0) 121421fcd02bSJ. Bruce Fields /* No upcall result: */ 12151ebede86SNeilBrown return SVC_CLOSE; 12162ed5282cSNeilBrown 12171ebede86SNeilBrown ret = SVC_CLOSE; 1218b2f42f1dSChuck Lever if (!svcauth_gss_proc_init_verf(sn->rsc_cache, rqstp, &rsip->out_handle, 1219b2f42f1dSChuck Lever &rsip->major_status, GSS_SEQ_WIN)) 1220980e5a40SJ. Bruce Fields goto out; 12214bcf0343SChuck Lever if (!svcxdr_set_accept_stat(rqstp)) 1222b2f42f1dSChuck Lever goto out; 1223b2f42f1dSChuck Lever if (!svcxdr_encode_gss_init_res(&rqstp->rq_res_stream, &rsip->out_handle, 1224b2f42f1dSChuck Lever &rsip->out_token, rsip->major_status, 1225b2f42f1dSChuck Lever rsip->minor_status, GSS_SEQ_WIN)) 1226980e5a40SJ. Bruce Fields goto out; 12272ed5282cSNeilBrown 1228980e5a40SJ. Bruce Fields ret = SVC_COMPLETE; 1229980e5a40SJ. Bruce Fields out: 1230a1db410dSStanislav Kinsbursky cache_put(&rsip->h, sn->rsi_cache); 1231980e5a40SJ. Bruce Fields return ret; 123221fcd02bSJ. Bruce Fields } 123321fcd02bSJ. Bruce Fields 1234030d794bSSimo Sorce static int gss_proxy_save_rsc(struct cache_detail *cd, 1235030d794bSSimo Sorce struct gssp_upcall_data *ud, 1236030d794bSSimo Sorce uint64_t *handle) 1237030d794bSSimo Sorce { 1238030d794bSSimo Sorce struct rsc rsci, *rscp = NULL; 1239030d794bSSimo Sorce static atomic64_t ctxhctr; 1240030d794bSSimo Sorce long long ctxh; 1241030d794bSSimo Sorce struct gss_api_mech *gm = NULL; 1242294ec5b8SArnd Bergmann time64_t expiry; 1243f6260b98SColin Ian King int status; 1244030d794bSSimo Sorce 1245030d794bSSimo Sorce memset(&rsci, 0, sizeof(rsci)); 1246030d794bSSimo Sorce /* context handle */ 1247030d794bSSimo Sorce status = -ENOMEM; 1248030d794bSSimo Sorce /* the handle needs to be just a unique id, 1249030d794bSSimo Sorce * use a static counter */ 1250030d794bSSimo Sorce ctxh = atomic64_inc_return(&ctxhctr); 1251030d794bSSimo Sorce 1252030d794bSSimo Sorce /* make a copy for the caller */ 1253030d794bSSimo Sorce *handle = ctxh; 1254030d794bSSimo Sorce 1255030d794bSSimo Sorce /* make a copy for the rsc cache */ 1256030d794bSSimo Sorce if (dup_to_netobj(&rsci.handle, (char *)handle, sizeof(uint64_t))) 1257030d794bSSimo Sorce goto out; 1258030d794bSSimo Sorce rscp = rsc_lookup(cd, &rsci); 1259030d794bSSimo Sorce if (!rscp) 1260030d794bSSimo Sorce goto out; 1261030d794bSSimo Sorce 1262030d794bSSimo Sorce /* creds */ 1263030d794bSSimo Sorce if (!ud->found_creds) { 1264030d794bSSimo Sorce /* userspace seem buggy, we should always get at least a 1265030d794bSSimo Sorce * mapping to nobody */ 12663be34555SJ. Bruce Fields goto out; 1267030d794bSSimo Sorce } else { 12683d96208cSRoberto Bergantinos Corpas struct timespec64 boot; 1269030d794bSSimo Sorce 1270030d794bSSimo Sorce /* steal creds */ 1271030d794bSSimo Sorce rsci.cred = ud->creds; 1272030d794bSSimo Sorce memset(&ud->creds, 0, sizeof(struct svc_cred)); 1273030d794bSSimo Sorce 1274030d794bSSimo Sorce status = -EOPNOTSUPP; 1275030d794bSSimo Sorce /* get mech handle from OID */ 1276030d794bSSimo Sorce gm = gss_mech_get_by_OID(&ud->mech_oid); 1277030d794bSSimo Sorce if (!gm) 1278030d794bSSimo Sorce goto out; 12797193bd17SJ. Bruce Fields rsci.cred.cr_gss_mech = gm; 1280030d794bSSimo Sorce 1281030d794bSSimo Sorce status = -EINVAL; 1282030d794bSSimo Sorce /* mech-specific data: */ 1283030d794bSSimo Sorce status = gss_import_sec_context(ud->out_handle.data, 1284030d794bSSimo Sorce ud->out_handle.len, 1285030d794bSSimo Sorce gm, &rsci.mechctx, 1286030d794bSSimo Sorce &expiry, GFP_KERNEL); 1287030d794bSSimo Sorce if (status) 1288030d794bSSimo Sorce goto out; 12893d96208cSRoberto Bergantinos Corpas 12903d96208cSRoberto Bergantinos Corpas getboottime64(&boot); 12913d96208cSRoberto Bergantinos Corpas expiry -= boot.tv_sec; 1292030d794bSSimo Sorce } 1293030d794bSSimo Sorce 1294030d794bSSimo Sorce rsci.h.expiry_time = expiry; 1295030d794bSSimo Sorce rscp = rsc_update(cd, &rsci, rscp); 1296030d794bSSimo Sorce status = 0; 1297030d794bSSimo Sorce out: 1298030d794bSSimo Sorce rsc_free(&rsci); 1299030d794bSSimo Sorce if (rscp) 1300030d794bSSimo Sorce cache_put(&rscp->h, cd); 1301030d794bSSimo Sorce else 1302030d794bSSimo Sorce status = -ENOMEM; 1303030d794bSSimo Sorce return status; 1304030d794bSSimo Sorce } 1305030d794bSSimo Sorce 1306030d794bSSimo Sorce static int svcauth_gss_proxy_init(struct svc_rqst *rqstp, 1307438623a0SChuck Lever struct rpc_gss_wire_cred *gc) 1308030d794bSSimo Sorce { 1309030d794bSSimo Sorce struct xdr_netobj cli_handle; 1310030d794bSSimo Sorce struct gssp_upcall_data ud; 1311030d794bSSimo Sorce uint64_t handle; 1312030d794bSSimo Sorce int status; 1313030d794bSSimo Sorce int ret; 1314b8be5674SVasily Averin struct net *net = SVC_NET(rqstp); 1315030d794bSSimo Sorce struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1316030d794bSSimo Sorce 1317030d794bSSimo Sorce memset(&ud, 0, sizeof(ud)); 1318438623a0SChuck Lever ret = gss_read_proxy_verf(rqstp, gc, &ud.in_handle, &ud.in_token); 1319030d794bSSimo Sorce if (ret) 1320030d794bSSimo Sorce return ret; 1321030d794bSSimo Sorce 1322030d794bSSimo Sorce ret = SVC_CLOSE; 1323030d794bSSimo Sorce 1324030d794bSSimo Sorce /* Perform synchronous upcall to gss-proxy */ 1325030d794bSSimo Sorce status = gssp_accept_sec_context_upcall(net, &ud); 1326030d794bSSimo Sorce if (status) 1327030d794bSSimo Sorce goto out; 1328030d794bSSimo Sorce 132910b9d99aSChuck Lever trace_rpcgss_svc_accept_upcall(rqstp, ud.major_status, ud.minor_status); 1330030d794bSSimo Sorce 1331030d794bSSimo Sorce switch (ud.major_status) { 1332030d794bSSimo Sorce case GSS_S_CONTINUE_NEEDED: 1333030d794bSSimo Sorce cli_handle = ud.out_handle; 1334030d794bSSimo Sorce break; 1335030d794bSSimo Sorce case GSS_S_COMPLETE: 1336030d794bSSimo Sorce status = gss_proxy_save_rsc(sn->rsc_cache, &ud, &handle); 133728155524SChuck Lever if (status) 1338030d794bSSimo Sorce goto out; 1339030d794bSSimo Sorce cli_handle.data = (u8 *)&handle; 1340030d794bSSimo Sorce cli_handle.len = sizeof(handle); 1341030d794bSSimo Sorce break; 1342030d794bSSimo Sorce default: 1343030d794bSSimo Sorce goto out; 1344030d794bSSimo Sorce } 1345030d794bSSimo Sorce 1346b2f42f1dSChuck Lever if (!svcauth_gss_proc_init_verf(sn->rsc_cache, rqstp, &cli_handle, 1347b2f42f1dSChuck Lever &ud.major_status, GSS_SEQ_WIN)) 1348030d794bSSimo Sorce goto out; 13494bcf0343SChuck Lever if (!svcxdr_set_accept_stat(rqstp)) 1350b2f42f1dSChuck Lever goto out; 1351b2f42f1dSChuck Lever if (!svcxdr_encode_gss_init_res(&rqstp->rq_res_stream, &cli_handle, 1352b2f42f1dSChuck Lever &ud.out_token, ud.major_status, 1353b2f42f1dSChuck Lever ud.minor_status, GSS_SEQ_WIN)) 1354030d794bSSimo Sorce goto out; 1355030d794bSSimo Sorce 1356030d794bSSimo Sorce ret = SVC_COMPLETE; 1357030d794bSSimo Sorce out: 13585866efa8SChuck Lever gss_free_in_token_pages(&ud.in_token); 1359030d794bSSimo Sorce gssp_free_upcall_data(&ud); 1360030d794bSSimo Sorce return ret; 1361030d794bSSimo Sorce } 1362030d794bSSimo Sorce 13630fdc2678SJeff Layton /* 13640fdc2678SJeff Layton * Try to set the sn->use_gss_proxy variable to a new value. We only allow 13650fdc2678SJeff Layton * it to be changed if it's currently undefined (-1). If it's any other value 13660fdc2678SJeff Layton * then return -EBUSY unless the type wouldn't have changed anyway. 13670fdc2678SJeff Layton */ 13680fdc2678SJeff Layton static int set_gss_proxy(struct net *net, int type) 13690fdc2678SJeff Layton { 13700fdc2678SJeff Layton struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 13710fdc2678SJeff Layton int ret; 13720fdc2678SJeff Layton 13730fdc2678SJeff Layton WARN_ON_ONCE(type != 0 && type != 1); 13740fdc2678SJeff Layton ret = cmpxchg(&sn->use_gss_proxy, -1, type); 13750fdc2678SJeff Layton if (ret != -1 && ret != type) 13760fdc2678SJeff Layton return -EBUSY; 13770fdc2678SJeff Layton return 0; 13780fdc2678SJeff Layton } 1379030d794bSSimo Sorce 1380030d794bSSimo Sorce static bool use_gss_proxy(struct net *net) 1381030d794bSSimo Sorce { 1382030d794bSSimo Sorce struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1383030d794bSSimo Sorce 13840fdc2678SJeff Layton /* If use_gss_proxy is still undefined, then try to disable it */ 13850fdc2678SJeff Layton if (sn->use_gss_proxy == -1) 13860fdc2678SJeff Layton set_gss_proxy(net, 0); 1387030d794bSSimo Sorce return sn->use_gss_proxy; 1388030d794bSSimo Sorce } 1389030d794bSSimo Sorce 13904ac5e7a6SChuck Lever static noinline_for_stack int 13914ac5e7a6SChuck Lever svcauth_gss_proc_init(struct svc_rqst *rqstp, struct rpc_gss_wire_cred *gc) 13924ac5e7a6SChuck Lever { 1393c020fa69SChuck Lever struct xdr_stream *xdr = &rqstp->rq_arg_stream; 1394c020fa69SChuck Lever u32 flavor, len; 1395c020fa69SChuck Lever void *body; 139620ebe927SChuck Lever 1397c020fa69SChuck Lever /* Call's verf field: */ 1398c020fa69SChuck Lever if (xdr_stream_decode_opaque_auth(xdr, &flavor, &body, &len) < 0) 1399c020fa69SChuck Lever return SVC_GARBAGE; 1400c020fa69SChuck Lever if (flavor != RPC_AUTH_NULL || len != 0) { 1401c020fa69SChuck Lever rqstp->rq_auth_stat = rpc_autherr_badverf; 140220ebe927SChuck Lever return SVC_DENIED; 1403c020fa69SChuck Lever } 140420ebe927SChuck Lever 140520ebe927SChuck Lever if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0) { 140620ebe927SChuck Lever rqstp->rq_auth_stat = rpc_autherr_badcred; 140720ebe927SChuck Lever return SVC_DENIED; 140820ebe927SChuck Lever } 140920ebe927SChuck Lever 14104ac5e7a6SChuck Lever if (!use_gss_proxy(SVC_NET(rqstp))) 14114ac5e7a6SChuck Lever return svcauth_gss_legacy_init(rqstp, gc); 14124ac5e7a6SChuck Lever return svcauth_gss_proxy_init(rqstp, gc); 14134ac5e7a6SChuck Lever } 14144ac5e7a6SChuck Lever 14150ff3bab5SJ. Bruce Fields #ifdef CONFIG_PROC_FS 14160ff3bab5SJ. Bruce Fields 1417030d794bSSimo Sorce static ssize_t write_gssp(struct file *file, const char __user *buf, 1418030d794bSSimo Sorce size_t count, loff_t *ppos) 1419030d794bSSimo Sorce { 1420359745d7SMuchun Song struct net *net = pde_data(file_inode(file)); 1421030d794bSSimo Sorce char tbuf[20]; 1422030d794bSSimo Sorce unsigned long i; 1423030d794bSSimo Sorce int res; 1424030d794bSSimo Sorce 1425030d794bSSimo Sorce if (*ppos || count > sizeof(tbuf)-1) 1426030d794bSSimo Sorce return -EINVAL; 1427030d794bSSimo Sorce if (copy_from_user(tbuf, buf, count)) 1428030d794bSSimo Sorce return -EFAULT; 1429030d794bSSimo Sorce 1430030d794bSSimo Sorce tbuf[count] = 0; 1431030d794bSSimo Sorce res = kstrtoul(tbuf, 0, &i); 1432030d794bSSimo Sorce if (res) 1433030d794bSSimo Sorce return res; 1434030d794bSSimo Sorce if (i != 1) 1435030d794bSSimo Sorce return -EINVAL; 1436a92e5eb1SJeff Layton res = set_gssp_clnt(net); 1437030d794bSSimo Sorce if (res) 1438030d794bSSimo Sorce return res; 1439a92e5eb1SJeff Layton res = set_gss_proxy(net, 1); 1440030d794bSSimo Sorce if (res) 1441030d794bSSimo Sorce return res; 1442030d794bSSimo Sorce return count; 1443030d794bSSimo Sorce } 1444030d794bSSimo Sorce 1445030d794bSSimo Sorce static ssize_t read_gssp(struct file *file, char __user *buf, 1446030d794bSSimo Sorce size_t count, loff_t *ppos) 1447030d794bSSimo Sorce { 1448359745d7SMuchun Song struct net *net = pde_data(file_inode(file)); 14491654a04cSJeff Layton struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1450030d794bSSimo Sorce unsigned long p = *ppos; 1451030d794bSSimo Sorce char tbuf[10]; 1452030d794bSSimo Sorce size_t len; 1453030d794bSSimo Sorce 14541654a04cSJeff Layton snprintf(tbuf, sizeof(tbuf), "%d\n", sn->use_gss_proxy); 1455030d794bSSimo Sorce len = strlen(tbuf); 1456030d794bSSimo Sorce if (p >= len) 1457030d794bSSimo Sorce return 0; 1458030d794bSSimo Sorce len -= p; 1459030d794bSSimo Sorce if (len > count) 1460030d794bSSimo Sorce len = count; 1461030d794bSSimo Sorce if (copy_to_user(buf, (void *)(tbuf+p), len)) 1462030d794bSSimo Sorce return -EFAULT; 1463030d794bSSimo Sorce *ppos += len; 1464030d794bSSimo Sorce return len; 1465030d794bSSimo Sorce } 1466030d794bSSimo Sorce 146797a32539SAlexey Dobriyan static const struct proc_ops use_gss_proxy_proc_ops = { 146897a32539SAlexey Dobriyan .proc_open = nonseekable_open, 146997a32539SAlexey Dobriyan .proc_write = write_gssp, 147097a32539SAlexey Dobriyan .proc_read = read_gssp, 1471030d794bSSimo Sorce }; 1472030d794bSSimo Sorce 1473030d794bSSimo Sorce static int create_use_gss_proxy_proc_entry(struct net *net) 1474030d794bSSimo Sorce { 1475030d794bSSimo Sorce struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1476030d794bSSimo Sorce struct proc_dir_entry **p = &sn->use_gssp_proc; 1477030d794bSSimo Sorce 1478030d794bSSimo Sorce sn->use_gss_proxy = -1; 1479d6444062SJoe Perches *p = proc_create_data("use-gss-proxy", S_IFREG | 0600, 1480030d794bSSimo Sorce sn->proc_net_rpc, 148197a32539SAlexey Dobriyan &use_gss_proxy_proc_ops, net); 1482030d794bSSimo Sorce if (!*p) 1483030d794bSSimo Sorce return -ENOMEM; 1484030d794bSSimo Sorce init_gssp_clnt(sn); 1485030d794bSSimo Sorce return 0; 1486030d794bSSimo Sorce } 1487030d794bSSimo Sorce 1488030d794bSSimo Sorce static void destroy_use_gss_proxy_proc_entry(struct net *net) 1489030d794bSSimo Sorce { 1490030d794bSSimo Sorce struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1491030d794bSSimo Sorce 1492030d794bSSimo Sorce if (sn->use_gssp_proc) { 1493030d794bSSimo Sorce remove_proc_entry("use-gss-proxy", sn->proc_net_rpc); 1494030d794bSSimo Sorce clear_gssp_clnt(sn); 1495030d794bSSimo Sorce } 1496030d794bSSimo Sorce } 1497bdb12fb1SChuck Lever 1498bdb12fb1SChuck Lever static ssize_t read_gss_krb5_enctypes(struct file *file, char __user *buf, 1499bdb12fb1SChuck Lever size_t count, loff_t *ppos) 1500bdb12fb1SChuck Lever { 1501bdb12fb1SChuck Lever struct rpcsec_gss_oid oid = { 1502bdb12fb1SChuck Lever .len = 9, 1503bdb12fb1SChuck Lever .data = "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02", 1504bdb12fb1SChuck Lever }; 1505bdb12fb1SChuck Lever struct gss_api_mech *mech; 1506bdb12fb1SChuck Lever ssize_t ret; 1507bdb12fb1SChuck Lever 1508bdb12fb1SChuck Lever mech = gss_mech_get_by_OID(&oid); 1509bdb12fb1SChuck Lever if (!mech) 1510bdb12fb1SChuck Lever return 0; 1511bdb12fb1SChuck Lever if (!mech->gm_upcall_enctypes) { 1512bdb12fb1SChuck Lever gss_mech_put(mech); 1513bdb12fb1SChuck Lever return 0; 1514bdb12fb1SChuck Lever } 1515bdb12fb1SChuck Lever 1516bdb12fb1SChuck Lever ret = simple_read_from_buffer(buf, count, ppos, 1517bdb12fb1SChuck Lever mech->gm_upcall_enctypes, 1518bdb12fb1SChuck Lever strlen(mech->gm_upcall_enctypes)); 1519bdb12fb1SChuck Lever gss_mech_put(mech); 1520bdb12fb1SChuck Lever return ret; 1521bdb12fb1SChuck Lever } 1522bdb12fb1SChuck Lever 1523bdb12fb1SChuck Lever static const struct proc_ops gss_krb5_enctypes_proc_ops = { 1524bdb12fb1SChuck Lever .proc_open = nonseekable_open, 1525bdb12fb1SChuck Lever .proc_read = read_gss_krb5_enctypes, 1526bdb12fb1SChuck Lever }; 1527bdb12fb1SChuck Lever 1528bdb12fb1SChuck Lever static int create_krb5_enctypes_proc_entry(struct net *net) 1529bdb12fb1SChuck Lever { 1530bdb12fb1SChuck Lever struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1531bdb12fb1SChuck Lever 15322172e84eSChuck Lever sn->gss_krb5_enctypes = 15332172e84eSChuck Lever proc_create_data("gss_krb5_enctypes", S_IFREG | 0444, 15342172e84eSChuck Lever sn->proc_net_rpc, &gss_krb5_enctypes_proc_ops, 15352172e84eSChuck Lever net); 15362172e84eSChuck Lever return sn->gss_krb5_enctypes ? 0 : -ENOMEM; 1537bdb12fb1SChuck Lever } 1538bdb12fb1SChuck Lever 1539bdb12fb1SChuck Lever static void destroy_krb5_enctypes_proc_entry(struct net *net) 1540bdb12fb1SChuck Lever { 1541bdb12fb1SChuck Lever struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1542bdb12fb1SChuck Lever 15432172e84eSChuck Lever if (sn->gss_krb5_enctypes) 1544bdb12fb1SChuck Lever remove_proc_entry("gss_krb5_enctypes", sn->proc_net_rpc); 1545bdb12fb1SChuck Lever } 1546bdb12fb1SChuck Lever 15470ff3bab5SJ. Bruce Fields #else /* CONFIG_PROC_FS */ 15480ff3bab5SJ. Bruce Fields 15490ff3bab5SJ. Bruce Fields static int create_use_gss_proxy_proc_entry(struct net *net) 15500ff3bab5SJ. Bruce Fields { 15510ff3bab5SJ. Bruce Fields return 0; 15520ff3bab5SJ. Bruce Fields } 15530ff3bab5SJ. Bruce Fields 15540ff3bab5SJ. Bruce Fields static void destroy_use_gss_proxy_proc_entry(struct net *net) {} 1555030d794bSSimo Sorce 1556bdb12fb1SChuck Lever static int create_krb5_enctypes_proc_entry(struct net *net) 1557bdb12fb1SChuck Lever { 1558bdb12fb1SChuck Lever return 0; 1559bdb12fb1SChuck Lever } 1560bdb12fb1SChuck Lever 1561bdb12fb1SChuck Lever static void destroy_krb5_enctypes_proc_entry(struct net *net) {} 1562bdb12fb1SChuck Lever 1563030d794bSSimo Sorce #endif /* CONFIG_PROC_FS */ 1564030d794bSSimo Sorce 156521fcd02bSJ. Bruce Fields /* 1566b0bc5347SChuck Lever * The Call's credential body should contain a struct rpc_gss_cred_t. 1567b0bc5347SChuck Lever * 1568b0bc5347SChuck Lever * RFC 2203 Section 5 1569b0bc5347SChuck Lever * 1570b0bc5347SChuck Lever * struct rpc_gss_cred_t { 1571b0bc5347SChuck Lever * union switch (unsigned int version) { 1572b0bc5347SChuck Lever * case RPCSEC_GSS_VERS_1: 1573b0bc5347SChuck Lever * struct { 1574b0bc5347SChuck Lever * rpc_gss_proc_t gss_proc; 1575b0bc5347SChuck Lever * unsigned int seq_num; 1576b0bc5347SChuck Lever * rpc_gss_service_t service; 1577b0bc5347SChuck Lever * opaque handle<>; 1578b0bc5347SChuck Lever * } rpc_gss_cred_vers_1_t; 1579b0bc5347SChuck Lever * } 1580b0bc5347SChuck Lever * }; 1581b0bc5347SChuck Lever */ 1582b0bc5347SChuck Lever static bool 1583b0bc5347SChuck Lever svcauth_gss_decode_credbody(struct xdr_stream *xdr, 1584b0bc5347SChuck Lever struct rpc_gss_wire_cred *gc, 1585b0bc5347SChuck Lever __be32 **rpcstart) 1586b0bc5347SChuck Lever { 1587b0bc5347SChuck Lever ssize_t handle_len; 1588b0bc5347SChuck Lever u32 body_len; 1589b0bc5347SChuck Lever __be32 *p; 1590b0bc5347SChuck Lever 1591b0bc5347SChuck Lever p = xdr_inline_decode(xdr, XDR_UNIT); 1592b0bc5347SChuck Lever if (!p) 1593b0bc5347SChuck Lever return false; 1594b0bc5347SChuck Lever /* 1595b0bc5347SChuck Lever * start of rpc packet is 7 u32's back from here: 1596b0bc5347SChuck Lever * xid direction rpcversion prog vers proc flavour 1597b0bc5347SChuck Lever */ 1598b0bc5347SChuck Lever *rpcstart = p - 7; 1599b0bc5347SChuck Lever body_len = be32_to_cpup(p); 1600b0bc5347SChuck Lever if (body_len > RPC_MAX_AUTH_SIZE) 1601b0bc5347SChuck Lever return false; 1602b0bc5347SChuck Lever 1603b0bc5347SChuck Lever /* struct rpc_gss_cred_t */ 1604b0bc5347SChuck Lever if (xdr_stream_decode_u32(xdr, &gc->gc_v) < 0) 1605b0bc5347SChuck Lever return false; 1606b0bc5347SChuck Lever if (xdr_stream_decode_u32(xdr, &gc->gc_proc) < 0) 1607b0bc5347SChuck Lever return false; 1608b0bc5347SChuck Lever if (xdr_stream_decode_u32(xdr, &gc->gc_seq) < 0) 1609b0bc5347SChuck Lever return false; 1610b0bc5347SChuck Lever if (xdr_stream_decode_u32(xdr, &gc->gc_svc) < 0) 1611b0bc5347SChuck Lever return false; 1612b0bc5347SChuck Lever handle_len = xdr_stream_decode_opaque_inline(xdr, 1613b0bc5347SChuck Lever (void **)&gc->gc_ctx.data, 1614b0bc5347SChuck Lever body_len); 1615b0bc5347SChuck Lever if (handle_len < 0) 1616b0bc5347SChuck Lever return false; 1617b0bc5347SChuck Lever if (body_len != XDR_UNIT * 5 + xdr_align_size(handle_len)) 1618b0bc5347SChuck Lever return false; 1619b0bc5347SChuck Lever 1620b0bc5347SChuck Lever gc->gc_ctx.len = handle_len; 1621b0bc5347SChuck Lever return true; 1622b0bc5347SChuck Lever } 1623b0bc5347SChuck Lever 1624b0bc5347SChuck Lever /** 1625b0bc5347SChuck Lever * svcauth_gss_accept - Decode and validate incoming RPC_AUTH_GSS credential 1626b0bc5347SChuck Lever * @rqstp: RPC transaction 1627b0bc5347SChuck Lever * 1628b0bc5347SChuck Lever * Return values: 1629b0bc5347SChuck Lever * %SVC_OK: Success 1630b0bc5347SChuck Lever * %SVC_COMPLETE: GSS context lifetime event 1631b0bc5347SChuck Lever * %SVC_DENIED: Credential or verifier is not valid 1632b0bc5347SChuck Lever * %SVC_GARBAGE: Failed to decode credential or verifier 1633b0bc5347SChuck Lever * %SVC_CLOSE: Temporary failure 1634b0bc5347SChuck Lever * 1635b0bc5347SChuck Lever * The rqstp->rq_auth_stat field is also set (see RFCs 2203 and 5531). 16361da177e4SLinus Torvalds */ 1637*78c542f9SChuck Lever static enum svc_auth_status 1638438623a0SChuck Lever svcauth_gss_accept(struct svc_rqst *rqstp) 16391da177e4SLinus Torvalds { 16401da177e4SLinus Torvalds struct gss_svc_data *svcdata = rqstp->rq_auth_data; 1641b0bc5347SChuck Lever __be32 *rpcstart; 16421da177e4SLinus Torvalds struct rpc_gss_wire_cred *gc; 16431da177e4SLinus Torvalds struct rsc *rsci = NULL; 16441da177e4SLinus Torvalds int ret; 1645b8be5674SVasily Averin struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id); 16461da177e4SLinus Torvalds 1647438623a0SChuck Lever rqstp->rq_auth_stat = rpc_autherr_badcred; 16481da177e4SLinus Torvalds if (!svcdata) 16491da177e4SLinus Torvalds svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL); 16501da177e4SLinus Torvalds if (!svcdata) 16511da177e4SLinus Torvalds goto auth_err; 16521da177e4SLinus Torvalds rqstp->rq_auth_data = svcdata; 1653db1d6165SChuck Lever svcdata->gsd_databody_offset = 0; 16541da177e4SLinus Torvalds svcdata->rsci = NULL; 16551da177e4SLinus Torvalds gc = &svcdata->clcred; 16561da177e4SLinus Torvalds 1657b0bc5347SChuck Lever if (!svcauth_gss_decode_credbody(&rqstp->rq_arg_stream, gc, &rpcstart)) 16581da177e4SLinus Torvalds goto auth_err; 1659b0bc5347SChuck Lever if (gc->gc_v != RPC_GSS_VERSION) 16601da177e4SLinus Torvalds goto auth_err; 16616734706bSChuck Lever 16621da177e4SLinus Torvalds switch (gc->gc_proc) { 16631da177e4SLinus Torvalds case RPC_GSS_PROC_INIT: 16641da177e4SLinus Torvalds case RPC_GSS_PROC_CONTINUE_INIT: 16656734706bSChuck Lever if (rqstp->rq_proc != 0) 16666734706bSChuck Lever goto auth_err; 16674ac5e7a6SChuck Lever return svcauth_gss_proc_init(rqstp, gc); 16681da177e4SLinus Torvalds case RPC_GSS_PROC_DESTROY: 16696734706bSChuck Lever if (rqstp->rq_proc != 0) 16706734706bSChuck Lever goto auth_err; 16716734706bSChuck Lever fallthrough; 16726734706bSChuck Lever case RPC_GSS_PROC_DATA: 1673438623a0SChuck Lever rqstp->rq_auth_stat = rpcsec_gsserr_credproblem; 1674a1db410dSStanislav Kinsbursky rsci = gss_svc_searchbyctx(sn->rsc_cache, &gc->gc_ctx); 16751da177e4SLinus Torvalds if (!rsci) 16761da177e4SLinus Torvalds goto auth_err; 16770653028eSChuck Lever switch (svcauth_gss_verify_header(rqstp, rsci, rpcstart, gc)) { 16781da177e4SLinus Torvalds case SVC_OK: 16791da177e4SLinus Torvalds break; 16801da177e4SLinus Torvalds case SVC_DENIED: 16811da177e4SLinus Torvalds goto auth_err; 16821da177e4SLinus Torvalds case SVC_DROP: 16831da177e4SLinus Torvalds goto drop; 16841da177e4SLinus Torvalds } 16851da177e4SLinus Torvalds break; 16861da177e4SLinus Torvalds default: 16876734706bSChuck Lever if (rqstp->rq_proc != 0) 16886734706bSChuck Lever goto auth_err; 1689438623a0SChuck Lever rqstp->rq_auth_stat = rpc_autherr_rejectedcred; 16901da177e4SLinus Torvalds goto auth_err; 16911da177e4SLinus Torvalds } 16921da177e4SLinus Torvalds 16931da177e4SLinus Torvalds /* now act upon the command: */ 16941da177e4SLinus Torvalds switch (gc->gc_proc) { 16951da177e4SLinus Torvalds case RPC_GSS_PROC_DESTROY: 169672a1e53aSChuck Lever if (!svcauth_gss_encode_verf(rqstp, rsci->mechctx, gc->gc_seq)) 1697c5e434c9SWei Yongjun goto auth_err; 16984bcf0343SChuck Lever if (!svcxdr_set_accept_stat(rqstp)) 16994bcf0343SChuck Lever goto auth_err; 17002b477c00SNeil Brown /* Delete the entry from the cache_list and call cache_put */ 17012b477c00SNeil Brown sunrpc_cache_unhash(sn->rsc_cache, &rsci->h); 17021da177e4SLinus Torvalds goto complete; 17031da177e4SLinus Torvalds case RPC_GSS_PROC_DATA: 1704438623a0SChuck Lever rqstp->rq_auth_stat = rpcsec_gsserr_ctxproblem; 170572a1e53aSChuck Lever if (!svcauth_gss_encode_verf(rqstp, rsci->mechctx, gc->gc_seq)) 17061da177e4SLinus Torvalds goto auth_err; 17074bcf0343SChuck Lever if (!svcxdr_set_accept_stat(rqstp)) 17084bcf0343SChuck Lever goto auth_err; 1709db1d6165SChuck Lever svcdata->gsd_databody_offset = xdr_stream_pos(&rqstp->rq_res_stream); 17101da177e4SLinus Torvalds rqstp->rq_cred = rsci->cred; 17111da177e4SLinus Torvalds get_group_info(rsci->cred.cr_group_info); 1712438623a0SChuck Lever rqstp->rq_auth_stat = rpc_autherr_badcred; 17131da177e4SLinus Torvalds switch (gc->gc_svc) { 17141da177e4SLinus Torvalds case RPC_GSS_SVC_NONE: 17151da177e4SLinus Torvalds break; 17161da177e4SLinus Torvalds case RPC_GSS_SVC_INTEGRITY: 17177bb0dfb2SChuck Lever /* placeholders for body length and seq. number: */ 17187bb0dfb2SChuck Lever xdr_reserve_space(&rqstp->rq_res_stream, XDR_UNIT * 2); 1719b68e4c5cSChuck Lever if (svcauth_gss_unwrap_integ(rqstp, gc->gc_seq, 1720b68e4c5cSChuck Lever rsci->mechctx)) 1721dd35210eSHarshula Jayasuriya goto garbage_args; 17227bb0dfb2SChuck Lever svcxdr_set_auth_slack(rqstp, RPC_MAX_AUTH_SIZE); 1723b620754bSJ. Bruce Fields break; 1724b620754bSJ. Bruce Fields case RPC_GSS_SVC_PRIVACY: 17257bb0dfb2SChuck Lever /* placeholders for body length and seq. number: */ 17267bb0dfb2SChuck Lever xdr_reserve_space(&rqstp->rq_res_stream, XDR_UNIT * 2); 172742140718SChuck Lever if (svcauth_gss_unwrap_priv(rqstp, gc->gc_seq, 172842140718SChuck Lever rsci->mechctx)) 1729dd35210eSHarshula Jayasuriya goto garbage_args; 17307bb0dfb2SChuck Lever svcxdr_set_auth_slack(rqstp, RPC_MAX_AUTH_SIZE * 2); 17317c9fdcfbSJ. Bruce Fields break; 17321da177e4SLinus Torvalds default: 17331da177e4SLinus Torvalds goto auth_err; 17341da177e4SLinus Torvalds } 17351da177e4SLinus Torvalds svcdata->rsci = rsci; 17361da177e4SLinus Torvalds cache_get(&rsci->h); 1737d5497fc6SJ. Bruce Fields rqstp->rq_cred.cr_flavor = gss_svc_to_pseudoflavor( 173883523d08SChuck Lever rsci->mechctx->mech_type, 173983523d08SChuck Lever GSS_C_QOP_DEFAULT, 174083523d08SChuck Lever gc->gc_svc); 17411da177e4SLinus Torvalds ret = SVC_OK; 174210b9d99aSChuck Lever trace_rpcgss_svc_authenticate(rqstp, gc); 17431da177e4SLinus Torvalds goto out; 17441da177e4SLinus Torvalds } 1745dd35210eSHarshula Jayasuriya garbage_args: 1746dd35210eSHarshula Jayasuriya ret = SVC_GARBAGE; 1747dd35210eSHarshula Jayasuriya goto out; 17481da177e4SLinus Torvalds auth_err: 174972a1e53aSChuck Lever xdr_truncate_encode(&rqstp->rq_res_stream, XDR_UNIT * 2); 17501da177e4SLinus Torvalds ret = SVC_DENIED; 17511da177e4SLinus Torvalds goto out; 17521da177e4SLinus Torvalds complete: 17531da177e4SLinus Torvalds ret = SVC_COMPLETE; 17541da177e4SLinus Torvalds goto out; 17551da177e4SLinus Torvalds drop: 17564d712ef1SChuck Lever ret = SVC_CLOSE; 17571da177e4SLinus Torvalds out: 17581da177e4SLinus Torvalds if (rsci) 1759a1db410dSStanislav Kinsbursky cache_put(&rsci->h, sn->rsc_cache); 17601da177e4SLinus Torvalds return ret; 17611da177e4SLinus Torvalds } 17621da177e4SLinus Torvalds 1763db1d6165SChuck Lever static u32 176499d074d6SChuck Lever svcauth_gss_prepare_to_wrap(struct svc_rqst *rqstp, struct gss_svc_data *gsd) 17653c15a486SJ.Bruce Fields { 1766db1d6165SChuck Lever u32 offset; 17673c15a486SJ.Bruce Fields 1768db1d6165SChuck Lever /* Release can be called twice, but we only wrap once. */ 1769db1d6165SChuck Lever offset = gsd->gsd_databody_offset; 1770db1d6165SChuck Lever gsd->gsd_databody_offset = 0; 17715b304bc5SJ.Bruce Fields 177299d074d6SChuck Lever /* AUTH_ERROR replies are not wrapped. */ 177399d074d6SChuck Lever if (rqstp->rq_auth_stat != rpc_auth_ok) 1774db1d6165SChuck Lever return 0; 17754bcf0343SChuck Lever 17764bcf0343SChuck Lever /* Also don't wrap if the accept_stat is nonzero: */ 17774bcf0343SChuck Lever if (*rqstp->rq_accept_statp != rpc_success) 1778db1d6165SChuck Lever return 0; 17794bcf0343SChuck Lever 1780db1d6165SChuck Lever return offset; 17813c15a486SJ.Bruce Fields } 17823c15a486SJ.Bruce Fields 17830adaddd3SChuck Lever /* 17840adaddd3SChuck Lever * RFC 2203, Section 5.3.2.2 17850adaddd3SChuck Lever * 17860adaddd3SChuck Lever * struct rpc_gss_integ_data { 17870adaddd3SChuck Lever * opaque databody_integ<>; 17880adaddd3SChuck Lever * opaque checksum<>; 17890adaddd3SChuck Lever * }; 17900adaddd3SChuck Lever * 17910adaddd3SChuck Lever * struct rpc_gss_data_t { 17920adaddd3SChuck Lever * unsigned int seq_num; 17930adaddd3SChuck Lever * proc_req_arg_t arg; 17940adaddd3SChuck Lever * }; 17950adaddd3SChuck Lever * 17960adaddd3SChuck Lever * The RPC Reply message has already been XDR-encoded. rq_res_stream 17970adaddd3SChuck Lever * is now positioned so that the checksum can be written just past 17980adaddd3SChuck Lever * the RPC Reply message. 17990adaddd3SChuck Lever */ 18000adaddd3SChuck Lever static int svcauth_gss_wrap_integ(struct svc_rqst *rqstp) 18011da177e4SLinus Torvalds { 18020adaddd3SChuck Lever struct gss_svc_data *gsd = rqstp->rq_auth_data; 18037702378aSChuck Lever struct xdr_stream *xdr = &rqstp->rq_res_stream; 18041da177e4SLinus Torvalds struct rpc_gss_wire_cred *gc = &gsd->clcred; 18057702378aSChuck Lever struct xdr_buf *buf = xdr->buf; 18060adaddd3SChuck Lever struct xdr_buf databody_integ; 18070adaddd3SChuck Lever struct xdr_netobj checksum; 1808db1d6165SChuck Lever u32 offset, maj_stat; 18091da177e4SLinus Torvalds 1810db1d6165SChuck Lever offset = svcauth_gss_prepare_to_wrap(rqstp, gsd); 1811db1d6165SChuck Lever if (!offset) 18121da177e4SLinus Torvalds goto out; 18137702378aSChuck Lever 1814db1d6165SChuck Lever if (xdr_buf_subsegment(buf, &databody_integ, offset + XDR_UNIT, 1815db1d6165SChuck Lever buf->len - offset - XDR_UNIT)) 181615d8f808SChuck Lever goto wrap_failed; 18177702378aSChuck Lever /* Buffer space for these has already been reserved in 18187702378aSChuck Lever * svcauth_gss_accept(). */ 1819db1d6165SChuck Lever if (xdr_encode_word(buf, offset, databody_integ.len)) 1820db1d6165SChuck Lever goto wrap_failed; 1821db1d6165SChuck Lever if (xdr_encode_word(buf, offset + XDR_UNIT, gc->gc_seq)) 1822db1d6165SChuck Lever goto wrap_failed; 1823d91f0323SChuck Lever 1824d91f0323SChuck Lever checksum.data = gsd->gsd_scratch; 182515d8f808SChuck Lever maj_stat = gss_get_mic(gsd->rsci->mechctx, &databody_integ, &checksum); 182615d8f808SChuck Lever if (maj_stat != GSS_S_COMPLETE) 182715d8f808SChuck Lever goto bad_mic; 18287702378aSChuck Lever 18297702378aSChuck Lever if (xdr_stream_encode_opaque(xdr, checksum.data, checksum.len) < 0) 183015d8f808SChuck Lever goto wrap_failed; 18317702378aSChuck Lever xdr_commit_encode(xdr); 18327702378aSChuck Lever 1833e142ede8SJ. Bruce Fields out: 183415d8f808SChuck Lever return 0; 18357702378aSChuck Lever 183615d8f808SChuck Lever bad_mic: 183715d8f808SChuck Lever trace_rpcgss_svc_get_mic(rqstp, maj_stat); 1838ba8b13e5SChuck Lever return -EINVAL; 183915d8f808SChuck Lever wrap_failed: 1840ba8b13e5SChuck Lever trace_rpcgss_svc_wrap_failed(rqstp); 184115d8f808SChuck Lever return -EINVAL; 1842e142ede8SJ. Bruce Fields } 1843e142ede8SJ. Bruce Fields 18447b135c65SChuck Lever /* 18457b135c65SChuck Lever * RFC 2203, Section 5.3.2.3 18467b135c65SChuck Lever * 18477b135c65SChuck Lever * struct rpc_gss_priv_data { 18487b135c65SChuck Lever * opaque databody_priv<> 18497b135c65SChuck Lever * }; 18507b135c65SChuck Lever * 18517b135c65SChuck Lever * struct rpc_gss_data_t { 18527b135c65SChuck Lever * unsigned int seq_num; 18537b135c65SChuck Lever * proc_req_arg_t arg; 18547b135c65SChuck Lever * }; 1855eb1b780fSChuck Lever * 1856eb1b780fSChuck Lever * gss_wrap() expands the size of the RPC message payload in the 1857eb1b780fSChuck Lever * response buffer. The main purpose of svcauth_gss_wrap_priv() 1858eb1b780fSChuck Lever * is to ensure there is adequate space in the response buffer to 1859eb1b780fSChuck Lever * avoid overflow during the wrap. 18607b135c65SChuck Lever */ 18617b135c65SChuck Lever static int svcauth_gss_wrap_priv(struct svc_rqst *rqstp) 18627c9fdcfbSJ. Bruce Fields { 18637b135c65SChuck Lever struct gss_svc_data *gsd = rqstp->rq_auth_data; 18647c9fdcfbSJ. Bruce Fields struct rpc_gss_wire_cred *gc = &gsd->clcred; 18657b135c65SChuck Lever struct xdr_buf *buf = &rqstp->rq_res; 1866a84cfbcdSChuck Lever struct kvec *head = buf->head; 1867a84cfbcdSChuck Lever struct kvec *tail = buf->tail; 1868ba8b13e5SChuck Lever u32 offset, pad, maj_stat; 1869db1d6165SChuck Lever __be32 *p; 18707c9fdcfbSJ. Bruce Fields 1871db1d6165SChuck Lever offset = svcauth_gss_prepare_to_wrap(rqstp, gsd); 1872db1d6165SChuck Lever if (!offset) 18737c9fdcfbSJ. Bruce Fields return 0; 1874ba8b13e5SChuck Lever 1875db1d6165SChuck Lever /* 1876db1d6165SChuck Lever * Buffer space for this field has already been reserved 1877db1d6165SChuck Lever * in svcauth_gss_accept(). Note that the GSS sequence 1878db1d6165SChuck Lever * number is encrypted along with the RPC reply payload. 1879db1d6165SChuck Lever */ 1880db1d6165SChuck Lever if (xdr_encode_word(buf, offset + XDR_UNIT, gc->gc_seq)) 1881db1d6165SChuck Lever goto wrap_failed; 18827561042fSKevin Coffman 18837561042fSKevin Coffman /* 18847561042fSKevin Coffman * If there is currently tail data, make sure there is 18857561042fSKevin Coffman * room for the head, tail, and 2 * RPC_MAX_AUTH_SIZE in 18867561042fSKevin Coffman * the page, and move the current tail data such that 18877561042fSKevin Coffman * there is RPC_MAX_AUTH_SIZE slack space available in 18887561042fSKevin Coffman * both the head and tail. 18897561042fSKevin Coffman */ 1890a84cfbcdSChuck Lever if (tail->iov_base) { 1891a84cfbcdSChuck Lever if (tail->iov_base >= head->iov_base + PAGE_SIZE) 1892ba8b13e5SChuck Lever goto wrap_failed; 1893a84cfbcdSChuck Lever if (tail->iov_base < head->iov_base) 1894ba8b13e5SChuck Lever goto wrap_failed; 1895a84cfbcdSChuck Lever if (tail->iov_len + head->iov_len 18967c9fdcfbSJ. Bruce Fields + 2 * RPC_MAX_AUTH_SIZE > PAGE_SIZE) 1897ba8b13e5SChuck Lever goto wrap_failed; 1898a84cfbcdSChuck Lever memmove(tail->iov_base + RPC_MAX_AUTH_SIZE, tail->iov_base, 1899a84cfbcdSChuck Lever tail->iov_len); 1900a84cfbcdSChuck Lever tail->iov_base += RPC_MAX_AUTH_SIZE; 19017c9fdcfbSJ. Bruce Fields } 19027561042fSKevin Coffman /* 19037561042fSKevin Coffman * If there is no current tail data, make sure there is 19047561042fSKevin Coffman * room for the head data, and 2 * RPC_MAX_AUTH_SIZE in the 19057561042fSKevin Coffman * allotted page, and set up tail information such that there 19067561042fSKevin Coffman * is RPC_MAX_AUTH_SIZE slack space available in both the 19077561042fSKevin Coffman * head and tail. 19087561042fSKevin Coffman */ 1909a84cfbcdSChuck Lever if (!tail->iov_base) { 1910a84cfbcdSChuck Lever if (head->iov_len + 2 * RPC_MAX_AUTH_SIZE > PAGE_SIZE) 1911ba8b13e5SChuck Lever goto wrap_failed; 1912a84cfbcdSChuck Lever tail->iov_base = head->iov_base 1913a84cfbcdSChuck Lever + head->iov_len + RPC_MAX_AUTH_SIZE; 1914a84cfbcdSChuck Lever tail->iov_len = 0; 19157c9fdcfbSJ. Bruce Fields } 1916ba8b13e5SChuck Lever 1917db1d6165SChuck Lever maj_stat = gss_wrap(gsd->rsci->mechctx, offset + XDR_UNIT, buf, 1918db1d6165SChuck Lever buf->pages); 1919ba8b13e5SChuck Lever if (maj_stat != GSS_S_COMPLETE) 1920ba8b13e5SChuck Lever goto bad_wrap; 1921ba8b13e5SChuck Lever 1922db1d6165SChuck Lever /* Wrapping can change the size of databody_priv. */ 1923db1d6165SChuck Lever if (xdr_encode_word(buf, offset, buf->len - offset - XDR_UNIT)) 1924db1d6165SChuck Lever goto wrap_failed; 1925db1d6165SChuck Lever pad = xdr_pad_size(buf->len - offset - XDR_UNIT); 1926a84cfbcdSChuck Lever p = (__be32 *)(tail->iov_base + tail->iov_len); 19277c9fdcfbSJ. Bruce Fields memset(p, 0, pad); 1928a84cfbcdSChuck Lever tail->iov_len += pad; 19297b135c65SChuck Lever buf->len += pad; 19307b135c65SChuck Lever 19317c9fdcfbSJ. Bruce Fields return 0; 1932ba8b13e5SChuck Lever wrap_failed: 1933ba8b13e5SChuck Lever trace_rpcgss_svc_wrap_failed(rqstp); 1934ba8b13e5SChuck Lever return -EINVAL; 1935ba8b13e5SChuck Lever bad_wrap: 1936ba8b13e5SChuck Lever trace_rpcgss_svc_wrap(rqstp, maj_stat); 1937ba8b13e5SChuck Lever return -ENOMEM; 19387c9fdcfbSJ. Bruce Fields } 19397c9fdcfbSJ. Bruce Fields 19405a929383SChuck Lever /** 19415a929383SChuck Lever * svcauth_gss_release - Wrap payload and release resources 19425a929383SChuck Lever * @rqstp: RPC transaction context 19435a929383SChuck Lever * 19445a929383SChuck Lever * Return values: 19455a929383SChuck Lever * %0: the Reply is ready to be sent 19465a929383SChuck Lever * %-ENOMEM: failed to allocate memory 19475a929383SChuck Lever * %-EINVAL: encoding error 19485a929383SChuck Lever */ 1949e142ede8SJ. Bruce Fields static int 1950e142ede8SJ. Bruce Fields svcauth_gss_release(struct svc_rqst *rqstp) 1951e142ede8SJ. Bruce Fields { 1952b8be5674SVasily Averin struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id); 19535a929383SChuck Lever struct gss_svc_data *gsd = rqstp->rq_auth_data; 19545a929383SChuck Lever struct rpc_gss_wire_cred *gc; 19555a929383SChuck Lever int stat; 1956e142ede8SJ. Bruce Fields 19570ddc9423SJ. Bruce Fields if (!gsd) 19580ddc9423SJ. Bruce Fields goto out; 19590ddc9423SJ. Bruce Fields gc = &gsd->clcred; 1960e142ede8SJ. Bruce Fields if (gc->gc_proc != RPC_GSS_PROC_DATA) 1961e142ede8SJ. Bruce Fields goto out; 19625a929383SChuck Lever 1963e142ede8SJ. Bruce Fields switch (gc->gc_svc) { 1964e142ede8SJ. Bruce Fields case RPC_GSS_SVC_NONE: 1965e142ede8SJ. Bruce Fields break; 1966e142ede8SJ. Bruce Fields case RPC_GSS_SVC_INTEGRITY: 19670adaddd3SChuck Lever stat = svcauth_gss_wrap_integ(rqstp); 19687c9fdcfbSJ. Bruce Fields if (stat) 19697c9fdcfbSJ. Bruce Fields goto out_err; 19701da177e4SLinus Torvalds break; 19711da177e4SLinus Torvalds case RPC_GSS_SVC_PRIVACY: 19727b135c65SChuck Lever stat = svcauth_gss_wrap_priv(rqstp); 19737c9fdcfbSJ. Bruce Fields if (stat) 19747c9fdcfbSJ. Bruce Fields goto out_err; 19757c9fdcfbSJ. Bruce Fields break; 1976eac81736SWei Yongjun /* 1977eac81736SWei Yongjun * For any other gc_svc value, svcauth_gss_accept() already set 1978eac81736SWei Yongjun * the auth_error appropriately; just fall through: 1979eac81736SWei Yongjun */ 19801da177e4SLinus Torvalds } 19811da177e4SLinus Torvalds 19821da177e4SLinus Torvalds out: 19831da177e4SLinus Torvalds stat = 0; 19841da177e4SLinus Torvalds out_err: 19851da177e4SLinus Torvalds if (rqstp->rq_client) 19861da177e4SLinus Torvalds auth_domain_put(rqstp->rq_client); 19871da177e4SLinus Torvalds rqstp->rq_client = NULL; 19883ab4d8b1SJ. Bruce Fields if (rqstp->rq_gssclient) 19893ab4d8b1SJ. Bruce Fields auth_domain_put(rqstp->rq_gssclient); 19903ab4d8b1SJ. Bruce Fields rqstp->rq_gssclient = NULL; 19911da177e4SLinus Torvalds if (rqstp->rq_cred.cr_group_info) 19921da177e4SLinus Torvalds put_group_info(rqstp->rq_cred.cr_group_info); 19931da177e4SLinus Torvalds rqstp->rq_cred.cr_group_info = NULL; 19940ddc9423SJ. Bruce Fields if (gsd && gsd->rsci) { 1995a1db410dSStanislav Kinsbursky cache_put(&gsd->rsci->h, sn->rsc_cache); 19961da177e4SLinus Torvalds gsd->rsci = NULL; 19970ddc9423SJ. Bruce Fields } 19981da177e4SLinus Torvalds return stat; 19991da177e4SLinus Torvalds } 20001da177e4SLinus Torvalds 20011da177e4SLinus Torvalds static void 2002608a0ab2STrond Myklebust svcauth_gss_domain_release_rcu(struct rcu_head *head) 20031da177e4SLinus Torvalds { 2004608a0ab2STrond Myklebust struct auth_domain *dom = container_of(head, struct auth_domain, rcu_head); 20051da177e4SLinus Torvalds struct gss_domain *gd = container_of(dom, struct gss_domain, h); 20061da177e4SLinus Torvalds 20071da177e4SLinus Torvalds kfree(dom->name); 20081da177e4SLinus Torvalds kfree(gd); 20091da177e4SLinus Torvalds } 20101da177e4SLinus Torvalds 2011608a0ab2STrond Myklebust static void 2012608a0ab2STrond Myklebust svcauth_gss_domain_release(struct auth_domain *dom) 2013608a0ab2STrond Myklebust { 2014608a0ab2STrond Myklebust call_rcu(&dom->rcu_head, svcauth_gss_domain_release_rcu); 2015608a0ab2STrond Myklebust } 2016608a0ab2STrond Myklebust 20171da177e4SLinus Torvalds static struct auth_ops svcauthops_gss = { 20181da177e4SLinus Torvalds .name = "rpcsec_gss", 20191da177e4SLinus Torvalds .owner = THIS_MODULE, 20201da177e4SLinus Torvalds .flavour = RPC_AUTH_GSS, 20211da177e4SLinus Torvalds .accept = svcauth_gss_accept, 20221da177e4SLinus Torvalds .release = svcauth_gss_release, 20231da177e4SLinus Torvalds .domain_release = svcauth_gss_domain_release, 20241da177e4SLinus Torvalds .set_client = svcauth_gss_set_client, 20251da177e4SLinus Torvalds }; 20261da177e4SLinus Torvalds 2027a1db410dSStanislav Kinsbursky static int rsi_cache_create_net(struct net *net) 2028a1db410dSStanislav Kinsbursky { 2029a1db410dSStanislav Kinsbursky struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 2030a1db410dSStanislav Kinsbursky struct cache_detail *cd; 2031a1db410dSStanislav Kinsbursky int err; 2032a1db410dSStanislav Kinsbursky 2033a1db410dSStanislav Kinsbursky cd = cache_create_net(&rsi_cache_template, net); 2034a1db410dSStanislav Kinsbursky if (IS_ERR(cd)) 2035a1db410dSStanislav Kinsbursky return PTR_ERR(cd); 2036a1db410dSStanislav Kinsbursky err = cache_register_net(cd, net); 2037a1db410dSStanislav Kinsbursky if (err) { 2038a1db410dSStanislav Kinsbursky cache_destroy_net(cd, net); 2039a1db410dSStanislav Kinsbursky return err; 2040a1db410dSStanislav Kinsbursky } 2041a1db410dSStanislav Kinsbursky sn->rsi_cache = cd; 2042a1db410dSStanislav Kinsbursky return 0; 2043a1db410dSStanislav Kinsbursky } 2044a1db410dSStanislav Kinsbursky 2045a1db410dSStanislav Kinsbursky static void rsi_cache_destroy_net(struct net *net) 2046a1db410dSStanislav Kinsbursky { 2047a1db410dSStanislav Kinsbursky struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 2048a1db410dSStanislav Kinsbursky struct cache_detail *cd = sn->rsi_cache; 2049a1db410dSStanislav Kinsbursky 2050a1db410dSStanislav Kinsbursky sn->rsi_cache = NULL; 2051a1db410dSStanislav Kinsbursky cache_purge(cd); 2052a1db410dSStanislav Kinsbursky cache_unregister_net(cd, net); 2053a1db410dSStanislav Kinsbursky cache_destroy_net(cd, net); 2054a1db410dSStanislav Kinsbursky } 2055a1db410dSStanislav Kinsbursky 2056a1db410dSStanislav Kinsbursky static int rsc_cache_create_net(struct net *net) 2057a1db410dSStanislav Kinsbursky { 2058a1db410dSStanislav Kinsbursky struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 2059a1db410dSStanislav Kinsbursky struct cache_detail *cd; 2060a1db410dSStanislav Kinsbursky int err; 2061a1db410dSStanislav Kinsbursky 2062a1db410dSStanislav Kinsbursky cd = cache_create_net(&rsc_cache_template, net); 2063a1db410dSStanislav Kinsbursky if (IS_ERR(cd)) 2064a1db410dSStanislav Kinsbursky return PTR_ERR(cd); 2065a1db410dSStanislav Kinsbursky err = cache_register_net(cd, net); 2066a1db410dSStanislav Kinsbursky if (err) { 2067a1db410dSStanislav Kinsbursky cache_destroy_net(cd, net); 2068a1db410dSStanislav Kinsbursky return err; 2069a1db410dSStanislav Kinsbursky } 2070a1db410dSStanislav Kinsbursky sn->rsc_cache = cd; 2071a1db410dSStanislav Kinsbursky return 0; 2072a1db410dSStanislav Kinsbursky } 2073a1db410dSStanislav Kinsbursky 2074a1db410dSStanislav Kinsbursky static void rsc_cache_destroy_net(struct net *net) 2075a1db410dSStanislav Kinsbursky { 2076a1db410dSStanislav Kinsbursky struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 2077a1db410dSStanislav Kinsbursky struct cache_detail *cd = sn->rsc_cache; 2078a1db410dSStanislav Kinsbursky 2079a1db410dSStanislav Kinsbursky sn->rsc_cache = NULL; 2080a1db410dSStanislav Kinsbursky cache_purge(cd); 2081a1db410dSStanislav Kinsbursky cache_unregister_net(cd, net); 2082a1db410dSStanislav Kinsbursky cache_destroy_net(cd, net); 2083a1db410dSStanislav Kinsbursky } 2084a1db410dSStanislav Kinsbursky 2085a1db410dSStanislav Kinsbursky int 2086a1db410dSStanislav Kinsbursky gss_svc_init_net(struct net *net) 2087a1db410dSStanislav Kinsbursky { 2088a1db410dSStanislav Kinsbursky int rv; 2089a1db410dSStanislav Kinsbursky 2090a1db410dSStanislav Kinsbursky rv = rsc_cache_create_net(net); 2091a1db410dSStanislav Kinsbursky if (rv) 2092a1db410dSStanislav Kinsbursky return rv; 2093a1db410dSStanislav Kinsbursky rv = rsi_cache_create_net(net); 2094a1db410dSStanislav Kinsbursky if (rv) 2095a1db410dSStanislav Kinsbursky goto out1; 2096030d794bSSimo Sorce rv = create_use_gss_proxy_proc_entry(net); 2097030d794bSSimo Sorce if (rv) 2098030d794bSSimo Sorce goto out2; 2099bdb12fb1SChuck Lever 2100bdb12fb1SChuck Lever rv = create_krb5_enctypes_proc_entry(net); 2101bdb12fb1SChuck Lever if (rv) 2102bdb12fb1SChuck Lever goto out3; 2103bdb12fb1SChuck Lever 2104a1db410dSStanislav Kinsbursky return 0; 2105bdb12fb1SChuck Lever 2106bdb12fb1SChuck Lever out3: 2107bdb12fb1SChuck Lever destroy_use_gss_proxy_proc_entry(net); 2108030d794bSSimo Sorce out2: 21095a475344SJ. Bruce Fields rsi_cache_destroy_net(net); 2110a1db410dSStanislav Kinsbursky out1: 2111a1db410dSStanislav Kinsbursky rsc_cache_destroy_net(net); 2112a1db410dSStanislav Kinsbursky return rv; 2113a1db410dSStanislav Kinsbursky } 2114a1db410dSStanislav Kinsbursky 2115a1db410dSStanislav Kinsbursky void 2116a1db410dSStanislav Kinsbursky gss_svc_shutdown_net(struct net *net) 2117a1db410dSStanislav Kinsbursky { 2118bdb12fb1SChuck Lever destroy_krb5_enctypes_proc_entry(net); 2119030d794bSSimo Sorce destroy_use_gss_proxy_proc_entry(net); 2120a1db410dSStanislav Kinsbursky rsi_cache_destroy_net(net); 2121a1db410dSStanislav Kinsbursky rsc_cache_destroy_net(net); 2122a1db410dSStanislav Kinsbursky } 2123a1db410dSStanislav Kinsbursky 21241da177e4SLinus Torvalds int 21251da177e4SLinus Torvalds gss_svc_init(void) 21261da177e4SLinus Torvalds { 2127a1db410dSStanislav Kinsbursky return svc_auth_register(RPC_AUTH_GSS, &svcauthops_gss); 21281da177e4SLinus Torvalds } 21291da177e4SLinus Torvalds 21301da177e4SLinus Torvalds void 21311da177e4SLinus Torvalds gss_svc_shutdown(void) 21321da177e4SLinus Torvalds { 21331da177e4SLinus Torvalds svc_auth_unregister(RPC_AUTH_GSS); 21341da177e4SLinus Torvalds } 2135