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 401da177e4SLinus Torvalds #include <linux/types.h> 411da177e4SLinus Torvalds #include <linux/module.h> 421da177e4SLinus Torvalds #include <linux/pagemap.h> 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds #include <linux/sunrpc/auth_gss.h> 451da177e4SLinus Torvalds #include <linux/sunrpc/gss_err.h> 461da177e4SLinus Torvalds #include <linux/sunrpc/svcauth.h> 471da177e4SLinus Torvalds #include <linux/sunrpc/svcauth_gss.h> 481da177e4SLinus Torvalds #include <linux/sunrpc/cache.h> 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds #ifdef RPC_DEBUG 511da177e4SLinus Torvalds # define RPCDBG_FACILITY RPCDBG_AUTH 521da177e4SLinus Torvalds #endif 531da177e4SLinus Torvalds 541da177e4SLinus Torvalds /* The rpcsec_init cache is used for mapping RPCSEC_GSS_{,CONT_}INIT requests 551da177e4SLinus Torvalds * into replies. 561da177e4SLinus Torvalds * 571da177e4SLinus Torvalds * Key is context handle (\x if empty) and gss_token. 581da177e4SLinus Torvalds * Content is major_status minor_status (integers) context_handle, reply_token. 591da177e4SLinus Torvalds * 601da177e4SLinus Torvalds */ 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds static int netobj_equal(struct xdr_netobj *a, struct xdr_netobj *b) 631da177e4SLinus Torvalds { 641da177e4SLinus Torvalds return a->len == b->len && 0 == memcmp(a->data, b->data, a->len); 651da177e4SLinus Torvalds } 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds #define RSI_HASHBITS 6 681da177e4SLinus Torvalds #define RSI_HASHMAX (1<<RSI_HASHBITS) 691da177e4SLinus Torvalds #define RSI_HASHMASK (RSI_HASHMAX-1) 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds struct rsi { 721da177e4SLinus Torvalds struct cache_head h; 731da177e4SLinus Torvalds struct xdr_netobj in_handle, in_token; 741da177e4SLinus Torvalds struct xdr_netobj out_handle, out_token; 751da177e4SLinus Torvalds int major_status, minor_status; 761da177e4SLinus Torvalds }; 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds static struct cache_head *rsi_table[RSI_HASHMAX]; 791da177e4SLinus Torvalds static struct cache_detail rsi_cache; 80d4d11ea9SNeilBrown static struct rsi *rsi_update(struct rsi *new, struct rsi *old); 81d4d11ea9SNeilBrown static struct rsi *rsi_lookup(struct rsi *item); 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds static void rsi_free(struct rsi *rsii) 841da177e4SLinus Torvalds { 851da177e4SLinus Torvalds kfree(rsii->in_handle.data); 861da177e4SLinus Torvalds kfree(rsii->in_token.data); 871da177e4SLinus Torvalds kfree(rsii->out_handle.data); 881da177e4SLinus Torvalds kfree(rsii->out_token.data); 891da177e4SLinus Torvalds } 901da177e4SLinus Torvalds 91baab935fSNeilBrown static void rsi_put(struct kref *ref) 921da177e4SLinus Torvalds { 93baab935fSNeilBrown struct rsi *rsii = container_of(ref, struct rsi, h.ref); 941da177e4SLinus Torvalds rsi_free(rsii); 951da177e4SLinus Torvalds kfree(rsii); 961da177e4SLinus Torvalds } 971da177e4SLinus Torvalds 981da177e4SLinus Torvalds static inline int rsi_hash(struct rsi *item) 991da177e4SLinus Torvalds { 1001da177e4SLinus Torvalds return hash_mem(item->in_handle.data, item->in_handle.len, RSI_HASHBITS) 1011da177e4SLinus Torvalds ^ hash_mem(item->in_token.data, item->in_token.len, RSI_HASHBITS); 1021da177e4SLinus Torvalds } 1031da177e4SLinus Torvalds 104d4d11ea9SNeilBrown static int rsi_match(struct cache_head *a, struct cache_head *b) 1051da177e4SLinus Torvalds { 106d4d11ea9SNeilBrown struct rsi *item = container_of(a, struct rsi, h); 107d4d11ea9SNeilBrown struct rsi *tmp = container_of(b, struct rsi, h); 1081da177e4SLinus Torvalds return netobj_equal(&item->in_handle, &tmp->in_handle) 1091da177e4SLinus Torvalds && netobj_equal(&item->in_token, &tmp->in_token); 1101da177e4SLinus Torvalds } 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds static int dup_to_netobj(struct xdr_netobj *dst, char *src, int len) 1131da177e4SLinus Torvalds { 1141da177e4SLinus Torvalds dst->len = len; 115e69062b4SArnaldo Carvalho de Melo dst->data = (len ? kmemdup(src, len, GFP_KERNEL) : NULL); 1161da177e4SLinus Torvalds if (len && !dst->data) 1171da177e4SLinus Torvalds return -ENOMEM; 1181da177e4SLinus Torvalds return 0; 1191da177e4SLinus Torvalds } 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds static inline int dup_netobj(struct xdr_netobj *dst, struct xdr_netobj *src) 1221da177e4SLinus Torvalds { 1231da177e4SLinus Torvalds return dup_to_netobj(dst, src->data, src->len); 1241da177e4SLinus Torvalds } 1251da177e4SLinus Torvalds 126d4d11ea9SNeilBrown static void rsi_init(struct cache_head *cnew, struct cache_head *citem) 1271da177e4SLinus Torvalds { 128d4d11ea9SNeilBrown struct rsi *new = container_of(cnew, struct rsi, h); 129d4d11ea9SNeilBrown struct rsi *item = container_of(citem, struct rsi, h); 130d4d11ea9SNeilBrown 1311da177e4SLinus Torvalds new->out_handle.data = NULL; 1321da177e4SLinus Torvalds new->out_handle.len = 0; 1331da177e4SLinus Torvalds new->out_token.data = NULL; 1341da177e4SLinus Torvalds new->out_token.len = 0; 1351da177e4SLinus Torvalds new->in_handle.len = item->in_handle.len; 1361da177e4SLinus Torvalds item->in_handle.len = 0; 1371da177e4SLinus Torvalds new->in_token.len = item->in_token.len; 1381da177e4SLinus Torvalds item->in_token.len = 0; 1391da177e4SLinus Torvalds new->in_handle.data = item->in_handle.data; 1401da177e4SLinus Torvalds item->in_handle.data = NULL; 1411da177e4SLinus Torvalds new->in_token.data = item->in_token.data; 1421da177e4SLinus Torvalds item->in_token.data = NULL; 1431da177e4SLinus Torvalds } 1441da177e4SLinus Torvalds 145d4d11ea9SNeilBrown static void update_rsi(struct cache_head *cnew, struct cache_head *citem) 1461da177e4SLinus Torvalds { 147d4d11ea9SNeilBrown struct rsi *new = container_of(cnew, struct rsi, h); 148d4d11ea9SNeilBrown struct rsi *item = container_of(citem, struct rsi, h); 149d4d11ea9SNeilBrown 1501da177e4SLinus Torvalds BUG_ON(new->out_handle.data || new->out_token.data); 1511da177e4SLinus Torvalds new->out_handle.len = item->out_handle.len; 1521da177e4SLinus Torvalds item->out_handle.len = 0; 1531da177e4SLinus Torvalds new->out_token.len = item->out_token.len; 1541da177e4SLinus Torvalds item->out_token.len = 0; 1551da177e4SLinus Torvalds new->out_handle.data = item->out_handle.data; 1561da177e4SLinus Torvalds item->out_handle.data = NULL; 1571da177e4SLinus Torvalds new->out_token.data = item->out_token.data; 1581da177e4SLinus Torvalds item->out_token.data = NULL; 1591da177e4SLinus Torvalds 1601da177e4SLinus Torvalds new->major_status = item->major_status; 1611da177e4SLinus Torvalds new->minor_status = item->minor_status; 1621da177e4SLinus Torvalds } 1631da177e4SLinus Torvalds 164d4d11ea9SNeilBrown static struct cache_head *rsi_alloc(void) 165d4d11ea9SNeilBrown { 166d4d11ea9SNeilBrown struct rsi *rsii = kmalloc(sizeof(*rsii), GFP_KERNEL); 167d4d11ea9SNeilBrown if (rsii) 168d4d11ea9SNeilBrown return &rsii->h; 169d4d11ea9SNeilBrown else 170d4d11ea9SNeilBrown return NULL; 171d4d11ea9SNeilBrown } 172d4d11ea9SNeilBrown 1731da177e4SLinus Torvalds static void rsi_request(struct cache_detail *cd, 1741da177e4SLinus Torvalds struct cache_head *h, 1751da177e4SLinus Torvalds char **bpp, int *blen) 1761da177e4SLinus Torvalds { 1771da177e4SLinus Torvalds struct rsi *rsii = container_of(h, struct rsi, h); 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds qword_addhex(bpp, blen, rsii->in_handle.data, rsii->in_handle.len); 1801da177e4SLinus Torvalds qword_addhex(bpp, blen, rsii->in_token.data, rsii->in_token.len); 1811da177e4SLinus Torvalds (*bpp)[-1] = '\n'; 1821da177e4SLinus Torvalds } 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 214d4d11ea9SNeilBrown rsip = rsi_lookup(&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; 256d4d11ea9SNeilBrown rsip = rsi_update(&rsii, rsip); 2571da177e4SLinus Torvalds status = 0; 2581da177e4SLinus Torvalds out: 2591da177e4SLinus Torvalds rsi_free(&rsii); 2601da177e4SLinus Torvalds if (rsip) 261baab935fSNeilBrown cache_put(&rsip->h, &rsi_cache); 262d4d11ea9SNeilBrown else 263d4d11ea9SNeilBrown status = -ENOMEM; 2641da177e4SLinus Torvalds return status; 2651da177e4SLinus Torvalds } 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds static struct cache_detail rsi_cache = { 268f35279d3SBruce Allan .owner = THIS_MODULE, 2691da177e4SLinus Torvalds .hash_size = RSI_HASHMAX, 2701da177e4SLinus Torvalds .hash_table = rsi_table, 2711da177e4SLinus Torvalds .name = "auth.rpcsec.init", 2721da177e4SLinus Torvalds .cache_put = rsi_put, 2731da177e4SLinus Torvalds .cache_request = rsi_request, 2741da177e4SLinus Torvalds .cache_parse = rsi_parse, 275d4d11ea9SNeilBrown .match = rsi_match, 276d4d11ea9SNeilBrown .init = rsi_init, 277d4d11ea9SNeilBrown .update = update_rsi, 278d4d11ea9SNeilBrown .alloc = rsi_alloc, 2791da177e4SLinus Torvalds }; 2801da177e4SLinus Torvalds 281d4d11ea9SNeilBrown static struct rsi *rsi_lookup(struct rsi *item) 282d4d11ea9SNeilBrown { 283d4d11ea9SNeilBrown struct cache_head *ch; 284d4d11ea9SNeilBrown int hash = rsi_hash(item); 285d4d11ea9SNeilBrown 286d4d11ea9SNeilBrown ch = sunrpc_cache_lookup(&rsi_cache, &item->h, hash); 287d4d11ea9SNeilBrown if (ch) 288d4d11ea9SNeilBrown return container_of(ch, struct rsi, h); 289d4d11ea9SNeilBrown else 290d4d11ea9SNeilBrown return NULL; 291d4d11ea9SNeilBrown } 292d4d11ea9SNeilBrown 293d4d11ea9SNeilBrown static struct rsi *rsi_update(struct rsi *new, struct rsi *old) 294d4d11ea9SNeilBrown { 295d4d11ea9SNeilBrown struct cache_head *ch; 296d4d11ea9SNeilBrown int hash = rsi_hash(new); 297d4d11ea9SNeilBrown 298d4d11ea9SNeilBrown ch = sunrpc_cache_update(&rsi_cache, &new->h, 299d4d11ea9SNeilBrown &old->h, hash); 300d4d11ea9SNeilBrown if (ch) 301d4d11ea9SNeilBrown return container_of(ch, struct rsi, h); 302d4d11ea9SNeilBrown else 303d4d11ea9SNeilBrown return NULL; 304d4d11ea9SNeilBrown } 305d4d11ea9SNeilBrown 3061da177e4SLinus Torvalds 3071da177e4SLinus Torvalds /* 3081da177e4SLinus Torvalds * The rpcsec_context cache is used to store a context that is 3091da177e4SLinus Torvalds * used in data exchange. 3101da177e4SLinus Torvalds * The key is a context handle. The content is: 3111da177e4SLinus Torvalds * uid, gidlist, mechanism, service-set, mech-specific-data 3121da177e4SLinus Torvalds */ 3131da177e4SLinus Torvalds 3141da177e4SLinus Torvalds #define RSC_HASHBITS 10 3151da177e4SLinus Torvalds #define RSC_HASHMAX (1<<RSC_HASHBITS) 3161da177e4SLinus Torvalds #define RSC_HASHMASK (RSC_HASHMAX-1) 3171da177e4SLinus Torvalds 3181da177e4SLinus Torvalds #define GSS_SEQ_WIN 128 3191da177e4SLinus Torvalds 3201da177e4SLinus Torvalds struct gss_svc_seq_data { 3211da177e4SLinus Torvalds /* highest seq number seen so far: */ 3221da177e4SLinus Torvalds int sd_max; 3231da177e4SLinus Torvalds /* for i such that sd_max-GSS_SEQ_WIN < i <= sd_max, the i-th bit of 3241da177e4SLinus Torvalds * sd_win is nonzero iff sequence number i has been seen already: */ 3251da177e4SLinus Torvalds unsigned long sd_win[GSS_SEQ_WIN/BITS_PER_LONG]; 3261da177e4SLinus Torvalds spinlock_t sd_lock; 3271da177e4SLinus Torvalds }; 3281da177e4SLinus Torvalds 3291da177e4SLinus Torvalds struct rsc { 3301da177e4SLinus Torvalds struct cache_head h; 3311da177e4SLinus Torvalds struct xdr_netobj handle; 3321da177e4SLinus Torvalds struct svc_cred cred; 3331da177e4SLinus Torvalds struct gss_svc_seq_data seqdata; 3341da177e4SLinus Torvalds struct gss_ctx *mechctx; 3351da177e4SLinus Torvalds }; 3361da177e4SLinus Torvalds 3371da177e4SLinus Torvalds static struct cache_head *rsc_table[RSC_HASHMAX]; 3381da177e4SLinus Torvalds static struct cache_detail rsc_cache; 33917f834b6SNeilBrown static struct rsc *rsc_update(struct rsc *new, struct rsc *old); 34017f834b6SNeilBrown static struct rsc *rsc_lookup(struct rsc *item); 3411da177e4SLinus Torvalds 3421da177e4SLinus Torvalds static void rsc_free(struct rsc *rsci) 3431da177e4SLinus Torvalds { 3441da177e4SLinus Torvalds kfree(rsci->handle.data); 3451da177e4SLinus Torvalds if (rsci->mechctx) 3461da177e4SLinus Torvalds gss_delete_sec_context(&rsci->mechctx); 3471da177e4SLinus Torvalds if (rsci->cred.cr_group_info) 3481da177e4SLinus Torvalds put_group_info(rsci->cred.cr_group_info); 3491da177e4SLinus Torvalds } 3501da177e4SLinus Torvalds 351baab935fSNeilBrown static void rsc_put(struct kref *ref) 3521da177e4SLinus Torvalds { 353baab935fSNeilBrown struct rsc *rsci = container_of(ref, struct rsc, h.ref); 3541da177e4SLinus Torvalds 3551da177e4SLinus Torvalds rsc_free(rsci); 3561da177e4SLinus Torvalds kfree(rsci); 3571da177e4SLinus Torvalds } 3581da177e4SLinus Torvalds 3591da177e4SLinus Torvalds static inline int 3601da177e4SLinus Torvalds rsc_hash(struct rsc *rsci) 3611da177e4SLinus Torvalds { 3621da177e4SLinus Torvalds return hash_mem(rsci->handle.data, rsci->handle.len, RSC_HASHBITS); 3631da177e4SLinus Torvalds } 3641da177e4SLinus Torvalds 36517f834b6SNeilBrown static int 36617f834b6SNeilBrown rsc_match(struct cache_head *a, struct cache_head *b) 3671da177e4SLinus Torvalds { 36817f834b6SNeilBrown struct rsc *new = container_of(a, struct rsc, h); 36917f834b6SNeilBrown struct rsc *tmp = container_of(b, struct rsc, h); 37017f834b6SNeilBrown 3711da177e4SLinus Torvalds return netobj_equal(&new->handle, &tmp->handle); 3721da177e4SLinus Torvalds } 3731da177e4SLinus Torvalds 37417f834b6SNeilBrown static void 37517f834b6SNeilBrown rsc_init(struct cache_head *cnew, struct cache_head *ctmp) 3761da177e4SLinus Torvalds { 37717f834b6SNeilBrown struct rsc *new = container_of(cnew, struct rsc, h); 37817f834b6SNeilBrown struct rsc *tmp = container_of(ctmp, struct rsc, h); 37917f834b6SNeilBrown 3801da177e4SLinus Torvalds new->handle.len = tmp->handle.len; 3811da177e4SLinus Torvalds tmp->handle.len = 0; 3821da177e4SLinus Torvalds new->handle.data = tmp->handle.data; 3831da177e4SLinus Torvalds tmp->handle.data = NULL; 3841da177e4SLinus Torvalds new->mechctx = NULL; 3851da177e4SLinus Torvalds new->cred.cr_group_info = NULL; 3861da177e4SLinus Torvalds } 3871da177e4SLinus Torvalds 38817f834b6SNeilBrown static void 38917f834b6SNeilBrown update_rsc(struct cache_head *cnew, struct cache_head *ctmp) 3901da177e4SLinus Torvalds { 39117f834b6SNeilBrown struct rsc *new = container_of(cnew, struct rsc, h); 39217f834b6SNeilBrown struct rsc *tmp = container_of(ctmp, struct rsc, h); 39317f834b6SNeilBrown 3941da177e4SLinus Torvalds new->mechctx = tmp->mechctx; 3951da177e4SLinus Torvalds tmp->mechctx = NULL; 3961da177e4SLinus Torvalds memset(&new->seqdata, 0, sizeof(new->seqdata)); 3971da177e4SLinus Torvalds spin_lock_init(&new->seqdata.sd_lock); 3981da177e4SLinus Torvalds new->cred = tmp->cred; 3991da177e4SLinus Torvalds tmp->cred.cr_group_info = NULL; 4001da177e4SLinus Torvalds } 4011da177e4SLinus Torvalds 40217f834b6SNeilBrown static struct cache_head * 40317f834b6SNeilBrown rsc_alloc(void) 40417f834b6SNeilBrown { 40517f834b6SNeilBrown struct rsc *rsci = kmalloc(sizeof(*rsci), GFP_KERNEL); 40617f834b6SNeilBrown if (rsci) 40717f834b6SNeilBrown return &rsci->h; 40817f834b6SNeilBrown else 40917f834b6SNeilBrown return NULL; 41017f834b6SNeilBrown } 41117f834b6SNeilBrown 4121da177e4SLinus Torvalds static int rsc_parse(struct cache_detail *cd, 4131da177e4SLinus Torvalds char *mesg, int mlen) 4141da177e4SLinus Torvalds { 4151da177e4SLinus Torvalds /* contexthandle expiry [ uid gid N <n gids> mechname ...mechdata... ] */ 4161da177e4SLinus Torvalds char *buf = mesg; 4171da177e4SLinus Torvalds int len, rv; 4181da177e4SLinus Torvalds struct rsc rsci, *rscp = NULL; 4191da177e4SLinus Torvalds time_t expiry; 4201da177e4SLinus Torvalds int status = -EINVAL; 4211df0cadaSJ. Bruce Fields struct gss_api_mech *gm = NULL; 4221da177e4SLinus Torvalds 4231da177e4SLinus Torvalds memset(&rsci, 0, sizeof(rsci)); 4241da177e4SLinus Torvalds /* context handle */ 4251da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 4261da177e4SLinus Torvalds if (len < 0) goto out; 4271da177e4SLinus Torvalds status = -ENOMEM; 4281da177e4SLinus Torvalds if (dup_to_netobj(&rsci.handle, buf, len)) 4291da177e4SLinus Torvalds goto out; 4301da177e4SLinus Torvalds 4311da177e4SLinus Torvalds rsci.h.flags = 0; 4321da177e4SLinus Torvalds /* expiry */ 4331da177e4SLinus Torvalds expiry = get_expiry(&mesg); 4341da177e4SLinus Torvalds status = -EINVAL; 4351da177e4SLinus Torvalds if (expiry == 0) 4361da177e4SLinus Torvalds goto out; 4371da177e4SLinus Torvalds 43817f834b6SNeilBrown rscp = rsc_lookup(&rsci); 43917f834b6SNeilBrown if (!rscp) 44017f834b6SNeilBrown goto out; 44117f834b6SNeilBrown 4421da177e4SLinus Torvalds /* uid, or NEGATIVE */ 4431da177e4SLinus Torvalds rv = get_int(&mesg, &rsci.cred.cr_uid); 4441da177e4SLinus Torvalds if (rv == -EINVAL) 4451da177e4SLinus Torvalds goto out; 4461da177e4SLinus Torvalds if (rv == -ENOENT) 4471da177e4SLinus Torvalds set_bit(CACHE_NEGATIVE, &rsci.h.flags); 4481da177e4SLinus Torvalds else { 4491da177e4SLinus Torvalds int N, i; 4501da177e4SLinus Torvalds 4511da177e4SLinus Torvalds /* gid */ 4521da177e4SLinus Torvalds if (get_int(&mesg, &rsci.cred.cr_gid)) 4531da177e4SLinus Torvalds goto out; 4541da177e4SLinus Torvalds 4551da177e4SLinus Torvalds /* number of additional gid's */ 4561da177e4SLinus Torvalds if (get_int(&mesg, &N)) 4571da177e4SLinus Torvalds goto out; 4581da177e4SLinus Torvalds status = -ENOMEM; 4591da177e4SLinus Torvalds rsci.cred.cr_group_info = groups_alloc(N); 4601da177e4SLinus Torvalds if (rsci.cred.cr_group_info == NULL) 4611da177e4SLinus Torvalds goto out; 4621da177e4SLinus Torvalds 4631da177e4SLinus Torvalds /* gid's */ 4641da177e4SLinus Torvalds status = -EINVAL; 4651da177e4SLinus Torvalds for (i=0; i<N; i++) { 4661da177e4SLinus Torvalds gid_t gid; 4671da177e4SLinus Torvalds if (get_int(&mesg, &gid)) 4681da177e4SLinus Torvalds goto out; 4691da177e4SLinus Torvalds GROUP_AT(rsci.cred.cr_group_info, i) = gid; 4701da177e4SLinus Torvalds } 4711da177e4SLinus Torvalds 4721da177e4SLinus Torvalds /* mech name */ 4731da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 4741da177e4SLinus Torvalds if (len < 0) 4751da177e4SLinus Torvalds goto out; 4761da177e4SLinus Torvalds gm = gss_mech_get_by_name(buf); 4771da177e4SLinus Torvalds status = -EOPNOTSUPP; 4781da177e4SLinus Torvalds if (!gm) 4791da177e4SLinus Torvalds goto out; 4801da177e4SLinus Torvalds 4811da177e4SLinus Torvalds status = -EINVAL; 4821da177e4SLinus Torvalds /* mech-specific data: */ 4831da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 4841df0cadaSJ. Bruce Fields if (len < 0) 4851da177e4SLinus Torvalds goto out; 4865fb8b49eSJ. Bruce Fields status = gss_import_sec_context(buf, len, gm, &rsci.mechctx); 4871df0cadaSJ. Bruce Fields if (status) 4881da177e4SLinus Torvalds goto out; 4891da177e4SLinus Torvalds } 4901da177e4SLinus Torvalds rsci.h.expiry_time = expiry; 49117f834b6SNeilBrown rscp = rsc_update(&rsci, rscp); 4921da177e4SLinus Torvalds status = 0; 4931da177e4SLinus Torvalds out: 4941df0cadaSJ. Bruce Fields gss_mech_put(gm); 4951da177e4SLinus Torvalds rsc_free(&rsci); 4961da177e4SLinus Torvalds if (rscp) 497baab935fSNeilBrown cache_put(&rscp->h, &rsc_cache); 49817f834b6SNeilBrown else 49917f834b6SNeilBrown status = -ENOMEM; 5001da177e4SLinus Torvalds return status; 5011da177e4SLinus Torvalds } 5021da177e4SLinus Torvalds 5031da177e4SLinus Torvalds static struct cache_detail rsc_cache = { 504f35279d3SBruce Allan .owner = THIS_MODULE, 5051da177e4SLinus Torvalds .hash_size = RSC_HASHMAX, 5061da177e4SLinus Torvalds .hash_table = rsc_table, 5071da177e4SLinus Torvalds .name = "auth.rpcsec.context", 5081da177e4SLinus Torvalds .cache_put = rsc_put, 5091da177e4SLinus Torvalds .cache_parse = rsc_parse, 51017f834b6SNeilBrown .match = rsc_match, 51117f834b6SNeilBrown .init = rsc_init, 51217f834b6SNeilBrown .update = update_rsc, 51317f834b6SNeilBrown .alloc = rsc_alloc, 5141da177e4SLinus Torvalds }; 5151da177e4SLinus Torvalds 51617f834b6SNeilBrown static struct rsc *rsc_lookup(struct rsc *item) 51717f834b6SNeilBrown { 51817f834b6SNeilBrown struct cache_head *ch; 51917f834b6SNeilBrown int hash = rsc_hash(item); 52017f834b6SNeilBrown 52117f834b6SNeilBrown ch = sunrpc_cache_lookup(&rsc_cache, &item->h, hash); 52217f834b6SNeilBrown if (ch) 52317f834b6SNeilBrown return container_of(ch, struct rsc, h); 52417f834b6SNeilBrown else 52517f834b6SNeilBrown return NULL; 52617f834b6SNeilBrown } 52717f834b6SNeilBrown 52817f834b6SNeilBrown static struct rsc *rsc_update(struct rsc *new, struct rsc *old) 52917f834b6SNeilBrown { 53017f834b6SNeilBrown struct cache_head *ch; 53117f834b6SNeilBrown int hash = rsc_hash(new); 53217f834b6SNeilBrown 53317f834b6SNeilBrown ch = sunrpc_cache_update(&rsc_cache, &new->h, 53417f834b6SNeilBrown &old->h, hash); 53517f834b6SNeilBrown if (ch) 53617f834b6SNeilBrown return container_of(ch, struct rsc, h); 53717f834b6SNeilBrown else 53817f834b6SNeilBrown return NULL; 53917f834b6SNeilBrown } 54017f834b6SNeilBrown 5411da177e4SLinus Torvalds 5421da177e4SLinus Torvalds static struct rsc * 5431da177e4SLinus Torvalds gss_svc_searchbyctx(struct xdr_netobj *handle) 5441da177e4SLinus Torvalds { 5451da177e4SLinus Torvalds struct rsc rsci; 5461da177e4SLinus Torvalds struct rsc *found; 5471da177e4SLinus Torvalds 5481da177e4SLinus Torvalds memset(&rsci, 0, sizeof(rsci)); 5491da177e4SLinus Torvalds if (dup_to_netobj(&rsci.handle, handle->data, handle->len)) 5501da177e4SLinus Torvalds return NULL; 55117f834b6SNeilBrown found = rsc_lookup(&rsci); 5521da177e4SLinus Torvalds rsc_free(&rsci); 5531da177e4SLinus Torvalds if (!found) 5541da177e4SLinus Torvalds return NULL; 5551da177e4SLinus Torvalds if (cache_check(&rsc_cache, &found->h, NULL)) 5561da177e4SLinus Torvalds return NULL; 5571da177e4SLinus Torvalds return found; 5581da177e4SLinus Torvalds } 5591da177e4SLinus Torvalds 5601da177e4SLinus Torvalds /* Implements sequence number algorithm as specified in RFC 2203. */ 5611da177e4SLinus Torvalds static int 5621da177e4SLinus Torvalds gss_check_seq_num(struct rsc *rsci, int seq_num) 5631da177e4SLinus Torvalds { 5641da177e4SLinus Torvalds struct gss_svc_seq_data *sd = &rsci->seqdata; 5651da177e4SLinus Torvalds 5661da177e4SLinus Torvalds spin_lock(&sd->sd_lock); 5671da177e4SLinus Torvalds if (seq_num > sd->sd_max) { 5681da177e4SLinus Torvalds if (seq_num >= sd->sd_max + GSS_SEQ_WIN) { 5691da177e4SLinus Torvalds memset(sd->sd_win,0,sizeof(sd->sd_win)); 5701da177e4SLinus Torvalds sd->sd_max = seq_num; 5711da177e4SLinus Torvalds } else while (sd->sd_max < seq_num) { 5721da177e4SLinus Torvalds sd->sd_max++; 5731da177e4SLinus Torvalds __clear_bit(sd->sd_max % GSS_SEQ_WIN, sd->sd_win); 5741da177e4SLinus Torvalds } 5751da177e4SLinus Torvalds __set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win); 5761da177e4SLinus Torvalds goto ok; 5771da177e4SLinus Torvalds } else if (seq_num <= sd->sd_max - GSS_SEQ_WIN) { 5781da177e4SLinus Torvalds goto drop; 5791da177e4SLinus Torvalds } 5801da177e4SLinus Torvalds /* sd_max - GSS_SEQ_WIN < seq_num <= sd_max */ 5811da177e4SLinus Torvalds if (__test_and_set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win)) 5821da177e4SLinus Torvalds goto drop; 5831da177e4SLinus Torvalds ok: 5841da177e4SLinus Torvalds spin_unlock(&sd->sd_lock); 5851da177e4SLinus Torvalds return 1; 5861da177e4SLinus Torvalds drop: 5871da177e4SLinus Torvalds spin_unlock(&sd->sd_lock); 5881da177e4SLinus Torvalds return 0; 5891da177e4SLinus Torvalds } 5901da177e4SLinus Torvalds 5911da177e4SLinus Torvalds static inline u32 round_up_to_quad(u32 i) 5921da177e4SLinus Torvalds { 5931da177e4SLinus Torvalds return (i + 3 ) & ~3; 5941da177e4SLinus Torvalds } 5951da177e4SLinus Torvalds 5961da177e4SLinus Torvalds static inline int 5971da177e4SLinus Torvalds svc_safe_getnetobj(struct kvec *argv, struct xdr_netobj *o) 5981da177e4SLinus Torvalds { 5991da177e4SLinus Torvalds int l; 6001da177e4SLinus Torvalds 6011da177e4SLinus Torvalds if (argv->iov_len < 4) 6021da177e4SLinus Torvalds return -1; 60376994313SAlexey Dobriyan o->len = svc_getnl(argv); 6041da177e4SLinus Torvalds l = round_up_to_quad(o->len); 6051da177e4SLinus Torvalds if (argv->iov_len < l) 6061da177e4SLinus Torvalds return -1; 6071da177e4SLinus Torvalds o->data = argv->iov_base; 6081da177e4SLinus Torvalds argv->iov_base += l; 6091da177e4SLinus Torvalds argv->iov_len -= l; 6101da177e4SLinus Torvalds return 0; 6111da177e4SLinus Torvalds } 6121da177e4SLinus Torvalds 6131da177e4SLinus Torvalds static inline int 6141da177e4SLinus Torvalds svc_safe_putnetobj(struct kvec *resv, struct xdr_netobj *o) 6151da177e4SLinus Torvalds { 616753ed90dSAl Viro u8 *p; 6171da177e4SLinus Torvalds 6181da177e4SLinus Torvalds if (resv->iov_len + 4 > PAGE_SIZE) 6191da177e4SLinus Torvalds return -1; 62076994313SAlexey Dobriyan svc_putnl(resv, o->len); 6211da177e4SLinus Torvalds p = resv->iov_base + resv->iov_len; 6221da177e4SLinus Torvalds resv->iov_len += round_up_to_quad(o->len); 6231da177e4SLinus Torvalds if (resv->iov_len > PAGE_SIZE) 6241da177e4SLinus Torvalds return -1; 6251da177e4SLinus Torvalds memcpy(p, o->data, o->len); 626753ed90dSAl Viro memset(p + o->len, 0, round_up_to_quad(o->len) - o->len); 6271da177e4SLinus Torvalds return 0; 6281da177e4SLinus Torvalds } 6291da177e4SLinus Torvalds 63021fcd02bSJ. Bruce Fields /* 63121fcd02bSJ. Bruce Fields * Verify the checksum on the header and return SVC_OK on success. 6321da177e4SLinus Torvalds * Otherwise, return SVC_DROP (in the case of a bad sequence number) 6331da177e4SLinus Torvalds * or return SVC_DENIED and indicate error in authp. 6341da177e4SLinus Torvalds */ 6351da177e4SLinus Torvalds static int 6361da177e4SLinus Torvalds gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci, 637d8ed029dSAlexey Dobriyan __be32 *rpcstart, struct rpc_gss_wire_cred *gc, __be32 *authp) 6381da177e4SLinus Torvalds { 6391da177e4SLinus Torvalds struct gss_ctx *ctx_id = rsci->mechctx; 6401da177e4SLinus Torvalds struct xdr_buf rpchdr; 6411da177e4SLinus Torvalds struct xdr_netobj checksum; 6421da177e4SLinus Torvalds u32 flavor = 0; 6431da177e4SLinus Torvalds struct kvec *argv = &rqstp->rq_arg.head[0]; 6441da177e4SLinus Torvalds struct kvec iov; 6451da177e4SLinus Torvalds 6461da177e4SLinus Torvalds /* data to compute the checksum over: */ 6471da177e4SLinus Torvalds iov.iov_base = rpcstart; 6481da177e4SLinus Torvalds iov.iov_len = (u8 *)argv->iov_base - (u8 *)rpcstart; 6491da177e4SLinus Torvalds xdr_buf_from_iov(&iov, &rpchdr); 6501da177e4SLinus Torvalds 6511da177e4SLinus Torvalds *authp = rpc_autherr_badverf; 6521da177e4SLinus Torvalds if (argv->iov_len < 4) 6531da177e4SLinus Torvalds return SVC_DENIED; 65476994313SAlexey Dobriyan flavor = svc_getnl(argv); 6551da177e4SLinus Torvalds if (flavor != RPC_AUTH_GSS) 6561da177e4SLinus Torvalds return SVC_DENIED; 6571da177e4SLinus Torvalds if (svc_safe_getnetobj(argv, &checksum)) 6581da177e4SLinus Torvalds return SVC_DENIED; 6591da177e4SLinus Torvalds 6601da177e4SLinus Torvalds if (rqstp->rq_deferred) /* skip verification of revisited request */ 6611da177e4SLinus Torvalds return SVC_OK; 66200fd6e14SJ. Bruce Fields if (gss_verify_mic(ctx_id, &rpchdr, &checksum) != GSS_S_COMPLETE) { 6631da177e4SLinus Torvalds *authp = rpcsec_gsserr_credproblem; 6641da177e4SLinus Torvalds return SVC_DENIED; 6651da177e4SLinus Torvalds } 6661da177e4SLinus Torvalds 6671da177e4SLinus Torvalds if (gc->gc_seq > MAXSEQ) { 6688885cb36SChuck Lever dprintk("RPC: svcauth_gss: discarding request with " 6698885cb36SChuck Lever "large sequence number %d\n", gc->gc_seq); 6701da177e4SLinus Torvalds *authp = rpcsec_gsserr_ctxproblem; 6711da177e4SLinus Torvalds return SVC_DENIED; 6721da177e4SLinus Torvalds } 6731da177e4SLinus Torvalds if (!gss_check_seq_num(rsci, gc->gc_seq)) { 6748885cb36SChuck Lever dprintk("RPC: svcauth_gss: discarding request with " 6758885cb36SChuck Lever "old sequence number %d\n", gc->gc_seq); 6761da177e4SLinus Torvalds return SVC_DROP; 6771da177e4SLinus Torvalds } 6781da177e4SLinus Torvalds return SVC_OK; 6791da177e4SLinus Torvalds } 6801da177e4SLinus Torvalds 6811da177e4SLinus Torvalds static int 682822f1005SAndy Adamson gss_write_null_verf(struct svc_rqst *rqstp) 683822f1005SAndy Adamson { 684d8ed029dSAlexey Dobriyan __be32 *p; 685822f1005SAndy Adamson 68676994313SAlexey Dobriyan svc_putnl(rqstp->rq_res.head, RPC_AUTH_NULL); 687822f1005SAndy Adamson p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len; 688822f1005SAndy Adamson /* don't really need to check if head->iov_len > PAGE_SIZE ... */ 689822f1005SAndy Adamson *p++ = 0; 690822f1005SAndy Adamson if (!xdr_ressize_check(rqstp, p)) 691822f1005SAndy Adamson return -1; 692822f1005SAndy Adamson return 0; 693822f1005SAndy Adamson } 694822f1005SAndy Adamson 695822f1005SAndy Adamson static int 6961da177e4SLinus Torvalds gss_write_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id, u32 seq) 6971da177e4SLinus Torvalds { 698d8ed029dSAlexey Dobriyan __be32 xdr_seq; 6991da177e4SLinus Torvalds u32 maj_stat; 7001da177e4SLinus Torvalds struct xdr_buf verf_data; 7011da177e4SLinus Torvalds struct xdr_netobj mic; 702d8ed029dSAlexey Dobriyan __be32 *p; 7031da177e4SLinus Torvalds struct kvec iov; 7041da177e4SLinus Torvalds 70576994313SAlexey Dobriyan svc_putnl(rqstp->rq_res.head, RPC_AUTH_GSS); 7061da177e4SLinus Torvalds xdr_seq = htonl(seq); 7071da177e4SLinus Torvalds 7081da177e4SLinus Torvalds iov.iov_base = &xdr_seq; 7091da177e4SLinus Torvalds iov.iov_len = sizeof(xdr_seq); 7101da177e4SLinus Torvalds xdr_buf_from_iov(&iov, &verf_data); 7111da177e4SLinus Torvalds p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len; 7121da177e4SLinus Torvalds mic.data = (u8 *)(p + 1); 71300fd6e14SJ. Bruce Fields maj_stat = gss_get_mic(ctx_id, &verf_data, &mic); 7141da177e4SLinus Torvalds if (maj_stat != GSS_S_COMPLETE) 7151da177e4SLinus Torvalds return -1; 7161da177e4SLinus Torvalds *p++ = htonl(mic.len); 7171da177e4SLinus Torvalds memset((u8 *)p + mic.len, 0, round_up_to_quad(mic.len) - mic.len); 7181da177e4SLinus Torvalds p += XDR_QUADLEN(mic.len); 7191da177e4SLinus Torvalds if (!xdr_ressize_check(rqstp, p)) 7201da177e4SLinus Torvalds return -1; 7211da177e4SLinus Torvalds return 0; 7221da177e4SLinus Torvalds } 7231da177e4SLinus Torvalds 7241da177e4SLinus Torvalds struct gss_domain { 7251da177e4SLinus Torvalds struct auth_domain h; 7261da177e4SLinus Torvalds u32 pseudoflavor; 7271da177e4SLinus Torvalds }; 7281da177e4SLinus Torvalds 7291da177e4SLinus Torvalds static struct auth_domain * 7301da177e4SLinus Torvalds find_gss_auth_domain(struct gss_ctx *ctx, u32 svc) 7311da177e4SLinus Torvalds { 7321da177e4SLinus Torvalds char *name; 7331da177e4SLinus Torvalds 7341da177e4SLinus Torvalds name = gss_service_to_auth_domain_name(ctx->mech_type, svc); 7351da177e4SLinus Torvalds if (!name) 7361da177e4SLinus Torvalds return NULL; 7371da177e4SLinus Torvalds return auth_domain_find(name); 7381da177e4SLinus Torvalds } 7391da177e4SLinus Torvalds 740efc36aa5SNeilBrown static struct auth_ops svcauthops_gss; 741efc36aa5SNeilBrown 7424796f457SJ. Bruce Fields u32 svcauth_gss_flavor(struct auth_domain *dom) 7434796f457SJ. Bruce Fields { 7444796f457SJ. Bruce Fields struct gss_domain *gd = container_of(dom, struct gss_domain, h); 7454796f457SJ. Bruce Fields 7464796f457SJ. Bruce Fields return gd->pseudoflavor; 7474796f457SJ. Bruce Fields } 7484796f457SJ. Bruce Fields 7494796f457SJ. Bruce Fields EXPORT_SYMBOL(svcauth_gss_flavor); 7504796f457SJ. Bruce Fields 7511da177e4SLinus Torvalds int 7521da177e4SLinus Torvalds svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name) 7531da177e4SLinus Torvalds { 7541da177e4SLinus Torvalds struct gss_domain *new; 7551da177e4SLinus Torvalds struct auth_domain *test; 7561da177e4SLinus Torvalds int stat = -ENOMEM; 7571da177e4SLinus Torvalds 7581da177e4SLinus Torvalds new = kmalloc(sizeof(*new), GFP_KERNEL); 7591da177e4SLinus Torvalds if (!new) 7601da177e4SLinus Torvalds goto out; 761efc36aa5SNeilBrown kref_init(&new->h.ref); 762e69062b4SArnaldo Carvalho de Melo new->h.name = kstrdup(name, GFP_KERNEL); 7631da177e4SLinus Torvalds if (!new->h.name) 7641da177e4SLinus Torvalds goto out_free_dom; 765efc36aa5SNeilBrown new->h.flavour = &svcauthops_gss; 7661da177e4SLinus Torvalds new->pseudoflavor = pseudoflavor; 7671da177e4SLinus Torvalds 768cb276805SJ. Bruce Fields stat = 0; 769efc36aa5SNeilBrown test = auth_domain_lookup(name, &new->h); 770cb276805SJ. Bruce Fields if (test != &new->h) { /* Duplicate registration */ 771cb276805SJ. Bruce Fields auth_domain_put(test); 772cb276805SJ. Bruce Fields kfree(new->h.name); 773cb276805SJ. Bruce Fields goto out_free_dom; 7741da177e4SLinus Torvalds } 7751da177e4SLinus Torvalds return 0; 7761da177e4SLinus Torvalds 7771da177e4SLinus Torvalds out_free_dom: 7781da177e4SLinus Torvalds kfree(new); 7791da177e4SLinus Torvalds out: 7801da177e4SLinus Torvalds return stat; 7811da177e4SLinus Torvalds } 7821da177e4SLinus Torvalds 7831da177e4SLinus Torvalds EXPORT_SYMBOL(svcauth_gss_register_pseudoflavor); 7841da177e4SLinus Torvalds 7851da177e4SLinus Torvalds static inline int 7861da177e4SLinus Torvalds read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj) 7871da177e4SLinus Torvalds { 788d8ed029dSAlexey Dobriyan __be32 raw; 7891da177e4SLinus Torvalds int status; 7901da177e4SLinus Torvalds 7911da177e4SLinus Torvalds status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj)); 7921da177e4SLinus Torvalds if (status) 7931da177e4SLinus Torvalds return status; 7941da177e4SLinus Torvalds *obj = ntohl(raw); 7951da177e4SLinus Torvalds return 0; 7961da177e4SLinus Torvalds } 7971da177e4SLinus Torvalds 7981da177e4SLinus Torvalds /* It would be nice if this bit of code could be shared with the client. 7991da177e4SLinus Torvalds * Obstacles: 8001da177e4SLinus Torvalds * The client shouldn't malloc(), would have to pass in own memory. 8011da177e4SLinus Torvalds * The server uses base of head iovec as read pointer, while the 8021da177e4SLinus Torvalds * client uses separate pointer. */ 8031da177e4SLinus Torvalds static int 8041da177e4SLinus Torvalds unwrap_integ_data(struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx) 8051da177e4SLinus Torvalds { 8061da177e4SLinus Torvalds int stat = -EINVAL; 8071da177e4SLinus Torvalds u32 integ_len, maj_stat; 8081da177e4SLinus Torvalds struct xdr_netobj mic; 8091da177e4SLinus Torvalds struct xdr_buf integ_buf; 8101da177e4SLinus Torvalds 81176994313SAlexey Dobriyan integ_len = svc_getnl(&buf->head[0]); 8121da177e4SLinus Torvalds if (integ_len & 3) 813b797b5beSJ.Bruce Fields return stat; 8141da177e4SLinus Torvalds if (integ_len > buf->len) 815b797b5beSJ.Bruce Fields return stat; 8161da177e4SLinus Torvalds if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len)) 8171da177e4SLinus Torvalds BUG(); 8181da177e4SLinus Torvalds /* copy out mic... */ 8191da177e4SLinus Torvalds if (read_u32_from_xdr_buf(buf, integ_len, &mic.len)) 8201da177e4SLinus Torvalds BUG(); 8211da177e4SLinus Torvalds if (mic.len > RPC_MAX_AUTH_SIZE) 822b797b5beSJ.Bruce Fields return stat; 8231da177e4SLinus Torvalds mic.data = kmalloc(mic.len, GFP_KERNEL); 8241da177e4SLinus Torvalds if (!mic.data) 825b797b5beSJ.Bruce Fields return stat; 8261da177e4SLinus Torvalds if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len)) 8271da177e4SLinus Torvalds goto out; 82800fd6e14SJ. Bruce Fields maj_stat = gss_verify_mic(ctx, &integ_buf, &mic); 8291da177e4SLinus Torvalds if (maj_stat != GSS_S_COMPLETE) 8301da177e4SLinus Torvalds goto out; 83176994313SAlexey Dobriyan if (svc_getnl(&buf->head[0]) != seq) 8321da177e4SLinus Torvalds goto out; 8331da177e4SLinus Torvalds stat = 0; 8341da177e4SLinus Torvalds out: 835b797b5beSJ.Bruce Fields kfree(mic.data); 8361da177e4SLinus Torvalds return stat; 8371da177e4SLinus Torvalds } 8381da177e4SLinus Torvalds 8397c9fdcfbSJ. Bruce Fields static inline int 8407c9fdcfbSJ. Bruce Fields total_buf_len(struct xdr_buf *buf) 8417c9fdcfbSJ. Bruce Fields { 8427c9fdcfbSJ. Bruce Fields return buf->head[0].iov_len + buf->page_len + buf->tail[0].iov_len; 8437c9fdcfbSJ. Bruce Fields } 8447c9fdcfbSJ. Bruce Fields 8457c9fdcfbSJ. Bruce Fields static void 8467c9fdcfbSJ. Bruce Fields fix_priv_head(struct xdr_buf *buf, int pad) 8477c9fdcfbSJ. Bruce Fields { 8487c9fdcfbSJ. Bruce Fields if (buf->page_len == 0) { 8497c9fdcfbSJ. Bruce Fields /* We need to adjust head and buf->len in tandem in this 8507c9fdcfbSJ. Bruce Fields * case to make svc_defer() work--it finds the original 8517c9fdcfbSJ. Bruce Fields * buffer start using buf->len - buf->head[0].iov_len. */ 8527c9fdcfbSJ. Bruce Fields buf->head[0].iov_len -= pad; 8537c9fdcfbSJ. Bruce Fields } 8547c9fdcfbSJ. Bruce Fields } 8557c9fdcfbSJ. Bruce Fields 8567c9fdcfbSJ. Bruce Fields static int 8577c9fdcfbSJ. Bruce Fields unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx) 8587c9fdcfbSJ. Bruce Fields { 8597c9fdcfbSJ. Bruce Fields u32 priv_len, maj_stat; 8607c9fdcfbSJ. Bruce Fields int pad, saved_len, remaining_len, offset; 8617c9fdcfbSJ. Bruce Fields 862cf8208d0SJens Axboe rqstp->rq_splice_ok = 0; 8637c9fdcfbSJ. Bruce Fields 86476994313SAlexey Dobriyan priv_len = svc_getnl(&buf->head[0]); 8657c9fdcfbSJ. Bruce Fields if (rqstp->rq_deferred) { 8667c9fdcfbSJ. Bruce Fields /* Already decrypted last time through! The sequence number 8677c9fdcfbSJ. Bruce Fields * check at out_seq is unnecessary but harmless: */ 8687c9fdcfbSJ. Bruce Fields goto out_seq; 8697c9fdcfbSJ. Bruce Fields } 8707c9fdcfbSJ. Bruce Fields /* buf->len is the number of bytes from the original start of the 8717c9fdcfbSJ. Bruce Fields * request to the end, where head[0].iov_len is just the bytes 8727c9fdcfbSJ. Bruce Fields * not yet read from the head, so these two values are different: */ 8737c9fdcfbSJ. Bruce Fields remaining_len = total_buf_len(buf); 8747c9fdcfbSJ. Bruce Fields if (priv_len > remaining_len) 8757c9fdcfbSJ. Bruce Fields return -EINVAL; 8767c9fdcfbSJ. Bruce Fields pad = remaining_len - priv_len; 8777c9fdcfbSJ. Bruce Fields buf->len -= pad; 8787c9fdcfbSJ. Bruce Fields fix_priv_head(buf, pad); 8797c9fdcfbSJ. Bruce Fields 8807c9fdcfbSJ. Bruce Fields /* Maybe it would be better to give gss_unwrap a length parameter: */ 8817c9fdcfbSJ. Bruce Fields saved_len = buf->len; 8827c9fdcfbSJ. Bruce Fields buf->len = priv_len; 8837c9fdcfbSJ. Bruce Fields maj_stat = gss_unwrap(ctx, 0, buf); 8847c9fdcfbSJ. Bruce Fields pad = priv_len - buf->len; 8857c9fdcfbSJ. Bruce Fields buf->len = saved_len; 8867c9fdcfbSJ. Bruce Fields buf->len -= pad; 8877c9fdcfbSJ. Bruce Fields /* The upper layers assume the buffer is aligned on 4-byte boundaries. 8887c9fdcfbSJ. Bruce Fields * In the krb5p case, at least, the data ends up offset, so we need to 8897c9fdcfbSJ. Bruce Fields * move it around. */ 8907c9fdcfbSJ. Bruce Fields /* XXX: This is very inefficient. It would be better to either do 8917c9fdcfbSJ. Bruce Fields * this while we encrypt, or maybe in the receive code, if we can peak 8927c9fdcfbSJ. Bruce Fields * ahead and work out the service and mechanism there. */ 8937c9fdcfbSJ. Bruce Fields offset = buf->head[0].iov_len % 4; 8947c9fdcfbSJ. Bruce Fields if (offset) { 8957c9fdcfbSJ. Bruce Fields buf->buflen = RPCSVC_MAXPAYLOAD; 8967c9fdcfbSJ. Bruce Fields xdr_shift_buf(buf, offset); 8977c9fdcfbSJ. Bruce Fields fix_priv_head(buf, pad); 8987c9fdcfbSJ. Bruce Fields } 8997c9fdcfbSJ. Bruce Fields if (maj_stat != GSS_S_COMPLETE) 9007c9fdcfbSJ. Bruce Fields return -EINVAL; 9017c9fdcfbSJ. Bruce Fields out_seq: 90276994313SAlexey Dobriyan if (svc_getnl(&buf->head[0]) != seq) 9037c9fdcfbSJ. Bruce Fields return -EINVAL; 9047c9fdcfbSJ. Bruce Fields return 0; 9057c9fdcfbSJ. Bruce Fields } 9067c9fdcfbSJ. Bruce Fields 9071da177e4SLinus Torvalds struct gss_svc_data { 9081da177e4SLinus Torvalds /* decoded gss client cred: */ 9091da177e4SLinus Torvalds struct rpc_gss_wire_cred clcred; 9105b304bc5SJ.Bruce Fields /* save a pointer to the beginning of the encoded verifier, 9115b304bc5SJ.Bruce Fields * for use in encryption/checksumming in svcauth_gss_release: */ 9125b304bc5SJ.Bruce Fields __be32 *verf_start; 9131da177e4SLinus Torvalds struct rsc *rsci; 9141da177e4SLinus Torvalds }; 9151da177e4SLinus Torvalds 9161da177e4SLinus Torvalds static int 9171da177e4SLinus Torvalds svcauth_gss_set_client(struct svc_rqst *rqstp) 9181da177e4SLinus Torvalds { 9191da177e4SLinus Torvalds struct gss_svc_data *svcdata = rqstp->rq_auth_data; 9201da177e4SLinus Torvalds struct rsc *rsci = svcdata->rsci; 9211da177e4SLinus Torvalds struct rpc_gss_wire_cred *gc = &svcdata->clcred; 9223ab4d8b1SJ. Bruce Fields int stat; 9231da177e4SLinus Torvalds 9243ab4d8b1SJ. Bruce Fields /* 9253ab4d8b1SJ. Bruce Fields * A gss export can be specified either by: 9263ab4d8b1SJ. Bruce Fields * export *(sec=krb5,rw) 9273ab4d8b1SJ. Bruce Fields * or by 9283ab4d8b1SJ. Bruce Fields * export gss/krb5(rw) 9293ab4d8b1SJ. Bruce Fields * The latter is deprecated; but for backwards compatibility reasons 9303ab4d8b1SJ. Bruce Fields * the nfsd code will still fall back on trying it if the former 9313ab4d8b1SJ. Bruce Fields * doesn't work; so we try to make both available to nfsd, below. 9323ab4d8b1SJ. Bruce Fields */ 9333ab4d8b1SJ. Bruce Fields rqstp->rq_gssclient = find_gss_auth_domain(rsci->mechctx, gc->gc_svc); 9343ab4d8b1SJ. Bruce Fields if (rqstp->rq_gssclient == NULL) 9351da177e4SLinus Torvalds return SVC_DENIED; 9363ab4d8b1SJ. Bruce Fields stat = svcauth_unix_set_client(rqstp); 9373ab4d8b1SJ. Bruce Fields if (stat == SVC_DROP) 9383ab4d8b1SJ. Bruce Fields return stat; 9391da177e4SLinus Torvalds return SVC_OK; 9401da177e4SLinus Torvalds } 9411da177e4SLinus Torvalds 94291a4762eSKevin Coffman static inline int 94391a4762eSKevin Coffman gss_write_init_verf(struct svc_rqst *rqstp, struct rsi *rsip) 94491a4762eSKevin Coffman { 94591a4762eSKevin Coffman struct rsc *rsci; 94654f9247bSFrank Filz int rc; 94791a4762eSKevin Coffman 94891a4762eSKevin Coffman if (rsip->major_status != GSS_S_COMPLETE) 94991a4762eSKevin Coffman return gss_write_null_verf(rqstp); 95091a4762eSKevin Coffman rsci = gss_svc_searchbyctx(&rsip->out_handle); 95191a4762eSKevin Coffman if (rsci == NULL) { 95291a4762eSKevin Coffman rsip->major_status = GSS_S_NO_CONTEXT; 95391a4762eSKevin Coffman return gss_write_null_verf(rqstp); 95491a4762eSKevin Coffman } 95554f9247bSFrank Filz rc = gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN); 95654f9247bSFrank Filz cache_put(&rsci->h, &rsc_cache); 95754f9247bSFrank Filz return rc; 95891a4762eSKevin Coffman } 95991a4762eSKevin Coffman 9601da177e4SLinus Torvalds /* 96121fcd02bSJ. Bruce Fields * Having read the cred already and found we're in the context 96221fcd02bSJ. Bruce Fields * initiation case, read the verifier and initiate (or check the results 96321fcd02bSJ. Bruce Fields * of) upcalls to userspace for help with context initiation. If 96421fcd02bSJ. Bruce Fields * the upcall results are available, write the verifier and result. 96521fcd02bSJ. Bruce Fields * Otherwise, drop the request pending an answer to the upcall. 96621fcd02bSJ. Bruce Fields */ 96721fcd02bSJ. Bruce Fields static int svcauth_gss_handle_init(struct svc_rqst *rqstp, 96821fcd02bSJ. Bruce Fields struct rpc_gss_wire_cred *gc, __be32 *authp) 96921fcd02bSJ. Bruce Fields { 97021fcd02bSJ. Bruce Fields struct kvec *argv = &rqstp->rq_arg.head[0]; 97121fcd02bSJ. Bruce Fields struct kvec *resv = &rqstp->rq_res.head[0]; 97221fcd02bSJ. Bruce Fields struct xdr_netobj tmpobj; 97321fcd02bSJ. Bruce Fields struct rsi *rsip, rsikey; 974980e5a40SJ. Bruce Fields int ret; 97521fcd02bSJ. Bruce Fields 97621fcd02bSJ. Bruce Fields /* Read the verifier; should be NULL: */ 97721fcd02bSJ. Bruce Fields *authp = rpc_autherr_badverf; 97821fcd02bSJ. Bruce Fields if (argv->iov_len < 2 * 4) 97921fcd02bSJ. Bruce Fields return SVC_DENIED; 98021fcd02bSJ. Bruce Fields if (svc_getnl(argv) != RPC_AUTH_NULL) 98121fcd02bSJ. Bruce Fields return SVC_DENIED; 98221fcd02bSJ. Bruce Fields if (svc_getnl(argv) != 0) 98321fcd02bSJ. Bruce Fields return SVC_DENIED; 98421fcd02bSJ. Bruce Fields 98521fcd02bSJ. Bruce Fields /* Martial context handle and token for upcall: */ 98621fcd02bSJ. Bruce Fields *authp = rpc_autherr_badcred; 98721fcd02bSJ. Bruce Fields if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0) 98821fcd02bSJ. Bruce Fields return SVC_DENIED; 98921fcd02bSJ. Bruce Fields memset(&rsikey, 0, sizeof(rsikey)); 99021fcd02bSJ. Bruce Fields if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx)) 99121fcd02bSJ. Bruce Fields return SVC_DROP; 99221fcd02bSJ. Bruce Fields *authp = rpc_autherr_badverf; 99321fcd02bSJ. Bruce Fields if (svc_safe_getnetobj(argv, &tmpobj)) { 99421fcd02bSJ. Bruce Fields kfree(rsikey.in_handle.data); 99521fcd02bSJ. Bruce Fields return SVC_DENIED; 99621fcd02bSJ. Bruce Fields } 99721fcd02bSJ. Bruce Fields if (dup_netobj(&rsikey.in_token, &tmpobj)) { 99821fcd02bSJ. Bruce Fields kfree(rsikey.in_handle.data); 99921fcd02bSJ. Bruce Fields return SVC_DROP; 100021fcd02bSJ. Bruce Fields } 100121fcd02bSJ. Bruce Fields 100221fcd02bSJ. Bruce Fields /* Perform upcall, or find upcall result: */ 100321fcd02bSJ. Bruce Fields rsip = rsi_lookup(&rsikey); 100421fcd02bSJ. Bruce Fields rsi_free(&rsikey); 100521fcd02bSJ. Bruce Fields if (!rsip) 100621fcd02bSJ. Bruce Fields return SVC_DROP; 100721fcd02bSJ. Bruce Fields switch (cache_check(&rsi_cache, &rsip->h, &rqstp->rq_chandle)) { 100821fcd02bSJ. Bruce Fields case -EAGAIN: 100921fcd02bSJ. Bruce Fields case -ETIMEDOUT: 101021fcd02bSJ. Bruce Fields case -ENOENT: 101121fcd02bSJ. Bruce Fields /* No upcall result: */ 101221fcd02bSJ. Bruce Fields return SVC_DROP; 101321fcd02bSJ. Bruce Fields case 0: 1014980e5a40SJ. Bruce Fields ret = SVC_DROP; 101521fcd02bSJ. Bruce Fields /* Got an answer to the upcall; use it: */ 101621fcd02bSJ. Bruce Fields if (gss_write_init_verf(rqstp, rsip)) 1017980e5a40SJ. Bruce Fields goto out; 101821fcd02bSJ. Bruce Fields if (resv->iov_len + 4 > PAGE_SIZE) 1019980e5a40SJ. Bruce Fields goto out; 102021fcd02bSJ. Bruce Fields svc_putnl(resv, RPC_SUCCESS); 102121fcd02bSJ. Bruce Fields if (svc_safe_putnetobj(resv, &rsip->out_handle)) 1022980e5a40SJ. Bruce Fields goto out; 102321fcd02bSJ. Bruce Fields if (resv->iov_len + 3 * 4 > PAGE_SIZE) 1024980e5a40SJ. Bruce Fields goto out; 102521fcd02bSJ. Bruce Fields svc_putnl(resv, rsip->major_status); 102621fcd02bSJ. Bruce Fields svc_putnl(resv, rsip->minor_status); 102721fcd02bSJ. Bruce Fields svc_putnl(resv, GSS_SEQ_WIN); 102821fcd02bSJ. Bruce Fields if (svc_safe_putnetobj(resv, &rsip->out_token)) 1029980e5a40SJ. Bruce Fields goto out; 103021fcd02bSJ. Bruce Fields } 1031980e5a40SJ. Bruce Fields ret = SVC_COMPLETE; 1032980e5a40SJ. Bruce Fields out: 1033980e5a40SJ. Bruce Fields cache_put(&rsip->h, &rsi_cache); 1034980e5a40SJ. Bruce Fields return ret; 103521fcd02bSJ. Bruce Fields } 103621fcd02bSJ. Bruce Fields 103721fcd02bSJ. Bruce Fields /* 10381da177e4SLinus Torvalds * Accept an rpcsec packet. 10391da177e4SLinus Torvalds * If context establishment, punt to user space 10401da177e4SLinus Torvalds * If data exchange, verify/decrypt 10411da177e4SLinus Torvalds * If context destruction, handle here 10421da177e4SLinus Torvalds * In the context establishment and destruction case we encode 10431da177e4SLinus Torvalds * response here and return SVC_COMPLETE. 10441da177e4SLinus Torvalds */ 10451da177e4SLinus Torvalds static int 1046d8ed029dSAlexey Dobriyan svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) 10471da177e4SLinus Torvalds { 10481da177e4SLinus Torvalds struct kvec *argv = &rqstp->rq_arg.head[0]; 10491da177e4SLinus Torvalds struct kvec *resv = &rqstp->rq_res.head[0]; 10501da177e4SLinus Torvalds u32 crlen; 10511da177e4SLinus Torvalds struct gss_svc_data *svcdata = rqstp->rq_auth_data; 10521da177e4SLinus Torvalds struct rpc_gss_wire_cred *gc; 10531da177e4SLinus Torvalds struct rsc *rsci = NULL; 1054d8ed029dSAlexey Dobriyan __be32 *rpcstart; 1055d8ed029dSAlexey Dobriyan __be32 *reject_stat = resv->iov_base + resv->iov_len; 10561da177e4SLinus Torvalds int ret; 10571da177e4SLinus Torvalds 10588885cb36SChuck Lever dprintk("RPC: svcauth_gss: argv->iov_len = %zd\n", 10598885cb36SChuck Lever argv->iov_len); 10601da177e4SLinus Torvalds 10611da177e4SLinus Torvalds *authp = rpc_autherr_badcred; 10621da177e4SLinus Torvalds if (!svcdata) 10631da177e4SLinus Torvalds svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL); 10641da177e4SLinus Torvalds if (!svcdata) 10651da177e4SLinus Torvalds goto auth_err; 10661da177e4SLinus Torvalds rqstp->rq_auth_data = svcdata; 10675b304bc5SJ.Bruce Fields svcdata->verf_start = NULL; 10681da177e4SLinus Torvalds svcdata->rsci = NULL; 10691da177e4SLinus Torvalds gc = &svcdata->clcred; 10701da177e4SLinus Torvalds 10711da177e4SLinus Torvalds /* start of rpc packet is 7 u32's back from here: 10721da177e4SLinus Torvalds * xid direction rpcversion prog vers proc flavour 10731da177e4SLinus Torvalds */ 10741da177e4SLinus Torvalds rpcstart = argv->iov_base; 10751da177e4SLinus Torvalds rpcstart -= 7; 10761da177e4SLinus Torvalds 10771da177e4SLinus Torvalds /* credential is: 10781da177e4SLinus Torvalds * version(==1), proc(0,1,2,3), seq, service (1,2,3), handle 10791da177e4SLinus Torvalds * at least 5 u32s, and is preceeded by length, so that makes 6. 10801da177e4SLinus Torvalds */ 10811da177e4SLinus Torvalds 10821da177e4SLinus Torvalds if (argv->iov_len < 5 * 4) 10831da177e4SLinus Torvalds goto auth_err; 108476994313SAlexey Dobriyan crlen = svc_getnl(argv); 108576994313SAlexey Dobriyan if (svc_getnl(argv) != RPC_GSS_VERSION) 10861da177e4SLinus Torvalds goto auth_err; 108776994313SAlexey Dobriyan gc->gc_proc = svc_getnl(argv); 108876994313SAlexey Dobriyan gc->gc_seq = svc_getnl(argv); 108976994313SAlexey Dobriyan gc->gc_svc = svc_getnl(argv); 10901da177e4SLinus Torvalds if (svc_safe_getnetobj(argv, &gc->gc_ctx)) 10911da177e4SLinus Torvalds goto auth_err; 10921da177e4SLinus Torvalds if (crlen != round_up_to_quad(gc->gc_ctx.len) + 5 * 4) 10931da177e4SLinus Torvalds goto auth_err; 10941da177e4SLinus Torvalds 10951da177e4SLinus Torvalds if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0)) 10961da177e4SLinus Torvalds goto auth_err; 10971da177e4SLinus Torvalds 10981da177e4SLinus Torvalds *authp = rpc_autherr_badverf; 10991da177e4SLinus Torvalds switch (gc->gc_proc) { 11001da177e4SLinus Torvalds case RPC_GSS_PROC_INIT: 11011da177e4SLinus Torvalds case RPC_GSS_PROC_CONTINUE_INIT: 110221fcd02bSJ. Bruce Fields return svcauth_gss_handle_init(rqstp, gc, authp); 11031da177e4SLinus Torvalds case RPC_GSS_PROC_DATA: 11041da177e4SLinus Torvalds case RPC_GSS_PROC_DESTROY: 110521fcd02bSJ. Bruce Fields /* Look up the context, and check the verifier: */ 11061da177e4SLinus Torvalds *authp = rpcsec_gsserr_credproblem; 11071da177e4SLinus Torvalds rsci = gss_svc_searchbyctx(&gc->gc_ctx); 11081da177e4SLinus Torvalds if (!rsci) 11091da177e4SLinus Torvalds goto auth_err; 11101da177e4SLinus Torvalds switch (gss_verify_header(rqstp, rsci, rpcstart, gc, authp)) { 11111da177e4SLinus Torvalds case SVC_OK: 11121da177e4SLinus Torvalds break; 11131da177e4SLinus Torvalds case SVC_DENIED: 11141da177e4SLinus Torvalds goto auth_err; 11151da177e4SLinus Torvalds case SVC_DROP: 11161da177e4SLinus Torvalds goto drop; 11171da177e4SLinus Torvalds } 11181da177e4SLinus Torvalds break; 11191da177e4SLinus Torvalds default: 11201da177e4SLinus Torvalds *authp = rpc_autherr_rejectedcred; 11211da177e4SLinus Torvalds goto auth_err; 11221da177e4SLinus Torvalds } 11231da177e4SLinus Torvalds 11241da177e4SLinus Torvalds /* now act upon the command: */ 11251da177e4SLinus Torvalds switch (gc->gc_proc) { 11261da177e4SLinus Torvalds case RPC_GSS_PROC_DESTROY: 1127c5e434c9SWei Yongjun if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq)) 1128c5e434c9SWei Yongjun goto auth_err; 1129cb5c7d66SJ. Bruce Fields rsci->h.expiry_time = get_seconds(); 11301da177e4SLinus Torvalds set_bit(CACHE_NEGATIVE, &rsci->h.flags); 11311da177e4SLinus Torvalds if (resv->iov_len + 4 > PAGE_SIZE) 11321da177e4SLinus Torvalds goto drop; 113376994313SAlexey Dobriyan svc_putnl(resv, RPC_SUCCESS); 11341da177e4SLinus Torvalds goto complete; 11351da177e4SLinus Torvalds case RPC_GSS_PROC_DATA: 11361da177e4SLinus Torvalds *authp = rpcsec_gsserr_ctxproblem; 11375b304bc5SJ.Bruce Fields svcdata->verf_start = resv->iov_base + resv->iov_len; 11381da177e4SLinus Torvalds if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq)) 11391da177e4SLinus Torvalds goto auth_err; 11401da177e4SLinus Torvalds rqstp->rq_cred = rsci->cred; 11411da177e4SLinus Torvalds get_group_info(rsci->cred.cr_group_info); 11421da177e4SLinus Torvalds *authp = rpc_autherr_badcred; 11431da177e4SLinus Torvalds switch (gc->gc_svc) { 11441da177e4SLinus Torvalds case RPC_GSS_SVC_NONE: 11451da177e4SLinus Torvalds break; 11461da177e4SLinus Torvalds case RPC_GSS_SVC_INTEGRITY: 1147b620754bSJ. Bruce Fields /* placeholders for length and seq. number: */ 1148b620754bSJ. Bruce Fields svc_putnl(resv, 0); 1149b620754bSJ. Bruce Fields svc_putnl(resv, 0); 11501da177e4SLinus Torvalds if (unwrap_integ_data(&rqstp->rq_arg, 11511da177e4SLinus Torvalds gc->gc_seq, rsci->mechctx)) 1152dd35210eSHarshula Jayasuriya goto garbage_args; 1153b620754bSJ. Bruce Fields break; 1154b620754bSJ. Bruce Fields case RPC_GSS_SVC_PRIVACY: 11551da177e4SLinus Torvalds /* placeholders for length and seq. number: */ 115676994313SAlexey Dobriyan svc_putnl(resv, 0); 115776994313SAlexey Dobriyan svc_putnl(resv, 0); 11587c9fdcfbSJ. Bruce Fields if (unwrap_priv_data(rqstp, &rqstp->rq_arg, 11597c9fdcfbSJ. Bruce Fields gc->gc_seq, rsci->mechctx)) 1160dd35210eSHarshula Jayasuriya goto garbage_args; 11617c9fdcfbSJ. Bruce Fields break; 11621da177e4SLinus Torvalds default: 11631da177e4SLinus Torvalds goto auth_err; 11641da177e4SLinus Torvalds } 11651da177e4SLinus Torvalds svcdata->rsci = rsci; 11661da177e4SLinus Torvalds cache_get(&rsci->h); 1167c4170583SAndy Adamson rqstp->rq_flavor = gss_svc_to_pseudoflavor( 1168c4170583SAndy Adamson rsci->mechctx->mech_type, gc->gc_svc); 11691da177e4SLinus Torvalds ret = SVC_OK; 11701da177e4SLinus Torvalds goto out; 11711da177e4SLinus Torvalds } 1172dd35210eSHarshula Jayasuriya garbage_args: 1173dd35210eSHarshula Jayasuriya ret = SVC_GARBAGE; 1174dd35210eSHarshula Jayasuriya goto out; 11751da177e4SLinus Torvalds auth_err: 117621fcd02bSJ. Bruce Fields /* Restore write pointer to its original value: */ 11771da177e4SLinus Torvalds xdr_ressize_check(rqstp, reject_stat); 11781da177e4SLinus Torvalds ret = SVC_DENIED; 11791da177e4SLinus Torvalds goto out; 11801da177e4SLinus Torvalds complete: 11811da177e4SLinus Torvalds ret = SVC_COMPLETE; 11821da177e4SLinus Torvalds goto out; 11831da177e4SLinus Torvalds drop: 11841da177e4SLinus Torvalds ret = SVC_DROP; 11851da177e4SLinus Torvalds out: 11861da177e4SLinus Torvalds if (rsci) 1187baab935fSNeilBrown cache_put(&rsci->h, &rsc_cache); 11881da177e4SLinus Torvalds return ret; 11891da177e4SLinus Torvalds } 11901da177e4SLinus Torvalds 1191cfbdbab0SAl Viro static __be32 * 11923c15a486SJ.Bruce Fields svcauth_gss_prepare_to_wrap(struct xdr_buf *resbuf, struct gss_svc_data *gsd) 11933c15a486SJ.Bruce Fields { 1194cfbdbab0SAl Viro __be32 *p; 1195cfbdbab0SAl Viro u32 verf_len; 11963c15a486SJ.Bruce Fields 11975b304bc5SJ.Bruce Fields p = gsd->verf_start; 11985b304bc5SJ.Bruce Fields gsd->verf_start = NULL; 11995b304bc5SJ.Bruce Fields 12005b304bc5SJ.Bruce Fields /* If the reply stat is nonzero, don't wrap: */ 12015b304bc5SJ.Bruce Fields if (*(p-1) != rpc_success) 12025b304bc5SJ.Bruce Fields return NULL; 12035b304bc5SJ.Bruce Fields /* Skip the verifier: */ 12045b304bc5SJ.Bruce Fields p += 1; 12055b304bc5SJ.Bruce Fields verf_len = ntohl(*p++); 12065b304bc5SJ.Bruce Fields p += XDR_QUADLEN(verf_len); 12073c15a486SJ.Bruce Fields /* move accept_stat to right place: */ 12083c15a486SJ.Bruce Fields memcpy(p, p + 2, 4); 12095b304bc5SJ.Bruce Fields /* Also don't wrap if the accept stat is nonzero: */ 12103c15a486SJ.Bruce Fields if (*p != rpc_success) { 12113c15a486SJ.Bruce Fields resbuf->head[0].iov_len -= 2 * 4; 12123c15a486SJ.Bruce Fields return NULL; 12133c15a486SJ.Bruce Fields } 12143c15a486SJ.Bruce Fields p++; 12153c15a486SJ.Bruce Fields return p; 12163c15a486SJ.Bruce Fields } 12173c15a486SJ.Bruce Fields 1218e142ede8SJ. Bruce Fields static inline int 1219e142ede8SJ. Bruce Fields svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp) 12201da177e4SLinus Torvalds { 12211da177e4SLinus Torvalds struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data; 12221da177e4SLinus Torvalds struct rpc_gss_wire_cred *gc = &gsd->clcred; 12231da177e4SLinus Torvalds struct xdr_buf *resbuf = &rqstp->rq_res; 12241da177e4SLinus Torvalds struct xdr_buf integ_buf; 12251da177e4SLinus Torvalds struct xdr_netobj mic; 12261da177e4SLinus Torvalds struct kvec *resv; 1227d8ed029dSAlexey Dobriyan __be32 *p; 12281da177e4SLinus Torvalds int integ_offset, integ_len; 12291da177e4SLinus Torvalds int stat = -EINVAL; 12301da177e4SLinus Torvalds 12313c15a486SJ.Bruce Fields p = svcauth_gss_prepare_to_wrap(resbuf, gsd); 12323c15a486SJ.Bruce Fields if (p == NULL) 12331da177e4SLinus Torvalds goto out; 12341da177e4SLinus Torvalds integ_offset = (u8 *)(p + 1) - (u8 *)resbuf->head[0].iov_base; 12351da177e4SLinus Torvalds integ_len = resbuf->len - integ_offset; 12361da177e4SLinus Torvalds BUG_ON(integ_len % 4); 12371da177e4SLinus Torvalds *p++ = htonl(integ_len); 12381da177e4SLinus Torvalds *p++ = htonl(gc->gc_seq); 12391da177e4SLinus Torvalds if (xdr_buf_subsegment(resbuf, &integ_buf, integ_offset, 12401da177e4SLinus Torvalds integ_len)) 12411da177e4SLinus Torvalds BUG(); 1242153e44d2SNeilBrown if (resbuf->tail[0].iov_base == NULL) { 1243e142ede8SJ. Bruce Fields if (resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE > PAGE_SIZE) 1244dfee55f0SNeilBrown goto out_err; 1245e142ede8SJ. Bruce Fields resbuf->tail[0].iov_base = resbuf->head[0].iov_base 1246dfee55f0SNeilBrown + resbuf->head[0].iov_len; 12471da177e4SLinus Torvalds resbuf->tail[0].iov_len = 0; 12481da177e4SLinus Torvalds resv = &resbuf->tail[0]; 12491da177e4SLinus Torvalds } else { 12501da177e4SLinus Torvalds resv = &resbuf->tail[0]; 12511da177e4SLinus Torvalds } 12521da177e4SLinus Torvalds mic.data = (u8 *)resv->iov_base + resv->iov_len + 4; 125300fd6e14SJ. Bruce Fields if (gss_get_mic(gsd->rsci->mechctx, &integ_buf, &mic)) 12541da177e4SLinus Torvalds goto out_err; 125576994313SAlexey Dobriyan svc_putnl(resv, mic.len); 12561da177e4SLinus Torvalds memset(mic.data + mic.len, 0, 12571da177e4SLinus Torvalds round_up_to_quad(mic.len) - mic.len); 12581da177e4SLinus Torvalds resv->iov_len += XDR_QUADLEN(mic.len) << 2; 12591da177e4SLinus Torvalds /* not strictly required: */ 12601da177e4SLinus Torvalds resbuf->len += XDR_QUADLEN(mic.len) << 2; 12611da177e4SLinus Torvalds BUG_ON(resv->iov_len > PAGE_SIZE); 1262e142ede8SJ. Bruce Fields out: 1263e142ede8SJ. Bruce Fields stat = 0; 1264e142ede8SJ. Bruce Fields out_err: 1265e142ede8SJ. Bruce Fields return stat; 1266e142ede8SJ. Bruce Fields } 1267e142ede8SJ. Bruce Fields 12687c9fdcfbSJ. Bruce Fields static inline int 12697c9fdcfbSJ. Bruce Fields svcauth_gss_wrap_resp_priv(struct svc_rqst *rqstp) 12707c9fdcfbSJ. Bruce Fields { 12717c9fdcfbSJ. Bruce Fields struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data; 12727c9fdcfbSJ. Bruce Fields struct rpc_gss_wire_cred *gc = &gsd->clcred; 12737c9fdcfbSJ. Bruce Fields struct xdr_buf *resbuf = &rqstp->rq_res; 12747c9fdcfbSJ. Bruce Fields struct page **inpages = NULL; 1275753ed90dSAl Viro __be32 *p, *len; 1276753ed90dSAl Viro int offset; 12777c9fdcfbSJ. Bruce Fields int pad; 12787c9fdcfbSJ. Bruce Fields 12793c15a486SJ.Bruce Fields p = svcauth_gss_prepare_to_wrap(resbuf, gsd); 12803c15a486SJ.Bruce Fields if (p == NULL) 12817c9fdcfbSJ. Bruce Fields return 0; 12827c9fdcfbSJ. Bruce Fields len = p++; 12837c9fdcfbSJ. Bruce Fields offset = (u8 *)p - (u8 *)resbuf->head[0].iov_base; 12847c9fdcfbSJ. Bruce Fields *p++ = htonl(gc->gc_seq); 12857c9fdcfbSJ. Bruce Fields inpages = resbuf->pages; 12867c9fdcfbSJ. Bruce Fields /* XXX: Would be better to write some xdr helper functions for 12877c9fdcfbSJ. Bruce Fields * nfs{2,3,4}xdr.c that place the data right, instead of copying: */ 128844524359SNeilBrown if (resbuf->tail[0].iov_base) { 12897c9fdcfbSJ. Bruce Fields BUG_ON(resbuf->tail[0].iov_base >= resbuf->head[0].iov_base 12907c9fdcfbSJ. Bruce Fields + PAGE_SIZE); 12917c9fdcfbSJ. Bruce Fields BUG_ON(resbuf->tail[0].iov_base < resbuf->head[0].iov_base); 12927c9fdcfbSJ. Bruce Fields if (resbuf->tail[0].iov_len + resbuf->head[0].iov_len 12937c9fdcfbSJ. Bruce Fields + 2 * RPC_MAX_AUTH_SIZE > PAGE_SIZE) 12947c9fdcfbSJ. Bruce Fields return -ENOMEM; 12957c9fdcfbSJ. Bruce Fields memmove(resbuf->tail[0].iov_base + RPC_MAX_AUTH_SIZE, 12967c9fdcfbSJ. Bruce Fields resbuf->tail[0].iov_base, 12977c9fdcfbSJ. Bruce Fields resbuf->tail[0].iov_len); 12987c9fdcfbSJ. Bruce Fields resbuf->tail[0].iov_base += RPC_MAX_AUTH_SIZE; 12997c9fdcfbSJ. Bruce Fields } 13007c9fdcfbSJ. Bruce Fields if (resbuf->tail[0].iov_base == NULL) { 13017c9fdcfbSJ. Bruce Fields if (resbuf->head[0].iov_len + 2*RPC_MAX_AUTH_SIZE > PAGE_SIZE) 13027c9fdcfbSJ. Bruce Fields return -ENOMEM; 13037c9fdcfbSJ. Bruce Fields resbuf->tail[0].iov_base = resbuf->head[0].iov_base 13047c9fdcfbSJ. Bruce Fields + resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE; 13057c9fdcfbSJ. Bruce Fields resbuf->tail[0].iov_len = 0; 13067c9fdcfbSJ. Bruce Fields } 13077c9fdcfbSJ. Bruce Fields if (gss_wrap(gsd->rsci->mechctx, offset, resbuf, inpages)) 13087c9fdcfbSJ. Bruce Fields return -ENOMEM; 13097c9fdcfbSJ. Bruce Fields *len = htonl(resbuf->len - offset); 13107c9fdcfbSJ. Bruce Fields pad = 3 - ((resbuf->len - offset - 1)&3); 1311d8ed029dSAlexey Dobriyan p = (__be32 *)(resbuf->tail[0].iov_base + resbuf->tail[0].iov_len); 13127c9fdcfbSJ. Bruce Fields memset(p, 0, pad); 13137c9fdcfbSJ. Bruce Fields resbuf->tail[0].iov_len += pad; 13147c9fdcfbSJ. Bruce Fields resbuf->len += pad; 13157c9fdcfbSJ. Bruce Fields return 0; 13167c9fdcfbSJ. Bruce Fields } 13177c9fdcfbSJ. Bruce Fields 1318e142ede8SJ. Bruce Fields static int 1319e142ede8SJ. Bruce Fields svcauth_gss_release(struct svc_rqst *rqstp) 1320e142ede8SJ. Bruce Fields { 1321e142ede8SJ. Bruce Fields struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data; 1322e142ede8SJ. Bruce Fields struct rpc_gss_wire_cred *gc = &gsd->clcred; 1323e142ede8SJ. Bruce Fields struct xdr_buf *resbuf = &rqstp->rq_res; 1324e142ede8SJ. Bruce Fields int stat = -EINVAL; 1325e142ede8SJ. Bruce Fields 1326e142ede8SJ. Bruce Fields if (gc->gc_proc != RPC_GSS_PROC_DATA) 1327e142ede8SJ. Bruce Fields goto out; 1328e142ede8SJ. Bruce Fields /* Release can be called twice, but we only wrap once. */ 13295b304bc5SJ.Bruce Fields if (gsd->verf_start == NULL) 1330e142ede8SJ. Bruce Fields goto out; 1331e142ede8SJ. Bruce Fields /* normally not set till svc_send, but we need it here: */ 13327c9fdcfbSJ. Bruce Fields /* XXX: what for? Do we mess it up the moment we call svc_putu32 13337c9fdcfbSJ. Bruce Fields * or whatever? */ 13347c9fdcfbSJ. Bruce Fields resbuf->len = total_buf_len(resbuf); 1335e142ede8SJ. Bruce Fields switch (gc->gc_svc) { 1336e142ede8SJ. Bruce Fields case RPC_GSS_SVC_NONE: 1337e142ede8SJ. Bruce Fields break; 1338e142ede8SJ. Bruce Fields case RPC_GSS_SVC_INTEGRITY: 13397c9fdcfbSJ. Bruce Fields stat = svcauth_gss_wrap_resp_integ(rqstp); 13407c9fdcfbSJ. Bruce Fields if (stat) 13417c9fdcfbSJ. Bruce Fields goto out_err; 13421da177e4SLinus Torvalds break; 13431da177e4SLinus Torvalds case RPC_GSS_SVC_PRIVACY: 13447c9fdcfbSJ. Bruce Fields stat = svcauth_gss_wrap_resp_priv(rqstp); 13457c9fdcfbSJ. Bruce Fields if (stat) 13467c9fdcfbSJ. Bruce Fields goto out_err; 13477c9fdcfbSJ. Bruce Fields break; 13481da177e4SLinus Torvalds default: 13491da177e4SLinus Torvalds goto out_err; 13501da177e4SLinus Torvalds } 13511da177e4SLinus Torvalds 13521da177e4SLinus Torvalds out: 13531da177e4SLinus Torvalds stat = 0; 13541da177e4SLinus Torvalds out_err: 13551da177e4SLinus Torvalds if (rqstp->rq_client) 13561da177e4SLinus Torvalds auth_domain_put(rqstp->rq_client); 13571da177e4SLinus Torvalds rqstp->rq_client = NULL; 13583ab4d8b1SJ. Bruce Fields if (rqstp->rq_gssclient) 13593ab4d8b1SJ. Bruce Fields auth_domain_put(rqstp->rq_gssclient); 13603ab4d8b1SJ. Bruce Fields rqstp->rq_gssclient = NULL; 13611da177e4SLinus Torvalds if (rqstp->rq_cred.cr_group_info) 13621da177e4SLinus Torvalds put_group_info(rqstp->rq_cred.cr_group_info); 13631da177e4SLinus Torvalds rqstp->rq_cred.cr_group_info = NULL; 13641da177e4SLinus Torvalds if (gsd->rsci) 1365baab935fSNeilBrown cache_put(&gsd->rsci->h, &rsc_cache); 13661da177e4SLinus Torvalds gsd->rsci = NULL; 13671da177e4SLinus Torvalds 13681da177e4SLinus Torvalds return stat; 13691da177e4SLinus Torvalds } 13701da177e4SLinus Torvalds 13711da177e4SLinus Torvalds static void 13721da177e4SLinus Torvalds svcauth_gss_domain_release(struct auth_domain *dom) 13731da177e4SLinus Torvalds { 13741da177e4SLinus Torvalds struct gss_domain *gd = container_of(dom, struct gss_domain, h); 13751da177e4SLinus Torvalds 13761da177e4SLinus Torvalds kfree(dom->name); 13771da177e4SLinus Torvalds kfree(gd); 13781da177e4SLinus Torvalds } 13791da177e4SLinus Torvalds 13801da177e4SLinus Torvalds static struct auth_ops svcauthops_gss = { 13811da177e4SLinus Torvalds .name = "rpcsec_gss", 13821da177e4SLinus Torvalds .owner = THIS_MODULE, 13831da177e4SLinus Torvalds .flavour = RPC_AUTH_GSS, 13841da177e4SLinus Torvalds .accept = svcauth_gss_accept, 13851da177e4SLinus Torvalds .release = svcauth_gss_release, 13861da177e4SLinus Torvalds .domain_release = svcauth_gss_domain_release, 13871da177e4SLinus Torvalds .set_client = svcauth_gss_set_client, 13881da177e4SLinus Torvalds }; 13891da177e4SLinus Torvalds 13901da177e4SLinus Torvalds int 13911da177e4SLinus Torvalds gss_svc_init(void) 13921da177e4SLinus Torvalds { 13931da177e4SLinus Torvalds int rv = svc_auth_register(RPC_AUTH_GSS, &svcauthops_gss); 1394dbf847ecSJ. Bruce Fields if (rv) 1395dbf847ecSJ. Bruce Fields return rv; 1396dbf847ecSJ. Bruce Fields rv = cache_register(&rsc_cache); 1397dbf847ecSJ. Bruce Fields if (rv) 1398dbf847ecSJ. Bruce Fields goto out1; 1399dbf847ecSJ. Bruce Fields rv = cache_register(&rsi_cache); 1400dbf847ecSJ. Bruce Fields if (rv) 1401dbf847ecSJ. Bruce Fields goto out2; 1402dbf847ecSJ. Bruce Fields return 0; 1403dbf847ecSJ. Bruce Fields out2: 1404dbf847ecSJ. Bruce Fields cache_unregister(&rsc_cache); 1405dbf847ecSJ. Bruce Fields out1: 1406dbf847ecSJ. Bruce Fields svc_auth_unregister(RPC_AUTH_GSS); 14071da177e4SLinus Torvalds return rv; 14081da177e4SLinus Torvalds } 14091da177e4SLinus Torvalds 14101da177e4SLinus Torvalds void 14111da177e4SLinus Torvalds gss_svc_shutdown(void) 14121da177e4SLinus Torvalds { 1413df95a9d4SJ. Bruce Fields cache_unregister(&rsc_cache); 1414df95a9d4SJ. Bruce Fields cache_unregister(&rsi_cache); 14151da177e4SLinus Torvalds svc_auth_unregister(RPC_AUTH_GSS); 14161da177e4SLinus Torvalds } 1417