11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Neil Brown <neilb@cse.unsw.edu.au> 31da177e4SLinus Torvalds * J. Bruce Fields <bfields@umich.edu> 41da177e4SLinus Torvalds * Andy Adamson <andros@umich.edu> 51da177e4SLinus Torvalds * Dug Song <dugsong@monkey.org> 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * RPCSEC_GSS server authentication. 81da177e4SLinus Torvalds * This implements RPCSEC_GSS as defined in rfc2203 (rpcsec_gss) and rfc2078 91da177e4SLinus Torvalds * (gssapi) 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * The RPCSEC_GSS involves three stages: 121da177e4SLinus Torvalds * 1/ context creation 131da177e4SLinus Torvalds * 2/ data exchange 141da177e4SLinus Torvalds * 3/ context destruction 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * Context creation is handled largely by upcalls to user-space. 171da177e4SLinus Torvalds * In particular, GSS_Accept_sec_context is handled by an upcall 181da177e4SLinus Torvalds * Data exchange is handled entirely within the kernel 191da177e4SLinus Torvalds * In particular, GSS_GetMIC, GSS_VerifyMIC, GSS_Seal, GSS_Unseal are in-kernel. 201da177e4SLinus Torvalds * Context destruction is handled in-kernel 211da177e4SLinus Torvalds * GSS_Delete_sec_context is in-kernel 221da177e4SLinus Torvalds * 231da177e4SLinus Torvalds * Context creation is initiated by a RPCSEC_GSS_INIT request arriving. 241da177e4SLinus Torvalds * The context handle and gss_token are used as a key into the rpcsec_init cache. 251da177e4SLinus Torvalds * The content of this cache includes some of the outputs of GSS_Accept_sec_context, 261da177e4SLinus Torvalds * being major_status, minor_status, context_handle, reply_token. 271da177e4SLinus Torvalds * These are sent back to the client. 281da177e4SLinus Torvalds * Sequence window management is handled by the kernel. The window size if currently 291da177e4SLinus Torvalds * a compile time constant. 301da177e4SLinus Torvalds * 311da177e4SLinus Torvalds * When user-space is happy that a context is established, it places an entry 321da177e4SLinus Torvalds * in the rpcsec_context cache. The key for this cache is the context_handle. 331da177e4SLinus Torvalds * The content includes: 341da177e4SLinus Torvalds * uid/gidlist - for determining access rights 351da177e4SLinus Torvalds * mechanism type 361da177e4SLinus Torvalds * mechanism specific information, such as a key 371da177e4SLinus Torvalds * 381da177e4SLinus Torvalds */ 391da177e4SLinus Torvalds 405a0e3ad6STejun Heo #include <linux/slab.h> 411da177e4SLinus Torvalds #include <linux/types.h> 421da177e4SLinus Torvalds #include <linux/module.h> 431da177e4SLinus Torvalds #include <linux/pagemap.h> 44ae2975bcSEric W. Biederman #include <linux/user_namespace.h> 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds #include <linux/sunrpc/auth_gss.h> 471da177e4SLinus Torvalds #include <linux/sunrpc/gss_err.h> 481da177e4SLinus Torvalds #include <linux/sunrpc/svcauth.h> 491da177e4SLinus Torvalds #include <linux/sunrpc/svcauth_gss.h> 501da177e4SLinus Torvalds #include <linux/sunrpc/cache.h> 51030d794bSSimo Sorce #include "gss_rpc_upcall.h" 521da177e4SLinus Torvalds 53a1db410dSStanislav Kinsbursky 541da177e4SLinus Torvalds #ifdef RPC_DEBUG 551da177e4SLinus Torvalds # define RPCDBG_FACILITY RPCDBG_AUTH 561da177e4SLinus Torvalds #endif 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds /* The rpcsec_init cache is used for mapping RPCSEC_GSS_{,CONT_}INIT requests 591da177e4SLinus Torvalds * into replies. 601da177e4SLinus Torvalds * 611da177e4SLinus Torvalds * Key is context handle (\x if empty) and gss_token. 621da177e4SLinus Torvalds * Content is major_status minor_status (integers) context_handle, reply_token. 631da177e4SLinus Torvalds * 641da177e4SLinus Torvalds */ 651da177e4SLinus Torvalds 661da177e4SLinus Torvalds static int netobj_equal(struct xdr_netobj *a, struct xdr_netobj *b) 671da177e4SLinus Torvalds { 681da177e4SLinus Torvalds return a->len == b->len && 0 == memcmp(a->data, b->data, a->len); 691da177e4SLinus Torvalds } 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds #define RSI_HASHBITS 6 721da177e4SLinus Torvalds #define RSI_HASHMAX (1<<RSI_HASHBITS) 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds struct rsi { 751da177e4SLinus Torvalds struct cache_head h; 761da177e4SLinus Torvalds struct xdr_netobj in_handle, in_token; 771da177e4SLinus Torvalds struct xdr_netobj out_handle, out_token; 781da177e4SLinus Torvalds int major_status, minor_status; 791da177e4SLinus Torvalds }; 801da177e4SLinus Torvalds 81a1db410dSStanislav Kinsbursky static struct rsi *rsi_update(struct cache_detail *cd, struct rsi *new, struct rsi *old); 82a1db410dSStanislav Kinsbursky static struct rsi *rsi_lookup(struct cache_detail *cd, struct rsi *item); 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds static void rsi_free(struct rsi *rsii) 851da177e4SLinus Torvalds { 861da177e4SLinus Torvalds kfree(rsii->in_handle.data); 871da177e4SLinus Torvalds kfree(rsii->in_token.data); 881da177e4SLinus Torvalds kfree(rsii->out_handle.data); 891da177e4SLinus Torvalds kfree(rsii->out_token.data); 901da177e4SLinus Torvalds } 911da177e4SLinus Torvalds 92baab935fSNeilBrown static void rsi_put(struct kref *ref) 931da177e4SLinus Torvalds { 94baab935fSNeilBrown struct rsi *rsii = container_of(ref, struct rsi, h.ref); 951da177e4SLinus Torvalds rsi_free(rsii); 961da177e4SLinus Torvalds kfree(rsii); 971da177e4SLinus Torvalds } 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds static inline int rsi_hash(struct rsi *item) 1001da177e4SLinus Torvalds { 1011da177e4SLinus Torvalds return hash_mem(item->in_handle.data, item->in_handle.len, RSI_HASHBITS) 1021da177e4SLinus Torvalds ^ hash_mem(item->in_token.data, item->in_token.len, RSI_HASHBITS); 1031da177e4SLinus Torvalds } 1041da177e4SLinus Torvalds 105d4d11ea9SNeilBrown static int rsi_match(struct cache_head *a, struct cache_head *b) 1061da177e4SLinus Torvalds { 107d4d11ea9SNeilBrown struct rsi *item = container_of(a, struct rsi, h); 108d4d11ea9SNeilBrown struct rsi *tmp = container_of(b, struct rsi, h); 109f64f9e71SJoe Perches return netobj_equal(&item->in_handle, &tmp->in_handle) && 110f64f9e71SJoe Perches netobj_equal(&item->in_token, &tmp->in_token); 1111da177e4SLinus Torvalds } 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds static int dup_to_netobj(struct xdr_netobj *dst, char *src, int len) 1141da177e4SLinus Torvalds { 1151da177e4SLinus Torvalds dst->len = len; 116e69062b4SArnaldo Carvalho de Melo dst->data = (len ? kmemdup(src, len, GFP_KERNEL) : NULL); 1171da177e4SLinus Torvalds if (len && !dst->data) 1181da177e4SLinus Torvalds return -ENOMEM; 1191da177e4SLinus Torvalds return 0; 1201da177e4SLinus Torvalds } 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds static inline int dup_netobj(struct xdr_netobj *dst, struct xdr_netobj *src) 1231da177e4SLinus Torvalds { 1241da177e4SLinus Torvalds return dup_to_netobj(dst, src->data, src->len); 1251da177e4SLinus Torvalds } 1261da177e4SLinus Torvalds 127d4d11ea9SNeilBrown static void rsi_init(struct cache_head *cnew, struct cache_head *citem) 1281da177e4SLinus Torvalds { 129d4d11ea9SNeilBrown struct rsi *new = container_of(cnew, struct rsi, h); 130d4d11ea9SNeilBrown struct rsi *item = container_of(citem, struct rsi, h); 131d4d11ea9SNeilBrown 1321da177e4SLinus Torvalds new->out_handle.data = NULL; 1331da177e4SLinus Torvalds new->out_handle.len = 0; 1341da177e4SLinus Torvalds new->out_token.data = NULL; 1351da177e4SLinus Torvalds new->out_token.len = 0; 1361da177e4SLinus Torvalds new->in_handle.len = item->in_handle.len; 1371da177e4SLinus Torvalds item->in_handle.len = 0; 1381da177e4SLinus Torvalds new->in_token.len = item->in_token.len; 1391da177e4SLinus Torvalds item->in_token.len = 0; 1401da177e4SLinus Torvalds new->in_handle.data = item->in_handle.data; 1411da177e4SLinus Torvalds item->in_handle.data = NULL; 1421da177e4SLinus Torvalds new->in_token.data = item->in_token.data; 1431da177e4SLinus Torvalds item->in_token.data = NULL; 1441da177e4SLinus Torvalds } 1451da177e4SLinus Torvalds 146d4d11ea9SNeilBrown static void update_rsi(struct cache_head *cnew, struct cache_head *citem) 1471da177e4SLinus Torvalds { 148d4d11ea9SNeilBrown struct rsi *new = container_of(cnew, struct rsi, h); 149d4d11ea9SNeilBrown struct rsi *item = container_of(citem, struct rsi, h); 150d4d11ea9SNeilBrown 1511da177e4SLinus Torvalds BUG_ON(new->out_handle.data || new->out_token.data); 1521da177e4SLinus Torvalds new->out_handle.len = item->out_handle.len; 1531da177e4SLinus Torvalds item->out_handle.len = 0; 1541da177e4SLinus Torvalds new->out_token.len = item->out_token.len; 1551da177e4SLinus Torvalds item->out_token.len = 0; 1561da177e4SLinus Torvalds new->out_handle.data = item->out_handle.data; 1571da177e4SLinus Torvalds item->out_handle.data = NULL; 1581da177e4SLinus Torvalds new->out_token.data = item->out_token.data; 1591da177e4SLinus Torvalds item->out_token.data = NULL; 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds new->major_status = item->major_status; 1621da177e4SLinus Torvalds new->minor_status = item->minor_status; 1631da177e4SLinus Torvalds } 1641da177e4SLinus Torvalds 165d4d11ea9SNeilBrown static struct cache_head *rsi_alloc(void) 166d4d11ea9SNeilBrown { 167d4d11ea9SNeilBrown struct rsi *rsii = kmalloc(sizeof(*rsii), GFP_KERNEL); 168d4d11ea9SNeilBrown if (rsii) 169d4d11ea9SNeilBrown return &rsii->h; 170d4d11ea9SNeilBrown else 171d4d11ea9SNeilBrown return NULL; 172d4d11ea9SNeilBrown } 173d4d11ea9SNeilBrown 1741da177e4SLinus Torvalds static void rsi_request(struct cache_detail *cd, 1751da177e4SLinus Torvalds struct cache_head *h, 1761da177e4SLinus Torvalds char **bpp, int *blen) 1771da177e4SLinus Torvalds { 1781da177e4SLinus Torvalds struct rsi *rsii = container_of(h, struct rsi, h); 1791da177e4SLinus Torvalds 1801da177e4SLinus Torvalds qword_addhex(bpp, blen, rsii->in_handle.data, rsii->in_handle.len); 1811da177e4SLinus Torvalds qword_addhex(bpp, blen, rsii->in_token.data, rsii->in_token.len); 1821da177e4SLinus Torvalds (*bpp)[-1] = '\n'; 1831da177e4SLinus Torvalds } 1841da177e4SLinus Torvalds 1851da177e4SLinus Torvalds static int rsi_parse(struct cache_detail *cd, 1861da177e4SLinus Torvalds char *mesg, int mlen) 1871da177e4SLinus Torvalds { 1881da177e4SLinus Torvalds /* context token expiry major minor context token */ 1891da177e4SLinus Torvalds char *buf = mesg; 1901da177e4SLinus Torvalds char *ep; 1911da177e4SLinus Torvalds int len; 1921da177e4SLinus Torvalds struct rsi rsii, *rsip = NULL; 1931da177e4SLinus Torvalds time_t expiry; 1941da177e4SLinus Torvalds int status = -EINVAL; 1951da177e4SLinus Torvalds 1961da177e4SLinus Torvalds memset(&rsii, 0, sizeof(rsii)); 1971da177e4SLinus Torvalds /* handle */ 1981da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 1991da177e4SLinus Torvalds if (len < 0) 2001da177e4SLinus Torvalds goto out; 2011da177e4SLinus Torvalds status = -ENOMEM; 2021da177e4SLinus Torvalds if (dup_to_netobj(&rsii.in_handle, buf, len)) 2031da177e4SLinus Torvalds goto out; 2041da177e4SLinus Torvalds 2051da177e4SLinus Torvalds /* token */ 2061da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2071da177e4SLinus Torvalds status = -EINVAL; 2081da177e4SLinus Torvalds if (len < 0) 2091da177e4SLinus Torvalds goto out; 2101da177e4SLinus Torvalds status = -ENOMEM; 2111da177e4SLinus Torvalds if (dup_to_netobj(&rsii.in_token, buf, len)) 2121da177e4SLinus Torvalds goto out; 2131da177e4SLinus Torvalds 214a1db410dSStanislav Kinsbursky rsip = rsi_lookup(cd, &rsii); 215d4d11ea9SNeilBrown if (!rsip) 216d4d11ea9SNeilBrown goto out; 217d4d11ea9SNeilBrown 2181da177e4SLinus Torvalds rsii.h.flags = 0; 2191da177e4SLinus Torvalds /* expiry */ 2201da177e4SLinus Torvalds expiry = get_expiry(&mesg); 2211da177e4SLinus Torvalds status = -EINVAL; 2221da177e4SLinus Torvalds if (expiry == 0) 2231da177e4SLinus Torvalds goto out; 2241da177e4SLinus Torvalds 2251da177e4SLinus Torvalds /* major/minor */ 2261da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 227b39c18fcSJ. Bruce Fields if (len <= 0) 2281da177e4SLinus Torvalds goto out; 2291da177e4SLinus Torvalds rsii.major_status = simple_strtoul(buf, &ep, 10); 2301da177e4SLinus Torvalds if (*ep) 2311da177e4SLinus Torvalds goto out; 2321da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2331da177e4SLinus Torvalds if (len <= 0) 2341da177e4SLinus Torvalds goto out; 2351da177e4SLinus Torvalds rsii.minor_status = simple_strtoul(buf, &ep, 10); 2361da177e4SLinus Torvalds if (*ep) 2371da177e4SLinus Torvalds goto out; 2381da177e4SLinus Torvalds 2391da177e4SLinus Torvalds /* out_handle */ 2401da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2411da177e4SLinus Torvalds if (len < 0) 2421da177e4SLinus Torvalds goto out; 2431da177e4SLinus Torvalds status = -ENOMEM; 2441da177e4SLinus Torvalds if (dup_to_netobj(&rsii.out_handle, buf, len)) 2451da177e4SLinus Torvalds goto out; 2461da177e4SLinus Torvalds 2471da177e4SLinus Torvalds /* out_token */ 2481da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2491da177e4SLinus Torvalds status = -EINVAL; 2501da177e4SLinus Torvalds if (len < 0) 2511da177e4SLinus Torvalds goto out; 2521da177e4SLinus Torvalds status = -ENOMEM; 2531da177e4SLinus Torvalds if (dup_to_netobj(&rsii.out_token, buf, len)) 2541da177e4SLinus Torvalds goto out; 2551da177e4SLinus Torvalds rsii.h.expiry_time = expiry; 256a1db410dSStanislav Kinsbursky rsip = rsi_update(cd, &rsii, rsip); 2571da177e4SLinus Torvalds status = 0; 2581da177e4SLinus Torvalds out: 2591da177e4SLinus Torvalds rsi_free(&rsii); 2601da177e4SLinus Torvalds if (rsip) 261a1db410dSStanislav Kinsbursky cache_put(&rsip->h, cd); 262d4d11ea9SNeilBrown else 263d4d11ea9SNeilBrown status = -ENOMEM; 2641da177e4SLinus Torvalds return status; 2651da177e4SLinus Torvalds } 2661da177e4SLinus Torvalds 267a1db410dSStanislav Kinsbursky static struct cache_detail rsi_cache_template = { 268f35279d3SBruce Allan .owner = THIS_MODULE, 2691da177e4SLinus Torvalds .hash_size = RSI_HASHMAX, 2701da177e4SLinus Torvalds .name = "auth.rpcsec.init", 2711da177e4SLinus Torvalds .cache_put = rsi_put, 27273fb847aSStanislav Kinsbursky .cache_request = rsi_request, 2731da177e4SLinus Torvalds .cache_parse = rsi_parse, 274d4d11ea9SNeilBrown .match = rsi_match, 275d4d11ea9SNeilBrown .init = rsi_init, 276d4d11ea9SNeilBrown .update = update_rsi, 277d4d11ea9SNeilBrown .alloc = rsi_alloc, 2781da177e4SLinus Torvalds }; 2791da177e4SLinus Torvalds 280a1db410dSStanislav Kinsbursky static struct rsi *rsi_lookup(struct cache_detail *cd, struct rsi *item) 281d4d11ea9SNeilBrown { 282d4d11ea9SNeilBrown struct cache_head *ch; 283d4d11ea9SNeilBrown int hash = rsi_hash(item); 284d4d11ea9SNeilBrown 285a1db410dSStanislav Kinsbursky ch = sunrpc_cache_lookup(cd, &item->h, hash); 286d4d11ea9SNeilBrown if (ch) 287d4d11ea9SNeilBrown return container_of(ch, struct rsi, h); 288d4d11ea9SNeilBrown else 289d4d11ea9SNeilBrown return NULL; 290d4d11ea9SNeilBrown } 291d4d11ea9SNeilBrown 292a1db410dSStanislav Kinsbursky static struct rsi *rsi_update(struct cache_detail *cd, struct rsi *new, struct rsi *old) 293d4d11ea9SNeilBrown { 294d4d11ea9SNeilBrown struct cache_head *ch; 295d4d11ea9SNeilBrown int hash = rsi_hash(new); 296d4d11ea9SNeilBrown 297a1db410dSStanislav Kinsbursky ch = sunrpc_cache_update(cd, &new->h, 298d4d11ea9SNeilBrown &old->h, hash); 299d4d11ea9SNeilBrown if (ch) 300d4d11ea9SNeilBrown return container_of(ch, struct rsi, h); 301d4d11ea9SNeilBrown else 302d4d11ea9SNeilBrown return NULL; 303d4d11ea9SNeilBrown } 304d4d11ea9SNeilBrown 3051da177e4SLinus Torvalds 3061da177e4SLinus Torvalds /* 3071da177e4SLinus Torvalds * The rpcsec_context cache is used to store a context that is 3081da177e4SLinus Torvalds * used in data exchange. 3091da177e4SLinus Torvalds * The key is a context handle. The content is: 3101da177e4SLinus Torvalds * uid, gidlist, mechanism, service-set, mech-specific-data 3111da177e4SLinus Torvalds */ 3121da177e4SLinus Torvalds 3131da177e4SLinus Torvalds #define RSC_HASHBITS 10 3141da177e4SLinus Torvalds #define RSC_HASHMAX (1<<RSC_HASHBITS) 3151da177e4SLinus Torvalds 3161da177e4SLinus Torvalds #define GSS_SEQ_WIN 128 3171da177e4SLinus Torvalds 3181da177e4SLinus Torvalds struct gss_svc_seq_data { 3191da177e4SLinus Torvalds /* highest seq number seen so far: */ 3201da177e4SLinus Torvalds int sd_max; 3211da177e4SLinus Torvalds /* for i such that sd_max-GSS_SEQ_WIN < i <= sd_max, the i-th bit of 3221da177e4SLinus Torvalds * sd_win is nonzero iff sequence number i has been seen already: */ 3231da177e4SLinus Torvalds unsigned long sd_win[GSS_SEQ_WIN/BITS_PER_LONG]; 3241da177e4SLinus Torvalds spinlock_t sd_lock; 3251da177e4SLinus Torvalds }; 3261da177e4SLinus Torvalds 3271da177e4SLinus Torvalds struct rsc { 3281da177e4SLinus Torvalds struct cache_head h; 3291da177e4SLinus Torvalds struct xdr_netobj handle; 3301da177e4SLinus Torvalds struct svc_cred cred; 3311da177e4SLinus Torvalds struct gss_svc_seq_data seqdata; 3321da177e4SLinus Torvalds struct gss_ctx *mechctx; 3331da177e4SLinus Torvalds }; 3341da177e4SLinus Torvalds 335a1db410dSStanislav Kinsbursky static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old); 336a1db410dSStanislav Kinsbursky static struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item); 3371da177e4SLinus Torvalds 3381da177e4SLinus Torvalds static void rsc_free(struct rsc *rsci) 3391da177e4SLinus Torvalds { 3401da177e4SLinus Torvalds kfree(rsci->handle.data); 3411da177e4SLinus Torvalds if (rsci->mechctx) 3421da177e4SLinus Torvalds gss_delete_sec_context(&rsci->mechctx); 34303a4e1f6SJ. Bruce Fields free_svc_cred(&rsci->cred); 3441da177e4SLinus Torvalds } 3451da177e4SLinus Torvalds 346baab935fSNeilBrown static void rsc_put(struct kref *ref) 3471da177e4SLinus Torvalds { 348baab935fSNeilBrown struct rsc *rsci = container_of(ref, struct rsc, h.ref); 3491da177e4SLinus Torvalds 3501da177e4SLinus Torvalds rsc_free(rsci); 3511da177e4SLinus Torvalds kfree(rsci); 3521da177e4SLinus Torvalds } 3531da177e4SLinus Torvalds 3541da177e4SLinus Torvalds static inline int 3551da177e4SLinus Torvalds rsc_hash(struct rsc *rsci) 3561da177e4SLinus Torvalds { 3571da177e4SLinus Torvalds return hash_mem(rsci->handle.data, rsci->handle.len, RSC_HASHBITS); 3581da177e4SLinus Torvalds } 3591da177e4SLinus Torvalds 36017f834b6SNeilBrown static int 36117f834b6SNeilBrown rsc_match(struct cache_head *a, struct cache_head *b) 3621da177e4SLinus Torvalds { 36317f834b6SNeilBrown struct rsc *new = container_of(a, struct rsc, h); 36417f834b6SNeilBrown struct rsc *tmp = container_of(b, struct rsc, h); 36517f834b6SNeilBrown 3661da177e4SLinus Torvalds return netobj_equal(&new->handle, &tmp->handle); 3671da177e4SLinus Torvalds } 3681da177e4SLinus Torvalds 36917f834b6SNeilBrown static void 37017f834b6SNeilBrown rsc_init(struct cache_head *cnew, struct cache_head *ctmp) 3711da177e4SLinus Torvalds { 37217f834b6SNeilBrown struct rsc *new = container_of(cnew, struct rsc, h); 37317f834b6SNeilBrown struct rsc *tmp = container_of(ctmp, struct rsc, h); 37417f834b6SNeilBrown 3751da177e4SLinus Torvalds new->handle.len = tmp->handle.len; 3761da177e4SLinus Torvalds tmp->handle.len = 0; 3771da177e4SLinus Torvalds new->handle.data = tmp->handle.data; 3781da177e4SLinus Torvalds tmp->handle.data = NULL; 3791da177e4SLinus Torvalds new->mechctx = NULL; 38044234063SJ. Bruce Fields init_svc_cred(&new->cred); 3811da177e4SLinus Torvalds } 3821da177e4SLinus Torvalds 38317f834b6SNeilBrown static void 38417f834b6SNeilBrown update_rsc(struct cache_head *cnew, struct cache_head *ctmp) 3851da177e4SLinus Torvalds { 38617f834b6SNeilBrown struct rsc *new = container_of(cnew, struct rsc, h); 38717f834b6SNeilBrown struct rsc *tmp = container_of(ctmp, struct rsc, h); 38817f834b6SNeilBrown 3891da177e4SLinus Torvalds new->mechctx = tmp->mechctx; 3901da177e4SLinus Torvalds tmp->mechctx = NULL; 3911da177e4SLinus Torvalds memset(&new->seqdata, 0, sizeof(new->seqdata)); 3921da177e4SLinus Torvalds spin_lock_init(&new->seqdata.sd_lock); 3931da177e4SLinus Torvalds new->cred = tmp->cred; 39444234063SJ. Bruce Fields init_svc_cred(&tmp->cred); 3951da177e4SLinus Torvalds } 3961da177e4SLinus Torvalds 39717f834b6SNeilBrown static struct cache_head * 39817f834b6SNeilBrown rsc_alloc(void) 39917f834b6SNeilBrown { 40017f834b6SNeilBrown struct rsc *rsci = kmalloc(sizeof(*rsci), GFP_KERNEL); 40117f834b6SNeilBrown if (rsci) 40217f834b6SNeilBrown return &rsci->h; 40317f834b6SNeilBrown else 40417f834b6SNeilBrown return NULL; 40517f834b6SNeilBrown } 40617f834b6SNeilBrown 4071da177e4SLinus Torvalds static int rsc_parse(struct cache_detail *cd, 4081da177e4SLinus Torvalds char *mesg, int mlen) 4091da177e4SLinus Torvalds { 4101da177e4SLinus Torvalds /* contexthandle expiry [ uid gid N <n gids> mechname ...mechdata... ] */ 4111da177e4SLinus Torvalds char *buf = mesg; 412683428faSEric W. Biederman int id; 4131da177e4SLinus Torvalds int len, rv; 4141da177e4SLinus Torvalds struct rsc rsci, *rscp = NULL; 4151da177e4SLinus Torvalds time_t expiry; 4161da177e4SLinus Torvalds int status = -EINVAL; 4171df0cadaSJ. Bruce Fields struct gss_api_mech *gm = NULL; 4181da177e4SLinus Torvalds 4191da177e4SLinus Torvalds memset(&rsci, 0, sizeof(rsci)); 4201da177e4SLinus Torvalds /* context handle */ 4211da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 4221da177e4SLinus Torvalds if (len < 0) goto out; 4231da177e4SLinus Torvalds status = -ENOMEM; 4241da177e4SLinus Torvalds if (dup_to_netobj(&rsci.handle, buf, len)) 4251da177e4SLinus Torvalds goto out; 4261da177e4SLinus Torvalds 4271da177e4SLinus Torvalds rsci.h.flags = 0; 4281da177e4SLinus Torvalds /* expiry */ 4291da177e4SLinus Torvalds expiry = get_expiry(&mesg); 4301da177e4SLinus Torvalds status = -EINVAL; 4311da177e4SLinus Torvalds if (expiry == 0) 4321da177e4SLinus Torvalds goto out; 4331da177e4SLinus Torvalds 434a1db410dSStanislav Kinsbursky rscp = rsc_lookup(cd, &rsci); 43517f834b6SNeilBrown if (!rscp) 43617f834b6SNeilBrown goto out; 43717f834b6SNeilBrown 4381da177e4SLinus Torvalds /* uid, or NEGATIVE */ 439683428faSEric W. Biederman rv = get_int(&mesg, &id); 4401da177e4SLinus Torvalds if (rv == -EINVAL) 4411da177e4SLinus Torvalds goto out; 4421da177e4SLinus Torvalds if (rv == -ENOENT) 4431da177e4SLinus Torvalds set_bit(CACHE_NEGATIVE, &rsci.h.flags); 4441da177e4SLinus Torvalds else { 4451da177e4SLinus Torvalds int N, i; 4461da177e4SLinus Torvalds 4473c34ae11SJ. Bruce Fields /* 4483c34ae11SJ. Bruce Fields * NOTE: we skip uid_valid()/gid_valid() checks here: 4493c34ae11SJ. Bruce Fields * instead, * -1 id's are later mapped to the 4503c34ae11SJ. Bruce Fields * (export-specific) anonymous id by nfsd_setuser. 4513c34ae11SJ. Bruce Fields * 4523c34ae11SJ. Bruce Fields * (But supplementary gid's get no such special 4533c34ae11SJ. Bruce Fields * treatment so are checked for validity here.) 4543c34ae11SJ. Bruce Fields */ 455683428faSEric W. Biederman /* uid */ 456683428faSEric W. Biederman rsci.cred.cr_uid = make_kuid(&init_user_ns, id); 457683428faSEric W. Biederman 4581da177e4SLinus Torvalds /* gid */ 459683428faSEric W. Biederman if (get_int(&mesg, &id)) 460683428faSEric W. Biederman goto out; 461683428faSEric W. Biederman rsci.cred.cr_gid = make_kgid(&init_user_ns, id); 4621da177e4SLinus Torvalds 4631da177e4SLinus Torvalds /* number of additional gid's */ 4641da177e4SLinus Torvalds if (get_int(&mesg, &N)) 4651da177e4SLinus Torvalds goto out; 4661da177e4SLinus Torvalds status = -ENOMEM; 4671da177e4SLinus Torvalds rsci.cred.cr_group_info = groups_alloc(N); 4681da177e4SLinus Torvalds if (rsci.cred.cr_group_info == NULL) 4691da177e4SLinus Torvalds goto out; 4701da177e4SLinus Torvalds 4711da177e4SLinus Torvalds /* gid's */ 4721da177e4SLinus Torvalds status = -EINVAL; 4731da177e4SLinus Torvalds for (i=0; i<N; i++) { 474ae2975bcSEric W. Biederman kgid_t kgid; 475683428faSEric W. Biederman if (get_int(&mesg, &id)) 4761da177e4SLinus Torvalds goto out; 477683428faSEric W. Biederman kgid = make_kgid(&init_user_ns, id); 478ae2975bcSEric W. Biederman if (!gid_valid(kgid)) 479ae2975bcSEric W. Biederman goto out; 480ae2975bcSEric W. Biederman GROUP_AT(rsci.cred.cr_group_info, i) = kgid; 4811da177e4SLinus Torvalds } 4821da177e4SLinus Torvalds 4831da177e4SLinus Torvalds /* mech name */ 4841da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 4851da177e4SLinus Torvalds if (len < 0) 4861da177e4SLinus Torvalds goto out; 4870dc1531aSJ. Bruce Fields gm = rsci.cred.cr_gss_mech = gss_mech_get_by_name(buf); 4881da177e4SLinus Torvalds status = -EOPNOTSUPP; 4891da177e4SLinus Torvalds if (!gm) 4901da177e4SLinus Torvalds goto out; 4911da177e4SLinus Torvalds 4921da177e4SLinus Torvalds status = -EINVAL; 4931da177e4SLinus Torvalds /* mech-specific data: */ 4941da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 4951df0cadaSJ. Bruce Fields if (len < 0) 4961da177e4SLinus Torvalds goto out; 497400f26b5SSimo Sorce status = gss_import_sec_context(buf, len, gm, &rsci.mechctx, 498400f26b5SSimo Sorce NULL, GFP_KERNEL); 4991df0cadaSJ. Bruce Fields if (status) 5001da177e4SLinus Torvalds goto out; 50168e76ad0SOlga Kornievskaia 50268e76ad0SOlga Kornievskaia /* get client name */ 50368e76ad0SOlga Kornievskaia len = qword_get(&mesg, buf, mlen); 50468e76ad0SOlga Kornievskaia if (len > 0) { 50503a4e1f6SJ. Bruce Fields rsci.cred.cr_principal = kstrdup(buf, GFP_KERNEL); 5061eb6d622SWei Yongjun if (!rsci.cred.cr_principal) { 5071eb6d622SWei Yongjun status = -ENOMEM; 50868e76ad0SOlga Kornievskaia goto out; 50968e76ad0SOlga Kornievskaia } 5101eb6d622SWei Yongjun } 51168e76ad0SOlga Kornievskaia 5121da177e4SLinus Torvalds } 5131da177e4SLinus Torvalds rsci.h.expiry_time = expiry; 514a1db410dSStanislav Kinsbursky rscp = rsc_update(cd, &rsci, rscp); 5151da177e4SLinus Torvalds status = 0; 5161da177e4SLinus Torvalds out: 5171da177e4SLinus Torvalds rsc_free(&rsci); 5181da177e4SLinus Torvalds if (rscp) 519a1db410dSStanislav Kinsbursky cache_put(&rscp->h, cd); 52017f834b6SNeilBrown else 52117f834b6SNeilBrown status = -ENOMEM; 5221da177e4SLinus Torvalds return status; 5231da177e4SLinus Torvalds } 5241da177e4SLinus Torvalds 525a1db410dSStanislav Kinsbursky static struct cache_detail rsc_cache_template = { 526f35279d3SBruce Allan .owner = THIS_MODULE, 5271da177e4SLinus Torvalds .hash_size = RSC_HASHMAX, 5281da177e4SLinus Torvalds .name = "auth.rpcsec.context", 5291da177e4SLinus Torvalds .cache_put = rsc_put, 5301da177e4SLinus Torvalds .cache_parse = rsc_parse, 53117f834b6SNeilBrown .match = rsc_match, 53217f834b6SNeilBrown .init = rsc_init, 53317f834b6SNeilBrown .update = update_rsc, 53417f834b6SNeilBrown .alloc = rsc_alloc, 5351da177e4SLinus Torvalds }; 5361da177e4SLinus Torvalds 537a1db410dSStanislav Kinsbursky static struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item) 53817f834b6SNeilBrown { 53917f834b6SNeilBrown struct cache_head *ch; 54017f834b6SNeilBrown int hash = rsc_hash(item); 54117f834b6SNeilBrown 542a1db410dSStanislav Kinsbursky ch = sunrpc_cache_lookup(cd, &item->h, hash); 54317f834b6SNeilBrown if (ch) 54417f834b6SNeilBrown return container_of(ch, struct rsc, h); 54517f834b6SNeilBrown else 54617f834b6SNeilBrown return NULL; 54717f834b6SNeilBrown } 54817f834b6SNeilBrown 549a1db410dSStanislav Kinsbursky static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old) 55017f834b6SNeilBrown { 55117f834b6SNeilBrown struct cache_head *ch; 55217f834b6SNeilBrown int hash = rsc_hash(new); 55317f834b6SNeilBrown 554a1db410dSStanislav Kinsbursky ch = sunrpc_cache_update(cd, &new->h, 55517f834b6SNeilBrown &old->h, hash); 55617f834b6SNeilBrown if (ch) 55717f834b6SNeilBrown return container_of(ch, struct rsc, h); 55817f834b6SNeilBrown else 55917f834b6SNeilBrown return NULL; 56017f834b6SNeilBrown } 56117f834b6SNeilBrown 5621da177e4SLinus Torvalds 5631da177e4SLinus Torvalds static struct rsc * 564a1db410dSStanislav Kinsbursky gss_svc_searchbyctx(struct cache_detail *cd, struct xdr_netobj *handle) 5651da177e4SLinus Torvalds { 5661da177e4SLinus Torvalds struct rsc rsci; 5671da177e4SLinus Torvalds struct rsc *found; 5681da177e4SLinus Torvalds 5691da177e4SLinus Torvalds memset(&rsci, 0, sizeof(rsci)); 5701da177e4SLinus Torvalds if (dup_to_netobj(&rsci.handle, handle->data, handle->len)) 5711da177e4SLinus Torvalds return NULL; 572a1db410dSStanislav Kinsbursky found = rsc_lookup(cd, &rsci); 5731da177e4SLinus Torvalds rsc_free(&rsci); 5741da177e4SLinus Torvalds if (!found) 5751da177e4SLinus Torvalds return NULL; 576a1db410dSStanislav Kinsbursky if (cache_check(cd, &found->h, NULL)) 5771da177e4SLinus Torvalds return NULL; 5781da177e4SLinus Torvalds return found; 5791da177e4SLinus Torvalds } 5801da177e4SLinus Torvalds 5811da177e4SLinus Torvalds /* Implements sequence number algorithm as specified in RFC 2203. */ 5821da177e4SLinus Torvalds static int 5831da177e4SLinus Torvalds gss_check_seq_num(struct rsc *rsci, int seq_num) 5841da177e4SLinus Torvalds { 5851da177e4SLinus Torvalds struct gss_svc_seq_data *sd = &rsci->seqdata; 5861da177e4SLinus Torvalds 5871da177e4SLinus Torvalds spin_lock(&sd->sd_lock); 5881da177e4SLinus Torvalds if (seq_num > sd->sd_max) { 5891da177e4SLinus Torvalds if (seq_num >= sd->sd_max + GSS_SEQ_WIN) { 5901da177e4SLinus Torvalds memset(sd->sd_win,0,sizeof(sd->sd_win)); 5911da177e4SLinus Torvalds sd->sd_max = seq_num; 5921da177e4SLinus Torvalds } else while (sd->sd_max < seq_num) { 5931da177e4SLinus Torvalds sd->sd_max++; 5941da177e4SLinus Torvalds __clear_bit(sd->sd_max % GSS_SEQ_WIN, sd->sd_win); 5951da177e4SLinus Torvalds } 5961da177e4SLinus Torvalds __set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win); 5971da177e4SLinus Torvalds goto ok; 5981da177e4SLinus Torvalds } else if (seq_num <= sd->sd_max - GSS_SEQ_WIN) { 5991da177e4SLinus Torvalds goto drop; 6001da177e4SLinus Torvalds } 6011da177e4SLinus Torvalds /* sd_max - GSS_SEQ_WIN < seq_num <= sd_max */ 6021da177e4SLinus Torvalds if (__test_and_set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win)) 6031da177e4SLinus Torvalds goto drop; 6041da177e4SLinus Torvalds ok: 6051da177e4SLinus Torvalds spin_unlock(&sd->sd_lock); 6061da177e4SLinus Torvalds return 1; 6071da177e4SLinus Torvalds drop: 6081da177e4SLinus Torvalds spin_unlock(&sd->sd_lock); 6091da177e4SLinus Torvalds return 0; 6101da177e4SLinus Torvalds } 6111da177e4SLinus Torvalds 6121da177e4SLinus Torvalds static inline u32 round_up_to_quad(u32 i) 6131da177e4SLinus Torvalds { 6141da177e4SLinus Torvalds return (i + 3 ) & ~3; 6151da177e4SLinus Torvalds } 6161da177e4SLinus Torvalds 6171da177e4SLinus Torvalds static inline int 6181da177e4SLinus Torvalds svc_safe_getnetobj(struct kvec *argv, struct xdr_netobj *o) 6191da177e4SLinus Torvalds { 6201da177e4SLinus Torvalds int l; 6211da177e4SLinus Torvalds 6221da177e4SLinus Torvalds if (argv->iov_len < 4) 6231da177e4SLinus Torvalds return -1; 62476994313SAlexey Dobriyan o->len = svc_getnl(argv); 6251da177e4SLinus Torvalds l = round_up_to_quad(o->len); 6261da177e4SLinus Torvalds if (argv->iov_len < l) 6271da177e4SLinus Torvalds return -1; 6281da177e4SLinus Torvalds o->data = argv->iov_base; 6291da177e4SLinus Torvalds argv->iov_base += l; 6301da177e4SLinus Torvalds argv->iov_len -= l; 6311da177e4SLinus Torvalds return 0; 6321da177e4SLinus Torvalds } 6331da177e4SLinus Torvalds 6341da177e4SLinus Torvalds static inline int 6351da177e4SLinus Torvalds svc_safe_putnetobj(struct kvec *resv, struct xdr_netobj *o) 6361da177e4SLinus Torvalds { 637753ed90dSAl Viro u8 *p; 6381da177e4SLinus Torvalds 6391da177e4SLinus Torvalds if (resv->iov_len + 4 > PAGE_SIZE) 6401da177e4SLinus Torvalds return -1; 64176994313SAlexey Dobriyan svc_putnl(resv, o->len); 6421da177e4SLinus Torvalds p = resv->iov_base + resv->iov_len; 6431da177e4SLinus Torvalds resv->iov_len += round_up_to_quad(o->len); 6441da177e4SLinus Torvalds if (resv->iov_len > PAGE_SIZE) 6451da177e4SLinus Torvalds return -1; 6461da177e4SLinus Torvalds memcpy(p, o->data, o->len); 647753ed90dSAl Viro memset(p + o->len, 0, round_up_to_quad(o->len) - o->len); 6481da177e4SLinus Torvalds return 0; 6491da177e4SLinus Torvalds } 6501da177e4SLinus Torvalds 65121fcd02bSJ. Bruce Fields /* 65221fcd02bSJ. Bruce Fields * Verify the checksum on the header and return SVC_OK on success. 6531da177e4SLinus Torvalds * Otherwise, return SVC_DROP (in the case of a bad sequence number) 6541da177e4SLinus Torvalds * or return SVC_DENIED and indicate error in authp. 6551da177e4SLinus Torvalds */ 6561da177e4SLinus Torvalds static int 6571da177e4SLinus Torvalds gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci, 658d8ed029dSAlexey Dobriyan __be32 *rpcstart, struct rpc_gss_wire_cred *gc, __be32 *authp) 6591da177e4SLinus Torvalds { 6601da177e4SLinus Torvalds struct gss_ctx *ctx_id = rsci->mechctx; 6611da177e4SLinus Torvalds struct xdr_buf rpchdr; 6621da177e4SLinus Torvalds struct xdr_netobj checksum; 6631da177e4SLinus Torvalds u32 flavor = 0; 6641da177e4SLinus Torvalds struct kvec *argv = &rqstp->rq_arg.head[0]; 6651da177e4SLinus Torvalds struct kvec iov; 6661da177e4SLinus Torvalds 6671da177e4SLinus Torvalds /* data to compute the checksum over: */ 6681da177e4SLinus Torvalds iov.iov_base = rpcstart; 6691da177e4SLinus Torvalds iov.iov_len = (u8 *)argv->iov_base - (u8 *)rpcstart; 6701da177e4SLinus Torvalds xdr_buf_from_iov(&iov, &rpchdr); 6711da177e4SLinus Torvalds 6721da177e4SLinus Torvalds *authp = rpc_autherr_badverf; 6731da177e4SLinus Torvalds if (argv->iov_len < 4) 6741da177e4SLinus Torvalds return SVC_DENIED; 67576994313SAlexey Dobriyan flavor = svc_getnl(argv); 6761da177e4SLinus Torvalds if (flavor != RPC_AUTH_GSS) 6771da177e4SLinus Torvalds return SVC_DENIED; 6781da177e4SLinus Torvalds if (svc_safe_getnetobj(argv, &checksum)) 6791da177e4SLinus Torvalds return SVC_DENIED; 6801da177e4SLinus Torvalds 6811da177e4SLinus Torvalds if (rqstp->rq_deferred) /* skip verification of revisited request */ 6821da177e4SLinus Torvalds return SVC_OK; 68300fd6e14SJ. Bruce Fields if (gss_verify_mic(ctx_id, &rpchdr, &checksum) != GSS_S_COMPLETE) { 6841da177e4SLinus Torvalds *authp = rpcsec_gsserr_credproblem; 6851da177e4SLinus Torvalds return SVC_DENIED; 6861da177e4SLinus Torvalds } 6871da177e4SLinus Torvalds 6881da177e4SLinus Torvalds if (gc->gc_seq > MAXSEQ) { 6898885cb36SChuck Lever dprintk("RPC: svcauth_gss: discarding request with " 6908885cb36SChuck Lever "large sequence number %d\n", gc->gc_seq); 6911da177e4SLinus Torvalds *authp = rpcsec_gsserr_ctxproblem; 6921da177e4SLinus Torvalds return SVC_DENIED; 6931da177e4SLinus Torvalds } 6941da177e4SLinus Torvalds if (!gss_check_seq_num(rsci, gc->gc_seq)) { 6958885cb36SChuck Lever dprintk("RPC: svcauth_gss: discarding request with " 6968885cb36SChuck Lever "old sequence number %d\n", gc->gc_seq); 6971da177e4SLinus Torvalds return SVC_DROP; 6981da177e4SLinus Torvalds } 6991da177e4SLinus Torvalds return SVC_OK; 7001da177e4SLinus Torvalds } 7011da177e4SLinus Torvalds 7021da177e4SLinus Torvalds static int 703822f1005SAndy Adamson gss_write_null_verf(struct svc_rqst *rqstp) 704822f1005SAndy Adamson { 705d8ed029dSAlexey Dobriyan __be32 *p; 706822f1005SAndy Adamson 70776994313SAlexey Dobriyan svc_putnl(rqstp->rq_res.head, RPC_AUTH_NULL); 708822f1005SAndy Adamson p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len; 709822f1005SAndy Adamson /* don't really need to check if head->iov_len > PAGE_SIZE ... */ 710822f1005SAndy Adamson *p++ = 0; 711822f1005SAndy Adamson if (!xdr_ressize_check(rqstp, p)) 712822f1005SAndy Adamson return -1; 713822f1005SAndy Adamson return 0; 714822f1005SAndy Adamson } 715822f1005SAndy Adamson 716822f1005SAndy Adamson static int 7171da177e4SLinus Torvalds gss_write_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id, u32 seq) 7181da177e4SLinus Torvalds { 719d8ed029dSAlexey Dobriyan __be32 xdr_seq; 7201da177e4SLinus Torvalds u32 maj_stat; 7211da177e4SLinus Torvalds struct xdr_buf verf_data; 7221da177e4SLinus Torvalds struct xdr_netobj mic; 723d8ed029dSAlexey Dobriyan __be32 *p; 7241da177e4SLinus Torvalds struct kvec iov; 7251da177e4SLinus Torvalds 72676994313SAlexey Dobriyan svc_putnl(rqstp->rq_res.head, RPC_AUTH_GSS); 7271da177e4SLinus Torvalds xdr_seq = htonl(seq); 7281da177e4SLinus Torvalds 7291da177e4SLinus Torvalds iov.iov_base = &xdr_seq; 7301da177e4SLinus Torvalds iov.iov_len = sizeof(xdr_seq); 7311da177e4SLinus Torvalds xdr_buf_from_iov(&iov, &verf_data); 7321da177e4SLinus Torvalds p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len; 7331da177e4SLinus Torvalds mic.data = (u8 *)(p + 1); 73400fd6e14SJ. Bruce Fields maj_stat = gss_get_mic(ctx_id, &verf_data, &mic); 7351da177e4SLinus Torvalds if (maj_stat != GSS_S_COMPLETE) 7361da177e4SLinus Torvalds return -1; 7371da177e4SLinus Torvalds *p++ = htonl(mic.len); 7381da177e4SLinus Torvalds memset((u8 *)p + mic.len, 0, round_up_to_quad(mic.len) - mic.len); 7391da177e4SLinus Torvalds p += XDR_QUADLEN(mic.len); 7401da177e4SLinus Torvalds if (!xdr_ressize_check(rqstp, p)) 7411da177e4SLinus Torvalds return -1; 7421da177e4SLinus Torvalds return 0; 7431da177e4SLinus Torvalds } 7441da177e4SLinus Torvalds 7451da177e4SLinus Torvalds struct gss_domain { 7461da177e4SLinus Torvalds struct auth_domain h; 7471da177e4SLinus Torvalds u32 pseudoflavor; 7481da177e4SLinus Torvalds }; 7491da177e4SLinus Torvalds 7501da177e4SLinus Torvalds static struct auth_domain * 7511da177e4SLinus Torvalds find_gss_auth_domain(struct gss_ctx *ctx, u32 svc) 7521da177e4SLinus Torvalds { 7531da177e4SLinus Torvalds char *name; 7541da177e4SLinus Torvalds 7551da177e4SLinus Torvalds name = gss_service_to_auth_domain_name(ctx->mech_type, svc); 7561da177e4SLinus Torvalds if (!name) 7571da177e4SLinus Torvalds return NULL; 7581da177e4SLinus Torvalds return auth_domain_find(name); 7591da177e4SLinus Torvalds } 7601da177e4SLinus Torvalds 761efc36aa5SNeilBrown static struct auth_ops svcauthops_gss; 762efc36aa5SNeilBrown 7634796f457SJ. Bruce Fields u32 svcauth_gss_flavor(struct auth_domain *dom) 7644796f457SJ. Bruce Fields { 7654796f457SJ. Bruce Fields struct gss_domain *gd = container_of(dom, struct gss_domain, h); 7664796f457SJ. Bruce Fields 7674796f457SJ. Bruce Fields return gd->pseudoflavor; 7684796f457SJ. Bruce Fields } 7694796f457SJ. Bruce Fields 7707bd88269STrond Myklebust EXPORT_SYMBOL_GPL(svcauth_gss_flavor); 7714796f457SJ. Bruce Fields 7721da177e4SLinus Torvalds int 7731da177e4SLinus Torvalds svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name) 7741da177e4SLinus Torvalds { 7751da177e4SLinus Torvalds struct gss_domain *new; 7761da177e4SLinus Torvalds struct auth_domain *test; 7771da177e4SLinus Torvalds int stat = -ENOMEM; 7781da177e4SLinus Torvalds 7791da177e4SLinus Torvalds new = kmalloc(sizeof(*new), GFP_KERNEL); 7801da177e4SLinus Torvalds if (!new) 7811da177e4SLinus Torvalds goto out; 782efc36aa5SNeilBrown kref_init(&new->h.ref); 783e69062b4SArnaldo Carvalho de Melo new->h.name = kstrdup(name, GFP_KERNEL); 7841da177e4SLinus Torvalds if (!new->h.name) 7851da177e4SLinus Torvalds goto out_free_dom; 786efc36aa5SNeilBrown new->h.flavour = &svcauthops_gss; 7871da177e4SLinus Torvalds new->pseudoflavor = pseudoflavor; 7881da177e4SLinus Torvalds 789cb276805SJ. Bruce Fields stat = 0; 790efc36aa5SNeilBrown test = auth_domain_lookup(name, &new->h); 791cb276805SJ. Bruce Fields if (test != &new->h) { /* Duplicate registration */ 792cb276805SJ. Bruce Fields auth_domain_put(test); 793cb276805SJ. Bruce Fields kfree(new->h.name); 794cb276805SJ. Bruce Fields goto out_free_dom; 7951da177e4SLinus Torvalds } 7961da177e4SLinus Torvalds return 0; 7971da177e4SLinus Torvalds 7981da177e4SLinus Torvalds out_free_dom: 7991da177e4SLinus Torvalds kfree(new); 8001da177e4SLinus Torvalds out: 8011da177e4SLinus Torvalds return stat; 8021da177e4SLinus Torvalds } 8031da177e4SLinus Torvalds 8047bd88269STrond Myklebust EXPORT_SYMBOL_GPL(svcauth_gss_register_pseudoflavor); 8051da177e4SLinus Torvalds 8061da177e4SLinus Torvalds static inline int 8071da177e4SLinus Torvalds read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj) 8081da177e4SLinus Torvalds { 809d8ed029dSAlexey Dobriyan __be32 raw; 8101da177e4SLinus Torvalds int status; 8111da177e4SLinus Torvalds 8121da177e4SLinus Torvalds status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj)); 8131da177e4SLinus Torvalds if (status) 8141da177e4SLinus Torvalds return status; 8151da177e4SLinus Torvalds *obj = ntohl(raw); 8161da177e4SLinus Torvalds return 0; 8171da177e4SLinus Torvalds } 8181da177e4SLinus Torvalds 8191da177e4SLinus Torvalds /* It would be nice if this bit of code could be shared with the client. 8201da177e4SLinus Torvalds * Obstacles: 8211da177e4SLinus Torvalds * The client shouldn't malloc(), would have to pass in own memory. 8221da177e4SLinus Torvalds * The server uses base of head iovec as read pointer, while the 8231da177e4SLinus Torvalds * client uses separate pointer. */ 8241da177e4SLinus Torvalds static int 8254c190e2fSJeff Layton unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx) 8261da177e4SLinus Torvalds { 8271da177e4SLinus Torvalds int stat = -EINVAL; 8281da177e4SLinus Torvalds u32 integ_len, maj_stat; 8291da177e4SLinus Torvalds struct xdr_netobj mic; 8301da177e4SLinus Torvalds struct xdr_buf integ_buf; 8311da177e4SLinus Torvalds 8324c190e2fSJeff Layton /* Did we already verify the signature on the original pass through? */ 8334c190e2fSJeff Layton if (rqstp->rq_deferred) 8344c190e2fSJeff Layton return 0; 8354c190e2fSJeff Layton 83676994313SAlexey Dobriyan integ_len = svc_getnl(&buf->head[0]); 8371da177e4SLinus Torvalds if (integ_len & 3) 838b797b5beSJ.Bruce Fields return stat; 8391da177e4SLinus Torvalds if (integ_len > buf->len) 840b797b5beSJ.Bruce Fields return stat; 8411da177e4SLinus Torvalds if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len)) 8421da177e4SLinus Torvalds BUG(); 8431da177e4SLinus Torvalds /* copy out mic... */ 8441da177e4SLinus Torvalds if (read_u32_from_xdr_buf(buf, integ_len, &mic.len)) 8451da177e4SLinus Torvalds BUG(); 8461da177e4SLinus Torvalds if (mic.len > RPC_MAX_AUTH_SIZE) 847b797b5beSJ.Bruce Fields return stat; 8481da177e4SLinus Torvalds mic.data = kmalloc(mic.len, GFP_KERNEL); 8491da177e4SLinus Torvalds if (!mic.data) 850b797b5beSJ.Bruce Fields return stat; 8511da177e4SLinus Torvalds if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len)) 8521da177e4SLinus Torvalds goto out; 85300fd6e14SJ. Bruce Fields maj_stat = gss_verify_mic(ctx, &integ_buf, &mic); 8541da177e4SLinus Torvalds if (maj_stat != GSS_S_COMPLETE) 8551da177e4SLinus Torvalds goto out; 85676994313SAlexey Dobriyan if (svc_getnl(&buf->head[0]) != seq) 8571da177e4SLinus Torvalds goto out; 8584c190e2fSJeff Layton /* trim off the mic at the end before returning */ 8594c190e2fSJeff Layton xdr_buf_trim(buf, mic.len + 4); 8601da177e4SLinus Torvalds stat = 0; 8611da177e4SLinus Torvalds out: 862b797b5beSJ.Bruce Fields kfree(mic.data); 8631da177e4SLinus Torvalds return stat; 8641da177e4SLinus Torvalds } 8651da177e4SLinus Torvalds 8667c9fdcfbSJ. Bruce Fields static inline int 8677c9fdcfbSJ. Bruce Fields total_buf_len(struct xdr_buf *buf) 8687c9fdcfbSJ. Bruce Fields { 8697c9fdcfbSJ. Bruce Fields return buf->head[0].iov_len + buf->page_len + buf->tail[0].iov_len; 8707c9fdcfbSJ. Bruce Fields } 8717c9fdcfbSJ. Bruce Fields 8727c9fdcfbSJ. Bruce Fields static void 8737c9fdcfbSJ. Bruce Fields fix_priv_head(struct xdr_buf *buf, int pad) 8747c9fdcfbSJ. Bruce Fields { 8757c9fdcfbSJ. Bruce Fields if (buf->page_len == 0) { 8767c9fdcfbSJ. Bruce Fields /* We need to adjust head and buf->len in tandem in this 8777c9fdcfbSJ. Bruce Fields * case to make svc_defer() work--it finds the original 8787c9fdcfbSJ. Bruce Fields * buffer start using buf->len - buf->head[0].iov_len. */ 8797c9fdcfbSJ. Bruce Fields buf->head[0].iov_len -= pad; 8807c9fdcfbSJ. Bruce Fields } 8817c9fdcfbSJ. Bruce Fields } 8827c9fdcfbSJ. Bruce Fields 8837c9fdcfbSJ. Bruce Fields static int 8847c9fdcfbSJ. Bruce Fields unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx) 8857c9fdcfbSJ. Bruce Fields { 8867c9fdcfbSJ. Bruce Fields u32 priv_len, maj_stat; 8877c9fdcfbSJ. Bruce Fields int pad, saved_len, remaining_len, offset; 8887c9fdcfbSJ. Bruce Fields 889cf8208d0SJens Axboe rqstp->rq_splice_ok = 0; 8907c9fdcfbSJ. Bruce Fields 89176994313SAlexey Dobriyan priv_len = svc_getnl(&buf->head[0]); 8927c9fdcfbSJ. Bruce Fields if (rqstp->rq_deferred) { 8937c9fdcfbSJ. Bruce Fields /* Already decrypted last time through! The sequence number 8947c9fdcfbSJ. Bruce Fields * check at out_seq is unnecessary but harmless: */ 8957c9fdcfbSJ. Bruce Fields goto out_seq; 8967c9fdcfbSJ. Bruce Fields } 8977c9fdcfbSJ. Bruce Fields /* buf->len is the number of bytes from the original start of the 8987c9fdcfbSJ. Bruce Fields * request to the end, where head[0].iov_len is just the bytes 8997c9fdcfbSJ. Bruce Fields * not yet read from the head, so these two values are different: */ 9007c9fdcfbSJ. Bruce Fields remaining_len = total_buf_len(buf); 9017c9fdcfbSJ. Bruce Fields if (priv_len > remaining_len) 9027c9fdcfbSJ. Bruce Fields return -EINVAL; 9037c9fdcfbSJ. Bruce Fields pad = remaining_len - priv_len; 9047c9fdcfbSJ. Bruce Fields buf->len -= pad; 9057c9fdcfbSJ. Bruce Fields fix_priv_head(buf, pad); 9067c9fdcfbSJ. Bruce Fields 9077c9fdcfbSJ. Bruce Fields /* Maybe it would be better to give gss_unwrap a length parameter: */ 9087c9fdcfbSJ. Bruce Fields saved_len = buf->len; 9097c9fdcfbSJ. Bruce Fields buf->len = priv_len; 9107c9fdcfbSJ. Bruce Fields maj_stat = gss_unwrap(ctx, 0, buf); 9117c9fdcfbSJ. Bruce Fields pad = priv_len - buf->len; 9127c9fdcfbSJ. Bruce Fields buf->len = saved_len; 9137c9fdcfbSJ. Bruce Fields buf->len -= pad; 9147c9fdcfbSJ. Bruce Fields /* The upper layers assume the buffer is aligned on 4-byte boundaries. 9157c9fdcfbSJ. Bruce Fields * In the krb5p case, at least, the data ends up offset, so we need to 9167c9fdcfbSJ. Bruce Fields * move it around. */ 9177c9fdcfbSJ. Bruce Fields /* XXX: This is very inefficient. It would be better to either do 9187c9fdcfbSJ. Bruce Fields * this while we encrypt, or maybe in the receive code, if we can peak 9197c9fdcfbSJ. Bruce Fields * ahead and work out the service and mechanism there. */ 9207c9fdcfbSJ. Bruce Fields offset = buf->head[0].iov_len % 4; 9217c9fdcfbSJ. Bruce Fields if (offset) { 9227c9fdcfbSJ. Bruce Fields buf->buflen = RPCSVC_MAXPAYLOAD; 9237c9fdcfbSJ. Bruce Fields xdr_shift_buf(buf, offset); 9247c9fdcfbSJ. Bruce Fields fix_priv_head(buf, pad); 9257c9fdcfbSJ. Bruce Fields } 9267c9fdcfbSJ. Bruce Fields if (maj_stat != GSS_S_COMPLETE) 9277c9fdcfbSJ. Bruce Fields return -EINVAL; 9287c9fdcfbSJ. Bruce Fields out_seq: 92976994313SAlexey Dobriyan if (svc_getnl(&buf->head[0]) != seq) 9307c9fdcfbSJ. Bruce Fields return -EINVAL; 9317c9fdcfbSJ. Bruce Fields return 0; 9327c9fdcfbSJ. Bruce Fields } 9337c9fdcfbSJ. Bruce Fields 9341da177e4SLinus Torvalds struct gss_svc_data { 9351da177e4SLinus Torvalds /* decoded gss client cred: */ 9361da177e4SLinus Torvalds struct rpc_gss_wire_cred clcred; 9375b304bc5SJ.Bruce Fields /* save a pointer to the beginning of the encoded verifier, 9385b304bc5SJ.Bruce Fields * for use in encryption/checksumming in svcauth_gss_release: */ 9395b304bc5SJ.Bruce Fields __be32 *verf_start; 9401da177e4SLinus Torvalds struct rsc *rsci; 9411da177e4SLinus Torvalds }; 9421da177e4SLinus Torvalds 9431da177e4SLinus Torvalds static int 9441da177e4SLinus Torvalds svcauth_gss_set_client(struct svc_rqst *rqstp) 9451da177e4SLinus Torvalds { 9461da177e4SLinus Torvalds struct gss_svc_data *svcdata = rqstp->rq_auth_data; 9471da177e4SLinus Torvalds struct rsc *rsci = svcdata->rsci; 9481da177e4SLinus Torvalds struct rpc_gss_wire_cred *gc = &svcdata->clcred; 9493ab4d8b1SJ. Bruce Fields int stat; 9501da177e4SLinus Torvalds 9513ab4d8b1SJ. Bruce Fields /* 9523ab4d8b1SJ. Bruce Fields * A gss export can be specified either by: 9533ab4d8b1SJ. Bruce Fields * export *(sec=krb5,rw) 9543ab4d8b1SJ. Bruce Fields * or by 9553ab4d8b1SJ. Bruce Fields * export gss/krb5(rw) 9563ab4d8b1SJ. Bruce Fields * The latter is deprecated; but for backwards compatibility reasons 9573ab4d8b1SJ. Bruce Fields * the nfsd code will still fall back on trying it if the former 9583ab4d8b1SJ. Bruce Fields * doesn't work; so we try to make both available to nfsd, below. 9593ab4d8b1SJ. Bruce Fields */ 9603ab4d8b1SJ. Bruce Fields rqstp->rq_gssclient = find_gss_auth_domain(rsci->mechctx, gc->gc_svc); 9613ab4d8b1SJ. Bruce Fields if (rqstp->rq_gssclient == NULL) 9621da177e4SLinus Torvalds return SVC_DENIED; 9633ab4d8b1SJ. Bruce Fields stat = svcauth_unix_set_client(rqstp); 9641ebede86SNeilBrown if (stat == SVC_DROP || stat == SVC_CLOSE) 9653ab4d8b1SJ. Bruce Fields return stat; 9661da177e4SLinus Torvalds return SVC_OK; 9671da177e4SLinus Torvalds } 9681da177e4SLinus Torvalds 96991a4762eSKevin Coffman static inline int 970fc2952a2SSimo Sorce gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp, 971fc2952a2SSimo Sorce struct xdr_netobj *out_handle, int *major_status) 97291a4762eSKevin Coffman { 97391a4762eSKevin Coffman struct rsc *rsci; 97454f9247bSFrank Filz int rc; 97591a4762eSKevin Coffman 976fc2952a2SSimo Sorce if (*major_status != GSS_S_COMPLETE) 97791a4762eSKevin Coffman return gss_write_null_verf(rqstp); 978fc2952a2SSimo Sorce rsci = gss_svc_searchbyctx(cd, out_handle); 97991a4762eSKevin Coffman if (rsci == NULL) { 980fc2952a2SSimo Sorce *major_status = GSS_S_NO_CONTEXT; 98191a4762eSKevin Coffman return gss_write_null_verf(rqstp); 98291a4762eSKevin Coffman } 98354f9247bSFrank Filz rc = gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN); 984a1db410dSStanislav Kinsbursky cache_put(&rsci->h, cd); 98554f9247bSFrank Filz return rc; 98691a4762eSKevin Coffman } 98791a4762eSKevin Coffman 988fc2952a2SSimo Sorce static inline int 989030d794bSSimo Sorce gss_read_common_verf(struct rpc_gss_wire_cred *gc, 990fc2952a2SSimo Sorce struct kvec *argv, __be32 *authp, 991030d794bSSimo Sorce struct xdr_netobj *in_handle) 992fc2952a2SSimo Sorce { 993fc2952a2SSimo Sorce /* Read the verifier; should be NULL: */ 994fc2952a2SSimo Sorce *authp = rpc_autherr_badverf; 995fc2952a2SSimo Sorce if (argv->iov_len < 2 * 4) 996fc2952a2SSimo Sorce return SVC_DENIED; 997fc2952a2SSimo Sorce if (svc_getnl(argv) != RPC_AUTH_NULL) 998fc2952a2SSimo Sorce return SVC_DENIED; 999fc2952a2SSimo Sorce if (svc_getnl(argv) != 0) 1000fc2952a2SSimo Sorce return SVC_DENIED; 1001fc2952a2SSimo Sorce /* Martial context handle and token for upcall: */ 1002fc2952a2SSimo Sorce *authp = rpc_autherr_badcred; 1003fc2952a2SSimo Sorce if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0) 1004fc2952a2SSimo Sorce return SVC_DENIED; 1005fc2952a2SSimo Sorce if (dup_netobj(in_handle, &gc->gc_ctx)) 1006fc2952a2SSimo Sorce return SVC_CLOSE; 1007fc2952a2SSimo Sorce *authp = rpc_autherr_badverf; 1008030d794bSSimo Sorce 1009030d794bSSimo Sorce return 0; 1010030d794bSSimo Sorce } 1011030d794bSSimo Sorce 1012030d794bSSimo Sorce static inline int 1013030d794bSSimo Sorce gss_read_verf(struct rpc_gss_wire_cred *gc, 1014030d794bSSimo Sorce struct kvec *argv, __be32 *authp, 1015030d794bSSimo Sorce struct xdr_netobj *in_handle, 1016030d794bSSimo Sorce struct xdr_netobj *in_token) 1017030d794bSSimo Sorce { 1018030d794bSSimo Sorce struct xdr_netobj tmpobj; 1019030d794bSSimo Sorce int res; 1020030d794bSSimo Sorce 1021030d794bSSimo Sorce res = gss_read_common_verf(gc, argv, authp, in_handle); 1022030d794bSSimo Sorce if (res) 1023030d794bSSimo Sorce return res; 1024030d794bSSimo Sorce 1025fc2952a2SSimo Sorce if (svc_safe_getnetobj(argv, &tmpobj)) { 1026fc2952a2SSimo Sorce kfree(in_handle->data); 1027fc2952a2SSimo Sorce return SVC_DENIED; 1028fc2952a2SSimo Sorce } 1029fc2952a2SSimo Sorce if (dup_netobj(in_token, &tmpobj)) { 1030fc2952a2SSimo Sorce kfree(in_handle->data); 1031fc2952a2SSimo Sorce return SVC_CLOSE; 1032fc2952a2SSimo Sorce } 1033fc2952a2SSimo Sorce 1034fc2952a2SSimo Sorce return 0; 1035fc2952a2SSimo Sorce } 1036fc2952a2SSimo Sorce 1037030d794bSSimo Sorce /* Ok this is really heavily depending on a set of semantics in 1038030d794bSSimo Sorce * how rqstp is set up by svc_recv and pages laid down by the 1039030d794bSSimo Sorce * server when reading a request. We are basically guaranteed that 1040030d794bSSimo Sorce * the token lays all down linearly across a set of pages, starting 1041030d794bSSimo Sorce * at iov_base in rq_arg.head[0] which happens to be the first of a 1042030d794bSSimo Sorce * set of pages stored in rq_pages[]. 1043030d794bSSimo Sorce * rq_arg.head[0].iov_base will provide us the page_base to pass 1044030d794bSSimo Sorce * to the upcall. 1045030d794bSSimo Sorce */ 1046030d794bSSimo Sorce static inline int 1047030d794bSSimo Sorce gss_read_proxy_verf(struct svc_rqst *rqstp, 1048030d794bSSimo Sorce struct rpc_gss_wire_cred *gc, __be32 *authp, 1049030d794bSSimo Sorce struct xdr_netobj *in_handle, 1050030d794bSSimo Sorce struct gssp_in_token *in_token) 1051030d794bSSimo Sorce { 1052030d794bSSimo Sorce struct kvec *argv = &rqstp->rq_arg.head[0]; 1053030d794bSSimo Sorce u32 inlen; 1054030d794bSSimo Sorce int res; 1055030d794bSSimo Sorce 1056030d794bSSimo Sorce res = gss_read_common_verf(gc, argv, authp, in_handle); 1057030d794bSSimo Sorce if (res) 1058030d794bSSimo Sorce return res; 1059030d794bSSimo Sorce 1060030d794bSSimo Sorce inlen = svc_getnl(argv); 1061030d794bSSimo Sorce if (inlen > (argv->iov_len + rqstp->rq_arg.page_len)) 1062030d794bSSimo Sorce return SVC_DENIED; 1063030d794bSSimo Sorce 1064030d794bSSimo Sorce in_token->pages = rqstp->rq_pages; 1065030d794bSSimo Sorce in_token->page_base = (ulong)argv->iov_base & ~PAGE_MASK; 1066030d794bSSimo Sorce in_token->page_len = inlen; 1067030d794bSSimo Sorce 1068030d794bSSimo Sorce return 0; 1069030d794bSSimo Sorce } 1070030d794bSSimo Sorce 1071fc2952a2SSimo Sorce static inline int 1072fc2952a2SSimo Sorce gss_write_resv(struct kvec *resv, size_t size_limit, 1073fc2952a2SSimo Sorce struct xdr_netobj *out_handle, struct xdr_netobj *out_token, 1074fc2952a2SSimo Sorce int major_status, int minor_status) 1075fc2952a2SSimo Sorce { 1076fc2952a2SSimo Sorce if (resv->iov_len + 4 > size_limit) 1077fc2952a2SSimo Sorce return -1; 1078fc2952a2SSimo Sorce svc_putnl(resv, RPC_SUCCESS); 1079fc2952a2SSimo Sorce if (svc_safe_putnetobj(resv, out_handle)) 1080fc2952a2SSimo Sorce return -1; 1081fc2952a2SSimo Sorce if (resv->iov_len + 3 * 4 > size_limit) 1082fc2952a2SSimo Sorce return -1; 1083fc2952a2SSimo Sorce svc_putnl(resv, major_status); 1084fc2952a2SSimo Sorce svc_putnl(resv, minor_status); 1085fc2952a2SSimo Sorce svc_putnl(resv, GSS_SEQ_WIN); 1086fc2952a2SSimo Sorce if (svc_safe_putnetobj(resv, out_token)) 1087fc2952a2SSimo Sorce return -1; 1088fc2952a2SSimo Sorce return 0; 1089fc2952a2SSimo Sorce } 1090fc2952a2SSimo Sorce 10911da177e4SLinus Torvalds /* 109221fcd02bSJ. Bruce Fields * Having read the cred already and found we're in the context 109321fcd02bSJ. Bruce Fields * initiation case, read the verifier and initiate (or check the results 109421fcd02bSJ. Bruce Fields * of) upcalls to userspace for help with context initiation. If 109521fcd02bSJ. Bruce Fields * the upcall results are available, write the verifier and result. 109621fcd02bSJ. Bruce Fields * Otherwise, drop the request pending an answer to the upcall. 109721fcd02bSJ. Bruce Fields */ 1098030d794bSSimo Sorce static int svcauth_gss_legacy_init(struct svc_rqst *rqstp, 109921fcd02bSJ. Bruce Fields struct rpc_gss_wire_cred *gc, __be32 *authp) 110021fcd02bSJ. Bruce Fields { 110121fcd02bSJ. Bruce Fields struct kvec *argv = &rqstp->rq_arg.head[0]; 110221fcd02bSJ. Bruce Fields struct kvec *resv = &rqstp->rq_res.head[0]; 110321fcd02bSJ. Bruce Fields struct rsi *rsip, rsikey; 1104980e5a40SJ. Bruce Fields int ret; 1105a1db410dSStanislav Kinsbursky struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net, sunrpc_net_id); 110621fcd02bSJ. Bruce Fields 110721fcd02bSJ. Bruce Fields memset(&rsikey, 0, sizeof(rsikey)); 1108fc2952a2SSimo Sorce ret = gss_read_verf(gc, argv, authp, 1109fc2952a2SSimo Sorce &rsikey.in_handle, &rsikey.in_token); 1110fc2952a2SSimo Sorce if (ret) 1111fc2952a2SSimo Sorce return ret; 111221fcd02bSJ. Bruce Fields 111321fcd02bSJ. Bruce Fields /* Perform upcall, or find upcall result: */ 1114a1db410dSStanislav Kinsbursky rsip = rsi_lookup(sn->rsi_cache, &rsikey); 111521fcd02bSJ. Bruce Fields rsi_free(&rsikey); 111621fcd02bSJ. Bruce Fields if (!rsip) 11171ebede86SNeilBrown return SVC_CLOSE; 1118a1db410dSStanislav Kinsbursky if (cache_check(sn->rsi_cache, &rsip->h, &rqstp->rq_chandle) < 0) 111921fcd02bSJ. Bruce Fields /* No upcall result: */ 11201ebede86SNeilBrown return SVC_CLOSE; 11212ed5282cSNeilBrown 11221ebede86SNeilBrown ret = SVC_CLOSE; 112321fcd02bSJ. Bruce Fields /* Got an answer to the upcall; use it: */ 1124fc2952a2SSimo Sorce if (gss_write_init_verf(sn->rsc_cache, rqstp, 1125fc2952a2SSimo Sorce &rsip->out_handle, &rsip->major_status)) 1126980e5a40SJ. Bruce Fields goto out; 1127fc2952a2SSimo Sorce if (gss_write_resv(resv, PAGE_SIZE, 1128fc2952a2SSimo Sorce &rsip->out_handle, &rsip->out_token, 1129fc2952a2SSimo Sorce rsip->major_status, rsip->minor_status)) 1130980e5a40SJ. Bruce Fields goto out; 11312ed5282cSNeilBrown 1132980e5a40SJ. Bruce Fields ret = SVC_COMPLETE; 1133980e5a40SJ. Bruce Fields out: 1134a1db410dSStanislav Kinsbursky cache_put(&rsip->h, sn->rsi_cache); 1135980e5a40SJ. Bruce Fields return ret; 113621fcd02bSJ. Bruce Fields } 113721fcd02bSJ. Bruce Fields 1138030d794bSSimo Sorce static int gss_proxy_save_rsc(struct cache_detail *cd, 1139030d794bSSimo Sorce struct gssp_upcall_data *ud, 1140030d794bSSimo Sorce uint64_t *handle) 1141030d794bSSimo Sorce { 1142030d794bSSimo Sorce struct rsc rsci, *rscp = NULL; 1143030d794bSSimo Sorce static atomic64_t ctxhctr; 1144030d794bSSimo Sorce long long ctxh; 1145030d794bSSimo Sorce struct gss_api_mech *gm = NULL; 1146030d794bSSimo Sorce time_t expiry; 1147030d794bSSimo Sorce int status = -EINVAL; 1148030d794bSSimo Sorce 1149030d794bSSimo Sorce memset(&rsci, 0, sizeof(rsci)); 1150030d794bSSimo Sorce /* context handle */ 1151030d794bSSimo Sorce status = -ENOMEM; 1152030d794bSSimo Sorce /* the handle needs to be just a unique id, 1153030d794bSSimo Sorce * use a static counter */ 1154030d794bSSimo Sorce ctxh = atomic64_inc_return(&ctxhctr); 1155030d794bSSimo Sorce 1156030d794bSSimo Sorce /* make a copy for the caller */ 1157030d794bSSimo Sorce *handle = ctxh; 1158030d794bSSimo Sorce 1159030d794bSSimo Sorce /* make a copy for the rsc cache */ 1160030d794bSSimo Sorce if (dup_to_netobj(&rsci.handle, (char *)handle, sizeof(uint64_t))) 1161030d794bSSimo Sorce goto out; 1162030d794bSSimo Sorce rscp = rsc_lookup(cd, &rsci); 1163030d794bSSimo Sorce if (!rscp) 1164030d794bSSimo Sorce goto out; 1165030d794bSSimo Sorce 1166030d794bSSimo Sorce /* creds */ 1167030d794bSSimo Sorce if (!ud->found_creds) { 1168030d794bSSimo Sorce /* userspace seem buggy, we should always get at least a 1169030d794bSSimo Sorce * mapping to nobody */ 11703be34555SJ. Bruce Fields dprintk("RPC: No creds found!\n"); 11713be34555SJ. Bruce Fields goto out; 1172030d794bSSimo Sorce } else { 1173030d794bSSimo Sorce 1174030d794bSSimo Sorce /* steal creds */ 1175030d794bSSimo Sorce rsci.cred = ud->creds; 1176030d794bSSimo Sorce memset(&ud->creds, 0, sizeof(struct svc_cred)); 1177030d794bSSimo Sorce 1178030d794bSSimo Sorce status = -EOPNOTSUPP; 1179030d794bSSimo Sorce /* get mech handle from OID */ 1180030d794bSSimo Sorce gm = gss_mech_get_by_OID(&ud->mech_oid); 1181030d794bSSimo Sorce if (!gm) 1182030d794bSSimo Sorce goto out; 11837193bd17SJ. Bruce Fields rsci.cred.cr_gss_mech = gm; 1184030d794bSSimo Sorce 1185030d794bSSimo Sorce status = -EINVAL; 1186030d794bSSimo Sorce /* mech-specific data: */ 1187030d794bSSimo Sorce status = gss_import_sec_context(ud->out_handle.data, 1188030d794bSSimo Sorce ud->out_handle.len, 1189030d794bSSimo Sorce gm, &rsci.mechctx, 1190030d794bSSimo Sorce &expiry, GFP_KERNEL); 1191030d794bSSimo Sorce if (status) 1192030d794bSSimo Sorce goto out; 1193030d794bSSimo Sorce } 1194030d794bSSimo Sorce 1195030d794bSSimo Sorce rsci.h.expiry_time = expiry; 1196030d794bSSimo Sorce rscp = rsc_update(cd, &rsci, rscp); 1197030d794bSSimo Sorce status = 0; 1198030d794bSSimo Sorce out: 1199030d794bSSimo Sorce rsc_free(&rsci); 1200030d794bSSimo Sorce if (rscp) 1201030d794bSSimo Sorce cache_put(&rscp->h, cd); 1202030d794bSSimo Sorce else 1203030d794bSSimo Sorce status = -ENOMEM; 1204030d794bSSimo Sorce return status; 1205030d794bSSimo Sorce } 1206030d794bSSimo Sorce 1207030d794bSSimo Sorce static int svcauth_gss_proxy_init(struct svc_rqst *rqstp, 1208030d794bSSimo Sorce struct rpc_gss_wire_cred *gc, __be32 *authp) 1209030d794bSSimo Sorce { 1210030d794bSSimo Sorce struct kvec *resv = &rqstp->rq_res.head[0]; 1211030d794bSSimo Sorce struct xdr_netobj cli_handle; 1212030d794bSSimo Sorce struct gssp_upcall_data ud; 1213030d794bSSimo Sorce uint64_t handle; 1214030d794bSSimo Sorce int status; 1215030d794bSSimo Sorce int ret; 1216030d794bSSimo Sorce struct net *net = rqstp->rq_xprt->xpt_net; 1217030d794bSSimo Sorce struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1218030d794bSSimo Sorce 1219030d794bSSimo Sorce memset(&ud, 0, sizeof(ud)); 1220030d794bSSimo Sorce ret = gss_read_proxy_verf(rqstp, gc, authp, 1221030d794bSSimo Sorce &ud.in_handle, &ud.in_token); 1222030d794bSSimo Sorce if (ret) 1223030d794bSSimo Sorce return ret; 1224030d794bSSimo Sorce 1225030d794bSSimo Sorce ret = SVC_CLOSE; 1226030d794bSSimo Sorce 1227030d794bSSimo Sorce /* Perform synchronous upcall to gss-proxy */ 1228030d794bSSimo Sorce status = gssp_accept_sec_context_upcall(net, &ud); 1229030d794bSSimo Sorce if (status) 1230030d794bSSimo Sorce goto out; 1231030d794bSSimo Sorce 1232030d794bSSimo Sorce dprintk("RPC: svcauth_gss: gss major status = %d\n", 1233030d794bSSimo Sorce ud.major_status); 1234030d794bSSimo Sorce 1235030d794bSSimo Sorce switch (ud.major_status) { 1236030d794bSSimo Sorce case GSS_S_CONTINUE_NEEDED: 1237030d794bSSimo Sorce cli_handle = ud.out_handle; 1238030d794bSSimo Sorce break; 1239030d794bSSimo Sorce case GSS_S_COMPLETE: 1240030d794bSSimo Sorce status = gss_proxy_save_rsc(sn->rsc_cache, &ud, &handle); 1241030d794bSSimo Sorce if (status) 1242030d794bSSimo Sorce goto out; 1243030d794bSSimo Sorce cli_handle.data = (u8 *)&handle; 1244030d794bSSimo Sorce cli_handle.len = sizeof(handle); 1245030d794bSSimo Sorce break; 1246030d794bSSimo Sorce default: 1247030d794bSSimo Sorce ret = SVC_CLOSE; 1248030d794bSSimo Sorce goto out; 1249030d794bSSimo Sorce } 1250030d794bSSimo Sorce 1251030d794bSSimo Sorce /* Got an answer to the upcall; use it: */ 1252030d794bSSimo Sorce if (gss_write_init_verf(sn->rsc_cache, rqstp, 1253030d794bSSimo Sorce &cli_handle, &ud.major_status)) 1254030d794bSSimo Sorce goto out; 1255030d794bSSimo Sorce if (gss_write_resv(resv, PAGE_SIZE, 1256030d794bSSimo Sorce &cli_handle, &ud.out_token, 1257030d794bSSimo Sorce ud.major_status, ud.minor_status)) 1258030d794bSSimo Sorce goto out; 1259030d794bSSimo Sorce 1260030d794bSSimo Sorce ret = SVC_COMPLETE; 1261030d794bSSimo Sorce out: 1262030d794bSSimo Sorce gssp_free_upcall_data(&ud); 1263030d794bSSimo Sorce return ret; 1264030d794bSSimo Sorce } 1265030d794bSSimo Sorce 1266030d794bSSimo Sorce DEFINE_SPINLOCK(use_gssp_lock); 1267030d794bSSimo Sorce 1268030d794bSSimo Sorce static bool use_gss_proxy(struct net *net) 1269030d794bSSimo Sorce { 1270030d794bSSimo Sorce struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1271030d794bSSimo Sorce 1272030d794bSSimo Sorce if (sn->use_gss_proxy != -1) 1273030d794bSSimo Sorce return sn->use_gss_proxy; 1274030d794bSSimo Sorce spin_lock(&use_gssp_lock); 1275030d794bSSimo Sorce /* 1276030d794bSSimo Sorce * If you wanted gss-proxy, you should have said so before 1277030d794bSSimo Sorce * starting to accept requests: 1278030d794bSSimo Sorce */ 1279030d794bSSimo Sorce sn->use_gss_proxy = 0; 1280030d794bSSimo Sorce spin_unlock(&use_gssp_lock); 1281030d794bSSimo Sorce return 0; 1282030d794bSSimo Sorce } 1283030d794bSSimo Sorce 12840ff3bab5SJ. Bruce Fields #ifdef CONFIG_PROC_FS 12850ff3bab5SJ. Bruce Fields 1286625cdd78SDan Carpenter static int set_gss_proxy(struct net *net, int type) 1287030d794bSSimo Sorce { 1288030d794bSSimo Sorce struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1289030d794bSSimo Sorce int ret = 0; 1290030d794bSSimo Sorce 1291030d794bSSimo Sorce WARN_ON_ONCE(type != 0 && type != 1); 1292030d794bSSimo Sorce spin_lock(&use_gssp_lock); 1293030d794bSSimo Sorce if (sn->use_gss_proxy == -1 || sn->use_gss_proxy == type) 1294030d794bSSimo Sorce sn->use_gss_proxy = type; 1295030d794bSSimo Sorce else 1296030d794bSSimo Sorce ret = -EBUSY; 1297030d794bSSimo Sorce spin_unlock(&use_gssp_lock); 1298030d794bSSimo Sorce wake_up(&sn->gssp_wq); 1299030d794bSSimo Sorce return ret; 1300030d794bSSimo Sorce } 1301030d794bSSimo Sorce 1302030d794bSSimo Sorce static inline bool gssp_ready(struct sunrpc_net *sn) 1303030d794bSSimo Sorce { 1304030d794bSSimo Sorce switch (sn->use_gss_proxy) { 1305030d794bSSimo Sorce case -1: 1306030d794bSSimo Sorce return false; 1307030d794bSSimo Sorce case 0: 1308030d794bSSimo Sorce return true; 1309030d794bSSimo Sorce case 1: 1310030d794bSSimo Sorce return sn->gssp_clnt; 1311030d794bSSimo Sorce } 1312030d794bSSimo Sorce WARN_ON_ONCE(1); 1313030d794bSSimo Sorce return false; 1314030d794bSSimo Sorce } 1315030d794bSSimo Sorce 1316b161c144SJ. Bruce Fields static int wait_for_gss_proxy(struct net *net, struct file *file) 1317030d794bSSimo Sorce { 1318030d794bSSimo Sorce struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1319030d794bSSimo Sorce 1320b161c144SJ. Bruce Fields if (file->f_flags & O_NONBLOCK && !gssp_ready(sn)) 1321b161c144SJ. Bruce Fields return -EAGAIN; 1322030d794bSSimo Sorce return wait_event_interruptible(sn->gssp_wq, gssp_ready(sn)); 1323030d794bSSimo Sorce } 1324030d794bSSimo Sorce 1325030d794bSSimo Sorce 1326030d794bSSimo Sorce static ssize_t write_gssp(struct file *file, const char __user *buf, 1327030d794bSSimo Sorce size_t count, loff_t *ppos) 1328030d794bSSimo Sorce { 1329e77e4300SAl Viro struct net *net = PDE_DATA(file_inode(file)); 1330030d794bSSimo Sorce char tbuf[20]; 1331030d794bSSimo Sorce unsigned long i; 1332030d794bSSimo Sorce int res; 1333030d794bSSimo Sorce 1334030d794bSSimo Sorce if (*ppos || count > sizeof(tbuf)-1) 1335030d794bSSimo Sorce return -EINVAL; 1336030d794bSSimo Sorce if (copy_from_user(tbuf, buf, count)) 1337030d794bSSimo Sorce return -EFAULT; 1338030d794bSSimo Sorce 1339030d794bSSimo Sorce tbuf[count] = 0; 1340030d794bSSimo Sorce res = kstrtoul(tbuf, 0, &i); 1341030d794bSSimo Sorce if (res) 1342030d794bSSimo Sorce return res; 1343030d794bSSimo Sorce if (i != 1) 1344030d794bSSimo Sorce return -EINVAL; 1345030d794bSSimo Sorce res = set_gss_proxy(net, 1); 1346030d794bSSimo Sorce if (res) 1347030d794bSSimo Sorce return res; 1348030d794bSSimo Sorce res = set_gssp_clnt(net); 1349030d794bSSimo Sorce if (res) 1350030d794bSSimo Sorce return res; 1351030d794bSSimo Sorce return count; 1352030d794bSSimo Sorce } 1353030d794bSSimo Sorce 1354030d794bSSimo Sorce static ssize_t read_gssp(struct file *file, char __user *buf, 1355030d794bSSimo Sorce size_t count, loff_t *ppos) 1356030d794bSSimo Sorce { 1357e77e4300SAl Viro struct net *net = PDE_DATA(file_inode(file)); 1358030d794bSSimo Sorce unsigned long p = *ppos; 1359030d794bSSimo Sorce char tbuf[10]; 1360030d794bSSimo Sorce size_t len; 1361030d794bSSimo Sorce int ret; 1362030d794bSSimo Sorce 1363b161c144SJ. Bruce Fields ret = wait_for_gss_proxy(net, file); 1364030d794bSSimo Sorce if (ret) 1365030d794bSSimo Sorce return ret; 1366030d794bSSimo Sorce 1367030d794bSSimo Sorce snprintf(tbuf, sizeof(tbuf), "%d\n", use_gss_proxy(net)); 1368030d794bSSimo Sorce len = strlen(tbuf); 1369030d794bSSimo Sorce if (p >= len) 1370030d794bSSimo Sorce return 0; 1371030d794bSSimo Sorce len -= p; 1372030d794bSSimo Sorce if (len > count) 1373030d794bSSimo Sorce len = count; 1374030d794bSSimo Sorce if (copy_to_user(buf, (void *)(tbuf+p), len)) 1375030d794bSSimo Sorce return -EFAULT; 1376030d794bSSimo Sorce *ppos += len; 1377030d794bSSimo Sorce return len; 1378030d794bSSimo Sorce } 1379030d794bSSimo Sorce 1380030d794bSSimo Sorce static const struct file_operations use_gss_proxy_ops = { 1381030d794bSSimo Sorce .open = nonseekable_open, 1382030d794bSSimo Sorce .write = write_gssp, 1383030d794bSSimo Sorce .read = read_gssp, 1384030d794bSSimo Sorce }; 1385030d794bSSimo Sorce 1386030d794bSSimo Sorce static int create_use_gss_proxy_proc_entry(struct net *net) 1387030d794bSSimo Sorce { 1388030d794bSSimo Sorce struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1389030d794bSSimo Sorce struct proc_dir_entry **p = &sn->use_gssp_proc; 1390030d794bSSimo Sorce 1391030d794bSSimo Sorce sn->use_gss_proxy = -1; 1392030d794bSSimo Sorce *p = proc_create_data("use-gss-proxy", S_IFREG|S_IRUSR|S_IWUSR, 1393030d794bSSimo Sorce sn->proc_net_rpc, 1394030d794bSSimo Sorce &use_gss_proxy_ops, net); 1395030d794bSSimo Sorce if (!*p) 1396030d794bSSimo Sorce return -ENOMEM; 1397030d794bSSimo Sorce init_gssp_clnt(sn); 1398030d794bSSimo Sorce return 0; 1399030d794bSSimo Sorce } 1400030d794bSSimo Sorce 1401030d794bSSimo Sorce static void destroy_use_gss_proxy_proc_entry(struct net *net) 1402030d794bSSimo Sorce { 1403030d794bSSimo Sorce struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1404030d794bSSimo Sorce 1405030d794bSSimo Sorce if (sn->use_gssp_proc) { 1406030d794bSSimo Sorce remove_proc_entry("use-gss-proxy", sn->proc_net_rpc); 1407030d794bSSimo Sorce clear_gssp_clnt(sn); 1408030d794bSSimo Sorce } 1409030d794bSSimo Sorce } 14100ff3bab5SJ. Bruce Fields #else /* CONFIG_PROC_FS */ 14110ff3bab5SJ. Bruce Fields 14120ff3bab5SJ. Bruce Fields static int create_use_gss_proxy_proc_entry(struct net *net) 14130ff3bab5SJ. Bruce Fields { 14140ff3bab5SJ. Bruce Fields return 0; 14150ff3bab5SJ. Bruce Fields } 14160ff3bab5SJ. Bruce Fields 14170ff3bab5SJ. Bruce Fields static void destroy_use_gss_proxy_proc_entry(struct net *net) {} 1418030d794bSSimo Sorce 1419030d794bSSimo Sorce #endif /* CONFIG_PROC_FS */ 1420030d794bSSimo Sorce 142121fcd02bSJ. Bruce Fields /* 14221da177e4SLinus Torvalds * Accept an rpcsec packet. 14231da177e4SLinus Torvalds * If context establishment, punt to user space 14241da177e4SLinus Torvalds * If data exchange, verify/decrypt 14251da177e4SLinus Torvalds * If context destruction, handle here 14261da177e4SLinus Torvalds * In the context establishment and destruction case we encode 14271da177e4SLinus Torvalds * response here and return SVC_COMPLETE. 14281da177e4SLinus Torvalds */ 14291da177e4SLinus Torvalds static int 1430d8ed029dSAlexey Dobriyan svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) 14311da177e4SLinus Torvalds { 14321da177e4SLinus Torvalds struct kvec *argv = &rqstp->rq_arg.head[0]; 14331da177e4SLinus Torvalds struct kvec *resv = &rqstp->rq_res.head[0]; 14341da177e4SLinus Torvalds u32 crlen; 14351da177e4SLinus Torvalds struct gss_svc_data *svcdata = rqstp->rq_auth_data; 14361da177e4SLinus Torvalds struct rpc_gss_wire_cred *gc; 14371da177e4SLinus Torvalds struct rsc *rsci = NULL; 1438d8ed029dSAlexey Dobriyan __be32 *rpcstart; 1439d8ed029dSAlexey Dobriyan __be32 *reject_stat = resv->iov_base + resv->iov_len; 14401da177e4SLinus Torvalds int ret; 1441a1db410dSStanislav Kinsbursky struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net, sunrpc_net_id); 14421da177e4SLinus Torvalds 14438885cb36SChuck Lever dprintk("RPC: svcauth_gss: argv->iov_len = %zd\n", 14448885cb36SChuck Lever argv->iov_len); 14451da177e4SLinus Torvalds 14461da177e4SLinus Torvalds *authp = rpc_autherr_badcred; 14471da177e4SLinus Torvalds if (!svcdata) 14481da177e4SLinus Torvalds svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL); 14491da177e4SLinus Torvalds if (!svcdata) 14501da177e4SLinus Torvalds goto auth_err; 14511da177e4SLinus Torvalds rqstp->rq_auth_data = svcdata; 14525b304bc5SJ.Bruce Fields svcdata->verf_start = NULL; 14531da177e4SLinus Torvalds svcdata->rsci = NULL; 14541da177e4SLinus Torvalds gc = &svcdata->clcred; 14551da177e4SLinus Torvalds 14561da177e4SLinus Torvalds /* start of rpc packet is 7 u32's back from here: 14571da177e4SLinus Torvalds * xid direction rpcversion prog vers proc flavour 14581da177e4SLinus Torvalds */ 14591da177e4SLinus Torvalds rpcstart = argv->iov_base; 14601da177e4SLinus Torvalds rpcstart -= 7; 14611da177e4SLinus Torvalds 14621da177e4SLinus Torvalds /* credential is: 14631da177e4SLinus Torvalds * version(==1), proc(0,1,2,3), seq, service (1,2,3), handle 146425985edcSLucas De Marchi * at least 5 u32s, and is preceded by length, so that makes 6. 14651da177e4SLinus Torvalds */ 14661da177e4SLinus Torvalds 14671da177e4SLinus Torvalds if (argv->iov_len < 5 * 4) 14681da177e4SLinus Torvalds goto auth_err; 146976994313SAlexey Dobriyan crlen = svc_getnl(argv); 147076994313SAlexey Dobriyan if (svc_getnl(argv) != RPC_GSS_VERSION) 14711da177e4SLinus Torvalds goto auth_err; 147276994313SAlexey Dobriyan gc->gc_proc = svc_getnl(argv); 147376994313SAlexey Dobriyan gc->gc_seq = svc_getnl(argv); 147476994313SAlexey Dobriyan gc->gc_svc = svc_getnl(argv); 14751da177e4SLinus Torvalds if (svc_safe_getnetobj(argv, &gc->gc_ctx)) 14761da177e4SLinus Torvalds goto auth_err; 14771da177e4SLinus Torvalds if (crlen != round_up_to_quad(gc->gc_ctx.len) + 5 * 4) 14781da177e4SLinus Torvalds goto auth_err; 14791da177e4SLinus Torvalds 14801da177e4SLinus Torvalds if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0)) 14811da177e4SLinus Torvalds goto auth_err; 14821da177e4SLinus Torvalds 14831da177e4SLinus Torvalds *authp = rpc_autherr_badverf; 14841da177e4SLinus Torvalds switch (gc->gc_proc) { 14851da177e4SLinus Torvalds case RPC_GSS_PROC_INIT: 14861da177e4SLinus Torvalds case RPC_GSS_PROC_CONTINUE_INIT: 1487030d794bSSimo Sorce if (use_gss_proxy(SVC_NET(rqstp))) 1488030d794bSSimo Sorce return svcauth_gss_proxy_init(rqstp, gc, authp); 1489030d794bSSimo Sorce else 1490030d794bSSimo Sorce return svcauth_gss_legacy_init(rqstp, gc, authp); 14911da177e4SLinus Torvalds case RPC_GSS_PROC_DATA: 14921da177e4SLinus Torvalds case RPC_GSS_PROC_DESTROY: 149321fcd02bSJ. Bruce Fields /* Look up the context, and check the verifier: */ 14941da177e4SLinus Torvalds *authp = rpcsec_gsserr_credproblem; 1495a1db410dSStanislav Kinsbursky rsci = gss_svc_searchbyctx(sn->rsc_cache, &gc->gc_ctx); 14961da177e4SLinus Torvalds if (!rsci) 14971da177e4SLinus Torvalds goto auth_err; 14981da177e4SLinus Torvalds switch (gss_verify_header(rqstp, rsci, rpcstart, gc, authp)) { 14991da177e4SLinus Torvalds case SVC_OK: 15001da177e4SLinus Torvalds break; 15011da177e4SLinus Torvalds case SVC_DENIED: 15021da177e4SLinus Torvalds goto auth_err; 15031da177e4SLinus Torvalds case SVC_DROP: 15041da177e4SLinus Torvalds goto drop; 15051da177e4SLinus Torvalds } 15061da177e4SLinus Torvalds break; 15071da177e4SLinus Torvalds default: 15081da177e4SLinus Torvalds *authp = rpc_autherr_rejectedcred; 15091da177e4SLinus Torvalds goto auth_err; 15101da177e4SLinus Torvalds } 15111da177e4SLinus Torvalds 15121da177e4SLinus Torvalds /* now act upon the command: */ 15131da177e4SLinus Torvalds switch (gc->gc_proc) { 15141da177e4SLinus Torvalds case RPC_GSS_PROC_DESTROY: 1515c5e434c9SWei Yongjun if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq)) 1516c5e434c9SWei Yongjun goto auth_err; 1517cb5c7d66SJ. Bruce Fields rsci->h.expiry_time = get_seconds(); 15181da177e4SLinus Torvalds set_bit(CACHE_NEGATIVE, &rsci->h.flags); 15191da177e4SLinus Torvalds if (resv->iov_len + 4 > PAGE_SIZE) 15201da177e4SLinus Torvalds goto drop; 152176994313SAlexey Dobriyan svc_putnl(resv, RPC_SUCCESS); 15221da177e4SLinus Torvalds goto complete; 15231da177e4SLinus Torvalds case RPC_GSS_PROC_DATA: 15241da177e4SLinus Torvalds *authp = rpcsec_gsserr_ctxproblem; 15255b304bc5SJ.Bruce Fields svcdata->verf_start = resv->iov_base + resv->iov_len; 15261da177e4SLinus Torvalds if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq)) 15271da177e4SLinus Torvalds goto auth_err; 15281da177e4SLinus Torvalds rqstp->rq_cred = rsci->cred; 15291da177e4SLinus Torvalds get_group_info(rsci->cred.cr_group_info); 15301da177e4SLinus Torvalds *authp = rpc_autherr_badcred; 15311da177e4SLinus Torvalds switch (gc->gc_svc) { 15321da177e4SLinus Torvalds case RPC_GSS_SVC_NONE: 15331da177e4SLinus Torvalds break; 15341da177e4SLinus Torvalds case RPC_GSS_SVC_INTEGRITY: 1535b620754bSJ. Bruce Fields /* placeholders for length and seq. number: */ 1536b620754bSJ. Bruce Fields svc_putnl(resv, 0); 1537b620754bSJ. Bruce Fields svc_putnl(resv, 0); 15384c190e2fSJeff Layton if (unwrap_integ_data(rqstp, &rqstp->rq_arg, 15391da177e4SLinus Torvalds gc->gc_seq, rsci->mechctx)) 1540dd35210eSHarshula Jayasuriya goto garbage_args; 1541b620754bSJ. Bruce Fields break; 1542b620754bSJ. Bruce Fields case RPC_GSS_SVC_PRIVACY: 15431da177e4SLinus Torvalds /* placeholders for length and seq. number: */ 154476994313SAlexey Dobriyan svc_putnl(resv, 0); 154576994313SAlexey Dobriyan svc_putnl(resv, 0); 15467c9fdcfbSJ. Bruce Fields if (unwrap_priv_data(rqstp, &rqstp->rq_arg, 15477c9fdcfbSJ. Bruce Fields gc->gc_seq, rsci->mechctx)) 1548dd35210eSHarshula Jayasuriya goto garbage_args; 15497c9fdcfbSJ. Bruce Fields break; 15501da177e4SLinus Torvalds default: 15511da177e4SLinus Torvalds goto auth_err; 15521da177e4SLinus Torvalds } 15531da177e4SLinus Torvalds svcdata->rsci = rsci; 15541da177e4SLinus Torvalds cache_get(&rsci->h); 1555d5497fc6SJ. Bruce Fields rqstp->rq_cred.cr_flavor = gss_svc_to_pseudoflavor( 155683523d08SChuck Lever rsci->mechctx->mech_type, 155783523d08SChuck Lever GSS_C_QOP_DEFAULT, 155883523d08SChuck Lever gc->gc_svc); 15591da177e4SLinus Torvalds ret = SVC_OK; 15601da177e4SLinus Torvalds goto out; 15611da177e4SLinus Torvalds } 1562dd35210eSHarshula Jayasuriya garbage_args: 1563dd35210eSHarshula Jayasuriya ret = SVC_GARBAGE; 1564dd35210eSHarshula Jayasuriya goto out; 15651da177e4SLinus Torvalds auth_err: 156621fcd02bSJ. Bruce Fields /* Restore write pointer to its original value: */ 15671da177e4SLinus Torvalds xdr_ressize_check(rqstp, reject_stat); 15681da177e4SLinus Torvalds ret = SVC_DENIED; 15691da177e4SLinus Torvalds goto out; 15701da177e4SLinus Torvalds complete: 15711da177e4SLinus Torvalds ret = SVC_COMPLETE; 15721da177e4SLinus Torvalds goto out; 15731da177e4SLinus Torvalds drop: 15741da177e4SLinus Torvalds ret = SVC_DROP; 15751da177e4SLinus Torvalds out: 15761da177e4SLinus Torvalds if (rsci) 1577a1db410dSStanislav Kinsbursky cache_put(&rsci->h, sn->rsc_cache); 15781da177e4SLinus Torvalds return ret; 15791da177e4SLinus Torvalds } 15801da177e4SLinus Torvalds 1581cfbdbab0SAl Viro static __be32 * 15823c15a486SJ.Bruce Fields svcauth_gss_prepare_to_wrap(struct xdr_buf *resbuf, struct gss_svc_data *gsd) 15833c15a486SJ.Bruce Fields { 1584cfbdbab0SAl Viro __be32 *p; 1585cfbdbab0SAl Viro u32 verf_len; 15863c15a486SJ.Bruce Fields 15875b304bc5SJ.Bruce Fields p = gsd->verf_start; 15885b304bc5SJ.Bruce Fields gsd->verf_start = NULL; 15895b304bc5SJ.Bruce Fields 15905b304bc5SJ.Bruce Fields /* If the reply stat is nonzero, don't wrap: */ 15915b304bc5SJ.Bruce Fields if (*(p-1) != rpc_success) 15925b304bc5SJ.Bruce Fields return NULL; 15935b304bc5SJ.Bruce Fields /* Skip the verifier: */ 15945b304bc5SJ.Bruce Fields p += 1; 15955b304bc5SJ.Bruce Fields verf_len = ntohl(*p++); 15965b304bc5SJ.Bruce Fields p += XDR_QUADLEN(verf_len); 15973c15a486SJ.Bruce Fields /* move accept_stat to right place: */ 15983c15a486SJ.Bruce Fields memcpy(p, p + 2, 4); 15995b304bc5SJ.Bruce Fields /* Also don't wrap if the accept stat is nonzero: */ 16003c15a486SJ.Bruce Fields if (*p != rpc_success) { 16013c15a486SJ.Bruce Fields resbuf->head[0].iov_len -= 2 * 4; 16023c15a486SJ.Bruce Fields return NULL; 16033c15a486SJ.Bruce Fields } 16043c15a486SJ.Bruce Fields p++; 16053c15a486SJ.Bruce Fields return p; 16063c15a486SJ.Bruce Fields } 16073c15a486SJ.Bruce Fields 1608e142ede8SJ. Bruce Fields static inline int 1609e142ede8SJ. Bruce Fields svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp) 16101da177e4SLinus Torvalds { 16111da177e4SLinus Torvalds struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data; 16121da177e4SLinus Torvalds struct rpc_gss_wire_cred *gc = &gsd->clcred; 16131da177e4SLinus Torvalds struct xdr_buf *resbuf = &rqstp->rq_res; 16141da177e4SLinus Torvalds struct xdr_buf integ_buf; 16151da177e4SLinus Torvalds struct xdr_netobj mic; 16161da177e4SLinus Torvalds struct kvec *resv; 1617d8ed029dSAlexey Dobriyan __be32 *p; 16181da177e4SLinus Torvalds int integ_offset, integ_len; 16191da177e4SLinus Torvalds int stat = -EINVAL; 16201da177e4SLinus Torvalds 16213c15a486SJ.Bruce Fields p = svcauth_gss_prepare_to_wrap(resbuf, gsd); 16223c15a486SJ.Bruce Fields if (p == NULL) 16231da177e4SLinus Torvalds goto out; 16241da177e4SLinus Torvalds integ_offset = (u8 *)(p + 1) - (u8 *)resbuf->head[0].iov_base; 16251da177e4SLinus Torvalds integ_len = resbuf->len - integ_offset; 16261da177e4SLinus Torvalds BUG_ON(integ_len % 4); 16271da177e4SLinus Torvalds *p++ = htonl(integ_len); 16281da177e4SLinus Torvalds *p++ = htonl(gc->gc_seq); 16291da177e4SLinus Torvalds if (xdr_buf_subsegment(resbuf, &integ_buf, integ_offset, 16301da177e4SLinus Torvalds integ_len)) 16311da177e4SLinus Torvalds BUG(); 1632153e44d2SNeilBrown if (resbuf->tail[0].iov_base == NULL) { 1633e142ede8SJ. Bruce Fields if (resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE > PAGE_SIZE) 1634dfee55f0SNeilBrown goto out_err; 1635e142ede8SJ. Bruce Fields resbuf->tail[0].iov_base = resbuf->head[0].iov_base 1636dfee55f0SNeilBrown + resbuf->head[0].iov_len; 16371da177e4SLinus Torvalds resbuf->tail[0].iov_len = 0; 16381da177e4SLinus Torvalds resv = &resbuf->tail[0]; 16391da177e4SLinus Torvalds } else { 16401da177e4SLinus Torvalds resv = &resbuf->tail[0]; 16411da177e4SLinus Torvalds } 16421da177e4SLinus Torvalds mic.data = (u8 *)resv->iov_base + resv->iov_len + 4; 164300fd6e14SJ. Bruce Fields if (gss_get_mic(gsd->rsci->mechctx, &integ_buf, &mic)) 16441da177e4SLinus Torvalds goto out_err; 164576994313SAlexey Dobriyan svc_putnl(resv, mic.len); 16461da177e4SLinus Torvalds memset(mic.data + mic.len, 0, 16471da177e4SLinus Torvalds round_up_to_quad(mic.len) - mic.len); 16481da177e4SLinus Torvalds resv->iov_len += XDR_QUADLEN(mic.len) << 2; 16491da177e4SLinus Torvalds /* not strictly required: */ 16501da177e4SLinus Torvalds resbuf->len += XDR_QUADLEN(mic.len) << 2; 16511da177e4SLinus Torvalds BUG_ON(resv->iov_len > PAGE_SIZE); 1652e142ede8SJ. Bruce Fields out: 1653e142ede8SJ. Bruce Fields stat = 0; 1654e142ede8SJ. Bruce Fields out_err: 1655e142ede8SJ. Bruce Fields return stat; 1656e142ede8SJ. Bruce Fields } 1657e142ede8SJ. Bruce Fields 16587c9fdcfbSJ. Bruce Fields static inline int 16597c9fdcfbSJ. Bruce Fields svcauth_gss_wrap_resp_priv(struct svc_rqst *rqstp) 16607c9fdcfbSJ. Bruce Fields { 16617c9fdcfbSJ. Bruce Fields struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data; 16627c9fdcfbSJ. Bruce Fields struct rpc_gss_wire_cred *gc = &gsd->clcred; 16637c9fdcfbSJ. Bruce Fields struct xdr_buf *resbuf = &rqstp->rq_res; 16647c9fdcfbSJ. Bruce Fields struct page **inpages = NULL; 1665753ed90dSAl Viro __be32 *p, *len; 1666753ed90dSAl Viro int offset; 16677c9fdcfbSJ. Bruce Fields int pad; 16687c9fdcfbSJ. Bruce Fields 16693c15a486SJ.Bruce Fields p = svcauth_gss_prepare_to_wrap(resbuf, gsd); 16703c15a486SJ.Bruce Fields if (p == NULL) 16717c9fdcfbSJ. Bruce Fields return 0; 16727c9fdcfbSJ. Bruce Fields len = p++; 16737c9fdcfbSJ. Bruce Fields offset = (u8 *)p - (u8 *)resbuf->head[0].iov_base; 16747c9fdcfbSJ. Bruce Fields *p++ = htonl(gc->gc_seq); 16757c9fdcfbSJ. Bruce Fields inpages = resbuf->pages; 16767c9fdcfbSJ. Bruce Fields /* XXX: Would be better to write some xdr helper functions for 16777c9fdcfbSJ. Bruce Fields * nfs{2,3,4}xdr.c that place the data right, instead of copying: */ 16787561042fSKevin Coffman 16797561042fSKevin Coffman /* 16807561042fSKevin Coffman * If there is currently tail data, make sure there is 16817561042fSKevin Coffman * room for the head, tail, and 2 * RPC_MAX_AUTH_SIZE in 16827561042fSKevin Coffman * the page, and move the current tail data such that 16837561042fSKevin Coffman * there is RPC_MAX_AUTH_SIZE slack space available in 16847561042fSKevin Coffman * both the head and tail. 16857561042fSKevin Coffman */ 168644524359SNeilBrown if (resbuf->tail[0].iov_base) { 16877c9fdcfbSJ. Bruce Fields BUG_ON(resbuf->tail[0].iov_base >= resbuf->head[0].iov_base 16887c9fdcfbSJ. Bruce Fields + PAGE_SIZE); 16897c9fdcfbSJ. Bruce Fields BUG_ON(resbuf->tail[0].iov_base < resbuf->head[0].iov_base); 16907c9fdcfbSJ. Bruce Fields if (resbuf->tail[0].iov_len + resbuf->head[0].iov_len 16917c9fdcfbSJ. Bruce Fields + 2 * RPC_MAX_AUTH_SIZE > PAGE_SIZE) 16927c9fdcfbSJ. Bruce Fields return -ENOMEM; 16937c9fdcfbSJ. Bruce Fields memmove(resbuf->tail[0].iov_base + RPC_MAX_AUTH_SIZE, 16947c9fdcfbSJ. Bruce Fields resbuf->tail[0].iov_base, 16957c9fdcfbSJ. Bruce Fields resbuf->tail[0].iov_len); 16967c9fdcfbSJ. Bruce Fields resbuf->tail[0].iov_base += RPC_MAX_AUTH_SIZE; 16977c9fdcfbSJ. Bruce Fields } 16987561042fSKevin Coffman /* 16997561042fSKevin Coffman * If there is no current tail data, make sure there is 17007561042fSKevin Coffman * room for the head data, and 2 * RPC_MAX_AUTH_SIZE in the 17017561042fSKevin Coffman * allotted page, and set up tail information such that there 17027561042fSKevin Coffman * is RPC_MAX_AUTH_SIZE slack space available in both the 17037561042fSKevin Coffman * head and tail. 17047561042fSKevin Coffman */ 17057c9fdcfbSJ. Bruce Fields if (resbuf->tail[0].iov_base == NULL) { 17067c9fdcfbSJ. Bruce Fields if (resbuf->head[0].iov_len + 2*RPC_MAX_AUTH_SIZE > PAGE_SIZE) 17077c9fdcfbSJ. Bruce Fields return -ENOMEM; 17087c9fdcfbSJ. Bruce Fields resbuf->tail[0].iov_base = resbuf->head[0].iov_base 17097c9fdcfbSJ. Bruce Fields + resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE; 17107c9fdcfbSJ. Bruce Fields resbuf->tail[0].iov_len = 0; 17117c9fdcfbSJ. Bruce Fields } 17127c9fdcfbSJ. Bruce Fields if (gss_wrap(gsd->rsci->mechctx, offset, resbuf, inpages)) 17137c9fdcfbSJ. Bruce Fields return -ENOMEM; 17147c9fdcfbSJ. Bruce Fields *len = htonl(resbuf->len - offset); 17157c9fdcfbSJ. Bruce Fields pad = 3 - ((resbuf->len - offset - 1)&3); 1716d8ed029dSAlexey Dobriyan p = (__be32 *)(resbuf->tail[0].iov_base + resbuf->tail[0].iov_len); 17177c9fdcfbSJ. Bruce Fields memset(p, 0, pad); 17187c9fdcfbSJ. Bruce Fields resbuf->tail[0].iov_len += pad; 17197c9fdcfbSJ. Bruce Fields resbuf->len += pad; 17207c9fdcfbSJ. Bruce Fields return 0; 17217c9fdcfbSJ. Bruce Fields } 17227c9fdcfbSJ. Bruce Fields 1723e142ede8SJ. Bruce Fields static int 1724e142ede8SJ. Bruce Fields svcauth_gss_release(struct svc_rqst *rqstp) 1725e142ede8SJ. Bruce Fields { 1726e142ede8SJ. Bruce Fields struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data; 1727e142ede8SJ. Bruce Fields struct rpc_gss_wire_cred *gc = &gsd->clcred; 1728e142ede8SJ. Bruce Fields struct xdr_buf *resbuf = &rqstp->rq_res; 1729e142ede8SJ. Bruce Fields int stat = -EINVAL; 1730a1db410dSStanislav Kinsbursky struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net, sunrpc_net_id); 1731e142ede8SJ. Bruce Fields 1732e142ede8SJ. Bruce Fields if (gc->gc_proc != RPC_GSS_PROC_DATA) 1733e142ede8SJ. Bruce Fields goto out; 1734e142ede8SJ. Bruce Fields /* Release can be called twice, but we only wrap once. */ 17355b304bc5SJ.Bruce Fields if (gsd->verf_start == NULL) 1736e142ede8SJ. Bruce Fields goto out; 1737e142ede8SJ. Bruce Fields /* normally not set till svc_send, but we need it here: */ 17387c9fdcfbSJ. Bruce Fields /* XXX: what for? Do we mess it up the moment we call svc_putu32 17397c9fdcfbSJ. Bruce Fields * or whatever? */ 17407c9fdcfbSJ. Bruce Fields resbuf->len = total_buf_len(resbuf); 1741e142ede8SJ. Bruce Fields switch (gc->gc_svc) { 1742e142ede8SJ. Bruce Fields case RPC_GSS_SVC_NONE: 1743e142ede8SJ. Bruce Fields break; 1744e142ede8SJ. Bruce Fields case RPC_GSS_SVC_INTEGRITY: 17457c9fdcfbSJ. Bruce Fields stat = svcauth_gss_wrap_resp_integ(rqstp); 17467c9fdcfbSJ. Bruce Fields if (stat) 17477c9fdcfbSJ. Bruce Fields goto out_err; 17481da177e4SLinus Torvalds break; 17491da177e4SLinus Torvalds case RPC_GSS_SVC_PRIVACY: 17507c9fdcfbSJ. Bruce Fields stat = svcauth_gss_wrap_resp_priv(rqstp); 17517c9fdcfbSJ. Bruce Fields if (stat) 17527c9fdcfbSJ. Bruce Fields goto out_err; 17537c9fdcfbSJ. Bruce Fields break; 1754eac81736SWei Yongjun /* 1755eac81736SWei Yongjun * For any other gc_svc value, svcauth_gss_accept() already set 1756eac81736SWei Yongjun * the auth_error appropriately; just fall through: 1757eac81736SWei Yongjun */ 17581da177e4SLinus Torvalds } 17591da177e4SLinus Torvalds 17601da177e4SLinus Torvalds out: 17611da177e4SLinus Torvalds stat = 0; 17621da177e4SLinus Torvalds out_err: 17631da177e4SLinus Torvalds if (rqstp->rq_client) 17641da177e4SLinus Torvalds auth_domain_put(rqstp->rq_client); 17651da177e4SLinus Torvalds rqstp->rq_client = NULL; 17663ab4d8b1SJ. Bruce Fields if (rqstp->rq_gssclient) 17673ab4d8b1SJ. Bruce Fields auth_domain_put(rqstp->rq_gssclient); 17683ab4d8b1SJ. Bruce Fields rqstp->rq_gssclient = NULL; 17691da177e4SLinus Torvalds if (rqstp->rq_cred.cr_group_info) 17701da177e4SLinus Torvalds put_group_info(rqstp->rq_cred.cr_group_info); 17711da177e4SLinus Torvalds rqstp->rq_cred.cr_group_info = NULL; 17721da177e4SLinus Torvalds if (gsd->rsci) 1773a1db410dSStanislav Kinsbursky cache_put(&gsd->rsci->h, sn->rsc_cache); 17741da177e4SLinus Torvalds gsd->rsci = NULL; 17751da177e4SLinus Torvalds 17761da177e4SLinus Torvalds return stat; 17771da177e4SLinus Torvalds } 17781da177e4SLinus Torvalds 17791da177e4SLinus Torvalds static void 17801da177e4SLinus Torvalds svcauth_gss_domain_release(struct auth_domain *dom) 17811da177e4SLinus Torvalds { 17821da177e4SLinus Torvalds struct gss_domain *gd = container_of(dom, struct gss_domain, h); 17831da177e4SLinus Torvalds 17841da177e4SLinus Torvalds kfree(dom->name); 17851da177e4SLinus Torvalds kfree(gd); 17861da177e4SLinus Torvalds } 17871da177e4SLinus Torvalds 17881da177e4SLinus Torvalds static struct auth_ops svcauthops_gss = { 17891da177e4SLinus Torvalds .name = "rpcsec_gss", 17901da177e4SLinus Torvalds .owner = THIS_MODULE, 17911da177e4SLinus Torvalds .flavour = RPC_AUTH_GSS, 17921da177e4SLinus Torvalds .accept = svcauth_gss_accept, 17931da177e4SLinus Torvalds .release = svcauth_gss_release, 17941da177e4SLinus Torvalds .domain_release = svcauth_gss_domain_release, 17951da177e4SLinus Torvalds .set_client = svcauth_gss_set_client, 17961da177e4SLinus Torvalds }; 17971da177e4SLinus Torvalds 1798a1db410dSStanislav Kinsbursky static int rsi_cache_create_net(struct net *net) 1799a1db410dSStanislav Kinsbursky { 1800a1db410dSStanislav Kinsbursky struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1801a1db410dSStanislav Kinsbursky struct cache_detail *cd; 1802a1db410dSStanislav Kinsbursky int err; 1803a1db410dSStanislav Kinsbursky 1804a1db410dSStanislav Kinsbursky cd = cache_create_net(&rsi_cache_template, net); 1805a1db410dSStanislav Kinsbursky if (IS_ERR(cd)) 1806a1db410dSStanislav Kinsbursky return PTR_ERR(cd); 1807a1db410dSStanislav Kinsbursky err = cache_register_net(cd, net); 1808a1db410dSStanislav Kinsbursky if (err) { 1809a1db410dSStanislav Kinsbursky cache_destroy_net(cd, net); 1810a1db410dSStanislav Kinsbursky return err; 1811a1db410dSStanislav Kinsbursky } 1812a1db410dSStanislav Kinsbursky sn->rsi_cache = cd; 1813a1db410dSStanislav Kinsbursky return 0; 1814a1db410dSStanislav Kinsbursky } 1815a1db410dSStanislav Kinsbursky 1816a1db410dSStanislav Kinsbursky static void rsi_cache_destroy_net(struct net *net) 1817a1db410dSStanislav Kinsbursky { 1818a1db410dSStanislav Kinsbursky struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1819a1db410dSStanislav Kinsbursky struct cache_detail *cd = sn->rsi_cache; 1820a1db410dSStanislav Kinsbursky 1821a1db410dSStanislav Kinsbursky sn->rsi_cache = NULL; 1822a1db410dSStanislav Kinsbursky cache_purge(cd); 1823a1db410dSStanislav Kinsbursky cache_unregister_net(cd, net); 1824a1db410dSStanislav Kinsbursky cache_destroy_net(cd, net); 1825a1db410dSStanislav Kinsbursky } 1826a1db410dSStanislav Kinsbursky 1827a1db410dSStanislav Kinsbursky static int rsc_cache_create_net(struct net *net) 1828a1db410dSStanislav Kinsbursky { 1829a1db410dSStanislav Kinsbursky struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1830a1db410dSStanislav Kinsbursky struct cache_detail *cd; 1831a1db410dSStanislav Kinsbursky int err; 1832a1db410dSStanislav Kinsbursky 1833a1db410dSStanislav Kinsbursky cd = cache_create_net(&rsc_cache_template, net); 1834a1db410dSStanislav Kinsbursky if (IS_ERR(cd)) 1835a1db410dSStanislav Kinsbursky return PTR_ERR(cd); 1836a1db410dSStanislav Kinsbursky err = cache_register_net(cd, net); 1837a1db410dSStanislav Kinsbursky if (err) { 1838a1db410dSStanislav Kinsbursky cache_destroy_net(cd, net); 1839a1db410dSStanislav Kinsbursky return err; 1840a1db410dSStanislav Kinsbursky } 1841a1db410dSStanislav Kinsbursky sn->rsc_cache = cd; 1842a1db410dSStanislav Kinsbursky return 0; 1843a1db410dSStanislav Kinsbursky } 1844a1db410dSStanislav Kinsbursky 1845a1db410dSStanislav Kinsbursky static void rsc_cache_destroy_net(struct net *net) 1846a1db410dSStanislav Kinsbursky { 1847a1db410dSStanislav Kinsbursky struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 1848a1db410dSStanislav Kinsbursky struct cache_detail *cd = sn->rsc_cache; 1849a1db410dSStanislav Kinsbursky 1850a1db410dSStanislav Kinsbursky sn->rsc_cache = NULL; 1851a1db410dSStanislav Kinsbursky cache_purge(cd); 1852a1db410dSStanislav Kinsbursky cache_unregister_net(cd, net); 1853a1db410dSStanislav Kinsbursky cache_destroy_net(cd, net); 1854a1db410dSStanislav Kinsbursky } 1855a1db410dSStanislav Kinsbursky 1856a1db410dSStanislav Kinsbursky int 1857a1db410dSStanislav Kinsbursky gss_svc_init_net(struct net *net) 1858a1db410dSStanislav Kinsbursky { 1859a1db410dSStanislav Kinsbursky int rv; 1860a1db410dSStanislav Kinsbursky 1861a1db410dSStanislav Kinsbursky rv = rsc_cache_create_net(net); 1862a1db410dSStanislav Kinsbursky if (rv) 1863a1db410dSStanislav Kinsbursky return rv; 1864a1db410dSStanislav Kinsbursky rv = rsi_cache_create_net(net); 1865a1db410dSStanislav Kinsbursky if (rv) 1866a1db410dSStanislav Kinsbursky goto out1; 1867030d794bSSimo Sorce rv = create_use_gss_proxy_proc_entry(net); 1868030d794bSSimo Sorce if (rv) 1869030d794bSSimo Sorce goto out2; 1870a1db410dSStanislav Kinsbursky return 0; 1871030d794bSSimo Sorce out2: 1872030d794bSSimo Sorce destroy_use_gss_proxy_proc_entry(net); 1873a1db410dSStanislav Kinsbursky out1: 1874a1db410dSStanislav Kinsbursky rsc_cache_destroy_net(net); 1875a1db410dSStanislav Kinsbursky return rv; 1876a1db410dSStanislav Kinsbursky } 1877a1db410dSStanislav Kinsbursky 1878a1db410dSStanislav Kinsbursky void 1879a1db410dSStanislav Kinsbursky gss_svc_shutdown_net(struct net *net) 1880a1db410dSStanislav Kinsbursky { 1881030d794bSSimo Sorce destroy_use_gss_proxy_proc_entry(net); 1882a1db410dSStanislav Kinsbursky rsi_cache_destroy_net(net); 1883a1db410dSStanislav Kinsbursky rsc_cache_destroy_net(net); 1884a1db410dSStanislav Kinsbursky } 1885a1db410dSStanislav Kinsbursky 18861da177e4SLinus Torvalds int 18871da177e4SLinus Torvalds gss_svc_init(void) 18881da177e4SLinus Torvalds { 1889a1db410dSStanislav Kinsbursky return svc_auth_register(RPC_AUTH_GSS, &svcauthops_gss); 18901da177e4SLinus Torvalds } 18911da177e4SLinus Torvalds 18921da177e4SLinus Torvalds void 18931da177e4SLinus Torvalds gss_svc_shutdown(void) 18941da177e4SLinus Torvalds { 18951da177e4SLinus Torvalds svc_auth_unregister(RPC_AUTH_GSS); 18961da177e4SLinus Torvalds } 1897