// SPDX-License-Identifier: GPL-2.0-only
/*
* linux / net / sunrpc / svcauth . c
*
* The generic interface for RPC authentication on the server side .
*
* Copyright ( C ) 1995 , 1996 Olaf Kirch < okir @ monad . swb . de >
*
* CHANGES
* 19 - Apr - 2000 Chris Evans - Security fix
*/
#include <linux/types.h>
#include <linux/module.h>
#include <linux/sunrpc/types.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/sunrpc/svcauth.h>
#include <linux/err.h>
#include <linux/hash.h>
#include <linux/user_namespace.h>
#include <trace/events/sunrpc.h>
#include "sunrpc.h"
#define RPCDBG_FACILITY RPCDBG_AUTH
/*
* Table of authenticators
*/
extern struct auth_ops svcauth_null;
extern struct auth_ops svcauth_unix;
extern struct auth_ops svcauth_tls;
static struct auth_ops __rcu *authtab[RPC_AUTH_MAXFLAVOR] = {
[RPC_AUTH_NULL] = (struct auth_ops __force __rcu *)&svcauth_null,
[RPC_AUTH_UNIX] = (struct auth_ops __force __rcu *)&svcauth_unix,
[RPC_AUTH_TLS] = (struct auth_ops __force __rcu *)&svcauth_tls,
};
static struct auth_ops *
svc_get_auth_ops(rpc_authflavor_t flavor)
{
struct auth_ops *aops;
if (flavor >= RPC_AUTH_MAXFLAVOR)
return NULL;
rcu_read_lock();
aops = rcu_dereference(authtab[flavor]);
if (aops != NULL && !try_module_get(aops->owner))
aops = NULL;
rcu_read_unlock();
return aops;
}
static void
svc_put_auth_ops(struct auth_ops *aops)
{
module_put(aops->owner);
}
/**
* svc_authenticate - Initialize an outgoing credential
* @ rqstp : RPC execution context
*
* Return values :
* % SVC_OK : XDR encoding of the result can begin
* % SVC_DENIED : Credential or verifier is not valid
* % SVC_GARBAGE : Failed to decode credential or verifier
* % SVC_COMPLETE : GSS context lifetime event ; no further action
* % SVC_DROP : Drop this request ; no further action
* % SVC_CLOSE : Like drop , but also close transport connection
*/
enum svc_auth_status svc_authenticate(struct svc_rqst *rqstp)
{
struct auth_ops *aops;
u32 flavor;
rqstp->rq_auth_stat = rpc_auth_ok;
/*
* Decode the Call credential ' s flavor field . The credential ' s
* body field is decoded in the chosen - > accept method below .
*/
if (xdr_stream_decode_u32(&rqstp->rq_arg_stream, &flavor) < 0 )
return SVC_GARBAGE;
aops = svc_get_auth_ops(flavor);
if (aops == NULL) {
rqstp->rq_auth_stat = rpc_autherr_badcred;
return SVC_DENIED;
}
rqstp->rq_auth_slack = 0 ;
init_svc_cred(&rqstp->rq_cred);
rqstp->rq_authop = aops;
return aops->accept(rqstp);
}
/**
* svc_set_client - Assign an appropriate ' auth_domain ' as the client
* @ rqstp : RPC execution context
*
* Return values :
* % SVC_OK : Client was found and assigned
* % SVC_DENY : Client was explicitly denied
* % SVC_DROP : Ignore this request
* % SVC_CLOSE : Ignore this request and close the connection
*/
enum svc_auth_status svc_set_client(struct svc_rqst *rqstp)
{
rqstp->rq_client = NULL;
return rqstp->rq_authop->set_client(rqstp);
}
EXPORT_SYMBOL_GPL(svc_set_client);
/**
* svc_authorise - Finalize credentials / verifier and release resources
* @ rqstp : RPC execution context
*
* Returns zero on success , or a negative errno .
*/
int svc_authorise(struct svc_rqst *rqstp)
{
struct auth_ops *aops = rqstp->rq_authop;
int rv = 0 ;
rqstp->rq_authop = NULL;
if (aops) {
rv = aops->release(rqstp);
svc_put_auth_ops(aops);
}
return rv;
}
int
svc_auth_register(rpc_authflavor_t flavor, struct auth_ops *aops)
{
struct auth_ops *old;
int rv = -EINVAL;
if (flavor < RPC_AUTH_MAXFLAVOR) {
old = cmpxchg((struct auth_ops ** __force)&authtab[flavor], NULL, aops);
if (old == NULL || old == aops)
rv = 0 ;
}
return rv;
}
EXPORT_SYMBOL_GPL(svc_auth_register);
void
svc_auth_unregister(rpc_authflavor_t flavor)
{
if (flavor < RPC_AUTH_MAXFLAVOR)
rcu_assign_pointer(authtab[flavor], NULL);
}
EXPORT_SYMBOL_GPL(svc_auth_unregister);
/**
* svc_auth_flavor - return RPC transaction ' s RPC_AUTH flavor
* @ rqstp : RPC transaction context
*
* Returns an RPC flavor or GSS pseudoflavor .
*/
rpc_authflavor_t svc_auth_flavor(struct svc_rqst *rqstp)
{
struct auth_ops *aops = rqstp->rq_authop;
if (!aops->pseudoflavor)
return aops->flavour;
return aops->pseudoflavor(rqstp);
}
EXPORT_SYMBOL_GPL(svc_auth_flavor);
/**
* svcauth_map_clnt_to_svc_cred_local - maps a generic cred
* to a svc_cred suitable for use in nfsd .
* @ clnt : rpc_clnt associated with nfs client
* @ cred : generic cred associated with nfs client
* @ svc : returned svc_cred that is suitable for use in nfsd
*/
void svcauth_map_clnt_to_svc_cred_local(struct rpc_clnt *clnt,
const struct cred *cred,
struct svc_cred *svc)
{
struct user_namespace *userns = clnt->cl_cred ?
clnt->cl_cred->user_ns : &init_user_ns;
memset(svc, 0 , sizeof (struct svc_cred));
svc->cr_uid = KUIDT_INIT(from_kuid_munged(userns, cred->fsuid));
svc->cr_gid = KGIDT_INIT(from_kgid_munged(userns, cred->fsgid));
svc->cr_flavor = clnt->cl_auth->au_flavor;
if (cred->group_info)
svc->cr_group_info = get_group_info(cred->group_info);
/* These aren't relevant for local (network is bypassed) */
svc->cr_principal = NULL;
svc->cr_gss_mech = NULL;
}
EXPORT_SYMBOL_GPL(svcauth_map_clnt_to_svc_cred_local);
/**************************************************
* ' auth_domains ' are stored in a hash table indexed by name .
* When the last reference to an ' auth_domain ' is dropped ,
* the object is unhashed and freed .
* If auth_domain_lookup fails to find an entry , it will return
* it ' s second argument ' new ' . If this is non - null , it will
* have been atomically linked into the table .
*/
#define DN_HASHBITS 6
#define DN_HASHMAX (1 <<DN_HASHBITS)
static struct hlist_head auth_domain_table[DN_HASHMAX];
static DEFINE_SPINLOCK(auth_domain_lock);
static void auth_domain_release(struct kref *kref)
__releases(&auth_domain_lock)
{
struct auth_domain *dom = container_of(kref, struct auth_domain, ref);
hlist_del_rcu(&dom->hash);
dom->flavour->domain_release(dom);
spin_unlock(&auth_domain_lock);
}
void auth_domain_put(struct auth_domain *dom)
{
kref_put_lock(&dom->ref, auth_domain_release, &auth_domain_lock);
}
EXPORT_SYMBOL_GPL(auth_domain_put);
struct auth_domain *
auth_domain_lookup(char *name, struct auth_domain *new )
{
struct auth_domain *hp;
struct hlist_head *head;
head = &auth_domain_table[hash_str(name, DN_HASHBITS)];
spin_lock(&auth_domain_lock);
hlist_for_each_entry(hp, head, hash) {
if (strcmp(hp->name, name)==0 ) {
kref_get(&hp->ref);
spin_unlock(&auth_domain_lock);
return hp;
}
}
if (new )
hlist_add_head_rcu(&new ->hash, head);
spin_unlock(&auth_domain_lock);
return new ;
}
EXPORT_SYMBOL_GPL(auth_domain_lookup);
struct auth_domain *auth_domain_find(char *name)
{
struct auth_domain *hp;
struct hlist_head *head;
head = &auth_domain_table[hash_str(name, DN_HASHBITS)];
rcu_read_lock();
hlist_for_each_entry_rcu(hp, head, hash) {
if (strcmp(hp->name, name)==0 ) {
if (!kref_get_unless_zero(&hp->ref))
hp = NULL;
rcu_read_unlock();
return hp;
}
}
rcu_read_unlock();
return NULL;
}
EXPORT_SYMBOL_GPL(auth_domain_find);
/**
* auth_domain_cleanup - check that the auth_domain table is empty
*
* On module unload the auth_domain_table must be empty . To make it
* easier to catch bugs which don ' t clean up domains properly , we
* warn if anything remains in the table at cleanup time .
*
* Note that we cannot proactively remove the domains at this stage .
* The - > release ( ) function might be in a module that has already been
* unloaded .
*/
void auth_domain_cleanup(void )
{
int h;
struct auth_domain *hp;
for (h = 0 ; h < DN_HASHMAX; h++)
hlist_for_each_entry(hp, &auth_domain_table[h], hash)
pr_warn("svc: domain %s still present at module unload.\n" ,
hp->name);
}
Messung V0.5 in Prozent C=97 H=98 G=97
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland