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/svcauth.h> 461da177e4SLinus Torvalds #include <linux/sunrpc/gss_err.h> 471da177e4SLinus Torvalds #include <linux/sunrpc/svcauth.h> 481da177e4SLinus Torvalds #include <linux/sunrpc/svcauth_gss.h> 491da177e4SLinus Torvalds #include <linux/sunrpc/cache.h> 501da177e4SLinus Torvalds 511da177e4SLinus Torvalds #ifdef RPC_DEBUG 521da177e4SLinus Torvalds # define RPCDBG_FACILITY RPCDBG_AUTH 531da177e4SLinus Torvalds #endif 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds /* The rpcsec_init cache is used for mapping RPCSEC_GSS_{,CONT_}INIT requests 561da177e4SLinus Torvalds * into replies. 571da177e4SLinus Torvalds * 581da177e4SLinus Torvalds * Key is context handle (\x if empty) and gss_token. 591da177e4SLinus Torvalds * Content is major_status minor_status (integers) context_handle, reply_token. 601da177e4SLinus Torvalds * 611da177e4SLinus Torvalds */ 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds static int netobj_equal(struct xdr_netobj *a, struct xdr_netobj *b) 641da177e4SLinus Torvalds { 651da177e4SLinus Torvalds return a->len == b->len && 0 == memcmp(a->data, b->data, a->len); 661da177e4SLinus Torvalds } 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds #define RSI_HASHBITS 6 691da177e4SLinus Torvalds #define RSI_HASHMAX (1<<RSI_HASHBITS) 701da177e4SLinus Torvalds #define RSI_HASHMASK (RSI_HASHMAX-1) 711da177e4SLinus Torvalds 721da177e4SLinus Torvalds struct rsi { 731da177e4SLinus Torvalds struct cache_head h; 741da177e4SLinus Torvalds struct xdr_netobj in_handle, in_token; 751da177e4SLinus Torvalds struct xdr_netobj out_handle, out_token; 761da177e4SLinus Torvalds int major_status, minor_status; 771da177e4SLinus Torvalds }; 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds static struct cache_head *rsi_table[RSI_HASHMAX]; 801da177e4SLinus Torvalds static struct cache_detail rsi_cache; 81d4d11ea9SNeilBrown static struct rsi *rsi_update(struct rsi *new, struct rsi *old); 82d4d11ea9SNeilBrown static struct rsi *rsi_lookup(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); 1091da177e4SLinus Torvalds return netobj_equal(&item->in_handle, &tmp->in_handle) 1101da177e4SLinus Torvalds && 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; 1161da177e4SLinus Torvalds dst->data = (len ? kmalloc(len, GFP_KERNEL) : NULL); 1171da177e4SLinus Torvalds if (dst->data) 1181da177e4SLinus Torvalds memcpy(dst->data, src, len); 1191da177e4SLinus Torvalds if (len && !dst->data) 1201da177e4SLinus Torvalds return -ENOMEM; 1211da177e4SLinus Torvalds return 0; 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds static inline int dup_netobj(struct xdr_netobj *dst, struct xdr_netobj *src) 1251da177e4SLinus Torvalds { 1261da177e4SLinus Torvalds return dup_to_netobj(dst, src->data, src->len); 1271da177e4SLinus Torvalds } 1281da177e4SLinus Torvalds 129d4d11ea9SNeilBrown static void rsi_init(struct cache_head *cnew, struct cache_head *citem) 1301da177e4SLinus Torvalds { 131d4d11ea9SNeilBrown struct rsi *new = container_of(cnew, struct rsi, h); 132d4d11ea9SNeilBrown struct rsi *item = container_of(citem, struct rsi, h); 133d4d11ea9SNeilBrown 1341da177e4SLinus Torvalds new->out_handle.data = NULL; 1351da177e4SLinus Torvalds new->out_handle.len = 0; 1361da177e4SLinus Torvalds new->out_token.data = NULL; 1371da177e4SLinus Torvalds new->out_token.len = 0; 1381da177e4SLinus Torvalds new->in_handle.len = item->in_handle.len; 1391da177e4SLinus Torvalds item->in_handle.len = 0; 1401da177e4SLinus Torvalds new->in_token.len = item->in_token.len; 1411da177e4SLinus Torvalds item->in_token.len = 0; 1421da177e4SLinus Torvalds new->in_handle.data = item->in_handle.data; 1431da177e4SLinus Torvalds item->in_handle.data = NULL; 1441da177e4SLinus Torvalds new->in_token.data = item->in_token.data; 1451da177e4SLinus Torvalds item->in_token.data = NULL; 1461da177e4SLinus Torvalds } 1471da177e4SLinus Torvalds 148d4d11ea9SNeilBrown static void update_rsi(struct cache_head *cnew, struct cache_head *citem) 1491da177e4SLinus Torvalds { 150d4d11ea9SNeilBrown struct rsi *new = container_of(cnew, struct rsi, h); 151d4d11ea9SNeilBrown struct rsi *item = container_of(citem, struct rsi, h); 152d4d11ea9SNeilBrown 1531da177e4SLinus Torvalds BUG_ON(new->out_handle.data || new->out_token.data); 1541da177e4SLinus Torvalds new->out_handle.len = item->out_handle.len; 1551da177e4SLinus Torvalds item->out_handle.len = 0; 1561da177e4SLinus Torvalds new->out_token.len = item->out_token.len; 1571da177e4SLinus Torvalds item->out_token.len = 0; 1581da177e4SLinus Torvalds new->out_handle.data = item->out_handle.data; 1591da177e4SLinus Torvalds item->out_handle.data = NULL; 1601da177e4SLinus Torvalds new->out_token.data = item->out_token.data; 1611da177e4SLinus Torvalds item->out_token.data = NULL; 1621da177e4SLinus Torvalds 1631da177e4SLinus Torvalds new->major_status = item->major_status; 1641da177e4SLinus Torvalds new->minor_status = item->minor_status; 1651da177e4SLinus Torvalds } 1661da177e4SLinus Torvalds 167d4d11ea9SNeilBrown static struct cache_head *rsi_alloc(void) 168d4d11ea9SNeilBrown { 169d4d11ea9SNeilBrown struct rsi *rsii = kmalloc(sizeof(*rsii), GFP_KERNEL); 170d4d11ea9SNeilBrown if (rsii) 171d4d11ea9SNeilBrown return &rsii->h; 172d4d11ea9SNeilBrown else 173d4d11ea9SNeilBrown return NULL; 174d4d11ea9SNeilBrown } 175d4d11ea9SNeilBrown 1761da177e4SLinus Torvalds static void rsi_request(struct cache_detail *cd, 1771da177e4SLinus Torvalds struct cache_head *h, 1781da177e4SLinus Torvalds char **bpp, int *blen) 1791da177e4SLinus Torvalds { 1801da177e4SLinus Torvalds struct rsi *rsii = container_of(h, struct rsi, h); 1811da177e4SLinus Torvalds 1821da177e4SLinus Torvalds qword_addhex(bpp, blen, rsii->in_handle.data, rsii->in_handle.len); 1831da177e4SLinus Torvalds qword_addhex(bpp, blen, rsii->in_token.data, rsii->in_token.len); 1841da177e4SLinus Torvalds (*bpp)[-1] = '\n'; 1851da177e4SLinus Torvalds } 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds static int rsi_parse(struct cache_detail *cd, 1891da177e4SLinus Torvalds char *mesg, int mlen) 1901da177e4SLinus Torvalds { 1911da177e4SLinus Torvalds /* context token expiry major minor context token */ 1921da177e4SLinus Torvalds char *buf = mesg; 1931da177e4SLinus Torvalds char *ep; 1941da177e4SLinus Torvalds int len; 1951da177e4SLinus Torvalds struct rsi rsii, *rsip = NULL; 1961da177e4SLinus Torvalds time_t expiry; 1971da177e4SLinus Torvalds int status = -EINVAL; 1981da177e4SLinus Torvalds 1991da177e4SLinus Torvalds memset(&rsii, 0, sizeof(rsii)); 2001da177e4SLinus Torvalds /* handle */ 2011da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2021da177e4SLinus Torvalds if (len < 0) 2031da177e4SLinus Torvalds goto out; 2041da177e4SLinus Torvalds status = -ENOMEM; 2051da177e4SLinus Torvalds if (dup_to_netobj(&rsii.in_handle, buf, len)) 2061da177e4SLinus Torvalds goto out; 2071da177e4SLinus Torvalds 2081da177e4SLinus Torvalds /* token */ 2091da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2101da177e4SLinus Torvalds status = -EINVAL; 2111da177e4SLinus Torvalds if (len < 0) 2121da177e4SLinus Torvalds goto out; 2131da177e4SLinus Torvalds status = -ENOMEM; 2141da177e4SLinus Torvalds if (dup_to_netobj(&rsii.in_token, buf, len)) 2151da177e4SLinus Torvalds goto out; 2161da177e4SLinus Torvalds 217d4d11ea9SNeilBrown rsip = rsi_lookup(&rsii); 218d4d11ea9SNeilBrown if (!rsip) 219d4d11ea9SNeilBrown goto out; 220d4d11ea9SNeilBrown 2211da177e4SLinus Torvalds rsii.h.flags = 0; 2221da177e4SLinus Torvalds /* expiry */ 2231da177e4SLinus Torvalds expiry = get_expiry(&mesg); 2241da177e4SLinus Torvalds status = -EINVAL; 2251da177e4SLinus Torvalds if (expiry == 0) 2261da177e4SLinus Torvalds goto out; 2271da177e4SLinus Torvalds 2281da177e4SLinus Torvalds /* major/minor */ 2291da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2301da177e4SLinus Torvalds if (len < 0) 2311da177e4SLinus Torvalds goto out; 2321da177e4SLinus Torvalds if (len == 0) { 2331da177e4SLinus Torvalds goto out; 2341da177e4SLinus Torvalds } else { 2351da177e4SLinus Torvalds rsii.major_status = simple_strtoul(buf, &ep, 10); 2361da177e4SLinus Torvalds if (*ep) 2371da177e4SLinus Torvalds goto out; 2381da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2391da177e4SLinus Torvalds if (len <= 0) 2401da177e4SLinus Torvalds goto out; 2411da177e4SLinus Torvalds rsii.minor_status = simple_strtoul(buf, &ep, 10); 2421da177e4SLinus Torvalds if (*ep) 2431da177e4SLinus Torvalds goto out; 2441da177e4SLinus Torvalds 2451da177e4SLinus Torvalds /* out_handle */ 2461da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2471da177e4SLinus Torvalds if (len < 0) 2481da177e4SLinus Torvalds goto out; 2491da177e4SLinus Torvalds status = -ENOMEM; 2501da177e4SLinus Torvalds if (dup_to_netobj(&rsii.out_handle, buf, len)) 2511da177e4SLinus Torvalds goto out; 2521da177e4SLinus Torvalds 2531da177e4SLinus Torvalds /* out_token */ 2541da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 2551da177e4SLinus Torvalds status = -EINVAL; 2561da177e4SLinus Torvalds if (len < 0) 2571da177e4SLinus Torvalds goto out; 2581da177e4SLinus Torvalds status = -ENOMEM; 2591da177e4SLinus Torvalds if (dup_to_netobj(&rsii.out_token, buf, len)) 2601da177e4SLinus Torvalds goto out; 2611da177e4SLinus Torvalds } 2621da177e4SLinus Torvalds rsii.h.expiry_time = expiry; 263d4d11ea9SNeilBrown rsip = rsi_update(&rsii, rsip); 2641da177e4SLinus Torvalds status = 0; 2651da177e4SLinus Torvalds out: 2661da177e4SLinus Torvalds rsi_free(&rsii); 2671da177e4SLinus Torvalds if (rsip) 268baab935fSNeilBrown cache_put(&rsip->h, &rsi_cache); 269d4d11ea9SNeilBrown else 270d4d11ea9SNeilBrown status = -ENOMEM; 2711da177e4SLinus Torvalds return status; 2721da177e4SLinus Torvalds } 2731da177e4SLinus Torvalds 2741da177e4SLinus Torvalds static struct cache_detail rsi_cache = { 275f35279d3SBruce Allan .owner = THIS_MODULE, 2761da177e4SLinus Torvalds .hash_size = RSI_HASHMAX, 2771da177e4SLinus Torvalds .hash_table = rsi_table, 2781da177e4SLinus Torvalds .name = "auth.rpcsec.init", 2791da177e4SLinus Torvalds .cache_put = rsi_put, 2801da177e4SLinus Torvalds .cache_request = rsi_request, 2811da177e4SLinus Torvalds .cache_parse = rsi_parse, 282d4d11ea9SNeilBrown .match = rsi_match, 283d4d11ea9SNeilBrown .init = rsi_init, 284d4d11ea9SNeilBrown .update = update_rsi, 285d4d11ea9SNeilBrown .alloc = rsi_alloc, 2861da177e4SLinus Torvalds }; 2871da177e4SLinus Torvalds 288d4d11ea9SNeilBrown static struct rsi *rsi_lookup(struct rsi *item) 289d4d11ea9SNeilBrown { 290d4d11ea9SNeilBrown struct cache_head *ch; 291d4d11ea9SNeilBrown int hash = rsi_hash(item); 292d4d11ea9SNeilBrown 293d4d11ea9SNeilBrown ch = sunrpc_cache_lookup(&rsi_cache, &item->h, hash); 294d4d11ea9SNeilBrown if (ch) 295d4d11ea9SNeilBrown return container_of(ch, struct rsi, h); 296d4d11ea9SNeilBrown else 297d4d11ea9SNeilBrown return NULL; 298d4d11ea9SNeilBrown } 299d4d11ea9SNeilBrown 300d4d11ea9SNeilBrown static struct rsi *rsi_update(struct rsi *new, struct rsi *old) 301d4d11ea9SNeilBrown { 302d4d11ea9SNeilBrown struct cache_head *ch; 303d4d11ea9SNeilBrown int hash = rsi_hash(new); 304d4d11ea9SNeilBrown 305d4d11ea9SNeilBrown ch = sunrpc_cache_update(&rsi_cache, &new->h, 306d4d11ea9SNeilBrown &old->h, hash); 307d4d11ea9SNeilBrown if (ch) 308d4d11ea9SNeilBrown return container_of(ch, struct rsi, h); 309d4d11ea9SNeilBrown else 310d4d11ea9SNeilBrown return NULL; 311d4d11ea9SNeilBrown } 312d4d11ea9SNeilBrown 3131da177e4SLinus Torvalds 3141da177e4SLinus Torvalds /* 3151da177e4SLinus Torvalds * The rpcsec_context cache is used to store a context that is 3161da177e4SLinus Torvalds * used in data exchange. 3171da177e4SLinus Torvalds * The key is a context handle. The content is: 3181da177e4SLinus Torvalds * uid, gidlist, mechanism, service-set, mech-specific-data 3191da177e4SLinus Torvalds */ 3201da177e4SLinus Torvalds 3211da177e4SLinus Torvalds #define RSC_HASHBITS 10 3221da177e4SLinus Torvalds #define RSC_HASHMAX (1<<RSC_HASHBITS) 3231da177e4SLinus Torvalds #define RSC_HASHMASK (RSC_HASHMAX-1) 3241da177e4SLinus Torvalds 3251da177e4SLinus Torvalds #define GSS_SEQ_WIN 128 3261da177e4SLinus Torvalds 3271da177e4SLinus Torvalds struct gss_svc_seq_data { 3281da177e4SLinus Torvalds /* highest seq number seen so far: */ 3291da177e4SLinus Torvalds int sd_max; 3301da177e4SLinus Torvalds /* for i such that sd_max-GSS_SEQ_WIN < i <= sd_max, the i-th bit of 3311da177e4SLinus Torvalds * sd_win is nonzero iff sequence number i has been seen already: */ 3321da177e4SLinus Torvalds unsigned long sd_win[GSS_SEQ_WIN/BITS_PER_LONG]; 3331da177e4SLinus Torvalds spinlock_t sd_lock; 3341da177e4SLinus Torvalds }; 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds struct rsc { 3371da177e4SLinus Torvalds struct cache_head h; 3381da177e4SLinus Torvalds struct xdr_netobj handle; 3391da177e4SLinus Torvalds struct svc_cred cred; 3401da177e4SLinus Torvalds struct gss_svc_seq_data seqdata; 3411da177e4SLinus Torvalds struct gss_ctx *mechctx; 3421da177e4SLinus Torvalds }; 3431da177e4SLinus Torvalds 3441da177e4SLinus Torvalds static struct cache_head *rsc_table[RSC_HASHMAX]; 3451da177e4SLinus Torvalds static struct cache_detail rsc_cache; 34617f834b6SNeilBrown static struct rsc *rsc_update(struct rsc *new, struct rsc *old); 34717f834b6SNeilBrown static struct rsc *rsc_lookup(struct rsc *item); 3481da177e4SLinus Torvalds 3491da177e4SLinus Torvalds static void rsc_free(struct rsc *rsci) 3501da177e4SLinus Torvalds { 3511da177e4SLinus Torvalds kfree(rsci->handle.data); 3521da177e4SLinus Torvalds if (rsci->mechctx) 3531da177e4SLinus Torvalds gss_delete_sec_context(&rsci->mechctx); 3541da177e4SLinus Torvalds if (rsci->cred.cr_group_info) 3551da177e4SLinus Torvalds put_group_info(rsci->cred.cr_group_info); 3561da177e4SLinus Torvalds } 3571da177e4SLinus Torvalds 358baab935fSNeilBrown static void rsc_put(struct kref *ref) 3591da177e4SLinus Torvalds { 360baab935fSNeilBrown struct rsc *rsci = container_of(ref, struct rsc, h.ref); 3611da177e4SLinus Torvalds 3621da177e4SLinus Torvalds rsc_free(rsci); 3631da177e4SLinus Torvalds kfree(rsci); 3641da177e4SLinus Torvalds } 3651da177e4SLinus Torvalds 3661da177e4SLinus Torvalds static inline int 3671da177e4SLinus Torvalds rsc_hash(struct rsc *rsci) 3681da177e4SLinus Torvalds { 3691da177e4SLinus Torvalds return hash_mem(rsci->handle.data, rsci->handle.len, RSC_HASHBITS); 3701da177e4SLinus Torvalds } 3711da177e4SLinus Torvalds 37217f834b6SNeilBrown static int 37317f834b6SNeilBrown rsc_match(struct cache_head *a, struct cache_head *b) 3741da177e4SLinus Torvalds { 37517f834b6SNeilBrown struct rsc *new = container_of(a, struct rsc, h); 37617f834b6SNeilBrown struct rsc *tmp = container_of(b, struct rsc, h); 37717f834b6SNeilBrown 3781da177e4SLinus Torvalds return netobj_equal(&new->handle, &tmp->handle); 3791da177e4SLinus Torvalds } 3801da177e4SLinus Torvalds 38117f834b6SNeilBrown static void 38217f834b6SNeilBrown rsc_init(struct cache_head *cnew, struct cache_head *ctmp) 3831da177e4SLinus Torvalds { 38417f834b6SNeilBrown struct rsc *new = container_of(cnew, struct rsc, h); 38517f834b6SNeilBrown struct rsc *tmp = container_of(ctmp, struct rsc, h); 38617f834b6SNeilBrown 3871da177e4SLinus Torvalds new->handle.len = tmp->handle.len; 3881da177e4SLinus Torvalds tmp->handle.len = 0; 3891da177e4SLinus Torvalds new->handle.data = tmp->handle.data; 3901da177e4SLinus Torvalds tmp->handle.data = NULL; 3911da177e4SLinus Torvalds new->mechctx = NULL; 3921da177e4SLinus Torvalds new->cred.cr_group_info = NULL; 3931da177e4SLinus Torvalds } 3941da177e4SLinus Torvalds 39517f834b6SNeilBrown static void 39617f834b6SNeilBrown update_rsc(struct cache_head *cnew, struct cache_head *ctmp) 3971da177e4SLinus Torvalds { 39817f834b6SNeilBrown struct rsc *new = container_of(cnew, struct rsc, h); 39917f834b6SNeilBrown struct rsc *tmp = container_of(ctmp, struct rsc, h); 40017f834b6SNeilBrown 4011da177e4SLinus Torvalds new->mechctx = tmp->mechctx; 4021da177e4SLinus Torvalds tmp->mechctx = NULL; 4031da177e4SLinus Torvalds memset(&new->seqdata, 0, sizeof(new->seqdata)); 4041da177e4SLinus Torvalds spin_lock_init(&new->seqdata.sd_lock); 4051da177e4SLinus Torvalds new->cred = tmp->cred; 4061da177e4SLinus Torvalds tmp->cred.cr_group_info = NULL; 4071da177e4SLinus Torvalds } 4081da177e4SLinus Torvalds 40917f834b6SNeilBrown static struct cache_head * 41017f834b6SNeilBrown rsc_alloc(void) 41117f834b6SNeilBrown { 41217f834b6SNeilBrown struct rsc *rsci = kmalloc(sizeof(*rsci), GFP_KERNEL); 41317f834b6SNeilBrown if (rsci) 41417f834b6SNeilBrown return &rsci->h; 41517f834b6SNeilBrown else 41617f834b6SNeilBrown return NULL; 41717f834b6SNeilBrown } 41817f834b6SNeilBrown 4191da177e4SLinus Torvalds static int rsc_parse(struct cache_detail *cd, 4201da177e4SLinus Torvalds char *mesg, int mlen) 4211da177e4SLinus Torvalds { 4221da177e4SLinus Torvalds /* contexthandle expiry [ uid gid N <n gids> mechname ...mechdata... ] */ 4231da177e4SLinus Torvalds char *buf = mesg; 4241da177e4SLinus Torvalds int len, rv; 4251da177e4SLinus Torvalds struct rsc rsci, *rscp = NULL; 4261da177e4SLinus Torvalds time_t expiry; 4271da177e4SLinus Torvalds int status = -EINVAL; 4281df0cadaSJ. Bruce Fields struct gss_api_mech *gm = NULL; 4291da177e4SLinus Torvalds 4301da177e4SLinus Torvalds memset(&rsci, 0, sizeof(rsci)); 4311da177e4SLinus Torvalds /* context handle */ 4321da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 4331da177e4SLinus Torvalds if (len < 0) goto out; 4341da177e4SLinus Torvalds status = -ENOMEM; 4351da177e4SLinus Torvalds if (dup_to_netobj(&rsci.handle, buf, len)) 4361da177e4SLinus Torvalds goto out; 4371da177e4SLinus Torvalds 4381da177e4SLinus Torvalds rsci.h.flags = 0; 4391da177e4SLinus Torvalds /* expiry */ 4401da177e4SLinus Torvalds expiry = get_expiry(&mesg); 4411da177e4SLinus Torvalds status = -EINVAL; 4421da177e4SLinus Torvalds if (expiry == 0) 4431da177e4SLinus Torvalds goto out; 4441da177e4SLinus Torvalds 44517f834b6SNeilBrown rscp = rsc_lookup(&rsci); 44617f834b6SNeilBrown if (!rscp) 44717f834b6SNeilBrown goto out; 44817f834b6SNeilBrown 4491da177e4SLinus Torvalds /* uid, or NEGATIVE */ 4501da177e4SLinus Torvalds rv = get_int(&mesg, &rsci.cred.cr_uid); 4511da177e4SLinus Torvalds if (rv == -EINVAL) 4521da177e4SLinus Torvalds goto out; 4531da177e4SLinus Torvalds if (rv == -ENOENT) 4541da177e4SLinus Torvalds set_bit(CACHE_NEGATIVE, &rsci.h.flags); 4551da177e4SLinus Torvalds else { 4561da177e4SLinus Torvalds int N, i; 4571da177e4SLinus Torvalds 4581da177e4SLinus Torvalds /* gid */ 4591da177e4SLinus Torvalds if (get_int(&mesg, &rsci.cred.cr_gid)) 4601da177e4SLinus Torvalds goto out; 4611da177e4SLinus Torvalds 4621da177e4SLinus Torvalds /* number of additional gid's */ 4631da177e4SLinus Torvalds if (get_int(&mesg, &N)) 4641da177e4SLinus Torvalds goto out; 4651da177e4SLinus Torvalds status = -ENOMEM; 4661da177e4SLinus Torvalds rsci.cred.cr_group_info = groups_alloc(N); 4671da177e4SLinus Torvalds if (rsci.cred.cr_group_info == NULL) 4681da177e4SLinus Torvalds goto out; 4691da177e4SLinus Torvalds 4701da177e4SLinus Torvalds /* gid's */ 4711da177e4SLinus Torvalds status = -EINVAL; 4721da177e4SLinus Torvalds for (i=0; i<N; i++) { 4731da177e4SLinus Torvalds gid_t gid; 4741da177e4SLinus Torvalds if (get_int(&mesg, &gid)) 4751da177e4SLinus Torvalds goto out; 4761da177e4SLinus Torvalds GROUP_AT(rsci.cred.cr_group_info, i) = gid; 4771da177e4SLinus Torvalds } 4781da177e4SLinus Torvalds 4791da177e4SLinus Torvalds /* mech name */ 4801da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 4811da177e4SLinus Torvalds if (len < 0) 4821da177e4SLinus Torvalds goto out; 4831da177e4SLinus Torvalds gm = gss_mech_get_by_name(buf); 4841da177e4SLinus Torvalds status = -EOPNOTSUPP; 4851da177e4SLinus Torvalds if (!gm) 4861da177e4SLinus Torvalds goto out; 4871da177e4SLinus Torvalds 4881da177e4SLinus Torvalds status = -EINVAL; 4891da177e4SLinus Torvalds /* mech-specific data: */ 4901da177e4SLinus Torvalds len = qword_get(&mesg, buf, mlen); 4911df0cadaSJ. Bruce Fields if (len < 0) 4921da177e4SLinus Torvalds goto out; 4935fb8b49eSJ. Bruce Fields status = gss_import_sec_context(buf, len, gm, &rsci.mechctx); 4941df0cadaSJ. Bruce Fields if (status) 4951da177e4SLinus Torvalds goto out; 4961da177e4SLinus Torvalds } 4971da177e4SLinus Torvalds rsci.h.expiry_time = expiry; 49817f834b6SNeilBrown rscp = rsc_update(&rsci, rscp); 4991da177e4SLinus Torvalds status = 0; 5001da177e4SLinus Torvalds out: 5011df0cadaSJ. Bruce Fields gss_mech_put(gm); 5021da177e4SLinus Torvalds rsc_free(&rsci); 5031da177e4SLinus Torvalds if (rscp) 504baab935fSNeilBrown cache_put(&rscp->h, &rsc_cache); 50517f834b6SNeilBrown else 50617f834b6SNeilBrown status = -ENOMEM; 5071da177e4SLinus Torvalds return status; 5081da177e4SLinus Torvalds } 5091da177e4SLinus Torvalds 5101da177e4SLinus Torvalds static struct cache_detail rsc_cache = { 511f35279d3SBruce Allan .owner = THIS_MODULE, 5121da177e4SLinus Torvalds .hash_size = RSC_HASHMAX, 5131da177e4SLinus Torvalds .hash_table = rsc_table, 5141da177e4SLinus Torvalds .name = "auth.rpcsec.context", 5151da177e4SLinus Torvalds .cache_put = rsc_put, 5161da177e4SLinus Torvalds .cache_parse = rsc_parse, 51717f834b6SNeilBrown .match = rsc_match, 51817f834b6SNeilBrown .init = rsc_init, 51917f834b6SNeilBrown .update = update_rsc, 52017f834b6SNeilBrown .alloc = rsc_alloc, 5211da177e4SLinus Torvalds }; 5221da177e4SLinus Torvalds 52317f834b6SNeilBrown static struct rsc *rsc_lookup(struct rsc *item) 52417f834b6SNeilBrown { 52517f834b6SNeilBrown struct cache_head *ch; 52617f834b6SNeilBrown int hash = rsc_hash(item); 52717f834b6SNeilBrown 52817f834b6SNeilBrown ch = sunrpc_cache_lookup(&rsc_cache, &item->h, hash); 52917f834b6SNeilBrown if (ch) 53017f834b6SNeilBrown return container_of(ch, struct rsc, h); 53117f834b6SNeilBrown else 53217f834b6SNeilBrown return NULL; 53317f834b6SNeilBrown } 53417f834b6SNeilBrown 53517f834b6SNeilBrown static struct rsc *rsc_update(struct rsc *new, struct rsc *old) 53617f834b6SNeilBrown { 53717f834b6SNeilBrown struct cache_head *ch; 53817f834b6SNeilBrown int hash = rsc_hash(new); 53917f834b6SNeilBrown 54017f834b6SNeilBrown ch = sunrpc_cache_update(&rsc_cache, &new->h, 54117f834b6SNeilBrown &old->h, hash); 54217f834b6SNeilBrown if (ch) 54317f834b6SNeilBrown return container_of(ch, struct rsc, h); 54417f834b6SNeilBrown else 54517f834b6SNeilBrown return NULL; 54617f834b6SNeilBrown } 54717f834b6SNeilBrown 5481da177e4SLinus Torvalds 5491da177e4SLinus Torvalds static struct rsc * 5501da177e4SLinus Torvalds gss_svc_searchbyctx(struct xdr_netobj *handle) 5511da177e4SLinus Torvalds { 5521da177e4SLinus Torvalds struct rsc rsci; 5531da177e4SLinus Torvalds struct rsc *found; 5541da177e4SLinus Torvalds 5551da177e4SLinus Torvalds memset(&rsci, 0, sizeof(rsci)); 5561da177e4SLinus Torvalds if (dup_to_netobj(&rsci.handle, handle->data, handle->len)) 5571da177e4SLinus Torvalds return NULL; 55817f834b6SNeilBrown found = rsc_lookup(&rsci); 5591da177e4SLinus Torvalds rsc_free(&rsci); 5601da177e4SLinus Torvalds if (!found) 5611da177e4SLinus Torvalds return NULL; 5621da177e4SLinus Torvalds if (cache_check(&rsc_cache, &found->h, NULL)) 5631da177e4SLinus Torvalds return NULL; 5641da177e4SLinus Torvalds return found; 5651da177e4SLinus Torvalds } 5661da177e4SLinus Torvalds 5671da177e4SLinus Torvalds /* Implements sequence number algorithm as specified in RFC 2203. */ 5681da177e4SLinus Torvalds static int 5691da177e4SLinus Torvalds gss_check_seq_num(struct rsc *rsci, int seq_num) 5701da177e4SLinus Torvalds { 5711da177e4SLinus Torvalds struct gss_svc_seq_data *sd = &rsci->seqdata; 5721da177e4SLinus Torvalds 5731da177e4SLinus Torvalds spin_lock(&sd->sd_lock); 5741da177e4SLinus Torvalds if (seq_num > sd->sd_max) { 5751da177e4SLinus Torvalds if (seq_num >= sd->sd_max + GSS_SEQ_WIN) { 5761da177e4SLinus Torvalds memset(sd->sd_win,0,sizeof(sd->sd_win)); 5771da177e4SLinus Torvalds sd->sd_max = seq_num; 5781da177e4SLinus Torvalds } else while (sd->sd_max < seq_num) { 5791da177e4SLinus Torvalds sd->sd_max++; 5801da177e4SLinus Torvalds __clear_bit(sd->sd_max % GSS_SEQ_WIN, sd->sd_win); 5811da177e4SLinus Torvalds } 5821da177e4SLinus Torvalds __set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win); 5831da177e4SLinus Torvalds goto ok; 5841da177e4SLinus Torvalds } else if (seq_num <= sd->sd_max - GSS_SEQ_WIN) { 5851da177e4SLinus Torvalds goto drop; 5861da177e4SLinus Torvalds } 5871da177e4SLinus Torvalds /* sd_max - GSS_SEQ_WIN < seq_num <= sd_max */ 5881da177e4SLinus Torvalds if (__test_and_set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win)) 5891da177e4SLinus Torvalds goto drop; 5901da177e4SLinus Torvalds ok: 5911da177e4SLinus Torvalds spin_unlock(&sd->sd_lock); 5921da177e4SLinus Torvalds return 1; 5931da177e4SLinus Torvalds drop: 5941da177e4SLinus Torvalds spin_unlock(&sd->sd_lock); 5951da177e4SLinus Torvalds return 0; 5961da177e4SLinus Torvalds } 5971da177e4SLinus Torvalds 5981da177e4SLinus Torvalds static inline u32 round_up_to_quad(u32 i) 5991da177e4SLinus Torvalds { 6001da177e4SLinus Torvalds return (i + 3 ) & ~3; 6011da177e4SLinus Torvalds } 6021da177e4SLinus Torvalds 6031da177e4SLinus Torvalds static inline int 6041da177e4SLinus Torvalds svc_safe_getnetobj(struct kvec *argv, struct xdr_netobj *o) 6051da177e4SLinus Torvalds { 6061da177e4SLinus Torvalds int l; 6071da177e4SLinus Torvalds 6081da177e4SLinus Torvalds if (argv->iov_len < 4) 6091da177e4SLinus Torvalds return -1; 6101da177e4SLinus Torvalds o->len = ntohl(svc_getu32(argv)); 6111da177e4SLinus Torvalds l = round_up_to_quad(o->len); 6121da177e4SLinus Torvalds if (argv->iov_len < l) 6131da177e4SLinus Torvalds return -1; 6141da177e4SLinus Torvalds o->data = argv->iov_base; 6151da177e4SLinus Torvalds argv->iov_base += l; 6161da177e4SLinus Torvalds argv->iov_len -= l; 6171da177e4SLinus Torvalds return 0; 6181da177e4SLinus Torvalds } 6191da177e4SLinus Torvalds 6201da177e4SLinus Torvalds static inline int 6211da177e4SLinus Torvalds svc_safe_putnetobj(struct kvec *resv, struct xdr_netobj *o) 6221da177e4SLinus Torvalds { 6231da177e4SLinus Torvalds u32 *p; 6241da177e4SLinus Torvalds 6251da177e4SLinus Torvalds if (resv->iov_len + 4 > PAGE_SIZE) 6261da177e4SLinus Torvalds return -1; 6271da177e4SLinus Torvalds svc_putu32(resv, htonl(o->len)); 6281da177e4SLinus Torvalds p = resv->iov_base + resv->iov_len; 6291da177e4SLinus Torvalds resv->iov_len += round_up_to_quad(o->len); 6301da177e4SLinus Torvalds if (resv->iov_len > PAGE_SIZE) 6311da177e4SLinus Torvalds return -1; 6321da177e4SLinus Torvalds memcpy(p, o->data, o->len); 6331da177e4SLinus Torvalds memset((u8 *)p + o->len, 0, round_up_to_quad(o->len) - o->len); 6341da177e4SLinus Torvalds return 0; 6351da177e4SLinus Torvalds } 6361da177e4SLinus Torvalds 6371da177e4SLinus Torvalds /* Verify the checksum on the header and return SVC_OK on success. 6381da177e4SLinus Torvalds * Otherwise, return SVC_DROP (in the case of a bad sequence number) 6391da177e4SLinus Torvalds * or return SVC_DENIED and indicate error in authp. 6401da177e4SLinus Torvalds */ 6411da177e4SLinus Torvalds static int 6421da177e4SLinus Torvalds gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci, 6431da177e4SLinus Torvalds u32 *rpcstart, struct rpc_gss_wire_cred *gc, u32 *authp) 6441da177e4SLinus Torvalds { 6451da177e4SLinus Torvalds struct gss_ctx *ctx_id = rsci->mechctx; 6461da177e4SLinus Torvalds struct xdr_buf rpchdr; 6471da177e4SLinus Torvalds struct xdr_netobj checksum; 6481da177e4SLinus Torvalds u32 flavor = 0; 6491da177e4SLinus Torvalds struct kvec *argv = &rqstp->rq_arg.head[0]; 6501da177e4SLinus Torvalds struct kvec iov; 6511da177e4SLinus Torvalds 6521da177e4SLinus Torvalds /* data to compute the checksum over: */ 6531da177e4SLinus Torvalds iov.iov_base = rpcstart; 6541da177e4SLinus Torvalds iov.iov_len = (u8 *)argv->iov_base - (u8 *)rpcstart; 6551da177e4SLinus Torvalds xdr_buf_from_iov(&iov, &rpchdr); 6561da177e4SLinus Torvalds 6571da177e4SLinus Torvalds *authp = rpc_autherr_badverf; 6581da177e4SLinus Torvalds if (argv->iov_len < 4) 6591da177e4SLinus Torvalds return SVC_DENIED; 6601da177e4SLinus Torvalds flavor = ntohl(svc_getu32(argv)); 6611da177e4SLinus Torvalds if (flavor != RPC_AUTH_GSS) 6621da177e4SLinus Torvalds return SVC_DENIED; 6631da177e4SLinus Torvalds if (svc_safe_getnetobj(argv, &checksum)) 6641da177e4SLinus Torvalds return SVC_DENIED; 6651da177e4SLinus Torvalds 6661da177e4SLinus Torvalds if (rqstp->rq_deferred) /* skip verification of revisited request */ 6671da177e4SLinus Torvalds return SVC_OK; 66800fd6e14SJ. Bruce Fields if (gss_verify_mic(ctx_id, &rpchdr, &checksum) != GSS_S_COMPLETE) { 6691da177e4SLinus Torvalds *authp = rpcsec_gsserr_credproblem; 6701da177e4SLinus Torvalds return SVC_DENIED; 6711da177e4SLinus Torvalds } 6721da177e4SLinus Torvalds 6731da177e4SLinus Torvalds if (gc->gc_seq > MAXSEQ) { 6741da177e4SLinus Torvalds dprintk("RPC: svcauth_gss: discarding request with large sequence number %d\n", 6751da177e4SLinus Torvalds gc->gc_seq); 6761da177e4SLinus Torvalds *authp = rpcsec_gsserr_ctxproblem; 6771da177e4SLinus Torvalds return SVC_DENIED; 6781da177e4SLinus Torvalds } 6791da177e4SLinus Torvalds if (!gss_check_seq_num(rsci, gc->gc_seq)) { 6801da177e4SLinus Torvalds dprintk("RPC: svcauth_gss: discarding request with old sequence number %d\n", 6811da177e4SLinus Torvalds gc->gc_seq); 6821da177e4SLinus Torvalds return SVC_DROP; 6831da177e4SLinus Torvalds } 6841da177e4SLinus Torvalds return SVC_OK; 6851da177e4SLinus Torvalds } 6861da177e4SLinus Torvalds 6871da177e4SLinus Torvalds static int 688822f1005SAndy Adamson gss_write_null_verf(struct svc_rqst *rqstp) 689822f1005SAndy Adamson { 690822f1005SAndy Adamson u32 *p; 691822f1005SAndy Adamson 692822f1005SAndy Adamson svc_putu32(rqstp->rq_res.head, htonl(RPC_AUTH_NULL)); 693822f1005SAndy Adamson p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len; 694822f1005SAndy Adamson /* don't really need to check if head->iov_len > PAGE_SIZE ... */ 695822f1005SAndy Adamson *p++ = 0; 696822f1005SAndy Adamson if (!xdr_ressize_check(rqstp, p)) 697822f1005SAndy Adamson return -1; 698822f1005SAndy Adamson return 0; 699822f1005SAndy Adamson } 700822f1005SAndy Adamson 701822f1005SAndy Adamson static int 7021da177e4SLinus Torvalds gss_write_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id, u32 seq) 7031da177e4SLinus Torvalds { 7041da177e4SLinus Torvalds u32 xdr_seq; 7051da177e4SLinus Torvalds u32 maj_stat; 7061da177e4SLinus Torvalds struct xdr_buf verf_data; 7071da177e4SLinus Torvalds struct xdr_netobj mic; 7081da177e4SLinus Torvalds u32 *p; 7091da177e4SLinus Torvalds struct kvec iov; 7101da177e4SLinus Torvalds 7111da177e4SLinus Torvalds svc_putu32(rqstp->rq_res.head, htonl(RPC_AUTH_GSS)); 7121da177e4SLinus Torvalds xdr_seq = htonl(seq); 7131da177e4SLinus Torvalds 7141da177e4SLinus Torvalds iov.iov_base = &xdr_seq; 7151da177e4SLinus Torvalds iov.iov_len = sizeof(xdr_seq); 7161da177e4SLinus Torvalds xdr_buf_from_iov(&iov, &verf_data); 7171da177e4SLinus Torvalds p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len; 7181da177e4SLinus Torvalds mic.data = (u8 *)(p + 1); 71900fd6e14SJ. Bruce Fields maj_stat = gss_get_mic(ctx_id, &verf_data, &mic); 7201da177e4SLinus Torvalds if (maj_stat != GSS_S_COMPLETE) 7211da177e4SLinus Torvalds return -1; 7221da177e4SLinus Torvalds *p++ = htonl(mic.len); 7231da177e4SLinus Torvalds memset((u8 *)p + mic.len, 0, round_up_to_quad(mic.len) - mic.len); 7241da177e4SLinus Torvalds p += XDR_QUADLEN(mic.len); 7251da177e4SLinus Torvalds if (!xdr_ressize_check(rqstp, p)) 7261da177e4SLinus Torvalds return -1; 7271da177e4SLinus Torvalds return 0; 7281da177e4SLinus Torvalds } 7291da177e4SLinus Torvalds 7301da177e4SLinus Torvalds struct gss_domain { 7311da177e4SLinus Torvalds struct auth_domain h; 7321da177e4SLinus Torvalds u32 pseudoflavor; 7331da177e4SLinus Torvalds }; 7341da177e4SLinus Torvalds 7351da177e4SLinus Torvalds static struct auth_domain * 7361da177e4SLinus Torvalds find_gss_auth_domain(struct gss_ctx *ctx, u32 svc) 7371da177e4SLinus Torvalds { 7381da177e4SLinus Torvalds char *name; 7391da177e4SLinus Torvalds 7401da177e4SLinus Torvalds name = gss_service_to_auth_domain_name(ctx->mech_type, svc); 7411da177e4SLinus Torvalds if (!name) 7421da177e4SLinus Torvalds return NULL; 7431da177e4SLinus Torvalds return auth_domain_find(name); 7441da177e4SLinus Torvalds } 7451da177e4SLinus Torvalds 746efc36aa5SNeilBrown static struct auth_ops svcauthops_gss; 747efc36aa5SNeilBrown 7481da177e4SLinus Torvalds int 7491da177e4SLinus Torvalds svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name) 7501da177e4SLinus Torvalds { 7511da177e4SLinus Torvalds struct gss_domain *new; 7521da177e4SLinus Torvalds struct auth_domain *test; 7531da177e4SLinus Torvalds int stat = -ENOMEM; 7541da177e4SLinus Torvalds 7551da177e4SLinus Torvalds new = kmalloc(sizeof(*new), GFP_KERNEL); 7561da177e4SLinus Torvalds if (!new) 7571da177e4SLinus Torvalds goto out; 758efc36aa5SNeilBrown kref_init(&new->h.ref); 7591da177e4SLinus Torvalds new->h.name = kmalloc(strlen(name) + 1, GFP_KERNEL); 7601da177e4SLinus Torvalds if (!new->h.name) 7611da177e4SLinus Torvalds goto out_free_dom; 7621da177e4SLinus Torvalds strcpy(new->h.name, name); 763efc36aa5SNeilBrown new->h.flavour = &svcauthops_gss; 7641da177e4SLinus Torvalds new->pseudoflavor = pseudoflavor; 7651da177e4SLinus Torvalds 766efc36aa5SNeilBrown test = auth_domain_lookup(name, &new->h); 767efc36aa5SNeilBrown if (test != &new->h) { /* XXX Duplicate registration? */ 7681da177e4SLinus Torvalds auth_domain_put(&new->h); 769efc36aa5SNeilBrown /* dangling ref-count... */ 7701da177e4SLinus Torvalds goto out; 7711da177e4SLinus Torvalds } 7721da177e4SLinus Torvalds return 0; 7731da177e4SLinus Torvalds 7741da177e4SLinus Torvalds out_free_dom: 7751da177e4SLinus Torvalds kfree(new); 7761da177e4SLinus Torvalds out: 7771da177e4SLinus Torvalds return stat; 7781da177e4SLinus Torvalds } 7791da177e4SLinus Torvalds 7801da177e4SLinus Torvalds EXPORT_SYMBOL(svcauth_gss_register_pseudoflavor); 7811da177e4SLinus Torvalds 7821da177e4SLinus Torvalds static inline int 7831da177e4SLinus Torvalds read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj) 7841da177e4SLinus Torvalds { 7851da177e4SLinus Torvalds u32 raw; 7861da177e4SLinus Torvalds int status; 7871da177e4SLinus Torvalds 7881da177e4SLinus Torvalds status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj)); 7891da177e4SLinus Torvalds if (status) 7901da177e4SLinus Torvalds return status; 7911da177e4SLinus Torvalds *obj = ntohl(raw); 7921da177e4SLinus Torvalds return 0; 7931da177e4SLinus Torvalds } 7941da177e4SLinus Torvalds 7951da177e4SLinus Torvalds /* It would be nice if this bit of code could be shared with the client. 7961da177e4SLinus Torvalds * Obstacles: 7971da177e4SLinus Torvalds * The client shouldn't malloc(), would have to pass in own memory. 7981da177e4SLinus Torvalds * The server uses base of head iovec as read pointer, while the 7991da177e4SLinus Torvalds * client uses separate pointer. */ 8001da177e4SLinus Torvalds static int 8011da177e4SLinus Torvalds unwrap_integ_data(struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx) 8021da177e4SLinus Torvalds { 8031da177e4SLinus Torvalds int stat = -EINVAL; 8041da177e4SLinus Torvalds u32 integ_len, maj_stat; 8051da177e4SLinus Torvalds struct xdr_netobj mic; 8061da177e4SLinus Torvalds struct xdr_buf integ_buf; 8071da177e4SLinus Torvalds 8081da177e4SLinus Torvalds integ_len = ntohl(svc_getu32(&buf->head[0])); 8091da177e4SLinus Torvalds if (integ_len & 3) 8101da177e4SLinus Torvalds goto out; 8111da177e4SLinus Torvalds if (integ_len > buf->len) 8121da177e4SLinus Torvalds goto out; 8131da177e4SLinus Torvalds if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len)) 8141da177e4SLinus Torvalds BUG(); 8151da177e4SLinus Torvalds /* copy out mic... */ 8161da177e4SLinus Torvalds if (read_u32_from_xdr_buf(buf, integ_len, &mic.len)) 8171da177e4SLinus Torvalds BUG(); 8181da177e4SLinus Torvalds if (mic.len > RPC_MAX_AUTH_SIZE) 8191da177e4SLinus Torvalds goto out; 8201da177e4SLinus Torvalds mic.data = kmalloc(mic.len, GFP_KERNEL); 8211da177e4SLinus Torvalds if (!mic.data) 8221da177e4SLinus Torvalds goto out; 8231da177e4SLinus Torvalds if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len)) 8241da177e4SLinus Torvalds goto out; 82500fd6e14SJ. Bruce Fields maj_stat = gss_verify_mic(ctx, &integ_buf, &mic); 8261da177e4SLinus Torvalds if (maj_stat != GSS_S_COMPLETE) 8271da177e4SLinus Torvalds goto out; 8281da177e4SLinus Torvalds if (ntohl(svc_getu32(&buf->head[0])) != seq) 8291da177e4SLinus Torvalds goto out; 8301da177e4SLinus Torvalds stat = 0; 8311da177e4SLinus Torvalds out: 8321da177e4SLinus Torvalds return stat; 8331da177e4SLinus Torvalds } 8341da177e4SLinus Torvalds 8351da177e4SLinus Torvalds struct gss_svc_data { 8361da177e4SLinus Torvalds /* decoded gss client cred: */ 8371da177e4SLinus Torvalds struct rpc_gss_wire_cred clcred; 8381da177e4SLinus Torvalds /* pointer to the beginning of the procedure-specific results, 8391da177e4SLinus Torvalds * which may be encrypted/checksummed in svcauth_gss_release: */ 8401da177e4SLinus Torvalds u32 *body_start; 8411da177e4SLinus Torvalds struct rsc *rsci; 8421da177e4SLinus Torvalds }; 8431da177e4SLinus Torvalds 8441da177e4SLinus Torvalds static int 8451da177e4SLinus Torvalds svcauth_gss_set_client(struct svc_rqst *rqstp) 8461da177e4SLinus Torvalds { 8471da177e4SLinus Torvalds struct gss_svc_data *svcdata = rqstp->rq_auth_data; 8481da177e4SLinus Torvalds struct rsc *rsci = svcdata->rsci; 8491da177e4SLinus Torvalds struct rpc_gss_wire_cred *gc = &svcdata->clcred; 8501da177e4SLinus Torvalds 8511da177e4SLinus Torvalds rqstp->rq_client = find_gss_auth_domain(rsci->mechctx, gc->gc_svc); 8521da177e4SLinus Torvalds if (rqstp->rq_client == NULL) 8531da177e4SLinus Torvalds return SVC_DENIED; 8541da177e4SLinus Torvalds return SVC_OK; 8551da177e4SLinus Torvalds } 8561da177e4SLinus Torvalds 85791a4762eSKevin Coffman static inline int 85891a4762eSKevin Coffman gss_write_init_verf(struct svc_rqst *rqstp, struct rsi *rsip) 85991a4762eSKevin Coffman { 86091a4762eSKevin Coffman struct rsc *rsci; 86191a4762eSKevin Coffman 86291a4762eSKevin Coffman if (rsip->major_status != GSS_S_COMPLETE) 86391a4762eSKevin Coffman return gss_write_null_verf(rqstp); 86491a4762eSKevin Coffman rsci = gss_svc_searchbyctx(&rsip->out_handle); 86591a4762eSKevin Coffman if (rsci == NULL) { 86691a4762eSKevin Coffman rsip->major_status = GSS_S_NO_CONTEXT; 86791a4762eSKevin Coffman return gss_write_null_verf(rqstp); 86891a4762eSKevin Coffman } 86991a4762eSKevin Coffman return gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN); 87091a4762eSKevin Coffman } 87191a4762eSKevin Coffman 8721da177e4SLinus Torvalds /* 8731da177e4SLinus Torvalds * Accept an rpcsec packet. 8741da177e4SLinus Torvalds * If context establishment, punt to user space 8751da177e4SLinus Torvalds * If data exchange, verify/decrypt 8761da177e4SLinus Torvalds * If context destruction, handle here 8771da177e4SLinus Torvalds * In the context establishment and destruction case we encode 8781da177e4SLinus Torvalds * response here and return SVC_COMPLETE. 8791da177e4SLinus Torvalds */ 8801da177e4SLinus Torvalds static int 8811da177e4SLinus Torvalds svcauth_gss_accept(struct svc_rqst *rqstp, u32 *authp) 8821da177e4SLinus Torvalds { 8831da177e4SLinus Torvalds struct kvec *argv = &rqstp->rq_arg.head[0]; 8841da177e4SLinus Torvalds struct kvec *resv = &rqstp->rq_res.head[0]; 8851da177e4SLinus Torvalds u32 crlen; 8861da177e4SLinus Torvalds struct xdr_netobj tmpobj; 8871da177e4SLinus Torvalds struct gss_svc_data *svcdata = rqstp->rq_auth_data; 8881da177e4SLinus Torvalds struct rpc_gss_wire_cred *gc; 8891da177e4SLinus Torvalds struct rsc *rsci = NULL; 8901da177e4SLinus Torvalds struct rsi *rsip, rsikey; 8911da177e4SLinus Torvalds u32 *rpcstart; 8921da177e4SLinus Torvalds u32 *reject_stat = resv->iov_base + resv->iov_len; 8931da177e4SLinus Torvalds int ret; 8941da177e4SLinus Torvalds 8951da177e4SLinus Torvalds dprintk("RPC: svcauth_gss: argv->iov_len = %zd\n",argv->iov_len); 8961da177e4SLinus Torvalds 8971da177e4SLinus Torvalds *authp = rpc_autherr_badcred; 8981da177e4SLinus Torvalds if (!svcdata) 8991da177e4SLinus Torvalds svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL); 9001da177e4SLinus Torvalds if (!svcdata) 9011da177e4SLinus Torvalds goto auth_err; 9021da177e4SLinus Torvalds rqstp->rq_auth_data = svcdata; 9031da177e4SLinus Torvalds svcdata->body_start = NULL; 9041da177e4SLinus Torvalds svcdata->rsci = NULL; 9051da177e4SLinus Torvalds gc = &svcdata->clcred; 9061da177e4SLinus Torvalds 9071da177e4SLinus Torvalds /* start of rpc packet is 7 u32's back from here: 9081da177e4SLinus Torvalds * xid direction rpcversion prog vers proc flavour 9091da177e4SLinus Torvalds */ 9101da177e4SLinus Torvalds rpcstart = argv->iov_base; 9111da177e4SLinus Torvalds rpcstart -= 7; 9121da177e4SLinus Torvalds 9131da177e4SLinus Torvalds /* credential is: 9141da177e4SLinus Torvalds * version(==1), proc(0,1,2,3), seq, service (1,2,3), handle 9151da177e4SLinus Torvalds * at least 5 u32s, and is preceeded by length, so that makes 6. 9161da177e4SLinus Torvalds */ 9171da177e4SLinus Torvalds 9181da177e4SLinus Torvalds if (argv->iov_len < 5 * 4) 9191da177e4SLinus Torvalds goto auth_err; 9201da177e4SLinus Torvalds crlen = ntohl(svc_getu32(argv)); 9211da177e4SLinus Torvalds if (ntohl(svc_getu32(argv)) != RPC_GSS_VERSION) 9221da177e4SLinus Torvalds goto auth_err; 9231da177e4SLinus Torvalds gc->gc_proc = ntohl(svc_getu32(argv)); 9241da177e4SLinus Torvalds gc->gc_seq = ntohl(svc_getu32(argv)); 9251da177e4SLinus Torvalds gc->gc_svc = ntohl(svc_getu32(argv)); 9261da177e4SLinus Torvalds if (svc_safe_getnetobj(argv, &gc->gc_ctx)) 9271da177e4SLinus Torvalds goto auth_err; 9281da177e4SLinus Torvalds if (crlen != round_up_to_quad(gc->gc_ctx.len) + 5 * 4) 9291da177e4SLinus Torvalds goto auth_err; 9301da177e4SLinus Torvalds 9311da177e4SLinus Torvalds if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0)) 9321da177e4SLinus Torvalds goto auth_err; 9331da177e4SLinus Torvalds 9341da177e4SLinus Torvalds /* 9351da177e4SLinus Torvalds * We've successfully parsed the credential. Let's check out the 9361da177e4SLinus Torvalds * verifier. An AUTH_NULL verifier is allowed (and required) for 9371da177e4SLinus Torvalds * INIT and CONTINUE_INIT requests. AUTH_RPCSEC_GSS is required for 9381da177e4SLinus Torvalds * PROC_DATA and PROC_DESTROY. 9391da177e4SLinus Torvalds * 9401da177e4SLinus Torvalds * AUTH_NULL verifier is 0 (AUTH_NULL), 0 (length). 9411da177e4SLinus Torvalds * AUTH_RPCSEC_GSS verifier is: 9421da177e4SLinus Torvalds * 6 (AUTH_RPCSEC_GSS), length, checksum. 9431da177e4SLinus Torvalds * checksum is calculated over rpcheader from xid up to here. 9441da177e4SLinus Torvalds */ 9451da177e4SLinus Torvalds *authp = rpc_autherr_badverf; 9461da177e4SLinus Torvalds switch (gc->gc_proc) { 9471da177e4SLinus Torvalds case RPC_GSS_PROC_INIT: 9481da177e4SLinus Torvalds case RPC_GSS_PROC_CONTINUE_INIT: 9491da177e4SLinus Torvalds if (argv->iov_len < 2 * 4) 9501da177e4SLinus Torvalds goto auth_err; 9511da177e4SLinus Torvalds if (ntohl(svc_getu32(argv)) != RPC_AUTH_NULL) 9521da177e4SLinus Torvalds goto auth_err; 9531da177e4SLinus Torvalds if (ntohl(svc_getu32(argv)) != 0) 9541da177e4SLinus Torvalds goto auth_err; 9551da177e4SLinus Torvalds break; 9561da177e4SLinus Torvalds case RPC_GSS_PROC_DATA: 9571da177e4SLinus Torvalds case RPC_GSS_PROC_DESTROY: 9581da177e4SLinus Torvalds *authp = rpcsec_gsserr_credproblem; 9591da177e4SLinus Torvalds rsci = gss_svc_searchbyctx(&gc->gc_ctx); 9601da177e4SLinus Torvalds if (!rsci) 9611da177e4SLinus Torvalds goto auth_err; 9621da177e4SLinus Torvalds switch (gss_verify_header(rqstp, rsci, rpcstart, gc, authp)) { 9631da177e4SLinus Torvalds case SVC_OK: 9641da177e4SLinus Torvalds break; 9651da177e4SLinus Torvalds case SVC_DENIED: 9661da177e4SLinus Torvalds goto auth_err; 9671da177e4SLinus Torvalds case SVC_DROP: 9681da177e4SLinus Torvalds goto drop; 9691da177e4SLinus Torvalds } 9701da177e4SLinus Torvalds break; 9711da177e4SLinus Torvalds default: 9721da177e4SLinus Torvalds *authp = rpc_autherr_rejectedcred; 9731da177e4SLinus Torvalds goto auth_err; 9741da177e4SLinus Torvalds } 9751da177e4SLinus Torvalds 9761da177e4SLinus Torvalds /* now act upon the command: */ 9771da177e4SLinus Torvalds switch (gc->gc_proc) { 9781da177e4SLinus Torvalds case RPC_GSS_PROC_INIT: 9791da177e4SLinus Torvalds case RPC_GSS_PROC_CONTINUE_INIT: 9801da177e4SLinus Torvalds *authp = rpc_autherr_badcred; 9811da177e4SLinus Torvalds if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0) 9821da177e4SLinus Torvalds goto auth_err; 9831da177e4SLinus Torvalds memset(&rsikey, 0, sizeof(rsikey)); 9841da177e4SLinus Torvalds if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx)) 9851da177e4SLinus Torvalds goto drop; 9861da177e4SLinus Torvalds *authp = rpc_autherr_badverf; 9871da177e4SLinus Torvalds if (svc_safe_getnetobj(argv, &tmpobj)) { 9881da177e4SLinus Torvalds kfree(rsikey.in_handle.data); 9891da177e4SLinus Torvalds goto auth_err; 9901da177e4SLinus Torvalds } 9911da177e4SLinus Torvalds if (dup_netobj(&rsikey.in_token, &tmpobj)) { 9921da177e4SLinus Torvalds kfree(rsikey.in_handle.data); 9931da177e4SLinus Torvalds goto drop; 9941da177e4SLinus Torvalds } 9951da177e4SLinus Torvalds 996d4d11ea9SNeilBrown rsip = rsi_lookup(&rsikey); 9971da177e4SLinus Torvalds rsi_free(&rsikey); 9981da177e4SLinus Torvalds if (!rsip) { 9991da177e4SLinus Torvalds goto drop; 10001da177e4SLinus Torvalds } 10011da177e4SLinus Torvalds switch(cache_check(&rsi_cache, &rsip->h, &rqstp->rq_chandle)) { 10021da177e4SLinus Torvalds case -EAGAIN: 10031da177e4SLinus Torvalds goto drop; 10041da177e4SLinus Torvalds case -ENOENT: 10051da177e4SLinus Torvalds goto drop; 10061da177e4SLinus Torvalds case 0: 100791a4762eSKevin Coffman if (gss_write_init_verf(rqstp, rsip)) 10081da177e4SLinus Torvalds goto drop; 10091da177e4SLinus Torvalds if (resv->iov_len + 4 > PAGE_SIZE) 10101da177e4SLinus Torvalds goto drop; 10111da177e4SLinus Torvalds svc_putu32(resv, rpc_success); 10121da177e4SLinus Torvalds if (svc_safe_putnetobj(resv, &rsip->out_handle)) 10131da177e4SLinus Torvalds goto drop; 10141da177e4SLinus Torvalds if (resv->iov_len + 3 * 4 > PAGE_SIZE) 10151da177e4SLinus Torvalds goto drop; 10161da177e4SLinus Torvalds svc_putu32(resv, htonl(rsip->major_status)); 10171da177e4SLinus Torvalds svc_putu32(resv, htonl(rsip->minor_status)); 10181da177e4SLinus Torvalds svc_putu32(resv, htonl(GSS_SEQ_WIN)); 10191da177e4SLinus Torvalds if (svc_safe_putnetobj(resv, &rsip->out_token)) 10201da177e4SLinus Torvalds goto drop; 10211da177e4SLinus Torvalds rqstp->rq_client = NULL; 10221da177e4SLinus Torvalds } 10231da177e4SLinus Torvalds goto complete; 10241da177e4SLinus Torvalds case RPC_GSS_PROC_DESTROY: 10251da177e4SLinus Torvalds set_bit(CACHE_NEGATIVE, &rsci->h.flags); 10261da177e4SLinus Torvalds if (resv->iov_len + 4 > PAGE_SIZE) 10271da177e4SLinus Torvalds goto drop; 10281da177e4SLinus Torvalds svc_putu32(resv, rpc_success); 10291da177e4SLinus Torvalds goto complete; 10301da177e4SLinus Torvalds case RPC_GSS_PROC_DATA: 10311da177e4SLinus Torvalds *authp = rpcsec_gsserr_ctxproblem; 10321da177e4SLinus Torvalds if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq)) 10331da177e4SLinus Torvalds goto auth_err; 10341da177e4SLinus Torvalds rqstp->rq_cred = rsci->cred; 10351da177e4SLinus Torvalds get_group_info(rsci->cred.cr_group_info); 10361da177e4SLinus Torvalds *authp = rpc_autherr_badcred; 10371da177e4SLinus Torvalds switch (gc->gc_svc) { 10381da177e4SLinus Torvalds case RPC_GSS_SVC_NONE: 10391da177e4SLinus Torvalds break; 10401da177e4SLinus Torvalds case RPC_GSS_SVC_INTEGRITY: 10411da177e4SLinus Torvalds if (unwrap_integ_data(&rqstp->rq_arg, 10421da177e4SLinus Torvalds gc->gc_seq, rsci->mechctx)) 10431da177e4SLinus Torvalds goto auth_err; 10441da177e4SLinus Torvalds /* placeholders for length and seq. number: */ 10451da177e4SLinus Torvalds svcdata->body_start = resv->iov_base + resv->iov_len; 10461da177e4SLinus Torvalds svc_putu32(resv, 0); 10471da177e4SLinus Torvalds svc_putu32(resv, 0); 10481da177e4SLinus Torvalds break; 10491da177e4SLinus Torvalds case RPC_GSS_SVC_PRIVACY: 10501da177e4SLinus Torvalds /* currently unsupported */ 10511da177e4SLinus Torvalds default: 10521da177e4SLinus Torvalds goto auth_err; 10531da177e4SLinus Torvalds } 10541da177e4SLinus Torvalds svcdata->rsci = rsci; 10551da177e4SLinus Torvalds cache_get(&rsci->h); 10561da177e4SLinus Torvalds ret = SVC_OK; 10571da177e4SLinus Torvalds goto out; 10581da177e4SLinus Torvalds } 10591da177e4SLinus Torvalds auth_err: 10601da177e4SLinus Torvalds /* Restore write pointer to original value: */ 10611da177e4SLinus Torvalds xdr_ressize_check(rqstp, reject_stat); 10621da177e4SLinus Torvalds ret = SVC_DENIED; 10631da177e4SLinus Torvalds goto out; 10641da177e4SLinus Torvalds complete: 10651da177e4SLinus Torvalds ret = SVC_COMPLETE; 10661da177e4SLinus Torvalds goto out; 10671da177e4SLinus Torvalds drop: 10681da177e4SLinus Torvalds ret = SVC_DROP; 10691da177e4SLinus Torvalds out: 10701da177e4SLinus Torvalds if (rsci) 1071baab935fSNeilBrown cache_put(&rsci->h, &rsc_cache); 10721da177e4SLinus Torvalds return ret; 10731da177e4SLinus Torvalds } 10741da177e4SLinus Torvalds 10751da177e4SLinus Torvalds static int 10761da177e4SLinus Torvalds svcauth_gss_release(struct svc_rqst *rqstp) 10771da177e4SLinus Torvalds { 10781da177e4SLinus Torvalds struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data; 10791da177e4SLinus Torvalds struct rpc_gss_wire_cred *gc = &gsd->clcred; 10801da177e4SLinus Torvalds struct xdr_buf *resbuf = &rqstp->rq_res; 10811da177e4SLinus Torvalds struct xdr_buf integ_buf; 10821da177e4SLinus Torvalds struct xdr_netobj mic; 10831da177e4SLinus Torvalds struct kvec *resv; 10841da177e4SLinus Torvalds u32 *p; 10851da177e4SLinus Torvalds int integ_offset, integ_len; 10861da177e4SLinus Torvalds int stat = -EINVAL; 10871da177e4SLinus Torvalds 10881da177e4SLinus Torvalds if (gc->gc_proc != RPC_GSS_PROC_DATA) 10891da177e4SLinus Torvalds goto out; 10901da177e4SLinus Torvalds /* Release can be called twice, but we only wrap once. */ 10911da177e4SLinus Torvalds if (gsd->body_start == NULL) 10921da177e4SLinus Torvalds goto out; 10931da177e4SLinus Torvalds /* normally not set till svc_send, but we need it here: */ 10941da177e4SLinus Torvalds resbuf->len = resbuf->head[0].iov_len 10951da177e4SLinus Torvalds + resbuf->page_len + resbuf->tail[0].iov_len; 10961da177e4SLinus Torvalds switch (gc->gc_svc) { 10971da177e4SLinus Torvalds case RPC_GSS_SVC_NONE: 10981da177e4SLinus Torvalds break; 10991da177e4SLinus Torvalds case RPC_GSS_SVC_INTEGRITY: 11001da177e4SLinus Torvalds p = gsd->body_start; 11011da177e4SLinus Torvalds gsd->body_start = NULL; 11021da177e4SLinus Torvalds /* move accept_stat to right place: */ 11031da177e4SLinus Torvalds memcpy(p, p + 2, 4); 11041da177e4SLinus Torvalds /* don't wrap in failure case: */ 11051da177e4SLinus Torvalds /* Note: counting on not getting here if call was not even 11061da177e4SLinus Torvalds * accepted! */ 11071da177e4SLinus Torvalds if (*p != rpc_success) { 11081da177e4SLinus Torvalds resbuf->head[0].iov_len -= 2 * 4; 11091da177e4SLinus Torvalds goto out; 11101da177e4SLinus Torvalds } 11111da177e4SLinus Torvalds p++; 11121da177e4SLinus Torvalds integ_offset = (u8 *)(p + 1) - (u8 *)resbuf->head[0].iov_base; 11131da177e4SLinus Torvalds integ_len = resbuf->len - integ_offset; 11141da177e4SLinus Torvalds BUG_ON(integ_len % 4); 11151da177e4SLinus Torvalds *p++ = htonl(integ_len); 11161da177e4SLinus Torvalds *p++ = htonl(gc->gc_seq); 11171da177e4SLinus Torvalds if (xdr_buf_subsegment(resbuf, &integ_buf, integ_offset, 11181da177e4SLinus Torvalds integ_len)) 11191da177e4SLinus Torvalds BUG(); 11201da177e4SLinus Torvalds if (resbuf->page_len == 0 1121dfee55f0SNeilBrown && resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE 11221da177e4SLinus Torvalds < PAGE_SIZE) { 11231da177e4SLinus Torvalds BUG_ON(resbuf->tail[0].iov_len); 11241da177e4SLinus Torvalds /* Use head for everything */ 11251da177e4SLinus Torvalds resv = &resbuf->head[0]; 11261da177e4SLinus Torvalds } else if (resbuf->tail[0].iov_base == NULL) { 1127dfee55f0SNeilBrown if (resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE 1128dfee55f0SNeilBrown > PAGE_SIZE) 1129dfee55f0SNeilBrown goto out_err; 1130dfee55f0SNeilBrown resbuf->tail[0].iov_base = 1131dfee55f0SNeilBrown resbuf->head[0].iov_base 1132dfee55f0SNeilBrown + resbuf->head[0].iov_len; 11331da177e4SLinus Torvalds resbuf->tail[0].iov_len = 0; 1134dfee55f0SNeilBrown rqstp->rq_restailpage = 0; 11351da177e4SLinus Torvalds resv = &resbuf->tail[0]; 11361da177e4SLinus Torvalds } else { 11371da177e4SLinus Torvalds resv = &resbuf->tail[0]; 11381da177e4SLinus Torvalds } 11391da177e4SLinus Torvalds mic.data = (u8 *)resv->iov_base + resv->iov_len + 4; 114000fd6e14SJ. Bruce Fields if (gss_get_mic(gsd->rsci->mechctx, &integ_buf, &mic)) 11411da177e4SLinus Torvalds goto out_err; 11421da177e4SLinus Torvalds svc_putu32(resv, htonl(mic.len)); 11431da177e4SLinus Torvalds memset(mic.data + mic.len, 0, 11441da177e4SLinus Torvalds round_up_to_quad(mic.len) - mic.len); 11451da177e4SLinus Torvalds resv->iov_len += XDR_QUADLEN(mic.len) << 2; 11461da177e4SLinus Torvalds /* not strictly required: */ 11471da177e4SLinus Torvalds resbuf->len += XDR_QUADLEN(mic.len) << 2; 11481da177e4SLinus Torvalds BUG_ON(resv->iov_len > PAGE_SIZE); 11491da177e4SLinus Torvalds break; 11501da177e4SLinus Torvalds case RPC_GSS_SVC_PRIVACY: 11511da177e4SLinus Torvalds default: 11521da177e4SLinus Torvalds goto out_err; 11531da177e4SLinus Torvalds } 11541da177e4SLinus Torvalds 11551da177e4SLinus Torvalds out: 11561da177e4SLinus Torvalds stat = 0; 11571da177e4SLinus Torvalds out_err: 11581da177e4SLinus Torvalds if (rqstp->rq_client) 11591da177e4SLinus Torvalds auth_domain_put(rqstp->rq_client); 11601da177e4SLinus Torvalds rqstp->rq_client = NULL; 11611da177e4SLinus Torvalds if (rqstp->rq_cred.cr_group_info) 11621da177e4SLinus Torvalds put_group_info(rqstp->rq_cred.cr_group_info); 11631da177e4SLinus Torvalds rqstp->rq_cred.cr_group_info = NULL; 11641da177e4SLinus Torvalds if (gsd->rsci) 1165baab935fSNeilBrown cache_put(&gsd->rsci->h, &rsc_cache); 11661da177e4SLinus Torvalds gsd->rsci = NULL; 11671da177e4SLinus Torvalds 11681da177e4SLinus Torvalds return stat; 11691da177e4SLinus Torvalds } 11701da177e4SLinus Torvalds 11711da177e4SLinus Torvalds static void 11721da177e4SLinus Torvalds svcauth_gss_domain_release(struct auth_domain *dom) 11731da177e4SLinus Torvalds { 11741da177e4SLinus Torvalds struct gss_domain *gd = container_of(dom, struct gss_domain, h); 11751da177e4SLinus Torvalds 11761da177e4SLinus Torvalds kfree(dom->name); 11771da177e4SLinus Torvalds kfree(gd); 11781da177e4SLinus Torvalds } 11791da177e4SLinus Torvalds 11801da177e4SLinus Torvalds static struct auth_ops svcauthops_gss = { 11811da177e4SLinus Torvalds .name = "rpcsec_gss", 11821da177e4SLinus Torvalds .owner = THIS_MODULE, 11831da177e4SLinus Torvalds .flavour = RPC_AUTH_GSS, 11841da177e4SLinus Torvalds .accept = svcauth_gss_accept, 11851da177e4SLinus Torvalds .release = svcauth_gss_release, 11861da177e4SLinus Torvalds .domain_release = svcauth_gss_domain_release, 11871da177e4SLinus Torvalds .set_client = svcauth_gss_set_client, 11881da177e4SLinus Torvalds }; 11891da177e4SLinus Torvalds 11901da177e4SLinus Torvalds int 11911da177e4SLinus Torvalds gss_svc_init(void) 11921da177e4SLinus Torvalds { 11931da177e4SLinus Torvalds int rv = svc_auth_register(RPC_AUTH_GSS, &svcauthops_gss); 11941da177e4SLinus Torvalds if (rv == 0) { 11951da177e4SLinus Torvalds cache_register(&rsc_cache); 11961da177e4SLinus Torvalds cache_register(&rsi_cache); 11971da177e4SLinus Torvalds } 11981da177e4SLinus Torvalds return rv; 11991da177e4SLinus Torvalds } 12001da177e4SLinus Torvalds 12011da177e4SLinus Torvalds void 12021da177e4SLinus Torvalds gss_svc_shutdown(void) 12031da177e4SLinus Torvalds { 1204f35279d3SBruce Allan if (cache_unregister(&rsc_cache)) 1205f35279d3SBruce Allan printk(KERN_ERR "auth_rpcgss: failed to unregister rsc cache\n"); 1206f35279d3SBruce Allan if (cache_unregister(&rsi_cache)) 1207f35279d3SBruce Allan printk(KERN_ERR "auth_rpcgss: failed to unregister rsi cache\n"); 12081da177e4SLinus Torvalds svc_auth_unregister(RPC_AUTH_GSS); 12091da177e4SLinus Torvalds } 1210