18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/net/sunrpc/svcauth.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * The generic interface for RPC authentication on the server side. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * CHANGES 108c2ecf20Sopenharmony_ci * 19-Apr-2000 Chris Evans - Security fix 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/types.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/sunrpc/types.h> 168c2ecf20Sopenharmony_ci#include <linux/sunrpc/xdr.h> 178c2ecf20Sopenharmony_ci#include <linux/sunrpc/svcsock.h> 188c2ecf20Sopenharmony_ci#include <linux/sunrpc/svcauth.h> 198c2ecf20Sopenharmony_ci#include <linux/err.h> 208c2ecf20Sopenharmony_ci#include <linux/hash.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <trace/events/sunrpc.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "sunrpc.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define RPCDBG_FACILITY RPCDBG_AUTH 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* 308c2ecf20Sopenharmony_ci * Table of authenticators 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ciextern struct auth_ops svcauth_null; 338c2ecf20Sopenharmony_ciextern struct auth_ops svcauth_unix; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic struct auth_ops __rcu *authtab[RPC_AUTH_MAXFLAVOR] = { 368c2ecf20Sopenharmony_ci [RPC_AUTH_NULL] = (struct auth_ops __force __rcu *)&svcauth_null, 378c2ecf20Sopenharmony_ci [RPC_AUTH_UNIX] = (struct auth_ops __force __rcu *)&svcauth_unix, 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic struct auth_ops * 418c2ecf20Sopenharmony_cisvc_get_auth_ops(rpc_authflavor_t flavor) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct auth_ops *aops; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (flavor >= RPC_AUTH_MAXFLAVOR) 468c2ecf20Sopenharmony_ci return NULL; 478c2ecf20Sopenharmony_ci rcu_read_lock(); 488c2ecf20Sopenharmony_ci aops = rcu_dereference(authtab[flavor]); 498c2ecf20Sopenharmony_ci if (aops != NULL && !try_module_get(aops->owner)) 508c2ecf20Sopenharmony_ci aops = NULL; 518c2ecf20Sopenharmony_ci rcu_read_unlock(); 528c2ecf20Sopenharmony_ci return aops; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void 568c2ecf20Sopenharmony_cisvc_put_auth_ops(struct auth_ops *aops) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci module_put(aops->owner); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ciint 628c2ecf20Sopenharmony_cisvc_authenticate(struct svc_rqst *rqstp, __be32 *authp) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci rpc_authflavor_t flavor; 658c2ecf20Sopenharmony_ci struct auth_ops *aops; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci *authp = rpc_auth_ok; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci flavor = svc_getnl(&rqstp->rq_arg.head[0]); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci dprintk("svc: svc_authenticate (%d)\n", flavor); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci aops = svc_get_auth_ops(flavor); 748c2ecf20Sopenharmony_ci if (aops == NULL) { 758c2ecf20Sopenharmony_ci *authp = rpc_autherr_badcred; 768c2ecf20Sopenharmony_ci return SVC_DENIED; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci rqstp->rq_auth_slack = 0; 808c2ecf20Sopenharmony_ci init_svc_cred(&rqstp->rq_cred); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci rqstp->rq_authop = aops; 838c2ecf20Sopenharmony_ci return aops->accept(rqstp, authp); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(svc_authenticate); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ciint svc_set_client(struct svc_rqst *rqstp) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci rqstp->rq_client = NULL; 908c2ecf20Sopenharmony_ci return rqstp->rq_authop->set_client(rqstp); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(svc_set_client); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* A request, which was authenticated, has now executed. 958c2ecf20Sopenharmony_ci * Time to finalise the credentials and verifier 968c2ecf20Sopenharmony_ci * and release and resources 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ciint svc_authorise(struct svc_rqst *rqstp) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct auth_ops *aops = rqstp->rq_authop; 1018c2ecf20Sopenharmony_ci int rv = 0; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci rqstp->rq_authop = NULL; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (aops) { 1068c2ecf20Sopenharmony_ci rv = aops->release(rqstp); 1078c2ecf20Sopenharmony_ci svc_put_auth_ops(aops); 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci return rv; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ciint 1138c2ecf20Sopenharmony_cisvc_auth_register(rpc_authflavor_t flavor, struct auth_ops *aops) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct auth_ops *old; 1168c2ecf20Sopenharmony_ci int rv = -EINVAL; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (flavor < RPC_AUTH_MAXFLAVOR) { 1198c2ecf20Sopenharmony_ci old = cmpxchg((struct auth_ops ** __force)&authtab[flavor], NULL, aops); 1208c2ecf20Sopenharmony_ci if (old == NULL || old == aops) 1218c2ecf20Sopenharmony_ci rv = 0; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci return rv; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(svc_auth_register); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_civoid 1288c2ecf20Sopenharmony_cisvc_auth_unregister(rpc_authflavor_t flavor) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci if (flavor < RPC_AUTH_MAXFLAVOR) 1318c2ecf20Sopenharmony_ci rcu_assign_pointer(authtab[flavor], NULL); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(svc_auth_unregister); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/************************************************** 1368c2ecf20Sopenharmony_ci * 'auth_domains' are stored in a hash table indexed by name. 1378c2ecf20Sopenharmony_ci * When the last reference to an 'auth_domain' is dropped, 1388c2ecf20Sopenharmony_ci * the object is unhashed and freed. 1398c2ecf20Sopenharmony_ci * If auth_domain_lookup fails to find an entry, it will return 1408c2ecf20Sopenharmony_ci * it's second argument 'new'. If this is non-null, it will 1418c2ecf20Sopenharmony_ci * have been atomically linked into the table. 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci#define DN_HASHBITS 6 1458c2ecf20Sopenharmony_ci#define DN_HASHMAX (1<<DN_HASHBITS) 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic struct hlist_head auth_domain_table[DN_HASHMAX]; 1488c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(auth_domain_lock); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic void auth_domain_release(struct kref *kref) 1518c2ecf20Sopenharmony_ci __releases(&auth_domain_lock) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct auth_domain *dom = container_of(kref, struct auth_domain, ref); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci hlist_del_rcu(&dom->hash); 1568c2ecf20Sopenharmony_ci dom->flavour->domain_release(dom); 1578c2ecf20Sopenharmony_ci spin_unlock(&auth_domain_lock); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_civoid auth_domain_put(struct auth_domain *dom) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci kref_put_lock(&dom->ref, auth_domain_release, &auth_domain_lock); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(auth_domain_put); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistruct auth_domain * 1678c2ecf20Sopenharmony_ciauth_domain_lookup(char *name, struct auth_domain *new) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci struct auth_domain *hp; 1708c2ecf20Sopenharmony_ci struct hlist_head *head; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci head = &auth_domain_table[hash_str(name, DN_HASHBITS)]; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci spin_lock(&auth_domain_lock); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci hlist_for_each_entry(hp, head, hash) { 1778c2ecf20Sopenharmony_ci if (strcmp(hp->name, name)==0) { 1788c2ecf20Sopenharmony_ci kref_get(&hp->ref); 1798c2ecf20Sopenharmony_ci spin_unlock(&auth_domain_lock); 1808c2ecf20Sopenharmony_ci return hp; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci if (new) 1848c2ecf20Sopenharmony_ci hlist_add_head_rcu(&new->hash, head); 1858c2ecf20Sopenharmony_ci spin_unlock(&auth_domain_lock); 1868c2ecf20Sopenharmony_ci return new; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(auth_domain_lookup); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistruct auth_domain *auth_domain_find(char *name) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct auth_domain *hp; 1938c2ecf20Sopenharmony_ci struct hlist_head *head; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci head = &auth_domain_table[hash_str(name, DN_HASHBITS)]; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci rcu_read_lock(); 1988c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(hp, head, hash) { 1998c2ecf20Sopenharmony_ci if (strcmp(hp->name, name)==0) { 2008c2ecf20Sopenharmony_ci if (!kref_get_unless_zero(&hp->ref)) 2018c2ecf20Sopenharmony_ci hp = NULL; 2028c2ecf20Sopenharmony_ci rcu_read_unlock(); 2038c2ecf20Sopenharmony_ci return hp; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci rcu_read_unlock(); 2078c2ecf20Sopenharmony_ci return NULL; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(auth_domain_find); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/** 2128c2ecf20Sopenharmony_ci * auth_domain_cleanup - check that the auth_domain table is empty 2138c2ecf20Sopenharmony_ci * 2148c2ecf20Sopenharmony_ci * On module unload the auth_domain_table must be empty. To make it 2158c2ecf20Sopenharmony_ci * easier to catch bugs which don't clean up domains properly, we 2168c2ecf20Sopenharmony_ci * warn if anything remains in the table at cleanup time. 2178c2ecf20Sopenharmony_ci * 2188c2ecf20Sopenharmony_ci * Note that we cannot proactively remove the domains at this stage. 2198c2ecf20Sopenharmony_ci * The ->release() function might be in a module that has already been 2208c2ecf20Sopenharmony_ci * unloaded. 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_civoid auth_domain_cleanup(void) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci int h; 2268c2ecf20Sopenharmony_ci struct auth_domain *hp; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci for (h = 0; h < DN_HASHMAX; h++) 2298c2ecf20Sopenharmony_ci hlist_for_each_entry(hp, &auth_domain_table[h], hash) 2308c2ecf20Sopenharmony_ci pr_warn("svc: domain %s still present at module unload.\n", 2318c2ecf20Sopenharmony_ci hp->name); 2328c2ecf20Sopenharmony_ci} 233