162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Neil Brown <neilb@cse.unsw.edu.au> 462306a36Sopenharmony_ci * J. Bruce Fields <bfields@umich.edu> 562306a36Sopenharmony_ci * Andy Adamson <andros@umich.edu> 662306a36Sopenharmony_ci * Dug Song <dugsong@monkey.org> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * RPCSEC_GSS server authentication. 962306a36Sopenharmony_ci * This implements RPCSEC_GSS as defined in rfc2203 (rpcsec_gss) and rfc2078 1062306a36Sopenharmony_ci * (gssapi) 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * The RPCSEC_GSS involves three stages: 1362306a36Sopenharmony_ci * 1/ context creation 1462306a36Sopenharmony_ci * 2/ data exchange 1562306a36Sopenharmony_ci * 3/ context destruction 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * Context creation is handled largely by upcalls to user-space. 1862306a36Sopenharmony_ci * In particular, GSS_Accept_sec_context is handled by an upcall 1962306a36Sopenharmony_ci * Data exchange is handled entirely within the kernel 2062306a36Sopenharmony_ci * In particular, GSS_GetMIC, GSS_VerifyMIC, GSS_Seal, GSS_Unseal are in-kernel. 2162306a36Sopenharmony_ci * Context destruction is handled in-kernel 2262306a36Sopenharmony_ci * GSS_Delete_sec_context is in-kernel 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * Context creation is initiated by a RPCSEC_GSS_INIT request arriving. 2562306a36Sopenharmony_ci * The context handle and gss_token are used as a key into the rpcsec_init cache. 2662306a36Sopenharmony_ci * The content of this cache includes some of the outputs of GSS_Accept_sec_context, 2762306a36Sopenharmony_ci * being major_status, minor_status, context_handle, reply_token. 2862306a36Sopenharmony_ci * These are sent back to the client. 2962306a36Sopenharmony_ci * Sequence window management is handled by the kernel. The window size if currently 3062306a36Sopenharmony_ci * a compile time constant. 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * When user-space is happy that a context is established, it places an entry 3362306a36Sopenharmony_ci * in the rpcsec_context cache. The key for this cache is the context_handle. 3462306a36Sopenharmony_ci * The content includes: 3562306a36Sopenharmony_ci * uid/gidlist - for determining access rights 3662306a36Sopenharmony_ci * mechanism type 3762306a36Sopenharmony_ci * mechanism specific information, such as a key 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#include <linux/slab.h> 4262306a36Sopenharmony_ci#include <linux/types.h> 4362306a36Sopenharmony_ci#include <linux/module.h> 4462306a36Sopenharmony_ci#include <linux/pagemap.h> 4562306a36Sopenharmony_ci#include <linux/user_namespace.h> 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#include <linux/sunrpc/auth_gss.h> 4862306a36Sopenharmony_ci#include <linux/sunrpc/gss_err.h> 4962306a36Sopenharmony_ci#include <linux/sunrpc/svcauth.h> 5062306a36Sopenharmony_ci#include <linux/sunrpc/svcauth_gss.h> 5162306a36Sopenharmony_ci#include <linux/sunrpc/cache.h> 5262306a36Sopenharmony_ci#include <linux/sunrpc/gss_krb5.h> 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#include <trace/events/rpcgss.h> 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#include "gss_rpc_upcall.h" 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * Unfortunately there isn't a maximum checksum size exported via the 6062306a36Sopenharmony_ci * GSS API. Manufacture one based on GSS mechanisms supported by this 6162306a36Sopenharmony_ci * implementation. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci#define GSS_MAX_CKSUMSIZE (GSS_KRB5_TOK_HDR_LEN + GSS_KRB5_MAX_CKSUM_LEN) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* 6662306a36Sopenharmony_ci * This value may be increased in the future to accommodate other 6762306a36Sopenharmony_ci * usage of the scratch buffer. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ci#define GSS_SCRATCH_SIZE GSS_MAX_CKSUMSIZE 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistruct gss_svc_data { 7262306a36Sopenharmony_ci /* decoded gss client cred: */ 7362306a36Sopenharmony_ci struct rpc_gss_wire_cred clcred; 7462306a36Sopenharmony_ci u32 gsd_databody_offset; 7562306a36Sopenharmony_ci struct rsc *rsci; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* for temporary results */ 7862306a36Sopenharmony_ci __be32 gsd_seq_num; 7962306a36Sopenharmony_ci u8 gsd_scratch[GSS_SCRATCH_SIZE]; 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* The rpcsec_init cache is used for mapping RPCSEC_GSS_{,CONT_}INIT requests 8362306a36Sopenharmony_ci * into replies. 8462306a36Sopenharmony_ci * 8562306a36Sopenharmony_ci * Key is context handle (\x if empty) and gss_token. 8662306a36Sopenharmony_ci * Content is major_status minor_status (integers) context_handle, reply_token. 8762306a36Sopenharmony_ci * 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic int netobj_equal(struct xdr_netobj *a, struct xdr_netobj *b) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci return a->len == b->len && 0 == memcmp(a->data, b->data, a->len); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define RSI_HASHBITS 6 9662306a36Sopenharmony_ci#define RSI_HASHMAX (1<<RSI_HASHBITS) 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistruct rsi { 9962306a36Sopenharmony_ci struct cache_head h; 10062306a36Sopenharmony_ci struct xdr_netobj in_handle, in_token; 10162306a36Sopenharmony_ci struct xdr_netobj out_handle, out_token; 10262306a36Sopenharmony_ci int major_status, minor_status; 10362306a36Sopenharmony_ci struct rcu_head rcu_head; 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic struct rsi *rsi_update(struct cache_detail *cd, struct rsi *new, struct rsi *old); 10762306a36Sopenharmony_cistatic struct rsi *rsi_lookup(struct cache_detail *cd, struct rsi *item); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic void rsi_free(struct rsi *rsii) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci kfree(rsii->in_handle.data); 11262306a36Sopenharmony_ci kfree(rsii->in_token.data); 11362306a36Sopenharmony_ci kfree(rsii->out_handle.data); 11462306a36Sopenharmony_ci kfree(rsii->out_token.data); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void rsi_free_rcu(struct rcu_head *head) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct rsi *rsii = container_of(head, struct rsi, rcu_head); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci rsi_free(rsii); 12262306a36Sopenharmony_ci kfree(rsii); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic void rsi_put(struct kref *ref) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct rsi *rsii = container_of(ref, struct rsi, h.ref); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci call_rcu(&rsii->rcu_head, rsi_free_rcu); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic inline int rsi_hash(struct rsi *item) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci return hash_mem(item->in_handle.data, item->in_handle.len, RSI_HASHBITS) 13562306a36Sopenharmony_ci ^ hash_mem(item->in_token.data, item->in_token.len, RSI_HASHBITS); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic int rsi_match(struct cache_head *a, struct cache_head *b) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct rsi *item = container_of(a, struct rsi, h); 14162306a36Sopenharmony_ci struct rsi *tmp = container_of(b, struct rsi, h); 14262306a36Sopenharmony_ci return netobj_equal(&item->in_handle, &tmp->in_handle) && 14362306a36Sopenharmony_ci netobj_equal(&item->in_token, &tmp->in_token); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic int dup_to_netobj(struct xdr_netobj *dst, char *src, int len) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci dst->len = len; 14962306a36Sopenharmony_ci dst->data = (len ? kmemdup(src, len, GFP_KERNEL) : NULL); 15062306a36Sopenharmony_ci if (len && !dst->data) 15162306a36Sopenharmony_ci return -ENOMEM; 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic inline int dup_netobj(struct xdr_netobj *dst, struct xdr_netobj *src) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci return dup_to_netobj(dst, src->data, src->len); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic void rsi_init(struct cache_head *cnew, struct cache_head *citem) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct rsi *new = container_of(cnew, struct rsi, h); 16362306a36Sopenharmony_ci struct rsi *item = container_of(citem, struct rsi, h); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci new->out_handle.data = NULL; 16662306a36Sopenharmony_ci new->out_handle.len = 0; 16762306a36Sopenharmony_ci new->out_token.data = NULL; 16862306a36Sopenharmony_ci new->out_token.len = 0; 16962306a36Sopenharmony_ci new->in_handle.len = item->in_handle.len; 17062306a36Sopenharmony_ci item->in_handle.len = 0; 17162306a36Sopenharmony_ci new->in_token.len = item->in_token.len; 17262306a36Sopenharmony_ci item->in_token.len = 0; 17362306a36Sopenharmony_ci new->in_handle.data = item->in_handle.data; 17462306a36Sopenharmony_ci item->in_handle.data = NULL; 17562306a36Sopenharmony_ci new->in_token.data = item->in_token.data; 17662306a36Sopenharmony_ci item->in_token.data = NULL; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic void update_rsi(struct cache_head *cnew, struct cache_head *citem) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct rsi *new = container_of(cnew, struct rsi, h); 18262306a36Sopenharmony_ci struct rsi *item = container_of(citem, struct rsi, h); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci BUG_ON(new->out_handle.data || new->out_token.data); 18562306a36Sopenharmony_ci new->out_handle.len = item->out_handle.len; 18662306a36Sopenharmony_ci item->out_handle.len = 0; 18762306a36Sopenharmony_ci new->out_token.len = item->out_token.len; 18862306a36Sopenharmony_ci item->out_token.len = 0; 18962306a36Sopenharmony_ci new->out_handle.data = item->out_handle.data; 19062306a36Sopenharmony_ci item->out_handle.data = NULL; 19162306a36Sopenharmony_ci new->out_token.data = item->out_token.data; 19262306a36Sopenharmony_ci item->out_token.data = NULL; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci new->major_status = item->major_status; 19562306a36Sopenharmony_ci new->minor_status = item->minor_status; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic struct cache_head *rsi_alloc(void) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct rsi *rsii = kmalloc(sizeof(*rsii), GFP_KERNEL); 20162306a36Sopenharmony_ci if (rsii) 20262306a36Sopenharmony_ci return &rsii->h; 20362306a36Sopenharmony_ci else 20462306a36Sopenharmony_ci return NULL; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int rsi_upcall(struct cache_detail *cd, struct cache_head *h) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci return sunrpc_cache_pipe_upcall_timeout(cd, h); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic void rsi_request(struct cache_detail *cd, 21362306a36Sopenharmony_ci struct cache_head *h, 21462306a36Sopenharmony_ci char **bpp, int *blen) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci struct rsi *rsii = container_of(h, struct rsi, h); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci qword_addhex(bpp, blen, rsii->in_handle.data, rsii->in_handle.len); 21962306a36Sopenharmony_ci qword_addhex(bpp, blen, rsii->in_token.data, rsii->in_token.len); 22062306a36Sopenharmony_ci (*bpp)[-1] = '\n'; 22162306a36Sopenharmony_ci WARN_ONCE(*blen < 0, 22262306a36Sopenharmony_ci "RPCSEC/GSS credential too large - please use gssproxy\n"); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic int rsi_parse(struct cache_detail *cd, 22662306a36Sopenharmony_ci char *mesg, int mlen) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci /* context token expiry major minor context token */ 22962306a36Sopenharmony_ci char *buf = mesg; 23062306a36Sopenharmony_ci char *ep; 23162306a36Sopenharmony_ci int len; 23262306a36Sopenharmony_ci struct rsi rsii, *rsip = NULL; 23362306a36Sopenharmony_ci time64_t expiry; 23462306a36Sopenharmony_ci int status = -EINVAL; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci memset(&rsii, 0, sizeof(rsii)); 23762306a36Sopenharmony_ci /* handle */ 23862306a36Sopenharmony_ci len = qword_get(&mesg, buf, mlen); 23962306a36Sopenharmony_ci if (len < 0) 24062306a36Sopenharmony_ci goto out; 24162306a36Sopenharmony_ci status = -ENOMEM; 24262306a36Sopenharmony_ci if (dup_to_netobj(&rsii.in_handle, buf, len)) 24362306a36Sopenharmony_ci goto out; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* token */ 24662306a36Sopenharmony_ci len = qword_get(&mesg, buf, mlen); 24762306a36Sopenharmony_ci status = -EINVAL; 24862306a36Sopenharmony_ci if (len < 0) 24962306a36Sopenharmony_ci goto out; 25062306a36Sopenharmony_ci status = -ENOMEM; 25162306a36Sopenharmony_ci if (dup_to_netobj(&rsii.in_token, buf, len)) 25262306a36Sopenharmony_ci goto out; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci rsip = rsi_lookup(cd, &rsii); 25562306a36Sopenharmony_ci if (!rsip) 25662306a36Sopenharmony_ci goto out; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci rsii.h.flags = 0; 25962306a36Sopenharmony_ci /* expiry */ 26062306a36Sopenharmony_ci status = get_expiry(&mesg, &expiry); 26162306a36Sopenharmony_ci if (status) 26262306a36Sopenharmony_ci goto out; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci status = -EINVAL; 26562306a36Sopenharmony_ci /* major/minor */ 26662306a36Sopenharmony_ci len = qword_get(&mesg, buf, mlen); 26762306a36Sopenharmony_ci if (len <= 0) 26862306a36Sopenharmony_ci goto out; 26962306a36Sopenharmony_ci rsii.major_status = simple_strtoul(buf, &ep, 10); 27062306a36Sopenharmony_ci if (*ep) 27162306a36Sopenharmony_ci goto out; 27262306a36Sopenharmony_ci len = qword_get(&mesg, buf, mlen); 27362306a36Sopenharmony_ci if (len <= 0) 27462306a36Sopenharmony_ci goto out; 27562306a36Sopenharmony_ci rsii.minor_status = simple_strtoul(buf, &ep, 10); 27662306a36Sopenharmony_ci if (*ep) 27762306a36Sopenharmony_ci goto out; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* out_handle */ 28062306a36Sopenharmony_ci len = qword_get(&mesg, buf, mlen); 28162306a36Sopenharmony_ci if (len < 0) 28262306a36Sopenharmony_ci goto out; 28362306a36Sopenharmony_ci status = -ENOMEM; 28462306a36Sopenharmony_ci if (dup_to_netobj(&rsii.out_handle, buf, len)) 28562306a36Sopenharmony_ci goto out; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* out_token */ 28862306a36Sopenharmony_ci len = qword_get(&mesg, buf, mlen); 28962306a36Sopenharmony_ci status = -EINVAL; 29062306a36Sopenharmony_ci if (len < 0) 29162306a36Sopenharmony_ci goto out; 29262306a36Sopenharmony_ci status = -ENOMEM; 29362306a36Sopenharmony_ci if (dup_to_netobj(&rsii.out_token, buf, len)) 29462306a36Sopenharmony_ci goto out; 29562306a36Sopenharmony_ci rsii.h.expiry_time = expiry; 29662306a36Sopenharmony_ci rsip = rsi_update(cd, &rsii, rsip); 29762306a36Sopenharmony_ci status = 0; 29862306a36Sopenharmony_ciout: 29962306a36Sopenharmony_ci rsi_free(&rsii); 30062306a36Sopenharmony_ci if (rsip) 30162306a36Sopenharmony_ci cache_put(&rsip->h, cd); 30262306a36Sopenharmony_ci else 30362306a36Sopenharmony_ci status = -ENOMEM; 30462306a36Sopenharmony_ci return status; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic const struct cache_detail rsi_cache_template = { 30862306a36Sopenharmony_ci .owner = THIS_MODULE, 30962306a36Sopenharmony_ci .hash_size = RSI_HASHMAX, 31062306a36Sopenharmony_ci .name = "auth.rpcsec.init", 31162306a36Sopenharmony_ci .cache_put = rsi_put, 31262306a36Sopenharmony_ci .cache_upcall = rsi_upcall, 31362306a36Sopenharmony_ci .cache_request = rsi_request, 31462306a36Sopenharmony_ci .cache_parse = rsi_parse, 31562306a36Sopenharmony_ci .match = rsi_match, 31662306a36Sopenharmony_ci .init = rsi_init, 31762306a36Sopenharmony_ci .update = update_rsi, 31862306a36Sopenharmony_ci .alloc = rsi_alloc, 31962306a36Sopenharmony_ci}; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic struct rsi *rsi_lookup(struct cache_detail *cd, struct rsi *item) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct cache_head *ch; 32462306a36Sopenharmony_ci int hash = rsi_hash(item); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci ch = sunrpc_cache_lookup_rcu(cd, &item->h, hash); 32762306a36Sopenharmony_ci if (ch) 32862306a36Sopenharmony_ci return container_of(ch, struct rsi, h); 32962306a36Sopenharmony_ci else 33062306a36Sopenharmony_ci return NULL; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic struct rsi *rsi_update(struct cache_detail *cd, struct rsi *new, struct rsi *old) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct cache_head *ch; 33662306a36Sopenharmony_ci int hash = rsi_hash(new); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci ch = sunrpc_cache_update(cd, &new->h, 33962306a36Sopenharmony_ci &old->h, hash); 34062306a36Sopenharmony_ci if (ch) 34162306a36Sopenharmony_ci return container_of(ch, struct rsi, h); 34262306a36Sopenharmony_ci else 34362306a36Sopenharmony_ci return NULL; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci/* 34862306a36Sopenharmony_ci * The rpcsec_context cache is used to store a context that is 34962306a36Sopenharmony_ci * used in data exchange. 35062306a36Sopenharmony_ci * The key is a context handle. The content is: 35162306a36Sopenharmony_ci * uid, gidlist, mechanism, service-set, mech-specific-data 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci#define RSC_HASHBITS 10 35562306a36Sopenharmony_ci#define RSC_HASHMAX (1<<RSC_HASHBITS) 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci#define GSS_SEQ_WIN 128 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistruct gss_svc_seq_data { 36062306a36Sopenharmony_ci /* highest seq number seen so far: */ 36162306a36Sopenharmony_ci u32 sd_max; 36262306a36Sopenharmony_ci /* for i such that sd_max-GSS_SEQ_WIN < i <= sd_max, the i-th bit of 36362306a36Sopenharmony_ci * sd_win is nonzero iff sequence number i has been seen already: */ 36462306a36Sopenharmony_ci unsigned long sd_win[GSS_SEQ_WIN/BITS_PER_LONG]; 36562306a36Sopenharmony_ci spinlock_t sd_lock; 36662306a36Sopenharmony_ci}; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistruct rsc { 36962306a36Sopenharmony_ci struct cache_head h; 37062306a36Sopenharmony_ci struct xdr_netobj handle; 37162306a36Sopenharmony_ci struct svc_cred cred; 37262306a36Sopenharmony_ci struct gss_svc_seq_data seqdata; 37362306a36Sopenharmony_ci struct gss_ctx *mechctx; 37462306a36Sopenharmony_ci struct rcu_head rcu_head; 37562306a36Sopenharmony_ci}; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old); 37862306a36Sopenharmony_cistatic struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic void rsc_free(struct rsc *rsci) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci kfree(rsci->handle.data); 38362306a36Sopenharmony_ci if (rsci->mechctx) 38462306a36Sopenharmony_ci gss_delete_sec_context(&rsci->mechctx); 38562306a36Sopenharmony_ci free_svc_cred(&rsci->cred); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic void rsc_free_rcu(struct rcu_head *head) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci struct rsc *rsci = container_of(head, struct rsc, rcu_head); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci kfree(rsci->handle.data); 39362306a36Sopenharmony_ci kfree(rsci); 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic void rsc_put(struct kref *ref) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct rsc *rsci = container_of(ref, struct rsc, h.ref); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (rsci->mechctx) 40162306a36Sopenharmony_ci gss_delete_sec_context(&rsci->mechctx); 40262306a36Sopenharmony_ci free_svc_cred(&rsci->cred); 40362306a36Sopenharmony_ci call_rcu(&rsci->rcu_head, rsc_free_rcu); 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic inline int 40762306a36Sopenharmony_cirsc_hash(struct rsc *rsci) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci return hash_mem(rsci->handle.data, rsci->handle.len, RSC_HASHBITS); 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic int 41362306a36Sopenharmony_cirsc_match(struct cache_head *a, struct cache_head *b) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci struct rsc *new = container_of(a, struct rsc, h); 41662306a36Sopenharmony_ci struct rsc *tmp = container_of(b, struct rsc, h); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci return netobj_equal(&new->handle, &tmp->handle); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic void 42262306a36Sopenharmony_cirsc_init(struct cache_head *cnew, struct cache_head *ctmp) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct rsc *new = container_of(cnew, struct rsc, h); 42562306a36Sopenharmony_ci struct rsc *tmp = container_of(ctmp, struct rsc, h); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci new->handle.len = tmp->handle.len; 42862306a36Sopenharmony_ci tmp->handle.len = 0; 42962306a36Sopenharmony_ci new->handle.data = tmp->handle.data; 43062306a36Sopenharmony_ci tmp->handle.data = NULL; 43162306a36Sopenharmony_ci new->mechctx = NULL; 43262306a36Sopenharmony_ci init_svc_cred(&new->cred); 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic void 43662306a36Sopenharmony_ciupdate_rsc(struct cache_head *cnew, struct cache_head *ctmp) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct rsc *new = container_of(cnew, struct rsc, h); 43962306a36Sopenharmony_ci struct rsc *tmp = container_of(ctmp, struct rsc, h); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci new->mechctx = tmp->mechctx; 44262306a36Sopenharmony_ci tmp->mechctx = NULL; 44362306a36Sopenharmony_ci memset(&new->seqdata, 0, sizeof(new->seqdata)); 44462306a36Sopenharmony_ci spin_lock_init(&new->seqdata.sd_lock); 44562306a36Sopenharmony_ci new->cred = tmp->cred; 44662306a36Sopenharmony_ci init_svc_cred(&tmp->cred); 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic struct cache_head * 45062306a36Sopenharmony_cirsc_alloc(void) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct rsc *rsci = kmalloc(sizeof(*rsci), GFP_KERNEL); 45362306a36Sopenharmony_ci if (rsci) 45462306a36Sopenharmony_ci return &rsci->h; 45562306a36Sopenharmony_ci else 45662306a36Sopenharmony_ci return NULL; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic int rsc_upcall(struct cache_detail *cd, struct cache_head *h) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci return -EINVAL; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic int rsc_parse(struct cache_detail *cd, 46562306a36Sopenharmony_ci char *mesg, int mlen) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci /* contexthandle expiry [ uid gid N <n gids> mechname ...mechdata... ] */ 46862306a36Sopenharmony_ci char *buf = mesg; 46962306a36Sopenharmony_ci int id; 47062306a36Sopenharmony_ci int len, rv; 47162306a36Sopenharmony_ci struct rsc rsci, *rscp = NULL; 47262306a36Sopenharmony_ci time64_t expiry; 47362306a36Sopenharmony_ci int status = -EINVAL; 47462306a36Sopenharmony_ci struct gss_api_mech *gm = NULL; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci memset(&rsci, 0, sizeof(rsci)); 47762306a36Sopenharmony_ci /* context handle */ 47862306a36Sopenharmony_ci len = qword_get(&mesg, buf, mlen); 47962306a36Sopenharmony_ci if (len < 0) goto out; 48062306a36Sopenharmony_ci status = -ENOMEM; 48162306a36Sopenharmony_ci if (dup_to_netobj(&rsci.handle, buf, len)) 48262306a36Sopenharmony_ci goto out; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci rsci.h.flags = 0; 48562306a36Sopenharmony_ci /* expiry */ 48662306a36Sopenharmony_ci status = get_expiry(&mesg, &expiry); 48762306a36Sopenharmony_ci if (status) 48862306a36Sopenharmony_ci goto out; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci status = -EINVAL; 49162306a36Sopenharmony_ci rscp = rsc_lookup(cd, &rsci); 49262306a36Sopenharmony_ci if (!rscp) 49362306a36Sopenharmony_ci goto out; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* uid, or NEGATIVE */ 49662306a36Sopenharmony_ci rv = get_int(&mesg, &id); 49762306a36Sopenharmony_ci if (rv == -EINVAL) 49862306a36Sopenharmony_ci goto out; 49962306a36Sopenharmony_ci if (rv == -ENOENT) 50062306a36Sopenharmony_ci set_bit(CACHE_NEGATIVE, &rsci.h.flags); 50162306a36Sopenharmony_ci else { 50262306a36Sopenharmony_ci int N, i; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* 50562306a36Sopenharmony_ci * NOTE: we skip uid_valid()/gid_valid() checks here: 50662306a36Sopenharmony_ci * instead, * -1 id's are later mapped to the 50762306a36Sopenharmony_ci * (export-specific) anonymous id by nfsd_setuser. 50862306a36Sopenharmony_ci * 50962306a36Sopenharmony_ci * (But supplementary gid's get no such special 51062306a36Sopenharmony_ci * treatment so are checked for validity here.) 51162306a36Sopenharmony_ci */ 51262306a36Sopenharmony_ci /* uid */ 51362306a36Sopenharmony_ci rsci.cred.cr_uid = make_kuid(current_user_ns(), id); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* gid */ 51662306a36Sopenharmony_ci if (get_int(&mesg, &id)) 51762306a36Sopenharmony_ci goto out; 51862306a36Sopenharmony_ci rsci.cred.cr_gid = make_kgid(current_user_ns(), id); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* number of additional gid's */ 52162306a36Sopenharmony_ci if (get_int(&mesg, &N)) 52262306a36Sopenharmony_ci goto out; 52362306a36Sopenharmony_ci if (N < 0 || N > NGROUPS_MAX) 52462306a36Sopenharmony_ci goto out; 52562306a36Sopenharmony_ci status = -ENOMEM; 52662306a36Sopenharmony_ci rsci.cred.cr_group_info = groups_alloc(N); 52762306a36Sopenharmony_ci if (rsci.cred.cr_group_info == NULL) 52862306a36Sopenharmony_ci goto out; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* gid's */ 53162306a36Sopenharmony_ci status = -EINVAL; 53262306a36Sopenharmony_ci for (i=0; i<N; i++) { 53362306a36Sopenharmony_ci kgid_t kgid; 53462306a36Sopenharmony_ci if (get_int(&mesg, &id)) 53562306a36Sopenharmony_ci goto out; 53662306a36Sopenharmony_ci kgid = make_kgid(current_user_ns(), id); 53762306a36Sopenharmony_ci if (!gid_valid(kgid)) 53862306a36Sopenharmony_ci goto out; 53962306a36Sopenharmony_ci rsci.cred.cr_group_info->gid[i] = kgid; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci groups_sort(rsci.cred.cr_group_info); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* mech name */ 54462306a36Sopenharmony_ci len = qword_get(&mesg, buf, mlen); 54562306a36Sopenharmony_ci if (len < 0) 54662306a36Sopenharmony_ci goto out; 54762306a36Sopenharmony_ci gm = rsci.cred.cr_gss_mech = gss_mech_get_by_name(buf); 54862306a36Sopenharmony_ci status = -EOPNOTSUPP; 54962306a36Sopenharmony_ci if (!gm) 55062306a36Sopenharmony_ci goto out; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci status = -EINVAL; 55362306a36Sopenharmony_ci /* mech-specific data: */ 55462306a36Sopenharmony_ci len = qword_get(&mesg, buf, mlen); 55562306a36Sopenharmony_ci if (len < 0) 55662306a36Sopenharmony_ci goto out; 55762306a36Sopenharmony_ci status = gss_import_sec_context(buf, len, gm, &rsci.mechctx, 55862306a36Sopenharmony_ci NULL, GFP_KERNEL); 55962306a36Sopenharmony_ci if (status) 56062306a36Sopenharmony_ci goto out; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* get client name */ 56362306a36Sopenharmony_ci len = qword_get(&mesg, buf, mlen); 56462306a36Sopenharmony_ci if (len > 0) { 56562306a36Sopenharmony_ci rsci.cred.cr_principal = kstrdup(buf, GFP_KERNEL); 56662306a36Sopenharmony_ci if (!rsci.cred.cr_principal) { 56762306a36Sopenharmony_ci status = -ENOMEM; 56862306a36Sopenharmony_ci goto out; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci rsci.h.expiry_time = expiry; 57462306a36Sopenharmony_ci rscp = rsc_update(cd, &rsci, rscp); 57562306a36Sopenharmony_ci status = 0; 57662306a36Sopenharmony_ciout: 57762306a36Sopenharmony_ci rsc_free(&rsci); 57862306a36Sopenharmony_ci if (rscp) 57962306a36Sopenharmony_ci cache_put(&rscp->h, cd); 58062306a36Sopenharmony_ci else 58162306a36Sopenharmony_ci status = -ENOMEM; 58262306a36Sopenharmony_ci return status; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic const struct cache_detail rsc_cache_template = { 58662306a36Sopenharmony_ci .owner = THIS_MODULE, 58762306a36Sopenharmony_ci .hash_size = RSC_HASHMAX, 58862306a36Sopenharmony_ci .name = "auth.rpcsec.context", 58962306a36Sopenharmony_ci .cache_put = rsc_put, 59062306a36Sopenharmony_ci .cache_upcall = rsc_upcall, 59162306a36Sopenharmony_ci .cache_parse = rsc_parse, 59262306a36Sopenharmony_ci .match = rsc_match, 59362306a36Sopenharmony_ci .init = rsc_init, 59462306a36Sopenharmony_ci .update = update_rsc, 59562306a36Sopenharmony_ci .alloc = rsc_alloc, 59662306a36Sopenharmony_ci}; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct cache_head *ch; 60162306a36Sopenharmony_ci int hash = rsc_hash(item); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci ch = sunrpc_cache_lookup_rcu(cd, &item->h, hash); 60462306a36Sopenharmony_ci if (ch) 60562306a36Sopenharmony_ci return container_of(ch, struct rsc, h); 60662306a36Sopenharmony_ci else 60762306a36Sopenharmony_ci return NULL; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct cache_head *ch; 61362306a36Sopenharmony_ci int hash = rsc_hash(new); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci ch = sunrpc_cache_update(cd, &new->h, 61662306a36Sopenharmony_ci &old->h, hash); 61762306a36Sopenharmony_ci if (ch) 61862306a36Sopenharmony_ci return container_of(ch, struct rsc, h); 61962306a36Sopenharmony_ci else 62062306a36Sopenharmony_ci return NULL; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic struct rsc * 62562306a36Sopenharmony_cigss_svc_searchbyctx(struct cache_detail *cd, struct xdr_netobj *handle) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct rsc rsci; 62862306a36Sopenharmony_ci struct rsc *found; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci memset(&rsci, 0, sizeof(rsci)); 63162306a36Sopenharmony_ci if (dup_to_netobj(&rsci.handle, handle->data, handle->len)) 63262306a36Sopenharmony_ci return NULL; 63362306a36Sopenharmony_ci found = rsc_lookup(cd, &rsci); 63462306a36Sopenharmony_ci rsc_free(&rsci); 63562306a36Sopenharmony_ci if (!found) 63662306a36Sopenharmony_ci return NULL; 63762306a36Sopenharmony_ci if (cache_check(cd, &found->h, NULL)) 63862306a36Sopenharmony_ci return NULL; 63962306a36Sopenharmony_ci return found; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci/** 64362306a36Sopenharmony_ci * gss_check_seq_num - GSS sequence number window check 64462306a36Sopenharmony_ci * @rqstp: RPC Call to use when reporting errors 64562306a36Sopenharmony_ci * @rsci: cached GSS context state (updated on return) 64662306a36Sopenharmony_ci * @seq_num: sequence number to check 64762306a36Sopenharmony_ci * 64862306a36Sopenharmony_ci * Implements sequence number algorithm as specified in 64962306a36Sopenharmony_ci * RFC 2203, Section 5.3.3.1. "Context Management". 65062306a36Sopenharmony_ci * 65162306a36Sopenharmony_ci * Return values: 65262306a36Sopenharmony_ci * %true: @rqstp's GSS sequence number is inside the window 65362306a36Sopenharmony_ci * %false: @rqstp's GSS sequence number is outside the window 65462306a36Sopenharmony_ci */ 65562306a36Sopenharmony_cistatic bool gss_check_seq_num(const struct svc_rqst *rqstp, struct rsc *rsci, 65662306a36Sopenharmony_ci u32 seq_num) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci struct gss_svc_seq_data *sd = &rsci->seqdata; 65962306a36Sopenharmony_ci bool result = false; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci spin_lock(&sd->sd_lock); 66262306a36Sopenharmony_ci if (seq_num > sd->sd_max) { 66362306a36Sopenharmony_ci if (seq_num >= sd->sd_max + GSS_SEQ_WIN) { 66462306a36Sopenharmony_ci memset(sd->sd_win, 0, sizeof(sd->sd_win)); 66562306a36Sopenharmony_ci sd->sd_max = seq_num; 66662306a36Sopenharmony_ci } else while (sd->sd_max < seq_num) { 66762306a36Sopenharmony_ci sd->sd_max++; 66862306a36Sopenharmony_ci __clear_bit(sd->sd_max % GSS_SEQ_WIN, sd->sd_win); 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci __set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win); 67162306a36Sopenharmony_ci goto ok; 67262306a36Sopenharmony_ci } else if (seq_num + GSS_SEQ_WIN <= sd->sd_max) { 67362306a36Sopenharmony_ci goto toolow; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci if (__test_and_set_bit(seq_num % GSS_SEQ_WIN, sd->sd_win)) 67662306a36Sopenharmony_ci goto alreadyseen; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ciok: 67962306a36Sopenharmony_ci result = true; 68062306a36Sopenharmony_ciout: 68162306a36Sopenharmony_ci spin_unlock(&sd->sd_lock); 68262306a36Sopenharmony_ci return result; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_citoolow: 68562306a36Sopenharmony_ci trace_rpcgss_svc_seqno_low(rqstp, seq_num, 68662306a36Sopenharmony_ci sd->sd_max - GSS_SEQ_WIN, 68762306a36Sopenharmony_ci sd->sd_max); 68862306a36Sopenharmony_ci goto out; 68962306a36Sopenharmony_cialreadyseen: 69062306a36Sopenharmony_ci trace_rpcgss_svc_seqno_seen(rqstp, seq_num); 69162306a36Sopenharmony_ci goto out; 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci/* 69562306a36Sopenharmony_ci * Decode and verify a Call's verifier field. For RPC_AUTH_GSS Calls, 69662306a36Sopenharmony_ci * the body of this field contains a variable length checksum. 69762306a36Sopenharmony_ci * 69862306a36Sopenharmony_ci * GSS-specific auth_stat values are mandated by RFC 2203 Section 69962306a36Sopenharmony_ci * 5.3.3.3. 70062306a36Sopenharmony_ci */ 70162306a36Sopenharmony_cistatic int 70262306a36Sopenharmony_cisvcauth_gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci, 70362306a36Sopenharmony_ci __be32 *rpcstart, struct rpc_gss_wire_cred *gc) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci struct xdr_stream *xdr = &rqstp->rq_arg_stream; 70662306a36Sopenharmony_ci struct gss_ctx *ctx_id = rsci->mechctx; 70762306a36Sopenharmony_ci u32 flavor, maj_stat; 70862306a36Sopenharmony_ci struct xdr_buf rpchdr; 70962306a36Sopenharmony_ci struct xdr_netobj checksum; 71062306a36Sopenharmony_ci struct kvec iov; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* 71362306a36Sopenharmony_ci * Compute the checksum of the incoming Call from the 71462306a36Sopenharmony_ci * XID field to credential field: 71562306a36Sopenharmony_ci */ 71662306a36Sopenharmony_ci iov.iov_base = rpcstart; 71762306a36Sopenharmony_ci iov.iov_len = (u8 *)xdr->p - (u8 *)rpcstart; 71862306a36Sopenharmony_ci xdr_buf_from_iov(&iov, &rpchdr); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* Call's verf field: */ 72162306a36Sopenharmony_ci if (xdr_stream_decode_opaque_auth(xdr, &flavor, 72262306a36Sopenharmony_ci (void **)&checksum.data, 72362306a36Sopenharmony_ci &checksum.len) < 0) { 72462306a36Sopenharmony_ci rqstp->rq_auth_stat = rpc_autherr_badverf; 72562306a36Sopenharmony_ci return SVC_DENIED; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci if (flavor != RPC_AUTH_GSS) { 72862306a36Sopenharmony_ci rqstp->rq_auth_stat = rpc_autherr_badverf; 72962306a36Sopenharmony_ci return SVC_DENIED; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (rqstp->rq_deferred) 73362306a36Sopenharmony_ci return SVC_OK; 73462306a36Sopenharmony_ci maj_stat = gss_verify_mic(ctx_id, &rpchdr, &checksum); 73562306a36Sopenharmony_ci if (maj_stat != GSS_S_COMPLETE) { 73662306a36Sopenharmony_ci trace_rpcgss_svc_mic(rqstp, maj_stat); 73762306a36Sopenharmony_ci rqstp->rq_auth_stat = rpcsec_gsserr_credproblem; 73862306a36Sopenharmony_ci return SVC_DENIED; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (gc->gc_seq > MAXSEQ) { 74262306a36Sopenharmony_ci trace_rpcgss_svc_seqno_large(rqstp, gc->gc_seq); 74362306a36Sopenharmony_ci rqstp->rq_auth_stat = rpcsec_gsserr_ctxproblem; 74462306a36Sopenharmony_ci return SVC_DENIED; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci if (!gss_check_seq_num(rqstp, rsci, gc->gc_seq)) 74762306a36Sopenharmony_ci return SVC_DROP; 74862306a36Sopenharmony_ci return SVC_OK; 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci/* 75262306a36Sopenharmony_ci * Construct and encode a Reply's verifier field. The verifier's body 75362306a36Sopenharmony_ci * field contains a variable-length checksum of the GSS sequence 75462306a36Sopenharmony_ci * number. 75562306a36Sopenharmony_ci */ 75662306a36Sopenharmony_cistatic bool 75762306a36Sopenharmony_cisvcauth_gss_encode_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id, u32 seq) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci struct gss_svc_data *gsd = rqstp->rq_auth_data; 76062306a36Sopenharmony_ci u32 maj_stat; 76162306a36Sopenharmony_ci struct xdr_buf verf_data; 76262306a36Sopenharmony_ci struct xdr_netobj checksum; 76362306a36Sopenharmony_ci struct kvec iov; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci gsd->gsd_seq_num = cpu_to_be32(seq); 76662306a36Sopenharmony_ci iov.iov_base = &gsd->gsd_seq_num; 76762306a36Sopenharmony_ci iov.iov_len = XDR_UNIT; 76862306a36Sopenharmony_ci xdr_buf_from_iov(&iov, &verf_data); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci checksum.data = gsd->gsd_scratch; 77162306a36Sopenharmony_ci maj_stat = gss_get_mic(ctx_id, &verf_data, &checksum); 77262306a36Sopenharmony_ci if (maj_stat != GSS_S_COMPLETE) 77362306a36Sopenharmony_ci goto bad_mic; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci return xdr_stream_encode_opaque_auth(&rqstp->rq_res_stream, RPC_AUTH_GSS, 77662306a36Sopenharmony_ci checksum.data, checksum.len) > 0; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cibad_mic: 77962306a36Sopenharmony_ci trace_rpcgss_svc_get_mic(rqstp, maj_stat); 78062306a36Sopenharmony_ci return false; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistruct gss_domain { 78462306a36Sopenharmony_ci struct auth_domain h; 78562306a36Sopenharmony_ci u32 pseudoflavor; 78662306a36Sopenharmony_ci}; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic struct auth_domain * 78962306a36Sopenharmony_cifind_gss_auth_domain(struct gss_ctx *ctx, u32 svc) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci char *name; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci name = gss_service_to_auth_domain_name(ctx->mech_type, svc); 79462306a36Sopenharmony_ci if (!name) 79562306a36Sopenharmony_ci return NULL; 79662306a36Sopenharmony_ci return auth_domain_find(name); 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic struct auth_ops svcauthops_gss; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ciu32 svcauth_gss_flavor(struct auth_domain *dom) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci struct gss_domain *gd = container_of(dom, struct gss_domain, h); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci return gd->pseudoflavor; 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(svcauth_gss_flavor); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cistruct auth_domain * 81162306a36Sopenharmony_cisvcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci struct gss_domain *new; 81462306a36Sopenharmony_ci struct auth_domain *test; 81562306a36Sopenharmony_ci int stat = -ENOMEM; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci new = kmalloc(sizeof(*new), GFP_KERNEL); 81862306a36Sopenharmony_ci if (!new) 81962306a36Sopenharmony_ci goto out; 82062306a36Sopenharmony_ci kref_init(&new->h.ref); 82162306a36Sopenharmony_ci new->h.name = kstrdup(name, GFP_KERNEL); 82262306a36Sopenharmony_ci if (!new->h.name) 82362306a36Sopenharmony_ci goto out_free_dom; 82462306a36Sopenharmony_ci new->h.flavour = &svcauthops_gss; 82562306a36Sopenharmony_ci new->pseudoflavor = pseudoflavor; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci test = auth_domain_lookup(name, &new->h); 82862306a36Sopenharmony_ci if (test != &new->h) { 82962306a36Sopenharmony_ci pr_warn("svc: duplicate registration of gss pseudo flavour %s.\n", 83062306a36Sopenharmony_ci name); 83162306a36Sopenharmony_ci stat = -EADDRINUSE; 83262306a36Sopenharmony_ci auth_domain_put(test); 83362306a36Sopenharmony_ci goto out_free_name; 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci return test; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ciout_free_name: 83862306a36Sopenharmony_ci kfree(new->h.name); 83962306a36Sopenharmony_ciout_free_dom: 84062306a36Sopenharmony_ci kfree(new); 84162306a36Sopenharmony_ciout: 84262306a36Sopenharmony_ci return ERR_PTR(stat); 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(svcauth_gss_register_pseudoflavor); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci/* 84762306a36Sopenharmony_ci * RFC 2203, Section 5.3.2.2 84862306a36Sopenharmony_ci * 84962306a36Sopenharmony_ci * struct rpc_gss_integ_data { 85062306a36Sopenharmony_ci * opaque databody_integ<>; 85162306a36Sopenharmony_ci * opaque checksum<>; 85262306a36Sopenharmony_ci * }; 85362306a36Sopenharmony_ci * 85462306a36Sopenharmony_ci * struct rpc_gss_data_t { 85562306a36Sopenharmony_ci * unsigned int seq_num; 85662306a36Sopenharmony_ci * proc_req_arg_t arg; 85762306a36Sopenharmony_ci * }; 85862306a36Sopenharmony_ci */ 85962306a36Sopenharmony_cistatic noinline_for_stack int 86062306a36Sopenharmony_cisvcauth_gss_unwrap_integ(struct svc_rqst *rqstp, u32 seq, struct gss_ctx *ctx) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci struct gss_svc_data *gsd = rqstp->rq_auth_data; 86362306a36Sopenharmony_ci struct xdr_stream *xdr = &rqstp->rq_arg_stream; 86462306a36Sopenharmony_ci u32 len, offset, seq_num, maj_stat; 86562306a36Sopenharmony_ci struct xdr_buf *buf = xdr->buf; 86662306a36Sopenharmony_ci struct xdr_buf databody_integ; 86762306a36Sopenharmony_ci struct xdr_netobj checksum; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci /* NFS READ normally uses splice to send data in-place. However 87062306a36Sopenharmony_ci * the data in cache can change after the reply's MIC is computed 87162306a36Sopenharmony_ci * but before the RPC reply is sent. To prevent the client from 87262306a36Sopenharmony_ci * rejecting the server-computed MIC in this somewhat rare case, 87362306a36Sopenharmony_ci * do not use splice with the GSS integrity service. 87462306a36Sopenharmony_ci */ 87562306a36Sopenharmony_ci clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /* Did we already verify the signature on the original pass through? */ 87862306a36Sopenharmony_ci if (rqstp->rq_deferred) 87962306a36Sopenharmony_ci return 0; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci if (xdr_stream_decode_u32(xdr, &len) < 0) 88262306a36Sopenharmony_ci goto unwrap_failed; 88362306a36Sopenharmony_ci if (len & 3) 88462306a36Sopenharmony_ci goto unwrap_failed; 88562306a36Sopenharmony_ci offset = xdr_stream_pos(xdr); 88662306a36Sopenharmony_ci if (xdr_buf_subsegment(buf, &databody_integ, offset, len)) 88762306a36Sopenharmony_ci goto unwrap_failed; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci /* 89062306a36Sopenharmony_ci * The xdr_stream now points to the @seq_num field. The next 89162306a36Sopenharmony_ci * XDR data item is the @arg field, which contains the clear 89262306a36Sopenharmony_ci * text RPC program payload. The checksum, which follows the 89362306a36Sopenharmony_ci * @arg field, is located and decoded without updating the 89462306a36Sopenharmony_ci * xdr_stream. 89562306a36Sopenharmony_ci */ 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci offset += len; 89862306a36Sopenharmony_ci if (xdr_decode_word(buf, offset, &checksum.len)) 89962306a36Sopenharmony_ci goto unwrap_failed; 90062306a36Sopenharmony_ci if (checksum.len > sizeof(gsd->gsd_scratch)) 90162306a36Sopenharmony_ci goto unwrap_failed; 90262306a36Sopenharmony_ci checksum.data = gsd->gsd_scratch; 90362306a36Sopenharmony_ci if (read_bytes_from_xdr_buf(buf, offset + XDR_UNIT, checksum.data, 90462306a36Sopenharmony_ci checksum.len)) 90562306a36Sopenharmony_ci goto unwrap_failed; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci maj_stat = gss_verify_mic(ctx, &databody_integ, &checksum); 90862306a36Sopenharmony_ci if (maj_stat != GSS_S_COMPLETE) 90962306a36Sopenharmony_ci goto bad_mic; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci /* The received seqno is protected by the checksum. */ 91262306a36Sopenharmony_ci if (xdr_stream_decode_u32(xdr, &seq_num) < 0) 91362306a36Sopenharmony_ci goto unwrap_failed; 91462306a36Sopenharmony_ci if (seq_num != seq) 91562306a36Sopenharmony_ci goto bad_seqno; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci xdr_truncate_decode(xdr, XDR_UNIT + checksum.len); 91862306a36Sopenharmony_ci return 0; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ciunwrap_failed: 92162306a36Sopenharmony_ci trace_rpcgss_svc_unwrap_failed(rqstp); 92262306a36Sopenharmony_ci return -EINVAL; 92362306a36Sopenharmony_cibad_seqno: 92462306a36Sopenharmony_ci trace_rpcgss_svc_seqno_bad(rqstp, seq, seq_num); 92562306a36Sopenharmony_ci return -EINVAL; 92662306a36Sopenharmony_cibad_mic: 92762306a36Sopenharmony_ci trace_rpcgss_svc_mic(rqstp, maj_stat); 92862306a36Sopenharmony_ci return -EINVAL; 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci/* 93262306a36Sopenharmony_ci * RFC 2203, Section 5.3.2.3 93362306a36Sopenharmony_ci * 93462306a36Sopenharmony_ci * struct rpc_gss_priv_data { 93562306a36Sopenharmony_ci * opaque databody_priv<> 93662306a36Sopenharmony_ci * }; 93762306a36Sopenharmony_ci * 93862306a36Sopenharmony_ci * struct rpc_gss_data_t { 93962306a36Sopenharmony_ci * unsigned int seq_num; 94062306a36Sopenharmony_ci * proc_req_arg_t arg; 94162306a36Sopenharmony_ci * }; 94262306a36Sopenharmony_ci */ 94362306a36Sopenharmony_cistatic noinline_for_stack int 94462306a36Sopenharmony_cisvcauth_gss_unwrap_priv(struct svc_rqst *rqstp, u32 seq, struct gss_ctx *ctx) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci struct xdr_stream *xdr = &rqstp->rq_arg_stream; 94762306a36Sopenharmony_ci u32 len, maj_stat, seq_num, offset; 94862306a36Sopenharmony_ci struct xdr_buf *buf = xdr->buf; 94962306a36Sopenharmony_ci unsigned int saved_len; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci if (xdr_stream_decode_u32(xdr, &len) < 0) 95462306a36Sopenharmony_ci goto unwrap_failed; 95562306a36Sopenharmony_ci if (rqstp->rq_deferred) { 95662306a36Sopenharmony_ci /* Already decrypted last time through! The sequence number 95762306a36Sopenharmony_ci * check at out_seq is unnecessary but harmless: */ 95862306a36Sopenharmony_ci goto out_seq; 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci if (len > xdr_stream_remaining(xdr)) 96162306a36Sopenharmony_ci goto unwrap_failed; 96262306a36Sopenharmony_ci offset = xdr_stream_pos(xdr); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci saved_len = buf->len; 96562306a36Sopenharmony_ci maj_stat = gss_unwrap(ctx, offset, offset + len, buf); 96662306a36Sopenharmony_ci if (maj_stat != GSS_S_COMPLETE) 96762306a36Sopenharmony_ci goto bad_unwrap; 96862306a36Sopenharmony_ci xdr->nwords -= XDR_QUADLEN(saved_len - buf->len); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ciout_seq: 97162306a36Sopenharmony_ci /* gss_unwrap() decrypted the sequence number. */ 97262306a36Sopenharmony_ci if (xdr_stream_decode_u32(xdr, &seq_num) < 0) 97362306a36Sopenharmony_ci goto unwrap_failed; 97462306a36Sopenharmony_ci if (seq_num != seq) 97562306a36Sopenharmony_ci goto bad_seqno; 97662306a36Sopenharmony_ci return 0; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ciunwrap_failed: 97962306a36Sopenharmony_ci trace_rpcgss_svc_unwrap_failed(rqstp); 98062306a36Sopenharmony_ci return -EINVAL; 98162306a36Sopenharmony_cibad_seqno: 98262306a36Sopenharmony_ci trace_rpcgss_svc_seqno_bad(rqstp, seq, seq_num); 98362306a36Sopenharmony_ci return -EINVAL; 98462306a36Sopenharmony_cibad_unwrap: 98562306a36Sopenharmony_ci trace_rpcgss_svc_unwrap(rqstp, maj_stat); 98662306a36Sopenharmony_ci return -EINVAL; 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_cistatic enum svc_auth_status 99062306a36Sopenharmony_cisvcauth_gss_set_client(struct svc_rqst *rqstp) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci struct gss_svc_data *svcdata = rqstp->rq_auth_data; 99362306a36Sopenharmony_ci struct rsc *rsci = svcdata->rsci; 99462306a36Sopenharmony_ci struct rpc_gss_wire_cred *gc = &svcdata->clcred; 99562306a36Sopenharmony_ci int stat; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci rqstp->rq_auth_stat = rpc_autherr_badcred; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci /* 100062306a36Sopenharmony_ci * A gss export can be specified either by: 100162306a36Sopenharmony_ci * export *(sec=krb5,rw) 100262306a36Sopenharmony_ci * or by 100362306a36Sopenharmony_ci * export gss/krb5(rw) 100462306a36Sopenharmony_ci * The latter is deprecated; but for backwards compatibility reasons 100562306a36Sopenharmony_ci * the nfsd code will still fall back on trying it if the former 100662306a36Sopenharmony_ci * doesn't work; so we try to make both available to nfsd, below. 100762306a36Sopenharmony_ci */ 100862306a36Sopenharmony_ci rqstp->rq_gssclient = find_gss_auth_domain(rsci->mechctx, gc->gc_svc); 100962306a36Sopenharmony_ci if (rqstp->rq_gssclient == NULL) 101062306a36Sopenharmony_ci return SVC_DENIED; 101162306a36Sopenharmony_ci stat = svcauth_unix_set_client(rqstp); 101262306a36Sopenharmony_ci if (stat == SVC_DROP || stat == SVC_CLOSE) 101362306a36Sopenharmony_ci return stat; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci rqstp->rq_auth_stat = rpc_auth_ok; 101662306a36Sopenharmony_ci return SVC_OK; 101762306a36Sopenharmony_ci} 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_cistatic bool 102062306a36Sopenharmony_cisvcauth_gss_proc_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp, 102162306a36Sopenharmony_ci struct xdr_netobj *out_handle, int *major_status, 102262306a36Sopenharmony_ci u32 seq_num) 102362306a36Sopenharmony_ci{ 102462306a36Sopenharmony_ci struct xdr_stream *xdr = &rqstp->rq_res_stream; 102562306a36Sopenharmony_ci struct rsc *rsci; 102662306a36Sopenharmony_ci bool rc; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci if (*major_status != GSS_S_COMPLETE) 102962306a36Sopenharmony_ci goto null_verifier; 103062306a36Sopenharmony_ci rsci = gss_svc_searchbyctx(cd, out_handle); 103162306a36Sopenharmony_ci if (rsci == NULL) { 103262306a36Sopenharmony_ci *major_status = GSS_S_NO_CONTEXT; 103362306a36Sopenharmony_ci goto null_verifier; 103462306a36Sopenharmony_ci } 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci rc = svcauth_gss_encode_verf(rqstp, rsci->mechctx, seq_num); 103762306a36Sopenharmony_ci cache_put(&rsci->h, cd); 103862306a36Sopenharmony_ci return rc; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_cinull_verifier: 104162306a36Sopenharmony_ci return xdr_stream_encode_opaque_auth(xdr, RPC_AUTH_NULL, NULL, 0) > 0; 104262306a36Sopenharmony_ci} 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_cistatic void gss_free_in_token_pages(struct gssp_in_token *in_token) 104562306a36Sopenharmony_ci{ 104662306a36Sopenharmony_ci u32 inlen; 104762306a36Sopenharmony_ci int i; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci i = 0; 105062306a36Sopenharmony_ci inlen = in_token->page_len; 105162306a36Sopenharmony_ci while (inlen) { 105262306a36Sopenharmony_ci if (in_token->pages[i]) 105362306a36Sopenharmony_ci put_page(in_token->pages[i]); 105462306a36Sopenharmony_ci inlen -= inlen > PAGE_SIZE ? PAGE_SIZE : inlen; 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci kfree(in_token->pages); 105862306a36Sopenharmony_ci in_token->pages = NULL; 105962306a36Sopenharmony_ci} 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_cistatic int gss_read_proxy_verf(struct svc_rqst *rqstp, 106262306a36Sopenharmony_ci struct rpc_gss_wire_cred *gc, 106362306a36Sopenharmony_ci struct xdr_netobj *in_handle, 106462306a36Sopenharmony_ci struct gssp_in_token *in_token) 106562306a36Sopenharmony_ci{ 106662306a36Sopenharmony_ci struct xdr_stream *xdr = &rqstp->rq_arg_stream; 106762306a36Sopenharmony_ci unsigned int length, pgto_offs, pgfrom_offs; 106862306a36Sopenharmony_ci int pages, i, pgto, pgfrom; 106962306a36Sopenharmony_ci size_t to_offs, from_offs; 107062306a36Sopenharmony_ci u32 inlen; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci if (dup_netobj(in_handle, &gc->gc_ctx)) 107362306a36Sopenharmony_ci return SVC_CLOSE; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci /* 107662306a36Sopenharmony_ci * RFC 2203 Section 5.2.2 107762306a36Sopenharmony_ci * 107862306a36Sopenharmony_ci * struct rpc_gss_init_arg { 107962306a36Sopenharmony_ci * opaque gss_token<>; 108062306a36Sopenharmony_ci * }; 108162306a36Sopenharmony_ci */ 108262306a36Sopenharmony_ci if (xdr_stream_decode_u32(xdr, &inlen) < 0) 108362306a36Sopenharmony_ci goto out_denied_free; 108462306a36Sopenharmony_ci if (inlen > xdr_stream_remaining(xdr)) 108562306a36Sopenharmony_ci goto out_denied_free; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci pages = DIV_ROUND_UP(inlen, PAGE_SIZE); 108862306a36Sopenharmony_ci in_token->pages = kcalloc(pages, sizeof(struct page *), GFP_KERNEL); 108962306a36Sopenharmony_ci if (!in_token->pages) 109062306a36Sopenharmony_ci goto out_denied_free; 109162306a36Sopenharmony_ci in_token->page_base = 0; 109262306a36Sopenharmony_ci in_token->page_len = inlen; 109362306a36Sopenharmony_ci for (i = 0; i < pages; i++) { 109462306a36Sopenharmony_ci in_token->pages[i] = alloc_page(GFP_KERNEL); 109562306a36Sopenharmony_ci if (!in_token->pages[i]) { 109662306a36Sopenharmony_ci gss_free_in_token_pages(in_token); 109762306a36Sopenharmony_ci goto out_denied_free; 109862306a36Sopenharmony_ci } 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci length = min_t(unsigned int, inlen, (char *)xdr->end - (char *)xdr->p); 110262306a36Sopenharmony_ci memcpy(page_address(in_token->pages[0]), xdr->p, length); 110362306a36Sopenharmony_ci inlen -= length; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci to_offs = length; 110662306a36Sopenharmony_ci from_offs = rqstp->rq_arg.page_base; 110762306a36Sopenharmony_ci while (inlen) { 110862306a36Sopenharmony_ci pgto = to_offs >> PAGE_SHIFT; 110962306a36Sopenharmony_ci pgfrom = from_offs >> PAGE_SHIFT; 111062306a36Sopenharmony_ci pgto_offs = to_offs & ~PAGE_MASK; 111162306a36Sopenharmony_ci pgfrom_offs = from_offs & ~PAGE_MASK; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci length = min_t(unsigned int, inlen, 111462306a36Sopenharmony_ci min_t(unsigned int, PAGE_SIZE - pgto_offs, 111562306a36Sopenharmony_ci PAGE_SIZE - pgfrom_offs)); 111662306a36Sopenharmony_ci memcpy(page_address(in_token->pages[pgto]) + pgto_offs, 111762306a36Sopenharmony_ci page_address(rqstp->rq_arg.pages[pgfrom]) + pgfrom_offs, 111862306a36Sopenharmony_ci length); 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci to_offs += length; 112162306a36Sopenharmony_ci from_offs += length; 112262306a36Sopenharmony_ci inlen -= length; 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci return 0; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ciout_denied_free: 112762306a36Sopenharmony_ci kfree(in_handle->data); 112862306a36Sopenharmony_ci return SVC_DENIED; 112962306a36Sopenharmony_ci} 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci/* 113262306a36Sopenharmony_ci * RFC 2203, Section 5.2.3.1. 113362306a36Sopenharmony_ci * 113462306a36Sopenharmony_ci * struct rpc_gss_init_res { 113562306a36Sopenharmony_ci * opaque handle<>; 113662306a36Sopenharmony_ci * unsigned int gss_major; 113762306a36Sopenharmony_ci * unsigned int gss_minor; 113862306a36Sopenharmony_ci * unsigned int seq_window; 113962306a36Sopenharmony_ci * opaque gss_token<>; 114062306a36Sopenharmony_ci * }; 114162306a36Sopenharmony_ci */ 114262306a36Sopenharmony_cistatic bool 114362306a36Sopenharmony_cisvcxdr_encode_gss_init_res(struct xdr_stream *xdr, 114462306a36Sopenharmony_ci struct xdr_netobj *handle, 114562306a36Sopenharmony_ci struct xdr_netobj *gss_token, 114662306a36Sopenharmony_ci unsigned int major_status, 114762306a36Sopenharmony_ci unsigned int minor_status, u32 seq_num) 114862306a36Sopenharmony_ci{ 114962306a36Sopenharmony_ci if (xdr_stream_encode_opaque(xdr, handle->data, handle->len) < 0) 115062306a36Sopenharmony_ci return false; 115162306a36Sopenharmony_ci if (xdr_stream_encode_u32(xdr, major_status) < 0) 115262306a36Sopenharmony_ci return false; 115362306a36Sopenharmony_ci if (xdr_stream_encode_u32(xdr, minor_status) < 0) 115462306a36Sopenharmony_ci return false; 115562306a36Sopenharmony_ci if (xdr_stream_encode_u32(xdr, seq_num) < 0) 115662306a36Sopenharmony_ci return false; 115762306a36Sopenharmony_ci if (xdr_stream_encode_opaque(xdr, gss_token->data, gss_token->len) < 0) 115862306a36Sopenharmony_ci return false; 115962306a36Sopenharmony_ci return true; 116062306a36Sopenharmony_ci} 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci/* 116362306a36Sopenharmony_ci * Having read the cred already and found we're in the context 116462306a36Sopenharmony_ci * initiation case, read the verifier and initiate (or check the results 116562306a36Sopenharmony_ci * of) upcalls to userspace for help with context initiation. If 116662306a36Sopenharmony_ci * the upcall results are available, write the verifier and result. 116762306a36Sopenharmony_ci * Otherwise, drop the request pending an answer to the upcall. 116862306a36Sopenharmony_ci */ 116962306a36Sopenharmony_cistatic int 117062306a36Sopenharmony_cisvcauth_gss_legacy_init(struct svc_rqst *rqstp, 117162306a36Sopenharmony_ci struct rpc_gss_wire_cred *gc) 117262306a36Sopenharmony_ci{ 117362306a36Sopenharmony_ci struct xdr_stream *xdr = &rqstp->rq_arg_stream; 117462306a36Sopenharmony_ci struct rsi *rsip, rsikey; 117562306a36Sopenharmony_ci __be32 *p; 117662306a36Sopenharmony_ci u32 len; 117762306a36Sopenharmony_ci int ret; 117862306a36Sopenharmony_ci struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id); 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci memset(&rsikey, 0, sizeof(rsikey)); 118162306a36Sopenharmony_ci if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx)) 118262306a36Sopenharmony_ci return SVC_CLOSE; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci /* 118562306a36Sopenharmony_ci * RFC 2203 Section 5.2.2 118662306a36Sopenharmony_ci * 118762306a36Sopenharmony_ci * struct rpc_gss_init_arg { 118862306a36Sopenharmony_ci * opaque gss_token<>; 118962306a36Sopenharmony_ci * }; 119062306a36Sopenharmony_ci */ 119162306a36Sopenharmony_ci if (xdr_stream_decode_u32(xdr, &len) < 0) { 119262306a36Sopenharmony_ci kfree(rsikey.in_handle.data); 119362306a36Sopenharmony_ci return SVC_DENIED; 119462306a36Sopenharmony_ci } 119562306a36Sopenharmony_ci p = xdr_inline_decode(xdr, len); 119662306a36Sopenharmony_ci if (!p) { 119762306a36Sopenharmony_ci kfree(rsikey.in_handle.data); 119862306a36Sopenharmony_ci return SVC_DENIED; 119962306a36Sopenharmony_ci } 120062306a36Sopenharmony_ci rsikey.in_token.data = kmalloc(len, GFP_KERNEL); 120162306a36Sopenharmony_ci if (ZERO_OR_NULL_PTR(rsikey.in_token.data)) { 120262306a36Sopenharmony_ci kfree(rsikey.in_handle.data); 120362306a36Sopenharmony_ci return SVC_CLOSE; 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci memcpy(rsikey.in_token.data, p, len); 120662306a36Sopenharmony_ci rsikey.in_token.len = len; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci /* Perform upcall, or find upcall result: */ 120962306a36Sopenharmony_ci rsip = rsi_lookup(sn->rsi_cache, &rsikey); 121062306a36Sopenharmony_ci rsi_free(&rsikey); 121162306a36Sopenharmony_ci if (!rsip) 121262306a36Sopenharmony_ci return SVC_CLOSE; 121362306a36Sopenharmony_ci if (cache_check(sn->rsi_cache, &rsip->h, &rqstp->rq_chandle) < 0) 121462306a36Sopenharmony_ci /* No upcall result: */ 121562306a36Sopenharmony_ci return SVC_CLOSE; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci ret = SVC_CLOSE; 121862306a36Sopenharmony_ci if (!svcauth_gss_proc_init_verf(sn->rsc_cache, rqstp, &rsip->out_handle, 121962306a36Sopenharmony_ci &rsip->major_status, GSS_SEQ_WIN)) 122062306a36Sopenharmony_ci goto out; 122162306a36Sopenharmony_ci if (!svcxdr_set_accept_stat(rqstp)) 122262306a36Sopenharmony_ci goto out; 122362306a36Sopenharmony_ci if (!svcxdr_encode_gss_init_res(&rqstp->rq_res_stream, &rsip->out_handle, 122462306a36Sopenharmony_ci &rsip->out_token, rsip->major_status, 122562306a36Sopenharmony_ci rsip->minor_status, GSS_SEQ_WIN)) 122662306a36Sopenharmony_ci goto out; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci ret = SVC_COMPLETE; 122962306a36Sopenharmony_ciout: 123062306a36Sopenharmony_ci cache_put(&rsip->h, sn->rsi_cache); 123162306a36Sopenharmony_ci return ret; 123262306a36Sopenharmony_ci} 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_cistatic int gss_proxy_save_rsc(struct cache_detail *cd, 123562306a36Sopenharmony_ci struct gssp_upcall_data *ud, 123662306a36Sopenharmony_ci uint64_t *handle) 123762306a36Sopenharmony_ci{ 123862306a36Sopenharmony_ci struct rsc rsci, *rscp = NULL; 123962306a36Sopenharmony_ci static atomic64_t ctxhctr; 124062306a36Sopenharmony_ci long long ctxh; 124162306a36Sopenharmony_ci struct gss_api_mech *gm = NULL; 124262306a36Sopenharmony_ci time64_t expiry; 124362306a36Sopenharmony_ci int status; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci memset(&rsci, 0, sizeof(rsci)); 124662306a36Sopenharmony_ci /* context handle */ 124762306a36Sopenharmony_ci status = -ENOMEM; 124862306a36Sopenharmony_ci /* the handle needs to be just a unique id, 124962306a36Sopenharmony_ci * use a static counter */ 125062306a36Sopenharmony_ci ctxh = atomic64_inc_return(&ctxhctr); 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci /* make a copy for the caller */ 125362306a36Sopenharmony_ci *handle = ctxh; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci /* make a copy for the rsc cache */ 125662306a36Sopenharmony_ci if (dup_to_netobj(&rsci.handle, (char *)handle, sizeof(uint64_t))) 125762306a36Sopenharmony_ci goto out; 125862306a36Sopenharmony_ci rscp = rsc_lookup(cd, &rsci); 125962306a36Sopenharmony_ci if (!rscp) 126062306a36Sopenharmony_ci goto out; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci /* creds */ 126362306a36Sopenharmony_ci if (!ud->found_creds) { 126462306a36Sopenharmony_ci /* userspace seem buggy, we should always get at least a 126562306a36Sopenharmony_ci * mapping to nobody */ 126662306a36Sopenharmony_ci goto out; 126762306a36Sopenharmony_ci } else { 126862306a36Sopenharmony_ci struct timespec64 boot; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci /* steal creds */ 127162306a36Sopenharmony_ci rsci.cred = ud->creds; 127262306a36Sopenharmony_ci memset(&ud->creds, 0, sizeof(struct svc_cred)); 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci status = -EOPNOTSUPP; 127562306a36Sopenharmony_ci /* get mech handle from OID */ 127662306a36Sopenharmony_ci gm = gss_mech_get_by_OID(&ud->mech_oid); 127762306a36Sopenharmony_ci if (!gm) 127862306a36Sopenharmony_ci goto out; 127962306a36Sopenharmony_ci rsci.cred.cr_gss_mech = gm; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci status = -EINVAL; 128262306a36Sopenharmony_ci /* mech-specific data: */ 128362306a36Sopenharmony_ci status = gss_import_sec_context(ud->out_handle.data, 128462306a36Sopenharmony_ci ud->out_handle.len, 128562306a36Sopenharmony_ci gm, &rsci.mechctx, 128662306a36Sopenharmony_ci &expiry, GFP_KERNEL); 128762306a36Sopenharmony_ci if (status) 128862306a36Sopenharmony_ci goto out; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci getboottime64(&boot); 129162306a36Sopenharmony_ci expiry -= boot.tv_sec; 129262306a36Sopenharmony_ci } 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci rsci.h.expiry_time = expiry; 129562306a36Sopenharmony_ci rscp = rsc_update(cd, &rsci, rscp); 129662306a36Sopenharmony_ci status = 0; 129762306a36Sopenharmony_ciout: 129862306a36Sopenharmony_ci rsc_free(&rsci); 129962306a36Sopenharmony_ci if (rscp) 130062306a36Sopenharmony_ci cache_put(&rscp->h, cd); 130162306a36Sopenharmony_ci else 130262306a36Sopenharmony_ci status = -ENOMEM; 130362306a36Sopenharmony_ci return status; 130462306a36Sopenharmony_ci} 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_cistatic int svcauth_gss_proxy_init(struct svc_rqst *rqstp, 130762306a36Sopenharmony_ci struct rpc_gss_wire_cred *gc) 130862306a36Sopenharmony_ci{ 130962306a36Sopenharmony_ci struct xdr_netobj cli_handle; 131062306a36Sopenharmony_ci struct gssp_upcall_data ud; 131162306a36Sopenharmony_ci uint64_t handle; 131262306a36Sopenharmony_ci int status; 131362306a36Sopenharmony_ci int ret; 131462306a36Sopenharmony_ci struct net *net = SVC_NET(rqstp); 131562306a36Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci memset(&ud, 0, sizeof(ud)); 131862306a36Sopenharmony_ci ret = gss_read_proxy_verf(rqstp, gc, &ud.in_handle, &ud.in_token); 131962306a36Sopenharmony_ci if (ret) 132062306a36Sopenharmony_ci return ret; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci ret = SVC_CLOSE; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci /* Perform synchronous upcall to gss-proxy */ 132562306a36Sopenharmony_ci status = gssp_accept_sec_context_upcall(net, &ud); 132662306a36Sopenharmony_ci if (status) 132762306a36Sopenharmony_ci goto out; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci trace_rpcgss_svc_accept_upcall(rqstp, ud.major_status, ud.minor_status); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci switch (ud.major_status) { 133262306a36Sopenharmony_ci case GSS_S_CONTINUE_NEEDED: 133362306a36Sopenharmony_ci cli_handle = ud.out_handle; 133462306a36Sopenharmony_ci break; 133562306a36Sopenharmony_ci case GSS_S_COMPLETE: 133662306a36Sopenharmony_ci status = gss_proxy_save_rsc(sn->rsc_cache, &ud, &handle); 133762306a36Sopenharmony_ci if (status) 133862306a36Sopenharmony_ci goto out; 133962306a36Sopenharmony_ci cli_handle.data = (u8 *)&handle; 134062306a36Sopenharmony_ci cli_handle.len = sizeof(handle); 134162306a36Sopenharmony_ci break; 134262306a36Sopenharmony_ci default: 134362306a36Sopenharmony_ci goto out; 134462306a36Sopenharmony_ci } 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci if (!svcauth_gss_proc_init_verf(sn->rsc_cache, rqstp, &cli_handle, 134762306a36Sopenharmony_ci &ud.major_status, GSS_SEQ_WIN)) 134862306a36Sopenharmony_ci goto out; 134962306a36Sopenharmony_ci if (!svcxdr_set_accept_stat(rqstp)) 135062306a36Sopenharmony_ci goto out; 135162306a36Sopenharmony_ci if (!svcxdr_encode_gss_init_res(&rqstp->rq_res_stream, &cli_handle, 135262306a36Sopenharmony_ci &ud.out_token, ud.major_status, 135362306a36Sopenharmony_ci ud.minor_status, GSS_SEQ_WIN)) 135462306a36Sopenharmony_ci goto out; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci ret = SVC_COMPLETE; 135762306a36Sopenharmony_ciout: 135862306a36Sopenharmony_ci gss_free_in_token_pages(&ud.in_token); 135962306a36Sopenharmony_ci gssp_free_upcall_data(&ud); 136062306a36Sopenharmony_ci return ret; 136162306a36Sopenharmony_ci} 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci/* 136462306a36Sopenharmony_ci * Try to set the sn->use_gss_proxy variable to a new value. We only allow 136562306a36Sopenharmony_ci * it to be changed if it's currently undefined (-1). If it's any other value 136662306a36Sopenharmony_ci * then return -EBUSY unless the type wouldn't have changed anyway. 136762306a36Sopenharmony_ci */ 136862306a36Sopenharmony_cistatic int set_gss_proxy(struct net *net, int type) 136962306a36Sopenharmony_ci{ 137062306a36Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 137162306a36Sopenharmony_ci int ret; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci WARN_ON_ONCE(type != 0 && type != 1); 137462306a36Sopenharmony_ci ret = cmpxchg(&sn->use_gss_proxy, -1, type); 137562306a36Sopenharmony_ci if (ret != -1 && ret != type) 137662306a36Sopenharmony_ci return -EBUSY; 137762306a36Sopenharmony_ci return 0; 137862306a36Sopenharmony_ci} 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_cistatic bool use_gss_proxy(struct net *net) 138162306a36Sopenharmony_ci{ 138262306a36Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci /* If use_gss_proxy is still undefined, then try to disable it */ 138562306a36Sopenharmony_ci if (sn->use_gss_proxy == -1) 138662306a36Sopenharmony_ci set_gss_proxy(net, 0); 138762306a36Sopenharmony_ci return sn->use_gss_proxy; 138862306a36Sopenharmony_ci} 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_cistatic noinline_for_stack int 139162306a36Sopenharmony_cisvcauth_gss_proc_init(struct svc_rqst *rqstp, struct rpc_gss_wire_cred *gc) 139262306a36Sopenharmony_ci{ 139362306a36Sopenharmony_ci struct xdr_stream *xdr = &rqstp->rq_arg_stream; 139462306a36Sopenharmony_ci u32 flavor, len; 139562306a36Sopenharmony_ci void *body; 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci /* Call's verf field: */ 139862306a36Sopenharmony_ci if (xdr_stream_decode_opaque_auth(xdr, &flavor, &body, &len) < 0) 139962306a36Sopenharmony_ci return SVC_GARBAGE; 140062306a36Sopenharmony_ci if (flavor != RPC_AUTH_NULL || len != 0) { 140162306a36Sopenharmony_ci rqstp->rq_auth_stat = rpc_autherr_badverf; 140262306a36Sopenharmony_ci return SVC_DENIED; 140362306a36Sopenharmony_ci } 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0) { 140662306a36Sopenharmony_ci rqstp->rq_auth_stat = rpc_autherr_badcred; 140762306a36Sopenharmony_ci return SVC_DENIED; 140862306a36Sopenharmony_ci } 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci if (!use_gss_proxy(SVC_NET(rqstp))) 141162306a36Sopenharmony_ci return svcauth_gss_legacy_init(rqstp, gc); 141262306a36Sopenharmony_ci return svcauth_gss_proxy_init(rqstp, gc); 141362306a36Sopenharmony_ci} 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_cistatic ssize_t write_gssp(struct file *file, const char __user *buf, 141862306a36Sopenharmony_ci size_t count, loff_t *ppos) 141962306a36Sopenharmony_ci{ 142062306a36Sopenharmony_ci struct net *net = pde_data(file_inode(file)); 142162306a36Sopenharmony_ci char tbuf[20]; 142262306a36Sopenharmony_ci unsigned long i; 142362306a36Sopenharmony_ci int res; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci if (*ppos || count > sizeof(tbuf)-1) 142662306a36Sopenharmony_ci return -EINVAL; 142762306a36Sopenharmony_ci if (copy_from_user(tbuf, buf, count)) 142862306a36Sopenharmony_ci return -EFAULT; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci tbuf[count] = 0; 143162306a36Sopenharmony_ci res = kstrtoul(tbuf, 0, &i); 143262306a36Sopenharmony_ci if (res) 143362306a36Sopenharmony_ci return res; 143462306a36Sopenharmony_ci if (i != 1) 143562306a36Sopenharmony_ci return -EINVAL; 143662306a36Sopenharmony_ci res = set_gssp_clnt(net); 143762306a36Sopenharmony_ci if (res) 143862306a36Sopenharmony_ci return res; 143962306a36Sopenharmony_ci res = set_gss_proxy(net, 1); 144062306a36Sopenharmony_ci if (res) 144162306a36Sopenharmony_ci return res; 144262306a36Sopenharmony_ci return count; 144362306a36Sopenharmony_ci} 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_cistatic ssize_t read_gssp(struct file *file, char __user *buf, 144662306a36Sopenharmony_ci size_t count, loff_t *ppos) 144762306a36Sopenharmony_ci{ 144862306a36Sopenharmony_ci struct net *net = pde_data(file_inode(file)); 144962306a36Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 145062306a36Sopenharmony_ci unsigned long p = *ppos; 145162306a36Sopenharmony_ci char tbuf[10]; 145262306a36Sopenharmony_ci size_t len; 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci snprintf(tbuf, sizeof(tbuf), "%d\n", sn->use_gss_proxy); 145562306a36Sopenharmony_ci len = strlen(tbuf); 145662306a36Sopenharmony_ci if (p >= len) 145762306a36Sopenharmony_ci return 0; 145862306a36Sopenharmony_ci len -= p; 145962306a36Sopenharmony_ci if (len > count) 146062306a36Sopenharmony_ci len = count; 146162306a36Sopenharmony_ci if (copy_to_user(buf, (void *)(tbuf+p), len)) 146262306a36Sopenharmony_ci return -EFAULT; 146362306a36Sopenharmony_ci *ppos += len; 146462306a36Sopenharmony_ci return len; 146562306a36Sopenharmony_ci} 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_cistatic const struct proc_ops use_gss_proxy_proc_ops = { 146862306a36Sopenharmony_ci .proc_open = nonseekable_open, 146962306a36Sopenharmony_ci .proc_write = write_gssp, 147062306a36Sopenharmony_ci .proc_read = read_gssp, 147162306a36Sopenharmony_ci}; 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_cistatic int create_use_gss_proxy_proc_entry(struct net *net) 147462306a36Sopenharmony_ci{ 147562306a36Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 147662306a36Sopenharmony_ci struct proc_dir_entry **p = &sn->use_gssp_proc; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci sn->use_gss_proxy = -1; 147962306a36Sopenharmony_ci *p = proc_create_data("use-gss-proxy", S_IFREG | 0600, 148062306a36Sopenharmony_ci sn->proc_net_rpc, 148162306a36Sopenharmony_ci &use_gss_proxy_proc_ops, net); 148262306a36Sopenharmony_ci if (!*p) 148362306a36Sopenharmony_ci return -ENOMEM; 148462306a36Sopenharmony_ci init_gssp_clnt(sn); 148562306a36Sopenharmony_ci return 0; 148662306a36Sopenharmony_ci} 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_cistatic void destroy_use_gss_proxy_proc_entry(struct net *net) 148962306a36Sopenharmony_ci{ 149062306a36Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci if (sn->use_gssp_proc) { 149362306a36Sopenharmony_ci remove_proc_entry("use-gss-proxy", sn->proc_net_rpc); 149462306a36Sopenharmony_ci clear_gssp_clnt(sn); 149562306a36Sopenharmony_ci } 149662306a36Sopenharmony_ci} 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_cistatic ssize_t read_gss_krb5_enctypes(struct file *file, char __user *buf, 149962306a36Sopenharmony_ci size_t count, loff_t *ppos) 150062306a36Sopenharmony_ci{ 150162306a36Sopenharmony_ci struct rpcsec_gss_oid oid = { 150262306a36Sopenharmony_ci .len = 9, 150362306a36Sopenharmony_ci .data = "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02", 150462306a36Sopenharmony_ci }; 150562306a36Sopenharmony_ci struct gss_api_mech *mech; 150662306a36Sopenharmony_ci ssize_t ret; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci mech = gss_mech_get_by_OID(&oid); 150962306a36Sopenharmony_ci if (!mech) 151062306a36Sopenharmony_ci return 0; 151162306a36Sopenharmony_ci if (!mech->gm_upcall_enctypes) { 151262306a36Sopenharmony_ci gss_mech_put(mech); 151362306a36Sopenharmony_ci return 0; 151462306a36Sopenharmony_ci } 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci ret = simple_read_from_buffer(buf, count, ppos, 151762306a36Sopenharmony_ci mech->gm_upcall_enctypes, 151862306a36Sopenharmony_ci strlen(mech->gm_upcall_enctypes)); 151962306a36Sopenharmony_ci gss_mech_put(mech); 152062306a36Sopenharmony_ci return ret; 152162306a36Sopenharmony_ci} 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_cistatic const struct proc_ops gss_krb5_enctypes_proc_ops = { 152462306a36Sopenharmony_ci .proc_open = nonseekable_open, 152562306a36Sopenharmony_ci .proc_read = read_gss_krb5_enctypes, 152662306a36Sopenharmony_ci}; 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_cistatic int create_krb5_enctypes_proc_entry(struct net *net) 152962306a36Sopenharmony_ci{ 153062306a36Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci sn->gss_krb5_enctypes = 153362306a36Sopenharmony_ci proc_create_data("gss_krb5_enctypes", S_IFREG | 0444, 153462306a36Sopenharmony_ci sn->proc_net_rpc, &gss_krb5_enctypes_proc_ops, 153562306a36Sopenharmony_ci net); 153662306a36Sopenharmony_ci return sn->gss_krb5_enctypes ? 0 : -ENOMEM; 153762306a36Sopenharmony_ci} 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_cistatic void destroy_krb5_enctypes_proc_entry(struct net *net) 154062306a36Sopenharmony_ci{ 154162306a36Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci if (sn->gss_krb5_enctypes) 154462306a36Sopenharmony_ci remove_proc_entry("gss_krb5_enctypes", sn->proc_net_rpc); 154562306a36Sopenharmony_ci} 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci#else /* CONFIG_PROC_FS */ 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_cistatic int create_use_gss_proxy_proc_entry(struct net *net) 155062306a36Sopenharmony_ci{ 155162306a36Sopenharmony_ci return 0; 155262306a36Sopenharmony_ci} 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_cistatic void destroy_use_gss_proxy_proc_entry(struct net *net) {} 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_cistatic int create_krb5_enctypes_proc_entry(struct net *net) 155762306a36Sopenharmony_ci{ 155862306a36Sopenharmony_ci return 0; 155962306a36Sopenharmony_ci} 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_cistatic void destroy_krb5_enctypes_proc_entry(struct net *net) {} 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci/* 156662306a36Sopenharmony_ci * The Call's credential body should contain a struct rpc_gss_cred_t. 156762306a36Sopenharmony_ci * 156862306a36Sopenharmony_ci * RFC 2203 Section 5 156962306a36Sopenharmony_ci * 157062306a36Sopenharmony_ci * struct rpc_gss_cred_t { 157162306a36Sopenharmony_ci * union switch (unsigned int version) { 157262306a36Sopenharmony_ci * case RPCSEC_GSS_VERS_1: 157362306a36Sopenharmony_ci * struct { 157462306a36Sopenharmony_ci * rpc_gss_proc_t gss_proc; 157562306a36Sopenharmony_ci * unsigned int seq_num; 157662306a36Sopenharmony_ci * rpc_gss_service_t service; 157762306a36Sopenharmony_ci * opaque handle<>; 157862306a36Sopenharmony_ci * } rpc_gss_cred_vers_1_t; 157962306a36Sopenharmony_ci * } 158062306a36Sopenharmony_ci * }; 158162306a36Sopenharmony_ci */ 158262306a36Sopenharmony_cistatic bool 158362306a36Sopenharmony_cisvcauth_gss_decode_credbody(struct xdr_stream *xdr, 158462306a36Sopenharmony_ci struct rpc_gss_wire_cred *gc, 158562306a36Sopenharmony_ci __be32 **rpcstart) 158662306a36Sopenharmony_ci{ 158762306a36Sopenharmony_ci ssize_t handle_len; 158862306a36Sopenharmony_ci u32 body_len; 158962306a36Sopenharmony_ci __be32 *p; 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci p = xdr_inline_decode(xdr, XDR_UNIT); 159262306a36Sopenharmony_ci if (!p) 159362306a36Sopenharmony_ci return false; 159462306a36Sopenharmony_ci /* 159562306a36Sopenharmony_ci * start of rpc packet is 7 u32's back from here: 159662306a36Sopenharmony_ci * xid direction rpcversion prog vers proc flavour 159762306a36Sopenharmony_ci */ 159862306a36Sopenharmony_ci *rpcstart = p - 7; 159962306a36Sopenharmony_ci body_len = be32_to_cpup(p); 160062306a36Sopenharmony_ci if (body_len > RPC_MAX_AUTH_SIZE) 160162306a36Sopenharmony_ci return false; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci /* struct rpc_gss_cred_t */ 160462306a36Sopenharmony_ci if (xdr_stream_decode_u32(xdr, &gc->gc_v) < 0) 160562306a36Sopenharmony_ci return false; 160662306a36Sopenharmony_ci if (xdr_stream_decode_u32(xdr, &gc->gc_proc) < 0) 160762306a36Sopenharmony_ci return false; 160862306a36Sopenharmony_ci if (xdr_stream_decode_u32(xdr, &gc->gc_seq) < 0) 160962306a36Sopenharmony_ci return false; 161062306a36Sopenharmony_ci if (xdr_stream_decode_u32(xdr, &gc->gc_svc) < 0) 161162306a36Sopenharmony_ci return false; 161262306a36Sopenharmony_ci handle_len = xdr_stream_decode_opaque_inline(xdr, 161362306a36Sopenharmony_ci (void **)&gc->gc_ctx.data, 161462306a36Sopenharmony_ci body_len); 161562306a36Sopenharmony_ci if (handle_len < 0) 161662306a36Sopenharmony_ci return false; 161762306a36Sopenharmony_ci if (body_len != XDR_UNIT * 5 + xdr_align_size(handle_len)) 161862306a36Sopenharmony_ci return false; 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci gc->gc_ctx.len = handle_len; 162162306a36Sopenharmony_ci return true; 162262306a36Sopenharmony_ci} 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci/** 162562306a36Sopenharmony_ci * svcauth_gss_accept - Decode and validate incoming RPC_AUTH_GSS credential 162662306a36Sopenharmony_ci * @rqstp: RPC transaction 162762306a36Sopenharmony_ci * 162862306a36Sopenharmony_ci * Return values: 162962306a36Sopenharmony_ci * %SVC_OK: Success 163062306a36Sopenharmony_ci * %SVC_COMPLETE: GSS context lifetime event 163162306a36Sopenharmony_ci * %SVC_DENIED: Credential or verifier is not valid 163262306a36Sopenharmony_ci * %SVC_GARBAGE: Failed to decode credential or verifier 163362306a36Sopenharmony_ci * %SVC_CLOSE: Temporary failure 163462306a36Sopenharmony_ci * 163562306a36Sopenharmony_ci * The rqstp->rq_auth_stat field is also set (see RFCs 2203 and 5531). 163662306a36Sopenharmony_ci */ 163762306a36Sopenharmony_cistatic enum svc_auth_status 163862306a36Sopenharmony_cisvcauth_gss_accept(struct svc_rqst *rqstp) 163962306a36Sopenharmony_ci{ 164062306a36Sopenharmony_ci struct gss_svc_data *svcdata = rqstp->rq_auth_data; 164162306a36Sopenharmony_ci __be32 *rpcstart; 164262306a36Sopenharmony_ci struct rpc_gss_wire_cred *gc; 164362306a36Sopenharmony_ci struct rsc *rsci = NULL; 164462306a36Sopenharmony_ci int ret; 164562306a36Sopenharmony_ci struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id); 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci rqstp->rq_auth_stat = rpc_autherr_badcred; 164862306a36Sopenharmony_ci if (!svcdata) 164962306a36Sopenharmony_ci svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL); 165062306a36Sopenharmony_ci if (!svcdata) 165162306a36Sopenharmony_ci goto auth_err; 165262306a36Sopenharmony_ci rqstp->rq_auth_data = svcdata; 165362306a36Sopenharmony_ci svcdata->gsd_databody_offset = 0; 165462306a36Sopenharmony_ci svcdata->rsci = NULL; 165562306a36Sopenharmony_ci gc = &svcdata->clcred; 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci if (!svcauth_gss_decode_credbody(&rqstp->rq_arg_stream, gc, &rpcstart)) 165862306a36Sopenharmony_ci goto auth_err; 165962306a36Sopenharmony_ci if (gc->gc_v != RPC_GSS_VERSION) 166062306a36Sopenharmony_ci goto auth_err; 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci switch (gc->gc_proc) { 166362306a36Sopenharmony_ci case RPC_GSS_PROC_INIT: 166462306a36Sopenharmony_ci case RPC_GSS_PROC_CONTINUE_INIT: 166562306a36Sopenharmony_ci if (rqstp->rq_proc != 0) 166662306a36Sopenharmony_ci goto auth_err; 166762306a36Sopenharmony_ci return svcauth_gss_proc_init(rqstp, gc); 166862306a36Sopenharmony_ci case RPC_GSS_PROC_DESTROY: 166962306a36Sopenharmony_ci if (rqstp->rq_proc != 0) 167062306a36Sopenharmony_ci goto auth_err; 167162306a36Sopenharmony_ci fallthrough; 167262306a36Sopenharmony_ci case RPC_GSS_PROC_DATA: 167362306a36Sopenharmony_ci rqstp->rq_auth_stat = rpcsec_gsserr_credproblem; 167462306a36Sopenharmony_ci rsci = gss_svc_searchbyctx(sn->rsc_cache, &gc->gc_ctx); 167562306a36Sopenharmony_ci if (!rsci) 167662306a36Sopenharmony_ci goto auth_err; 167762306a36Sopenharmony_ci switch (svcauth_gss_verify_header(rqstp, rsci, rpcstart, gc)) { 167862306a36Sopenharmony_ci case SVC_OK: 167962306a36Sopenharmony_ci break; 168062306a36Sopenharmony_ci case SVC_DENIED: 168162306a36Sopenharmony_ci goto auth_err; 168262306a36Sopenharmony_ci case SVC_DROP: 168362306a36Sopenharmony_ci goto drop; 168462306a36Sopenharmony_ci } 168562306a36Sopenharmony_ci break; 168662306a36Sopenharmony_ci default: 168762306a36Sopenharmony_ci if (rqstp->rq_proc != 0) 168862306a36Sopenharmony_ci goto auth_err; 168962306a36Sopenharmony_ci rqstp->rq_auth_stat = rpc_autherr_rejectedcred; 169062306a36Sopenharmony_ci goto auth_err; 169162306a36Sopenharmony_ci } 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci /* now act upon the command: */ 169462306a36Sopenharmony_ci switch (gc->gc_proc) { 169562306a36Sopenharmony_ci case RPC_GSS_PROC_DESTROY: 169662306a36Sopenharmony_ci if (!svcauth_gss_encode_verf(rqstp, rsci->mechctx, gc->gc_seq)) 169762306a36Sopenharmony_ci goto auth_err; 169862306a36Sopenharmony_ci if (!svcxdr_set_accept_stat(rqstp)) 169962306a36Sopenharmony_ci goto auth_err; 170062306a36Sopenharmony_ci /* Delete the entry from the cache_list and call cache_put */ 170162306a36Sopenharmony_ci sunrpc_cache_unhash(sn->rsc_cache, &rsci->h); 170262306a36Sopenharmony_ci goto complete; 170362306a36Sopenharmony_ci case RPC_GSS_PROC_DATA: 170462306a36Sopenharmony_ci rqstp->rq_auth_stat = rpcsec_gsserr_ctxproblem; 170562306a36Sopenharmony_ci if (!svcauth_gss_encode_verf(rqstp, rsci->mechctx, gc->gc_seq)) 170662306a36Sopenharmony_ci goto auth_err; 170762306a36Sopenharmony_ci if (!svcxdr_set_accept_stat(rqstp)) 170862306a36Sopenharmony_ci goto auth_err; 170962306a36Sopenharmony_ci svcdata->gsd_databody_offset = xdr_stream_pos(&rqstp->rq_res_stream); 171062306a36Sopenharmony_ci rqstp->rq_cred = rsci->cred; 171162306a36Sopenharmony_ci get_group_info(rsci->cred.cr_group_info); 171262306a36Sopenharmony_ci rqstp->rq_auth_stat = rpc_autherr_badcred; 171362306a36Sopenharmony_ci switch (gc->gc_svc) { 171462306a36Sopenharmony_ci case RPC_GSS_SVC_NONE: 171562306a36Sopenharmony_ci break; 171662306a36Sopenharmony_ci case RPC_GSS_SVC_INTEGRITY: 171762306a36Sopenharmony_ci /* placeholders for body length and seq. number: */ 171862306a36Sopenharmony_ci xdr_reserve_space(&rqstp->rq_res_stream, XDR_UNIT * 2); 171962306a36Sopenharmony_ci if (svcauth_gss_unwrap_integ(rqstp, gc->gc_seq, 172062306a36Sopenharmony_ci rsci->mechctx)) 172162306a36Sopenharmony_ci goto garbage_args; 172262306a36Sopenharmony_ci svcxdr_set_auth_slack(rqstp, RPC_MAX_AUTH_SIZE); 172362306a36Sopenharmony_ci break; 172462306a36Sopenharmony_ci case RPC_GSS_SVC_PRIVACY: 172562306a36Sopenharmony_ci /* placeholders for body length and seq. number: */ 172662306a36Sopenharmony_ci xdr_reserve_space(&rqstp->rq_res_stream, XDR_UNIT * 2); 172762306a36Sopenharmony_ci if (svcauth_gss_unwrap_priv(rqstp, gc->gc_seq, 172862306a36Sopenharmony_ci rsci->mechctx)) 172962306a36Sopenharmony_ci goto garbage_args; 173062306a36Sopenharmony_ci svcxdr_set_auth_slack(rqstp, RPC_MAX_AUTH_SIZE * 2); 173162306a36Sopenharmony_ci break; 173262306a36Sopenharmony_ci default: 173362306a36Sopenharmony_ci goto auth_err; 173462306a36Sopenharmony_ci } 173562306a36Sopenharmony_ci svcdata->rsci = rsci; 173662306a36Sopenharmony_ci cache_get(&rsci->h); 173762306a36Sopenharmony_ci rqstp->rq_cred.cr_flavor = gss_svc_to_pseudoflavor( 173862306a36Sopenharmony_ci rsci->mechctx->mech_type, 173962306a36Sopenharmony_ci GSS_C_QOP_DEFAULT, 174062306a36Sopenharmony_ci gc->gc_svc); 174162306a36Sopenharmony_ci ret = SVC_OK; 174262306a36Sopenharmony_ci trace_rpcgss_svc_authenticate(rqstp, gc); 174362306a36Sopenharmony_ci goto out; 174462306a36Sopenharmony_ci } 174562306a36Sopenharmony_cigarbage_args: 174662306a36Sopenharmony_ci ret = SVC_GARBAGE; 174762306a36Sopenharmony_ci goto out; 174862306a36Sopenharmony_ciauth_err: 174962306a36Sopenharmony_ci xdr_truncate_encode(&rqstp->rq_res_stream, XDR_UNIT * 2); 175062306a36Sopenharmony_ci ret = SVC_DENIED; 175162306a36Sopenharmony_ci goto out; 175262306a36Sopenharmony_cicomplete: 175362306a36Sopenharmony_ci ret = SVC_COMPLETE; 175462306a36Sopenharmony_ci goto out; 175562306a36Sopenharmony_cidrop: 175662306a36Sopenharmony_ci ret = SVC_CLOSE; 175762306a36Sopenharmony_ciout: 175862306a36Sopenharmony_ci if (rsci) 175962306a36Sopenharmony_ci cache_put(&rsci->h, sn->rsc_cache); 176062306a36Sopenharmony_ci return ret; 176162306a36Sopenharmony_ci} 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_cistatic u32 176462306a36Sopenharmony_cisvcauth_gss_prepare_to_wrap(struct svc_rqst *rqstp, struct gss_svc_data *gsd) 176562306a36Sopenharmony_ci{ 176662306a36Sopenharmony_ci u32 offset; 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci /* Release can be called twice, but we only wrap once. */ 176962306a36Sopenharmony_ci offset = gsd->gsd_databody_offset; 177062306a36Sopenharmony_ci gsd->gsd_databody_offset = 0; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci /* AUTH_ERROR replies are not wrapped. */ 177362306a36Sopenharmony_ci if (rqstp->rq_auth_stat != rpc_auth_ok) 177462306a36Sopenharmony_ci return 0; 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci /* Also don't wrap if the accept_stat is nonzero: */ 177762306a36Sopenharmony_ci if (*rqstp->rq_accept_statp != rpc_success) 177862306a36Sopenharmony_ci return 0; 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci return offset; 178162306a36Sopenharmony_ci} 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci/* 178462306a36Sopenharmony_ci * RFC 2203, Section 5.3.2.2 178562306a36Sopenharmony_ci * 178662306a36Sopenharmony_ci * struct rpc_gss_integ_data { 178762306a36Sopenharmony_ci * opaque databody_integ<>; 178862306a36Sopenharmony_ci * opaque checksum<>; 178962306a36Sopenharmony_ci * }; 179062306a36Sopenharmony_ci * 179162306a36Sopenharmony_ci * struct rpc_gss_data_t { 179262306a36Sopenharmony_ci * unsigned int seq_num; 179362306a36Sopenharmony_ci * proc_req_arg_t arg; 179462306a36Sopenharmony_ci * }; 179562306a36Sopenharmony_ci * 179662306a36Sopenharmony_ci * The RPC Reply message has already been XDR-encoded. rq_res_stream 179762306a36Sopenharmony_ci * is now positioned so that the checksum can be written just past 179862306a36Sopenharmony_ci * the RPC Reply message. 179962306a36Sopenharmony_ci */ 180062306a36Sopenharmony_cistatic int svcauth_gss_wrap_integ(struct svc_rqst *rqstp) 180162306a36Sopenharmony_ci{ 180262306a36Sopenharmony_ci struct gss_svc_data *gsd = rqstp->rq_auth_data; 180362306a36Sopenharmony_ci struct xdr_stream *xdr = &rqstp->rq_res_stream; 180462306a36Sopenharmony_ci struct rpc_gss_wire_cred *gc = &gsd->clcred; 180562306a36Sopenharmony_ci struct xdr_buf *buf = xdr->buf; 180662306a36Sopenharmony_ci struct xdr_buf databody_integ; 180762306a36Sopenharmony_ci struct xdr_netobj checksum; 180862306a36Sopenharmony_ci u32 offset, maj_stat; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci offset = svcauth_gss_prepare_to_wrap(rqstp, gsd); 181162306a36Sopenharmony_ci if (!offset) 181262306a36Sopenharmony_ci goto out; 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci if (xdr_buf_subsegment(buf, &databody_integ, offset + XDR_UNIT, 181562306a36Sopenharmony_ci buf->len - offset - XDR_UNIT)) 181662306a36Sopenharmony_ci goto wrap_failed; 181762306a36Sopenharmony_ci /* Buffer space for these has already been reserved in 181862306a36Sopenharmony_ci * svcauth_gss_accept(). */ 181962306a36Sopenharmony_ci if (xdr_encode_word(buf, offset, databody_integ.len)) 182062306a36Sopenharmony_ci goto wrap_failed; 182162306a36Sopenharmony_ci if (xdr_encode_word(buf, offset + XDR_UNIT, gc->gc_seq)) 182262306a36Sopenharmony_ci goto wrap_failed; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci checksum.data = gsd->gsd_scratch; 182562306a36Sopenharmony_ci maj_stat = gss_get_mic(gsd->rsci->mechctx, &databody_integ, &checksum); 182662306a36Sopenharmony_ci if (maj_stat != GSS_S_COMPLETE) 182762306a36Sopenharmony_ci goto bad_mic; 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci if (xdr_stream_encode_opaque(xdr, checksum.data, checksum.len) < 0) 183062306a36Sopenharmony_ci goto wrap_failed; 183162306a36Sopenharmony_ci xdr_commit_encode(xdr); 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ciout: 183462306a36Sopenharmony_ci return 0; 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_cibad_mic: 183762306a36Sopenharmony_ci trace_rpcgss_svc_get_mic(rqstp, maj_stat); 183862306a36Sopenharmony_ci return -EINVAL; 183962306a36Sopenharmony_ciwrap_failed: 184062306a36Sopenharmony_ci trace_rpcgss_svc_wrap_failed(rqstp); 184162306a36Sopenharmony_ci return -EINVAL; 184262306a36Sopenharmony_ci} 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci/* 184562306a36Sopenharmony_ci * RFC 2203, Section 5.3.2.3 184662306a36Sopenharmony_ci * 184762306a36Sopenharmony_ci * struct rpc_gss_priv_data { 184862306a36Sopenharmony_ci * opaque databody_priv<> 184962306a36Sopenharmony_ci * }; 185062306a36Sopenharmony_ci * 185162306a36Sopenharmony_ci * struct rpc_gss_data_t { 185262306a36Sopenharmony_ci * unsigned int seq_num; 185362306a36Sopenharmony_ci * proc_req_arg_t arg; 185462306a36Sopenharmony_ci * }; 185562306a36Sopenharmony_ci * 185662306a36Sopenharmony_ci * gss_wrap() expands the size of the RPC message payload in the 185762306a36Sopenharmony_ci * response buffer. The main purpose of svcauth_gss_wrap_priv() 185862306a36Sopenharmony_ci * is to ensure there is adequate space in the response buffer to 185962306a36Sopenharmony_ci * avoid overflow during the wrap. 186062306a36Sopenharmony_ci */ 186162306a36Sopenharmony_cistatic int svcauth_gss_wrap_priv(struct svc_rqst *rqstp) 186262306a36Sopenharmony_ci{ 186362306a36Sopenharmony_ci struct gss_svc_data *gsd = rqstp->rq_auth_data; 186462306a36Sopenharmony_ci struct rpc_gss_wire_cred *gc = &gsd->clcred; 186562306a36Sopenharmony_ci struct xdr_buf *buf = &rqstp->rq_res; 186662306a36Sopenharmony_ci struct kvec *head = buf->head; 186762306a36Sopenharmony_ci struct kvec *tail = buf->tail; 186862306a36Sopenharmony_ci u32 offset, pad, maj_stat; 186962306a36Sopenharmony_ci __be32 *p; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci offset = svcauth_gss_prepare_to_wrap(rqstp, gsd); 187262306a36Sopenharmony_ci if (!offset) 187362306a36Sopenharmony_ci return 0; 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci /* 187662306a36Sopenharmony_ci * Buffer space for this field has already been reserved 187762306a36Sopenharmony_ci * in svcauth_gss_accept(). Note that the GSS sequence 187862306a36Sopenharmony_ci * number is encrypted along with the RPC reply payload. 187962306a36Sopenharmony_ci */ 188062306a36Sopenharmony_ci if (xdr_encode_word(buf, offset + XDR_UNIT, gc->gc_seq)) 188162306a36Sopenharmony_ci goto wrap_failed; 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci /* 188462306a36Sopenharmony_ci * If there is currently tail data, make sure there is 188562306a36Sopenharmony_ci * room for the head, tail, and 2 * RPC_MAX_AUTH_SIZE in 188662306a36Sopenharmony_ci * the page, and move the current tail data such that 188762306a36Sopenharmony_ci * there is RPC_MAX_AUTH_SIZE slack space available in 188862306a36Sopenharmony_ci * both the head and tail. 188962306a36Sopenharmony_ci */ 189062306a36Sopenharmony_ci if (tail->iov_base) { 189162306a36Sopenharmony_ci if (tail->iov_base >= head->iov_base + PAGE_SIZE) 189262306a36Sopenharmony_ci goto wrap_failed; 189362306a36Sopenharmony_ci if (tail->iov_base < head->iov_base) 189462306a36Sopenharmony_ci goto wrap_failed; 189562306a36Sopenharmony_ci if (tail->iov_len + head->iov_len 189662306a36Sopenharmony_ci + 2 * RPC_MAX_AUTH_SIZE > PAGE_SIZE) 189762306a36Sopenharmony_ci goto wrap_failed; 189862306a36Sopenharmony_ci memmove(tail->iov_base + RPC_MAX_AUTH_SIZE, tail->iov_base, 189962306a36Sopenharmony_ci tail->iov_len); 190062306a36Sopenharmony_ci tail->iov_base += RPC_MAX_AUTH_SIZE; 190162306a36Sopenharmony_ci } 190262306a36Sopenharmony_ci /* 190362306a36Sopenharmony_ci * If there is no current tail data, make sure there is 190462306a36Sopenharmony_ci * room for the head data, and 2 * RPC_MAX_AUTH_SIZE in the 190562306a36Sopenharmony_ci * allotted page, and set up tail information such that there 190662306a36Sopenharmony_ci * is RPC_MAX_AUTH_SIZE slack space available in both the 190762306a36Sopenharmony_ci * head and tail. 190862306a36Sopenharmony_ci */ 190962306a36Sopenharmony_ci if (!tail->iov_base) { 191062306a36Sopenharmony_ci if (head->iov_len + 2 * RPC_MAX_AUTH_SIZE > PAGE_SIZE) 191162306a36Sopenharmony_ci goto wrap_failed; 191262306a36Sopenharmony_ci tail->iov_base = head->iov_base 191362306a36Sopenharmony_ci + head->iov_len + RPC_MAX_AUTH_SIZE; 191462306a36Sopenharmony_ci tail->iov_len = 0; 191562306a36Sopenharmony_ci } 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci maj_stat = gss_wrap(gsd->rsci->mechctx, offset + XDR_UNIT, buf, 191862306a36Sopenharmony_ci buf->pages); 191962306a36Sopenharmony_ci if (maj_stat != GSS_S_COMPLETE) 192062306a36Sopenharmony_ci goto bad_wrap; 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci /* Wrapping can change the size of databody_priv. */ 192362306a36Sopenharmony_ci if (xdr_encode_word(buf, offset, buf->len - offset - XDR_UNIT)) 192462306a36Sopenharmony_ci goto wrap_failed; 192562306a36Sopenharmony_ci pad = xdr_pad_size(buf->len - offset - XDR_UNIT); 192662306a36Sopenharmony_ci p = (__be32 *)(tail->iov_base + tail->iov_len); 192762306a36Sopenharmony_ci memset(p, 0, pad); 192862306a36Sopenharmony_ci tail->iov_len += pad; 192962306a36Sopenharmony_ci buf->len += pad; 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci return 0; 193262306a36Sopenharmony_ciwrap_failed: 193362306a36Sopenharmony_ci trace_rpcgss_svc_wrap_failed(rqstp); 193462306a36Sopenharmony_ci return -EINVAL; 193562306a36Sopenharmony_cibad_wrap: 193662306a36Sopenharmony_ci trace_rpcgss_svc_wrap(rqstp, maj_stat); 193762306a36Sopenharmony_ci return -ENOMEM; 193862306a36Sopenharmony_ci} 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci/** 194162306a36Sopenharmony_ci * svcauth_gss_release - Wrap payload and release resources 194262306a36Sopenharmony_ci * @rqstp: RPC transaction context 194362306a36Sopenharmony_ci * 194462306a36Sopenharmony_ci * Return values: 194562306a36Sopenharmony_ci * %0: the Reply is ready to be sent 194662306a36Sopenharmony_ci * %-ENOMEM: failed to allocate memory 194762306a36Sopenharmony_ci * %-EINVAL: encoding error 194862306a36Sopenharmony_ci */ 194962306a36Sopenharmony_cistatic int 195062306a36Sopenharmony_cisvcauth_gss_release(struct svc_rqst *rqstp) 195162306a36Sopenharmony_ci{ 195262306a36Sopenharmony_ci struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id); 195362306a36Sopenharmony_ci struct gss_svc_data *gsd = rqstp->rq_auth_data; 195462306a36Sopenharmony_ci struct rpc_gss_wire_cred *gc; 195562306a36Sopenharmony_ci int stat; 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci if (!gsd) 195862306a36Sopenharmony_ci goto out; 195962306a36Sopenharmony_ci gc = &gsd->clcred; 196062306a36Sopenharmony_ci if (gc->gc_proc != RPC_GSS_PROC_DATA) 196162306a36Sopenharmony_ci goto out; 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci switch (gc->gc_svc) { 196462306a36Sopenharmony_ci case RPC_GSS_SVC_NONE: 196562306a36Sopenharmony_ci break; 196662306a36Sopenharmony_ci case RPC_GSS_SVC_INTEGRITY: 196762306a36Sopenharmony_ci stat = svcauth_gss_wrap_integ(rqstp); 196862306a36Sopenharmony_ci if (stat) 196962306a36Sopenharmony_ci goto out_err; 197062306a36Sopenharmony_ci break; 197162306a36Sopenharmony_ci case RPC_GSS_SVC_PRIVACY: 197262306a36Sopenharmony_ci stat = svcauth_gss_wrap_priv(rqstp); 197362306a36Sopenharmony_ci if (stat) 197462306a36Sopenharmony_ci goto out_err; 197562306a36Sopenharmony_ci break; 197662306a36Sopenharmony_ci /* 197762306a36Sopenharmony_ci * For any other gc_svc value, svcauth_gss_accept() already set 197862306a36Sopenharmony_ci * the auth_error appropriately; just fall through: 197962306a36Sopenharmony_ci */ 198062306a36Sopenharmony_ci } 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ciout: 198362306a36Sopenharmony_ci stat = 0; 198462306a36Sopenharmony_ciout_err: 198562306a36Sopenharmony_ci if (rqstp->rq_client) 198662306a36Sopenharmony_ci auth_domain_put(rqstp->rq_client); 198762306a36Sopenharmony_ci rqstp->rq_client = NULL; 198862306a36Sopenharmony_ci if (rqstp->rq_gssclient) 198962306a36Sopenharmony_ci auth_domain_put(rqstp->rq_gssclient); 199062306a36Sopenharmony_ci rqstp->rq_gssclient = NULL; 199162306a36Sopenharmony_ci if (rqstp->rq_cred.cr_group_info) 199262306a36Sopenharmony_ci put_group_info(rqstp->rq_cred.cr_group_info); 199362306a36Sopenharmony_ci rqstp->rq_cred.cr_group_info = NULL; 199462306a36Sopenharmony_ci if (gsd && gsd->rsci) { 199562306a36Sopenharmony_ci cache_put(&gsd->rsci->h, sn->rsc_cache); 199662306a36Sopenharmony_ci gsd->rsci = NULL; 199762306a36Sopenharmony_ci } 199862306a36Sopenharmony_ci return stat; 199962306a36Sopenharmony_ci} 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_cistatic void 200262306a36Sopenharmony_cisvcauth_gss_domain_release_rcu(struct rcu_head *head) 200362306a36Sopenharmony_ci{ 200462306a36Sopenharmony_ci struct auth_domain *dom = container_of(head, struct auth_domain, rcu_head); 200562306a36Sopenharmony_ci struct gss_domain *gd = container_of(dom, struct gss_domain, h); 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci kfree(dom->name); 200862306a36Sopenharmony_ci kfree(gd); 200962306a36Sopenharmony_ci} 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_cistatic void 201262306a36Sopenharmony_cisvcauth_gss_domain_release(struct auth_domain *dom) 201362306a36Sopenharmony_ci{ 201462306a36Sopenharmony_ci call_rcu(&dom->rcu_head, svcauth_gss_domain_release_rcu); 201562306a36Sopenharmony_ci} 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_cistatic struct auth_ops svcauthops_gss = { 201862306a36Sopenharmony_ci .name = "rpcsec_gss", 201962306a36Sopenharmony_ci .owner = THIS_MODULE, 202062306a36Sopenharmony_ci .flavour = RPC_AUTH_GSS, 202162306a36Sopenharmony_ci .accept = svcauth_gss_accept, 202262306a36Sopenharmony_ci .release = svcauth_gss_release, 202362306a36Sopenharmony_ci .domain_release = svcauth_gss_domain_release, 202462306a36Sopenharmony_ci .set_client = svcauth_gss_set_client, 202562306a36Sopenharmony_ci}; 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_cistatic int rsi_cache_create_net(struct net *net) 202862306a36Sopenharmony_ci{ 202962306a36Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 203062306a36Sopenharmony_ci struct cache_detail *cd; 203162306a36Sopenharmony_ci int err; 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci cd = cache_create_net(&rsi_cache_template, net); 203462306a36Sopenharmony_ci if (IS_ERR(cd)) 203562306a36Sopenharmony_ci return PTR_ERR(cd); 203662306a36Sopenharmony_ci err = cache_register_net(cd, net); 203762306a36Sopenharmony_ci if (err) { 203862306a36Sopenharmony_ci cache_destroy_net(cd, net); 203962306a36Sopenharmony_ci return err; 204062306a36Sopenharmony_ci } 204162306a36Sopenharmony_ci sn->rsi_cache = cd; 204262306a36Sopenharmony_ci return 0; 204362306a36Sopenharmony_ci} 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_cistatic void rsi_cache_destroy_net(struct net *net) 204662306a36Sopenharmony_ci{ 204762306a36Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 204862306a36Sopenharmony_ci struct cache_detail *cd = sn->rsi_cache; 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci sn->rsi_cache = NULL; 205162306a36Sopenharmony_ci cache_purge(cd); 205262306a36Sopenharmony_ci cache_unregister_net(cd, net); 205362306a36Sopenharmony_ci cache_destroy_net(cd, net); 205462306a36Sopenharmony_ci} 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_cistatic int rsc_cache_create_net(struct net *net) 205762306a36Sopenharmony_ci{ 205862306a36Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 205962306a36Sopenharmony_ci struct cache_detail *cd; 206062306a36Sopenharmony_ci int err; 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci cd = cache_create_net(&rsc_cache_template, net); 206362306a36Sopenharmony_ci if (IS_ERR(cd)) 206462306a36Sopenharmony_ci return PTR_ERR(cd); 206562306a36Sopenharmony_ci err = cache_register_net(cd, net); 206662306a36Sopenharmony_ci if (err) { 206762306a36Sopenharmony_ci cache_destroy_net(cd, net); 206862306a36Sopenharmony_ci return err; 206962306a36Sopenharmony_ci } 207062306a36Sopenharmony_ci sn->rsc_cache = cd; 207162306a36Sopenharmony_ci return 0; 207262306a36Sopenharmony_ci} 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_cistatic void rsc_cache_destroy_net(struct net *net) 207562306a36Sopenharmony_ci{ 207662306a36Sopenharmony_ci struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); 207762306a36Sopenharmony_ci struct cache_detail *cd = sn->rsc_cache; 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci sn->rsc_cache = NULL; 208062306a36Sopenharmony_ci cache_purge(cd); 208162306a36Sopenharmony_ci cache_unregister_net(cd, net); 208262306a36Sopenharmony_ci cache_destroy_net(cd, net); 208362306a36Sopenharmony_ci} 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ciint 208662306a36Sopenharmony_cigss_svc_init_net(struct net *net) 208762306a36Sopenharmony_ci{ 208862306a36Sopenharmony_ci int rv; 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci rv = rsc_cache_create_net(net); 209162306a36Sopenharmony_ci if (rv) 209262306a36Sopenharmony_ci return rv; 209362306a36Sopenharmony_ci rv = rsi_cache_create_net(net); 209462306a36Sopenharmony_ci if (rv) 209562306a36Sopenharmony_ci goto out1; 209662306a36Sopenharmony_ci rv = create_use_gss_proxy_proc_entry(net); 209762306a36Sopenharmony_ci if (rv) 209862306a36Sopenharmony_ci goto out2; 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci rv = create_krb5_enctypes_proc_entry(net); 210162306a36Sopenharmony_ci if (rv) 210262306a36Sopenharmony_ci goto out3; 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci return 0; 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ciout3: 210762306a36Sopenharmony_ci destroy_use_gss_proxy_proc_entry(net); 210862306a36Sopenharmony_ciout2: 210962306a36Sopenharmony_ci rsi_cache_destroy_net(net); 211062306a36Sopenharmony_ciout1: 211162306a36Sopenharmony_ci rsc_cache_destroy_net(net); 211262306a36Sopenharmony_ci return rv; 211362306a36Sopenharmony_ci} 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_civoid 211662306a36Sopenharmony_cigss_svc_shutdown_net(struct net *net) 211762306a36Sopenharmony_ci{ 211862306a36Sopenharmony_ci destroy_krb5_enctypes_proc_entry(net); 211962306a36Sopenharmony_ci destroy_use_gss_proxy_proc_entry(net); 212062306a36Sopenharmony_ci rsi_cache_destroy_net(net); 212162306a36Sopenharmony_ci rsc_cache_destroy_net(net); 212262306a36Sopenharmony_ci} 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ciint 212562306a36Sopenharmony_cigss_svc_init(void) 212662306a36Sopenharmony_ci{ 212762306a36Sopenharmony_ci return svc_auth_register(RPC_AUTH_GSS, &svcauthops_gss); 212862306a36Sopenharmony_ci} 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_civoid 213162306a36Sopenharmony_cigss_svc_shutdown(void) 213262306a36Sopenharmony_ci{ 213362306a36Sopenharmony_ci svc_auth_unregister(RPC_AUTH_GSS); 213462306a36Sopenharmony_ci} 2135