162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Mapping of UID/GIDs to name and vice versa. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2002, 2003 The Regents of the University of 562306a36Sopenharmony_ci * Michigan. All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Marius Aamodt Eriksen <marius@umich.edu> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 1062306a36Sopenharmony_ci * modification, are permitted provided that the following conditions 1162306a36Sopenharmony_ci * are met: 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 1462306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 1562306a36Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 1662306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 1762306a36Sopenharmony_ci * documentation and/or other materials provided with the distribution. 1862306a36Sopenharmony_ci * 3. Neither the name of the University nor the names of its 1962306a36Sopenharmony_ci * contributors may be used to endorse or promote products derived 2062306a36Sopenharmony_ci * from this software without specific prior written permission. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 2362306a36Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 2462306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 2562306a36Sopenharmony_ci * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2662306a36Sopenharmony_ci * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2762306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2862306a36Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 2962306a36Sopenharmony_ci * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 3062306a36Sopenharmony_ci * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 3162306a36Sopenharmony_ci * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 3262306a36Sopenharmony_ci * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <linux/module.h> 3662306a36Sopenharmony_ci#include <linux/seq_file.h> 3762306a36Sopenharmony_ci#include <linux/sched.h> 3862306a36Sopenharmony_ci#include <linux/slab.h> 3962306a36Sopenharmony_ci#include <linux/sunrpc/svc_xprt.h> 4062306a36Sopenharmony_ci#include <net/net_namespace.h> 4162306a36Sopenharmony_ci#include "idmap.h" 4262306a36Sopenharmony_ci#include "nfsd.h" 4362306a36Sopenharmony_ci#include "netns.h" 4462306a36Sopenharmony_ci#include "vfs.h" 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * Turn off idmapping when using AUTH_SYS. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_cistatic bool nfs4_disable_idmapping = true; 5062306a36Sopenharmony_cimodule_param(nfs4_disable_idmapping, bool, 0644); 5162306a36Sopenharmony_ciMODULE_PARM_DESC(nfs4_disable_idmapping, 5262306a36Sopenharmony_ci "Turn off server's NFSv4 idmapping when using 'sec=sys'"); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* 5562306a36Sopenharmony_ci * Cache entry 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * XXX we know that IDMAP_NAMESZ < PAGE_SIZE, but it's ugly to rely on 6062306a36Sopenharmony_ci * that. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistruct ent { 6462306a36Sopenharmony_ci struct cache_head h; 6562306a36Sopenharmony_ci int type; /* User / Group */ 6662306a36Sopenharmony_ci u32 id; 6762306a36Sopenharmony_ci char name[IDMAP_NAMESZ]; 6862306a36Sopenharmony_ci char authname[IDMAP_NAMESZ]; 6962306a36Sopenharmony_ci struct rcu_head rcu_head; 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* Common entry handling */ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define ENT_HASHBITS 8 7562306a36Sopenharmony_ci#define ENT_HASHMAX (1 << ENT_HASHBITS) 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic void 7862306a36Sopenharmony_cient_init(struct cache_head *cnew, struct cache_head *citm) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct ent *new = container_of(cnew, struct ent, h); 8162306a36Sopenharmony_ci struct ent *itm = container_of(citm, struct ent, h); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci new->id = itm->id; 8462306a36Sopenharmony_ci new->type = itm->type; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci strscpy(new->name, itm->name, sizeof(new->name)); 8762306a36Sopenharmony_ci strscpy(new->authname, itm->authname, sizeof(new->authname)); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic void 9162306a36Sopenharmony_cient_put(struct kref *ref) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct ent *map = container_of(ref, struct ent, h.ref); 9462306a36Sopenharmony_ci kfree_rcu(map, rcu_head); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic struct cache_head * 9862306a36Sopenharmony_cient_alloc(void) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct ent *e = kmalloc(sizeof(*e), GFP_KERNEL); 10162306a36Sopenharmony_ci if (e) 10262306a36Sopenharmony_ci return &e->h; 10362306a36Sopenharmony_ci else 10462306a36Sopenharmony_ci return NULL; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* 10862306a36Sopenharmony_ci * ID -> Name cache 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic uint32_t 11262306a36Sopenharmony_ciidtoname_hash(struct ent *ent) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci uint32_t hash; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci hash = hash_str(ent->authname, ENT_HASHBITS); 11762306a36Sopenharmony_ci hash = hash_long(hash ^ ent->id, ENT_HASHBITS); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* Flip LSB for user/group */ 12062306a36Sopenharmony_ci if (ent->type == IDMAP_TYPE_GROUP) 12162306a36Sopenharmony_ci hash ^= 1; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return hash; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int 12762306a36Sopenharmony_ciidtoname_upcall(struct cache_detail *cd, struct cache_head *h) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci return sunrpc_cache_pipe_upcall_timeout(cd, h); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic void 13362306a36Sopenharmony_ciidtoname_request(struct cache_detail *cd, struct cache_head *ch, char **bpp, 13462306a36Sopenharmony_ci int *blen) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct ent *ent = container_of(ch, struct ent, h); 13762306a36Sopenharmony_ci char idstr[11]; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci qword_add(bpp, blen, ent->authname); 14062306a36Sopenharmony_ci snprintf(idstr, sizeof(idstr), "%u", ent->id); 14162306a36Sopenharmony_ci qword_add(bpp, blen, ent->type == IDMAP_TYPE_GROUP ? "group" : "user"); 14262306a36Sopenharmony_ci qword_add(bpp, blen, idstr); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci (*bpp)[-1] = '\n'; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int 14862306a36Sopenharmony_ciidtoname_match(struct cache_head *ca, struct cache_head *cb) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct ent *a = container_of(ca, struct ent, h); 15162306a36Sopenharmony_ci struct ent *b = container_of(cb, struct ent, h); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return (a->id == b->id && a->type == b->type && 15462306a36Sopenharmony_ci strcmp(a->authname, b->authname) == 0); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int 15862306a36Sopenharmony_ciidtoname_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct ent *ent; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (h == NULL) { 16362306a36Sopenharmony_ci seq_puts(m, "#domain type id [name]\n"); 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci ent = container_of(h, struct ent, h); 16762306a36Sopenharmony_ci seq_printf(m, "%s %s %u", ent->authname, 16862306a36Sopenharmony_ci ent->type == IDMAP_TYPE_GROUP ? "group" : "user", 16962306a36Sopenharmony_ci ent->id); 17062306a36Sopenharmony_ci if (test_bit(CACHE_VALID, &h->flags)) 17162306a36Sopenharmony_ci seq_printf(m, " %s", ent->name); 17262306a36Sopenharmony_ci seq_putc(m, '\n'); 17362306a36Sopenharmony_ci return 0; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic void 17762306a36Sopenharmony_ciwarn_no_idmapd(struct cache_detail *detail, int has_died) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci printk("nfsd: nfsv4 idmapping failing: has idmapd %s?\n", 18062306a36Sopenharmony_ci has_died ? "died" : "not been started"); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int idtoname_parse(struct cache_detail *, char *, int); 18562306a36Sopenharmony_cistatic struct ent *idtoname_lookup(struct cache_detail *, struct ent *); 18662306a36Sopenharmony_cistatic struct ent *idtoname_update(struct cache_detail *, struct ent *, 18762306a36Sopenharmony_ci struct ent *); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic const struct cache_detail idtoname_cache_template = { 19062306a36Sopenharmony_ci .owner = THIS_MODULE, 19162306a36Sopenharmony_ci .hash_size = ENT_HASHMAX, 19262306a36Sopenharmony_ci .name = "nfs4.idtoname", 19362306a36Sopenharmony_ci .cache_put = ent_put, 19462306a36Sopenharmony_ci .cache_upcall = idtoname_upcall, 19562306a36Sopenharmony_ci .cache_request = idtoname_request, 19662306a36Sopenharmony_ci .cache_parse = idtoname_parse, 19762306a36Sopenharmony_ci .cache_show = idtoname_show, 19862306a36Sopenharmony_ci .warn_no_listener = warn_no_idmapd, 19962306a36Sopenharmony_ci .match = idtoname_match, 20062306a36Sopenharmony_ci .init = ent_init, 20162306a36Sopenharmony_ci .update = ent_init, 20262306a36Sopenharmony_ci .alloc = ent_alloc, 20362306a36Sopenharmony_ci}; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic int 20662306a36Sopenharmony_ciidtoname_parse(struct cache_detail *cd, char *buf, int buflen) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct ent ent, *res; 20962306a36Sopenharmony_ci char *buf1, *bp; 21062306a36Sopenharmony_ci int len; 21162306a36Sopenharmony_ci int error = -EINVAL; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (buf[buflen - 1] != '\n') 21462306a36Sopenharmony_ci return (-EINVAL); 21562306a36Sopenharmony_ci buf[buflen - 1]= '\0'; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci buf1 = kmalloc(PAGE_SIZE, GFP_KERNEL); 21862306a36Sopenharmony_ci if (buf1 == NULL) 21962306a36Sopenharmony_ci return (-ENOMEM); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci memset(&ent, 0, sizeof(ent)); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* Authentication name */ 22462306a36Sopenharmony_ci len = qword_get(&buf, buf1, PAGE_SIZE); 22562306a36Sopenharmony_ci if (len <= 0 || len >= IDMAP_NAMESZ) 22662306a36Sopenharmony_ci goto out; 22762306a36Sopenharmony_ci memcpy(ent.authname, buf1, sizeof(ent.authname)); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Type */ 23062306a36Sopenharmony_ci if (qword_get(&buf, buf1, PAGE_SIZE) <= 0) 23162306a36Sopenharmony_ci goto out; 23262306a36Sopenharmony_ci ent.type = strcmp(buf1, "user") == 0 ? 23362306a36Sopenharmony_ci IDMAP_TYPE_USER : IDMAP_TYPE_GROUP; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* ID */ 23662306a36Sopenharmony_ci if (qword_get(&buf, buf1, PAGE_SIZE) <= 0) 23762306a36Sopenharmony_ci goto out; 23862306a36Sopenharmony_ci ent.id = simple_strtoul(buf1, &bp, 10); 23962306a36Sopenharmony_ci if (bp == buf1) 24062306a36Sopenharmony_ci goto out; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* expiry */ 24362306a36Sopenharmony_ci error = get_expiry(&buf, &ent.h.expiry_time); 24462306a36Sopenharmony_ci if (error) 24562306a36Sopenharmony_ci goto out; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci error = -ENOMEM; 24862306a36Sopenharmony_ci res = idtoname_lookup(cd, &ent); 24962306a36Sopenharmony_ci if (!res) 25062306a36Sopenharmony_ci goto out; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Name */ 25362306a36Sopenharmony_ci error = -EINVAL; 25462306a36Sopenharmony_ci len = qword_get(&buf, buf1, PAGE_SIZE); 25562306a36Sopenharmony_ci if (len < 0 || len >= IDMAP_NAMESZ) 25662306a36Sopenharmony_ci goto out; 25762306a36Sopenharmony_ci if (len == 0) 25862306a36Sopenharmony_ci set_bit(CACHE_NEGATIVE, &ent.h.flags); 25962306a36Sopenharmony_ci else 26062306a36Sopenharmony_ci memcpy(ent.name, buf1, sizeof(ent.name)); 26162306a36Sopenharmony_ci error = -ENOMEM; 26262306a36Sopenharmony_ci res = idtoname_update(cd, &ent, res); 26362306a36Sopenharmony_ci if (res == NULL) 26462306a36Sopenharmony_ci goto out; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci cache_put(&res->h, cd); 26762306a36Sopenharmony_ci error = 0; 26862306a36Sopenharmony_ciout: 26962306a36Sopenharmony_ci kfree(buf1); 27062306a36Sopenharmony_ci return error; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic struct ent * 27462306a36Sopenharmony_ciidtoname_lookup(struct cache_detail *cd, struct ent *item) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct cache_head *ch = sunrpc_cache_lookup_rcu(cd, &item->h, 27762306a36Sopenharmony_ci idtoname_hash(item)); 27862306a36Sopenharmony_ci if (ch) 27962306a36Sopenharmony_ci return container_of(ch, struct ent, h); 28062306a36Sopenharmony_ci else 28162306a36Sopenharmony_ci return NULL; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic struct ent * 28562306a36Sopenharmony_ciidtoname_update(struct cache_detail *cd, struct ent *new, struct ent *old) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct cache_head *ch = sunrpc_cache_update(cd, &new->h, &old->h, 28862306a36Sopenharmony_ci idtoname_hash(new)); 28962306a36Sopenharmony_ci if (ch) 29062306a36Sopenharmony_ci return container_of(ch, struct ent, h); 29162306a36Sopenharmony_ci else 29262306a36Sopenharmony_ci return NULL; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci/* 29762306a36Sopenharmony_ci * Name -> ID cache 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic inline int 30162306a36Sopenharmony_cinametoid_hash(struct ent *ent) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci return hash_str(ent->name, ENT_HASHBITS); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic int 30762306a36Sopenharmony_cinametoid_upcall(struct cache_detail *cd, struct cache_head *h) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci return sunrpc_cache_pipe_upcall_timeout(cd, h); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic void 31362306a36Sopenharmony_cinametoid_request(struct cache_detail *cd, struct cache_head *ch, char **bpp, 31462306a36Sopenharmony_ci int *blen) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct ent *ent = container_of(ch, struct ent, h); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci qword_add(bpp, blen, ent->authname); 31962306a36Sopenharmony_ci qword_add(bpp, blen, ent->type == IDMAP_TYPE_GROUP ? "group" : "user"); 32062306a36Sopenharmony_ci qword_add(bpp, blen, ent->name); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci (*bpp)[-1] = '\n'; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic int 32662306a36Sopenharmony_cinametoid_match(struct cache_head *ca, struct cache_head *cb) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct ent *a = container_of(ca, struct ent, h); 32962306a36Sopenharmony_ci struct ent *b = container_of(cb, struct ent, h); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return (a->type == b->type && strcmp(a->name, b->name) == 0 && 33262306a36Sopenharmony_ci strcmp(a->authname, b->authname) == 0); 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int 33662306a36Sopenharmony_cinametoid_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct ent *ent; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (h == NULL) { 34162306a36Sopenharmony_ci seq_puts(m, "#domain type name [id]\n"); 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci ent = container_of(h, struct ent, h); 34562306a36Sopenharmony_ci seq_printf(m, "%s %s %s", ent->authname, 34662306a36Sopenharmony_ci ent->type == IDMAP_TYPE_GROUP ? "group" : "user", 34762306a36Sopenharmony_ci ent->name); 34862306a36Sopenharmony_ci if (test_bit(CACHE_VALID, &h->flags)) 34962306a36Sopenharmony_ci seq_printf(m, " %u", ent->id); 35062306a36Sopenharmony_ci seq_putc(m, '\n'); 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic struct ent *nametoid_lookup(struct cache_detail *, struct ent *); 35562306a36Sopenharmony_cistatic struct ent *nametoid_update(struct cache_detail *, struct ent *, 35662306a36Sopenharmony_ci struct ent *); 35762306a36Sopenharmony_cistatic int nametoid_parse(struct cache_detail *, char *, int); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic const struct cache_detail nametoid_cache_template = { 36062306a36Sopenharmony_ci .owner = THIS_MODULE, 36162306a36Sopenharmony_ci .hash_size = ENT_HASHMAX, 36262306a36Sopenharmony_ci .name = "nfs4.nametoid", 36362306a36Sopenharmony_ci .cache_put = ent_put, 36462306a36Sopenharmony_ci .cache_upcall = nametoid_upcall, 36562306a36Sopenharmony_ci .cache_request = nametoid_request, 36662306a36Sopenharmony_ci .cache_parse = nametoid_parse, 36762306a36Sopenharmony_ci .cache_show = nametoid_show, 36862306a36Sopenharmony_ci .warn_no_listener = warn_no_idmapd, 36962306a36Sopenharmony_ci .match = nametoid_match, 37062306a36Sopenharmony_ci .init = ent_init, 37162306a36Sopenharmony_ci .update = ent_init, 37262306a36Sopenharmony_ci .alloc = ent_alloc, 37362306a36Sopenharmony_ci}; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic int 37662306a36Sopenharmony_cinametoid_parse(struct cache_detail *cd, char *buf, int buflen) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct ent ent, *res; 37962306a36Sopenharmony_ci char *buf1; 38062306a36Sopenharmony_ci int len, error = -EINVAL; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (buf[buflen - 1] != '\n') 38362306a36Sopenharmony_ci return (-EINVAL); 38462306a36Sopenharmony_ci buf[buflen - 1]= '\0'; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci buf1 = kmalloc(PAGE_SIZE, GFP_KERNEL); 38762306a36Sopenharmony_ci if (buf1 == NULL) 38862306a36Sopenharmony_ci return (-ENOMEM); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci memset(&ent, 0, sizeof(ent)); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* Authentication name */ 39362306a36Sopenharmony_ci len = qword_get(&buf, buf1, PAGE_SIZE); 39462306a36Sopenharmony_ci if (len <= 0 || len >= IDMAP_NAMESZ) 39562306a36Sopenharmony_ci goto out; 39662306a36Sopenharmony_ci memcpy(ent.authname, buf1, sizeof(ent.authname)); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* Type */ 39962306a36Sopenharmony_ci if (qword_get(&buf, buf1, PAGE_SIZE) <= 0) 40062306a36Sopenharmony_ci goto out; 40162306a36Sopenharmony_ci ent.type = strcmp(buf1, "user") == 0 ? 40262306a36Sopenharmony_ci IDMAP_TYPE_USER : IDMAP_TYPE_GROUP; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* Name */ 40562306a36Sopenharmony_ci len = qword_get(&buf, buf1, PAGE_SIZE); 40662306a36Sopenharmony_ci if (len <= 0 || len >= IDMAP_NAMESZ) 40762306a36Sopenharmony_ci goto out; 40862306a36Sopenharmony_ci memcpy(ent.name, buf1, sizeof(ent.name)); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* expiry */ 41162306a36Sopenharmony_ci error = get_expiry(&buf, &ent.h.expiry_time); 41262306a36Sopenharmony_ci if (error) 41362306a36Sopenharmony_ci goto out; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* ID */ 41662306a36Sopenharmony_ci error = get_int(&buf, &ent.id); 41762306a36Sopenharmony_ci if (error == -EINVAL) 41862306a36Sopenharmony_ci goto out; 41962306a36Sopenharmony_ci if (error == -ENOENT) 42062306a36Sopenharmony_ci set_bit(CACHE_NEGATIVE, &ent.h.flags); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci error = -ENOMEM; 42362306a36Sopenharmony_ci res = nametoid_lookup(cd, &ent); 42462306a36Sopenharmony_ci if (res == NULL) 42562306a36Sopenharmony_ci goto out; 42662306a36Sopenharmony_ci res = nametoid_update(cd, &ent, res); 42762306a36Sopenharmony_ci if (res == NULL) 42862306a36Sopenharmony_ci goto out; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci cache_put(&res->h, cd); 43162306a36Sopenharmony_ci error = 0; 43262306a36Sopenharmony_ciout: 43362306a36Sopenharmony_ci kfree(buf1); 43462306a36Sopenharmony_ci return (error); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic struct ent * 43962306a36Sopenharmony_cinametoid_lookup(struct cache_detail *cd, struct ent *item) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci struct cache_head *ch = sunrpc_cache_lookup_rcu(cd, &item->h, 44262306a36Sopenharmony_ci nametoid_hash(item)); 44362306a36Sopenharmony_ci if (ch) 44462306a36Sopenharmony_ci return container_of(ch, struct ent, h); 44562306a36Sopenharmony_ci else 44662306a36Sopenharmony_ci return NULL; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic struct ent * 45062306a36Sopenharmony_cinametoid_update(struct cache_detail *cd, struct ent *new, struct ent *old) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct cache_head *ch = sunrpc_cache_update(cd, &new->h, &old->h, 45362306a36Sopenharmony_ci nametoid_hash(new)); 45462306a36Sopenharmony_ci if (ch) 45562306a36Sopenharmony_ci return container_of(ch, struct ent, h); 45662306a36Sopenharmony_ci else 45762306a36Sopenharmony_ci return NULL; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci/* 46162306a36Sopenharmony_ci * Exported API 46262306a36Sopenharmony_ci */ 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ciint 46562306a36Sopenharmony_cinfsd_idmap_init(struct net *net) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci int rv; 46862306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(net, nfsd_net_id); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci nn->idtoname_cache = cache_create_net(&idtoname_cache_template, net); 47162306a36Sopenharmony_ci if (IS_ERR(nn->idtoname_cache)) 47262306a36Sopenharmony_ci return PTR_ERR(nn->idtoname_cache); 47362306a36Sopenharmony_ci rv = cache_register_net(nn->idtoname_cache, net); 47462306a36Sopenharmony_ci if (rv) 47562306a36Sopenharmony_ci goto destroy_idtoname_cache; 47662306a36Sopenharmony_ci nn->nametoid_cache = cache_create_net(&nametoid_cache_template, net); 47762306a36Sopenharmony_ci if (IS_ERR(nn->nametoid_cache)) { 47862306a36Sopenharmony_ci rv = PTR_ERR(nn->nametoid_cache); 47962306a36Sopenharmony_ci goto unregister_idtoname_cache; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci rv = cache_register_net(nn->nametoid_cache, net); 48262306a36Sopenharmony_ci if (rv) 48362306a36Sopenharmony_ci goto destroy_nametoid_cache; 48462306a36Sopenharmony_ci return 0; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cidestroy_nametoid_cache: 48762306a36Sopenharmony_ci cache_destroy_net(nn->nametoid_cache, net); 48862306a36Sopenharmony_ciunregister_idtoname_cache: 48962306a36Sopenharmony_ci cache_unregister_net(nn->idtoname_cache, net); 49062306a36Sopenharmony_cidestroy_idtoname_cache: 49162306a36Sopenharmony_ci cache_destroy_net(nn->idtoname_cache, net); 49262306a36Sopenharmony_ci return rv; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_civoid 49662306a36Sopenharmony_cinfsd_idmap_shutdown(struct net *net) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(net, nfsd_net_id); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci cache_unregister_net(nn->idtoname_cache, net); 50162306a36Sopenharmony_ci cache_unregister_net(nn->nametoid_cache, net); 50262306a36Sopenharmony_ci cache_destroy_net(nn->idtoname_cache, net); 50362306a36Sopenharmony_ci cache_destroy_net(nn->nametoid_cache, net); 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic int 50762306a36Sopenharmony_ciidmap_lookup(struct svc_rqst *rqstp, 50862306a36Sopenharmony_ci struct ent *(*lookup_fn)(struct cache_detail *, struct ent *), 50962306a36Sopenharmony_ci struct ent *key, struct cache_detail *detail, struct ent **item) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci int ret; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci *item = lookup_fn(detail, key); 51462306a36Sopenharmony_ci if (!*item) 51562306a36Sopenharmony_ci return -ENOMEM; 51662306a36Sopenharmony_ci retry: 51762306a36Sopenharmony_ci ret = cache_check(detail, &(*item)->h, &rqstp->rq_chandle); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (ret == -ETIMEDOUT) { 52062306a36Sopenharmony_ci struct ent *prev_item = *item; 52162306a36Sopenharmony_ci *item = lookup_fn(detail, key); 52262306a36Sopenharmony_ci if (*item != prev_item) 52362306a36Sopenharmony_ci goto retry; 52462306a36Sopenharmony_ci cache_put(&(*item)->h, detail); 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci return ret; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic char * 53062306a36Sopenharmony_cirqst_authname(struct svc_rqst *rqstp) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci struct auth_domain *clp; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci clp = rqstp->rq_gssclient ? rqstp->rq_gssclient : rqstp->rq_client; 53562306a36Sopenharmony_ci return clp->name; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic __be32 53962306a36Sopenharmony_ciidmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, 54062306a36Sopenharmony_ci u32 *id) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct ent *item, key = { 54362306a36Sopenharmony_ci .type = type, 54462306a36Sopenharmony_ci }; 54562306a36Sopenharmony_ci int ret; 54662306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (namelen + 1 > sizeof(key.name)) 54962306a36Sopenharmony_ci return nfserr_badowner; 55062306a36Sopenharmony_ci memcpy(key.name, name, namelen); 55162306a36Sopenharmony_ci key.name[namelen] = '\0'; 55262306a36Sopenharmony_ci strscpy(key.authname, rqst_authname(rqstp), sizeof(key.authname)); 55362306a36Sopenharmony_ci ret = idmap_lookup(rqstp, nametoid_lookup, &key, nn->nametoid_cache, &item); 55462306a36Sopenharmony_ci if (ret == -ENOENT) 55562306a36Sopenharmony_ci return nfserr_badowner; 55662306a36Sopenharmony_ci if (ret) 55762306a36Sopenharmony_ci return nfserrno(ret); 55862306a36Sopenharmony_ci *id = item->id; 55962306a36Sopenharmony_ci cache_put(&item->h, nn->nametoid_cache); 56062306a36Sopenharmony_ci return 0; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic __be32 encode_ascii_id(struct xdr_stream *xdr, u32 id) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci char buf[11]; 56662306a36Sopenharmony_ci int len; 56762306a36Sopenharmony_ci __be32 *p; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci len = sprintf(buf, "%u", id); 57062306a36Sopenharmony_ci p = xdr_reserve_space(xdr, len + 4); 57162306a36Sopenharmony_ci if (!p) 57262306a36Sopenharmony_ci return nfserr_resource; 57362306a36Sopenharmony_ci p = xdr_encode_opaque(p, buf, len); 57462306a36Sopenharmony_ci return 0; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic __be32 idmap_id_to_name(struct xdr_stream *xdr, 57862306a36Sopenharmony_ci struct svc_rqst *rqstp, int type, u32 id) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct ent *item, key = { 58162306a36Sopenharmony_ci .id = id, 58262306a36Sopenharmony_ci .type = type, 58362306a36Sopenharmony_ci }; 58462306a36Sopenharmony_ci __be32 *p; 58562306a36Sopenharmony_ci int ret; 58662306a36Sopenharmony_ci struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci strscpy(key.authname, rqst_authname(rqstp), sizeof(key.authname)); 58962306a36Sopenharmony_ci ret = idmap_lookup(rqstp, idtoname_lookup, &key, nn->idtoname_cache, &item); 59062306a36Sopenharmony_ci if (ret == -ENOENT) 59162306a36Sopenharmony_ci return encode_ascii_id(xdr, id); 59262306a36Sopenharmony_ci if (ret) 59362306a36Sopenharmony_ci return nfserrno(ret); 59462306a36Sopenharmony_ci ret = strlen(item->name); 59562306a36Sopenharmony_ci WARN_ON_ONCE(ret > IDMAP_NAMESZ); 59662306a36Sopenharmony_ci p = xdr_reserve_space(xdr, ret + 4); 59762306a36Sopenharmony_ci if (!p) 59862306a36Sopenharmony_ci return nfserr_resource; 59962306a36Sopenharmony_ci p = xdr_encode_opaque(p, item->name, ret); 60062306a36Sopenharmony_ci cache_put(&item->h, nn->idtoname_cache); 60162306a36Sopenharmony_ci return 0; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic bool 60562306a36Sopenharmony_cinumeric_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u32 *id) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci int ret; 60862306a36Sopenharmony_ci char buf[11]; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (namelen + 1 > sizeof(buf)) 61162306a36Sopenharmony_ci /* too long to represent a 32-bit id: */ 61262306a36Sopenharmony_ci return false; 61362306a36Sopenharmony_ci /* Just to make sure it's null-terminated: */ 61462306a36Sopenharmony_ci memcpy(buf, name, namelen); 61562306a36Sopenharmony_ci buf[namelen] = '\0'; 61662306a36Sopenharmony_ci ret = kstrtouint(buf, 10, id); 61762306a36Sopenharmony_ci return ret == 0; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic __be32 62162306a36Sopenharmony_cido_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u32 *id) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS) 62462306a36Sopenharmony_ci if (numeric_name_to_id(rqstp, type, name, namelen, id)) 62562306a36Sopenharmony_ci return 0; 62662306a36Sopenharmony_ci /* 62762306a36Sopenharmony_ci * otherwise, fall through and try idmapping, for 62862306a36Sopenharmony_ci * backwards compatibility with clients sending names: 62962306a36Sopenharmony_ci */ 63062306a36Sopenharmony_ci return idmap_name_to_id(rqstp, type, name, namelen, id); 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic __be32 encode_name_from_id(struct xdr_stream *xdr, 63462306a36Sopenharmony_ci struct svc_rqst *rqstp, int type, u32 id) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS) 63762306a36Sopenharmony_ci return encode_ascii_id(xdr, id); 63862306a36Sopenharmony_ci return idmap_id_to_name(xdr, rqstp, type, id); 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci__be32 64262306a36Sopenharmony_cinfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, size_t namelen, 64362306a36Sopenharmony_ci kuid_t *uid) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci __be32 status; 64662306a36Sopenharmony_ci u32 id = -1; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (name == NULL || namelen == 0) 64962306a36Sopenharmony_ci return nfserr_inval; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci status = do_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, &id); 65262306a36Sopenharmony_ci *uid = make_kuid(nfsd_user_namespace(rqstp), id); 65362306a36Sopenharmony_ci if (!uid_valid(*uid)) 65462306a36Sopenharmony_ci status = nfserr_badowner; 65562306a36Sopenharmony_ci return status; 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci__be32 65962306a36Sopenharmony_cinfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen, 66062306a36Sopenharmony_ci kgid_t *gid) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci __be32 status; 66362306a36Sopenharmony_ci u32 id = -1; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (name == NULL || namelen == 0) 66662306a36Sopenharmony_ci return nfserr_inval; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci status = do_name_to_id(rqstp, IDMAP_TYPE_GROUP, name, namelen, &id); 66962306a36Sopenharmony_ci *gid = make_kgid(nfsd_user_namespace(rqstp), id); 67062306a36Sopenharmony_ci if (!gid_valid(*gid)) 67162306a36Sopenharmony_ci status = nfserr_badowner; 67262306a36Sopenharmony_ci return status; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci__be32 nfsd4_encode_user(struct xdr_stream *xdr, struct svc_rqst *rqstp, 67662306a36Sopenharmony_ci kuid_t uid) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci u32 id = from_kuid_munged(nfsd_user_namespace(rqstp), uid); 67962306a36Sopenharmony_ci return encode_name_from_id(xdr, rqstp, IDMAP_TYPE_USER, id); 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci__be32 nfsd4_encode_group(struct xdr_stream *xdr, struct svc_rqst *rqstp, 68362306a36Sopenharmony_ci kgid_t gid) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci u32 id = from_kgid_munged(nfsd_user_namespace(rqstp), gid); 68662306a36Sopenharmony_ci return encode_name_from_id(xdr, rqstp, IDMAP_TYPE_GROUP, id); 68762306a36Sopenharmony_ci} 688