Commit 624361e4 authored by Neil Brown's avatar Neil Brown Committed by Linus Torvalds

[PATCH] kNFSd: Use new cache infrastructure for auth_unix specific lookups.

This patch introduces two caches using the new infrastucture, and the
concept of a 'domain'.

A 'domain' refers to a collection of clients that all have the same
view of the nfs server, and all have the same access rights (modulo
different users on the clients).  For AUTH_UNIX (and AUTH_NULL), the
domain is determined from the IP address.  For other authentication
styles, the domain might be determined directly from the credentials.

Each auth flavour knows how to allocate and free it's domain-specific
infomation.

auth_domain_cache maps a name to a domain which is owned by
an auth flavour.

ip_map_cache is a cache specific to AUTH_UNIX which maps
IP address to domain.

With this patch, svcauth_unix.c is created to store all
auth_unix related code.

The IP address lookup code is removed from nfsd/exports.c

sunrpc module initilisation is moved out of stats.c into sunrpc_syms
which seemed to be the most central .c file.  It now registers these
two caches.

Now that the caches are being used, nfsd needs to call cache_clean
periodically.
parent f00a9f4d
This diff is collapsed.
...@@ -42,6 +42,8 @@ nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file *filp) ...@@ -42,6 +42,8 @@ nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file *filp)
mntget(filp->f_vfsmnt); mntget(filp->f_vfsmnt);
} }
fh_put(&fh); fh_put(&fh);
if (rqstp->rq_client)
auth_domain_put(rqstp->rq_client);
rqstp->rq_client = NULL; rqstp->rq_client = NULL;
exp_readunlock(); exp_readunlock();
/* nlm and nfsd don't share error codes. /* nlm and nfsd don't share error codes.
......
...@@ -226,7 +226,7 @@ static ssize_t write_getfs(struct file *file, const char *buf, size_t size) ...@@ -226,7 +226,7 @@ static ssize_t write_getfs(struct file *file, const char *buf, size_t size)
{ {
struct nfsctl_fsparm data; struct nfsctl_fsparm data;
struct sockaddr_in *sin; struct sockaddr_in *sin;
struct svc_client *clp; struct auth_domain *clp;
int err = 0; int err = 0;
struct knfsd_fh *res; struct knfsd_fh *res;
...@@ -248,8 +248,10 @@ static ssize_t write_getfs(struct file *file, const char *buf, size_t size) ...@@ -248,8 +248,10 @@ static ssize_t write_getfs(struct file *file, const char *buf, size_t size)
exp_readlock(); exp_readlock();
if (!(clp = exp_getclient(sin))) if (!(clp = exp_getclient(sin)))
err = -EPERM; err = -EPERM;
else else {
err = exp_rootfh(clp, data.gd_path, res, data.gd_maxlen); err = exp_rootfh(clp, data.gd_path, res, data.gd_maxlen);
auth_domain_put(clp);
}
exp_readunlock(); exp_readunlock();
down(&file->f_dentry->d_inode->i_sem); down(&file->f_dentry->d_inode->i_sem);
...@@ -271,7 +273,7 @@ static ssize_t write_getfd(struct file *file, const char *buf, size_t size) ...@@ -271,7 +273,7 @@ static ssize_t write_getfd(struct file *file, const char *buf, size_t size)
{ {
struct nfsctl_fdparm data; struct nfsctl_fdparm data;
struct sockaddr_in *sin; struct sockaddr_in *sin;
struct svc_client *clp; struct auth_domain *clp;
int err = 0; int err = 0;
struct knfsd_fh fh; struct knfsd_fh fh;
char *res; char *res;
...@@ -293,8 +295,10 @@ static ssize_t write_getfd(struct file *file, const char *buf, size_t size) ...@@ -293,8 +295,10 @@ static ssize_t write_getfd(struct file *file, const char *buf, size_t size)
exp_readlock(); exp_readlock();
if (!(clp = exp_getclient(sin))) if (!(clp = exp_getclient(sin)))
err = -EPERM; err = -EPERM;
else else {
err = exp_rootfh(clp, data.gd_path, &fh, NFS_FHSIZE); err = exp_rootfh(clp, data.gd_path, &fh, NFS_FHSIZE);
auth_domain_put(clp);
}
exp_readunlock(); exp_readunlock();
down(&file->f_dentry->d_inode->i_sem); down(&file->f_dentry->d_inode->i_sem);
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/sunrpc/stats.h> #include <linux/sunrpc/stats.h>
#include <linux/sunrpc/svc.h> #include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svcsock.h> #include <linux/sunrpc/svcsock.h>
#include <linux/sunrpc/cache.h>
#include <linux/nfsd/nfsd.h> #include <linux/nfsd/nfsd.h>
#include <linux/nfsd/stats.h> #include <linux/nfsd/stats.h>
#include <linux/nfsd/cache.h> #include <linux/nfsd/cache.h>
...@@ -196,7 +197,7 @@ nfsd(struct svc_rqst *rqstp) ...@@ -196,7 +197,7 @@ nfsd(struct svc_rqst *rqstp)
*/ */
while ((err = svc_recv(serv, rqstp, while ((err = svc_recv(serv, rqstp,
5*60*HZ)) == -EAGAIN) 5*60*HZ)) == -EAGAIN)
; cache_clean();
if (err < 0) if (err < 0)
break; break;
update_thread_usage(atomic_read(&nfsd_busy)); update_thread_usage(atomic_read(&nfsd_busy));
...@@ -218,6 +219,10 @@ nfsd(struct svc_rqst *rqstp) ...@@ -218,6 +219,10 @@ nfsd(struct svc_rqst *rqstp)
svc_process(serv, rqstp); svc_process(serv, rqstp);
/* Unlock export hash tables */ /* Unlock export hash tables */
if (rqstp->rq_client) {
auth_domain_put(rqstp->rq_client);
rqstp->rq_client = NULL;
}
exp_readunlock(); exp_readunlock();
update_thread_usage(atomic_read(&nfsd_busy)); update_thread_usage(atomic_read(&nfsd_busy));
atomic_dec(&nfsd_busy); atomic_dec(&nfsd_busy);
......
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
/* Dummy declarations */ /* Dummy declarations */
struct svc_rqst; struct svc_rqst;
struct svc_client; /* opaque type */
/* /*
* This is the set of functions for lockd->nfsd communication * This is the set of functions for lockd->nfsd communication
......
...@@ -45,14 +45,9 @@ ...@@ -45,14 +45,9 @@
#ifdef __KERNEL__ #ifdef __KERNEL__
struct svc_client {
struct svc_client * cl_next;
char cl_ident[NFSCLNT_IDMAX];
};
struct svc_export { struct svc_export {
struct list_head ex_hash; struct list_head ex_hash;
struct svc_client * ex_client; struct auth_domain * ex_client;
int ex_flags; int ex_flags;
struct vfsmount * ex_mnt; struct vfsmount * ex_mnt;
struct dentry * ex_dentry; struct dentry * ex_dentry;
...@@ -68,11 +63,11 @@ struct svc_export { ...@@ -68,11 +63,11 @@ struct svc_export {
struct svc_expkey { struct svc_expkey {
struct list_head ek_hash; struct list_head ek_hash;
struct svc_client *ek_client; struct auth_domain * ek_client;
int ek_fsidtype; int ek_fsidtype;
u32 ek_fsid[2]; u32 ek_fsid[2];
struct svc_export *ek_export; struct svc_export * ek_export;
}; };
#define EX_SECURE(exp) (!((exp)->ex_flags & NFSEXP_INSECURE_PORT)) #define EX_SECURE(exp) (!((exp)->ex_flags & NFSEXP_INSECURE_PORT))
...@@ -90,21 +85,22 @@ void nfsd_export_init(void); ...@@ -90,21 +85,22 @@ void nfsd_export_init(void);
void nfsd_export_shutdown(void); void nfsd_export_shutdown(void);
void exp_readlock(void); void exp_readlock(void);
void exp_readunlock(void); void exp_readunlock(void);
struct svc_client * exp_getclient(struct sockaddr_in *sin); struct auth_domain * exp_getclient(struct sockaddr_in *sin);
void exp_putclient(struct svc_client *clp); struct svc_expkey * exp_find_key(struct auth_domain *clp,
struct svc_expkey * exp_find_key(struct svc_client *clp, int fsid_type, u32 *fsidv); int fsid_type, u32 *fsidv);
struct svc_export * exp_get_by_name(struct svc_client *clp, struct svc_export * exp_get_by_name(struct auth_domain *clp,
struct vfsmount *mnt, struct vfsmount *mnt,
struct dentry *dentry); struct dentry *dentry);
struct svc_export * exp_parent(struct svc_client *clp, struct vfsmount *mnt, struct svc_export * exp_parent(struct auth_domain *clp,
struct vfsmount *mnt,
struct dentry *dentry); struct dentry *dentry);
int exp_rootfh(struct svc_client *, int exp_rootfh(struct auth_domain *,
char *path, struct knfsd_fh *, int maxsize); char *path, struct knfsd_fh *, int maxsize);
int exp_pseudoroot(struct svc_client *, struct svc_fh *fhp); int exp_pseudoroot(struct auth_domain *, struct svc_fh *fhp);
int nfserrno(int errno); int nfserrno(int errno);
static inline struct svc_export * static inline struct svc_export *
exp_find(struct svc_client *clp, int fsid_type, u32 *fsidv) exp_find(struct auth_domain *clp, int fsid_type, u32 *fsidv)
{ {
struct svc_expkey *ek = exp_find_key(clp, fsid_type, fsidv); struct svc_expkey *ek = exp_find_key(clp, fsid_type, fsidv);
if (ek) if (ek)
......
...@@ -139,7 +139,6 @@ int nfsd_permission(struct svc_export *, struct dentry *, int); ...@@ -139,7 +139,6 @@ int nfsd_permission(struct svc_export *, struct dentry *, int);
*/ */
void nfsd_lockd_init(void); void nfsd_lockd_init(void);
void nfsd_lockd_shutdown(void); void nfsd_lockd_shutdown(void);
void nfsd_lockd_unexport(struct svc_client *);
/* /*
......
...@@ -122,7 +122,7 @@ struct svc_rqst { ...@@ -122,7 +122,7 @@ struct svc_rqst {
*/ */
/* Catering to nfsd */ /* Catering to nfsd */
struct svc_client * rq_client; /* RPC peer info */ struct auth_domain * rq_client; /* RPC peer info */
struct svc_cacherep * rq_cacherep; /* cache info */ struct svc_cacherep * rq_cacherep; /* cache info */
struct knfsd_fh * rq_reffh; /* Referrence filehandle, used to struct knfsd_fh * rq_reffh; /* Referrence filehandle, used to
* determine what device number * determine what device number
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/sunrpc/msg_prot.h> #include <linux/sunrpc/msg_prot.h>
#include <linux/sunrpc/cache.h>
struct svc_cred { struct svc_cred {
uid_t cr_uid; uid_t cr_uid;
...@@ -20,6 +21,30 @@ struct svc_cred { ...@@ -20,6 +21,30 @@ struct svc_cred {
}; };
struct svc_rqst; /* forward decl */ struct svc_rqst; /* forward decl */
/* Authentication is done in the context of a domain.
* For a server, a domain represents a group of clients using
* a common mechanism for authentication and having a common mapping
* between local identity (uid) and network identity. All clients
* in a domain have similar general access rights. Each domain can
* contain multiple principals which will have different specific right
* based on normal Discretionary Access Control.
*
* For a client, a domain represents a number of servers which all
* use a common authentication mechanism and network identity name space.
*
* A domain is created by an authentication flavour module based on name
* only. Userspace then fills in detail on demand.
*
* The creation of a domain typically implies creation of one or
* more caches for storing domain specific information.
*/
struct auth_domain {
struct cache_head h;
char *name;
int flavour;
};
/* /*
* Each authentication flavour registers an auth_ops * Each authentication flavour registers an auth_ops
* structure. * structure.
...@@ -49,12 +74,16 @@ struct svc_rqst; /* forward decl */ ...@@ -49,12 +74,16 @@ struct svc_rqst; /* forward decl */
* DROP - the reply should be quitely dropped * DROP - the reply should be quitely dropped
* DENIED - authp holds a reason for MSG_DENIED * DENIED - authp holds a reason for MSG_DENIED
* SYSERR - rpc system_err * SYSERR - rpc system_err
*
* domain_release()
* This call releases a domain.
*/ */
struct auth_ops { struct auth_ops {
char * name; char * name;
int flavour; int flavour;
int (*accept)(struct svc_rqst *rq, u32 *authp, int proc); int (*accept)(struct svc_rqst *rq, u32 *authp, int proc);
int (*release)(struct svc_rqst *rq); int (*release)(struct svc_rqst *rq);
void (*domain_release)(struct auth_domain *);
}; };
extern struct auth_ops *authtab[RPC_AUTH_MAXFLAVOR]; extern struct auth_ops *authtab[RPC_AUTH_MAXFLAVOR];
...@@ -73,6 +102,18 @@ extern int svc_authorise(struct svc_rqst *rqstp); ...@@ -73,6 +102,18 @@ extern int svc_authorise(struct svc_rqst *rqstp);
extern int svc_auth_register(rpc_authflavor_t flavor, struct auth_ops *aops); extern int svc_auth_register(rpc_authflavor_t flavor, struct auth_ops *aops);
extern void svc_auth_unregister(rpc_authflavor_t flavor); extern void svc_auth_unregister(rpc_authflavor_t flavor);
extern struct auth_domain *unix_domain_find(char *name);
extern void auth_domain_put(struct auth_domain *item);
extern int auth_unix_add_addr(struct in_addr addr, struct auth_domain *dom);
extern struct auth_domain *auth_domain_lookup(struct auth_domain *item, int set);
extern struct auth_domain *auth_domain_find(char *name);
extern struct auth_domain *auth_unix_lookup(struct in_addr addr);
extern int auth_unix_forget_old(struct auth_domain *dom);
extern void svcauth_unix_purge(void);
extern int name_hash(char *name, int size);
extern struct cache_detail auth_domain_cache, ip_map_cache;
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
...@@ -8,7 +8,7 @@ export-objs := sunrpc_syms.o ...@@ -8,7 +8,7 @@ export-objs := sunrpc_syms.o
sunrpc-y := clnt.o xprt.o sched.o \ sunrpc-y := clnt.o xprt.o sched.o \
auth.o auth_null.o auth_unix.o \ auth.o auth_null.o auth_unix.o \
svc.o svcsock.o svcauth.o \ svc.o svcsock.o svcauth.o svcauth_unix.o \
pmap_clnt.o timer.o xdr.o \ pmap_clnt.o timer.o xdr.o \
sunrpc_syms.o cache.o sunrpc_syms.o cache.o
sunrpc-$(CONFIG_PROC_FS) += stats.o sunrpc-$(CONFIG_PROC_FS) += stats.o
......
...@@ -183,25 +183,3 @@ rpc_proc_exit(void) ...@@ -183,25 +183,3 @@ rpc_proc_exit(void)
} }
} }
static int __init
init_sunrpc(void)
{
#ifdef RPC_DEBUG
rpc_register_sysctl();
#endif
rpc_proc_init();
return 0;
}
static void __exit
cleanup_sunrpc(void)
{
#ifdef RPC_DEBUG
rpc_unregister_sysctl();
#endif
rpc_proc_exit();
}
MODULE_LICENSE("GPL");
module_init(init_sunrpc);
module_exit(cleanup_sunrpc);
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/unistd.h> #include <linux/unistd.h>
#include <linux/init.h>
#include <linux/sunrpc/sched.h> #include <linux/sunrpc/sched.h>
#include <linux/sunrpc/clnt.h> #include <linux/sunrpc/clnt.h>
...@@ -22,6 +23,7 @@ ...@@ -22,6 +23,7 @@
#include <linux/sunrpc/svcsock.h> #include <linux/sunrpc/svcsock.h>
#include <linux/sunrpc/auth.h> #include <linux/sunrpc/auth.h>
/* RPC scheduler */ /* RPC scheduler */
EXPORT_SYMBOL(rpc_allocate); EXPORT_SYMBOL(rpc_allocate);
EXPORT_SYMBOL(rpc_free); EXPORT_SYMBOL(rpc_free);
...@@ -108,3 +110,33 @@ EXPORT_SYMBOL(nfs_debug); ...@@ -108,3 +110,33 @@ EXPORT_SYMBOL(nfs_debug);
EXPORT_SYMBOL(nfsd_debug); EXPORT_SYMBOL(nfsd_debug);
EXPORT_SYMBOL(nlm_debug); EXPORT_SYMBOL(nlm_debug);
#endif #endif
static int __init
init_sunrpc(void)
{
#ifdef RPC_DEBUG
rpc_register_sysctl();
#endif
#ifdef CONFIG_PROC_FS
rpc_proc_init();
#endif
cache_register(&auth_domain_cache);
cache_register(&ip_map_cache);
return 0;
}
static void __exit
cleanup_sunrpc(void)
{
cache_unregister(&auth_domain_cache);
cache_unregister(&ip_map_cache);
#ifdef RPC_DEBUG
rpc_unregister_sysctl();
#endif
#ifdef CONFIG_PROCFS
rpc_proc_exit();
#endif
}
MODULE_LICENSE("GPL");
module_init(init_sunrpc);
module_exit(cleanup_sunrpc);
...@@ -13,37 +13,19 @@ ...@@ -13,37 +13,19 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/sunrpc/types.h> #include <linux/sunrpc/types.h>
#include <linux/sunrpc/xdr.h> #include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/svcauth.h>
#include <linux/sunrpc/svcsock.h> #include <linux/sunrpc/svcsock.h>
#include <linux/sunrpc/svcauth.h>
#include <linux/err.h> #include <linux/err.h>
#define RPCDBG_FACILITY RPCDBG_AUTH #define RPCDBG_FACILITY RPCDBG_AUTH
/*
* Builtin auth flavors
*/
static int svcauth_null_accept(struct svc_rqst *rqstp, u32 *authp, int proc);
static int svcauth_null_release(struct svc_rqst *rqstp);
static int svcauth_unix_accept(struct svc_rqst *rqstp, u32 *authp, int proc);
static int svcauth_unix_release(struct svc_rqst *rqstp);
struct auth_ops svcauth_null = {
.name = "null",
.flavour = RPC_AUTH_NULL,
.accept = svcauth_null_accept,
.release = svcauth_null_release,
};
struct auth_ops svcauth_unix = {
.name = "unix",
.flavour = RPC_AUTH_UNIX,
.accept = svcauth_unix_accept,
.release = svcauth_unix_release,
};
/* /*
* Table of authenticators * Table of authenticators
*/ */
extern struct auth_ops svcauth_null;
extern struct auth_ops svcauth_unix;
static struct auth_ops *authtab[RPC_AUTH_MAXFLAVOR] = { static struct auth_ops *authtab[RPC_AUTH_MAXFLAVOR] = {
[0] = &svcauth_null, [0] = &svcauth_null,
[1] = &svcauth_unix, [1] = &svcauth_unix,
...@@ -119,97 +101,91 @@ svc_auth_unregister(rpc_authflavor_t flavor) ...@@ -119,97 +101,91 @@ svc_auth_unregister(rpc_authflavor_t flavor)
authtab[flavor] = NULL; authtab[flavor] = NULL;
} }
static int /**************************************************
svcauth_null_accept(struct svc_rqst *rqstp, u32 *authp, int proc) * cache for domain name to auth_domain
{ * Entries are only added by flavours which will normally
struct svc_buf *argp = &rqstp->rq_argbuf; * have a structure that 'inherits' from auth_domain.
struct svc_buf *resp = &rqstp->rq_resbuf; * e.g. when an IP -> domainname is given to auth_unix,
* and the domain name doesn't exist, it will create a
* auth_unix_domain and add it to this hash table.
* If it finds the name does exist, but isn't AUTH_UNIX,
* it will complain.
*/
if ((argp->len -= 3) < 0) { int name_hash(char *name, int size)
return SVC_GARBAGE; {
} int hash = 0;
if (*(argp->buf)++ != 0) { /* we already skipped the flavor */ while (*name) {
dprintk("svc: bad null cred\n"); hash += *name;
*authp = rpc_autherr_badcred; name++;
return SVC_DENIED;
}
if (*(argp->buf)++ != RPC_AUTH_NULL || *(argp->buf)++ != 0) {
dprintk("svc: bad null verf\n");
*authp = rpc_autherr_badverf;
return SVC_DENIED;
} }
return hash % size;
}
/* Signal that mapping to nobody uid/gid is required */
rqstp->rq_cred.cr_uid = (uid_t) -1;
rqstp->rq_cred.cr_gid = (gid_t) -1;
rqstp->rq_cred.cr_groups[0] = NOGROUP;
/* Put NULL verifier */ /*
svc_putu32(resp, RPC_AUTH_NULL); * Auth auth_domain cache is somewhat different to other caches,
svc_putu32(resp, 0); * largely because the entries are possibly of different types:
return SVC_OK; * each auth flavour has it's own type.
} * One consequence of this that DefineCacheLookup cannot
* allocate a new structure as it cannot know the size.
* Notice that the "INIT" code fragment is quite different
* from other caches. When auth_domain_lookup might be
* creating a new domain, the new domain is passed in
* complete and it is used as-is rather than being copied into
* another structure.
*/
#define DN_HASHBITS 6
#define DN_HASHMAX (1<<DN_HASHBITS)
#define DN_HASHMASK (DN_HASHMAX-1)
static int static struct cache_head *auth_domain_table[DN_HASHMAX];
svcauth_null_release(struct svc_rqst *rqstp) void auth_domain_drop(struct cache_head *item, struct cache_detail *cd)
{ {
return 0; /* don't drop */ struct auth_domain *dom = container_of(item, struct auth_domain, h);
if (cache_put(item,cd))
authtab[dom->flavour]->domain_release(dom);
} }
static int
svcauth_unix_accept(struct svc_rqst *rqstp, u32 *authp, int proc)
{
struct svc_buf *argp = &rqstp->rq_argbuf;
struct svc_buf *resp = &rqstp->rq_resbuf;
struct svc_cred *cred = &rqstp->rq_cred;
u32 *bufp = argp->buf, slen, i;
int len = argp->len;
if ((len -= 3) < 0)
return SVC_GARBAGE;
bufp++; /* length */
bufp++; /* time stamp */
slen = XDR_QUADLEN(ntohl(*bufp++)); /* machname length */
if (slen > 64 || (len -= slen + 3) < 0)
goto badcred;
bufp += slen; /* skip machname */
cred->cr_uid = ntohl(*bufp++); /* uid */
cred->cr_gid = ntohl(*bufp++); /* gid */
slen = ntohl(*bufp++); /* gids length */
if (slen > 16 || (len -= slen + 2) < 0)
goto badcred;
for (i = 0; i < NGROUPS && i < slen; i++)
cred->cr_groups[i] = ntohl(*bufp++);
if (i < NGROUPS)
cred->cr_groups[i] = NOGROUP;
bufp += (slen - i);
if (*bufp++ != RPC_AUTH_NULL || *bufp++ != 0) {
*authp = rpc_autherr_badverf;
return SVC_DENIED;
}
argp->buf = bufp;
argp->len = len;
/* Put NULL verifier */
svc_putu32(resp, RPC_AUTH_NULL);
svc_putu32(resp, 0);
return SVC_OK; struct cache_detail auth_domain_cache = {
.hash_size = DN_HASHMAX,
.hash_table = auth_domain_table,
.name = "auth.domain",
.cache_put = auth_domain_drop,
};
badcred: void auth_domain_put(struct auth_domain *dom)
*authp = rpc_autherr_badcred; {
return SVC_DENIED; auth_domain_drop(&dom->h, &auth_domain_cache);
} }
static int static inline int auth_domain_hash(struct auth_domain *item)
svcauth_unix_release(struct svc_rqst *rqstp)
{ {
/* Verifier (such as it is) is already in place. return name_hash(item->name, DN_HASHMAX);
*/ }
return 0; static inline int auth_domain_match(struct auth_domain *tmp, struct auth_domain *item)
{
return strcmp(tmp->name, item->name) == 0;
}
DefineCacheLookup(struct auth_domain,
h,
auth_domain_lookup,
(struct auth_domain *item, int set),
/* no setup */,
&auth_domain_cache,
auth_domain_hash(item),
auth_domain_match(tmp, item),
kfree(new); if(!set) return NULL;
new=item; atomic_inc(&new->h.refcnt),
/* no update */
)
struct auth_domain *auth_domain_find(char *name)
{
struct auth_domain *rv, ad;
ad.name = name;
rv = auth_domain_lookup(&ad, 0);
return rv;
} }
#include <linux/types.h>
#include <linux/sched.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>
#define RPCDBG_FACILITY RPCDBG_AUTH
/*
* AUTHUNIX and AUTHNULL credentials are both handled here.
* AUTHNULL is treated just like AUTHUNIX except that the uid/gid
* are always nobody (-2). i.e. we do the same IP address checks for
* AUTHNULL as for AUTHUNIX, and that is done here.
*/
char *strdup(char *s)
{
char *rv = kmalloc(strlen(s)+1, GFP_KERNEL);
if (rv)
strcpy(rv, s);
return rv;
}
struct unix_domain {
struct auth_domain h;
int addr_changes;
/* other stuff later */
};
struct auth_domain *unix_domain_find(char *name)
{
struct auth_domain *rv, ud;
struct unix_domain *new;
ud.name = name;
rv = auth_domain_lookup(&ud, 0);
foundit:
if (rv && rv->flavour != RPC_AUTH_UNIX) {
auth_domain_put(rv);
return NULL;
}
if (rv)
return rv;
new = kmalloc(sizeof(*new), GFP_KERNEL);
cache_init(&new->h.h);
atomic_inc(&new->h.h.refcnt);
new->h.name = strdup(name);
new->h.flavour = RPC_AUTH_UNIX;
new->addr_changes = 0;
new->h.h.expiry_time = NEVER;
new->h.h.flags = 0;
rv = auth_domain_lookup(&new->h, 2);
if (rv == &new->h) {
if (atomic_dec_and_test(&new->h.h.refcnt)) BUG();
} else {
auth_domain_put(&new->h);
goto foundit;
}
return rv;
}
/**************************************************
* cache for IP address to unix_domain
* as needed by AUTH_UNIX
*/
#define IP_HASHBITS 8
#define IP_HASHMAX (1<<IP_HASHBITS)
#define IP_HASHMASK (IP_HASHMAX-1)
struct ip_map {
struct cache_head h;
char *m_class; /* e.g. "nfsd" */
struct in_addr m_addr;
struct unix_domain *m_client;
int m_add_change;
};
static struct cache_head *ip_table[IP_HASHMAX];
void ip_map_put(struct cache_head *item, struct cache_detail *cd)
{
struct ip_map *im = container_of(item, struct ip_map,h);
if (cache_put(item, cd)) {
auth_domain_put(&im->m_client->h);
kfree(im);
}
}
static inline int ip_map_hash(struct ip_map *item)
{
return (name_hash(item->m_class, IP_HASHMAX) ^ item->m_addr.s_addr) & IP_HASHMASK;
}
static inline int ip_map_match(struct ip_map *item, struct ip_map *tmp)
{
return strcmp(tmp->m_class, item->m_class) == 0
&& tmp->m_addr.s_addr == item->m_addr.s_addr;
}
static inline void ip_map_init(struct ip_map *new, struct ip_map *item)
{
new->m_class = strdup(item->m_class);
new->m_addr.s_addr = item->m_addr.s_addr;
}
static inline void ip_map_update(struct ip_map *new, struct ip_map *item)
{
cache_get(&item->m_client->h.h);
new->m_client = item->m_client;
new->m_add_change = item->m_add_change;
}
struct cache_detail ip_map_cache = {
.hash_size = IP_HASHMAX,
.hash_table = ip_table,
.name = "auth.unix.ip",
.cache_put = ip_map_put,
};
static DefineSimpleCacheLookup(ip_map)
int auth_unix_add_addr(struct in_addr addr, struct auth_domain *dom)
{
struct unix_domain *udom;
struct ip_map ip, *ipmp;
if (dom->flavour != RPC_AUTH_UNIX)
return -EINVAL;
udom = container_of(dom, struct unix_domain, h);
ip.m_class = "nfsd";
ip.m_addr = addr;
ip.m_client = udom;
ip.m_add_change = udom->addr_changes+1;
ip.h.flags = 0;
ip.h.expiry_time = NEVER;
ipmp = ip_map_lookup(&ip, 1);
if (ipmp) {
ip_map_put(&ipmp->h, &ip_map_cache);
return 0;
} else
return -ENOMEM;
}
int auth_unix_forget_old(struct auth_domain *dom)
{
struct unix_domain *udom;
if (dom->flavour != RPC_AUTH_UNIX)
return -EINVAL;
udom = container_of(dom, struct unix_domain, h);
udom->addr_changes++;
return 0;
}
struct auth_domain *auth_unix_lookup(struct in_addr addr)
{
struct ip_map key, *ipm;
struct auth_domain *rv;
key.m_class = "nfsd";
key.m_addr = addr;
ipm = ip_map_lookup(&key, 0);
if (!ipm)
return NULL;
if (test_bit(CACHE_VALID, &ipm->h.flags) &&
(ipm->m_client->addr_changes - ipm->m_add_change) >0)
set_bit(CACHE_NEGATIVE, &ipm->h.flags);
if (!test_bit(CACHE_VALID, &ipm->h.flags))
rv = NULL;
else if (test_bit(CACHE_NEGATIVE, &ipm->h.flags))
rv = NULL;
else {
rv = &ipm->m_client->h;
cache_get(&rv->h);
}
if (ipm) ip_map_put(&ipm->h, &ip_map_cache);
return rv;
}
void svcauth_unix_purge(void)
{
cache_purge(&ip_map_cache);
cache_purge(&auth_domain_cache);
}
static int
svcauth_null_accept(struct svc_rqst *rqstp, u32 *authp, int proc)
{
struct svc_buf *argp = &rqstp->rq_argbuf;
struct svc_buf *resp = &rqstp->rq_resbuf;
if ((argp->len -= 3) < 0) {
return SVC_GARBAGE;
}
if (*(argp->buf)++ != 0) { /* we already skipped the flavor */
dprintk("svc: bad null cred\n");
*authp = rpc_autherr_badcred;
return SVC_DENIED;
}
if (*(argp->buf)++ != RPC_AUTH_NULL || *(argp->buf)++ != 0) {
dprintk("svc: bad null verf\n");
*authp = rpc_autherr_badverf;
return SVC_DENIED;
}
/* Signal that mapping to nobody uid/gid is required */
rqstp->rq_cred.cr_uid = (uid_t) -1;
rqstp->rq_cred.cr_gid = (gid_t) -1;
rqstp->rq_cred.cr_groups[0] = NOGROUP;
/* Put NULL verifier */
svc_putu32(resp, RPC_AUTH_NULL);
svc_putu32(resp, 0);
return SVC_OK;
}
static int
svcauth_null_release(struct svc_rqst *rqstp)
{
return 0; /* don't drop */
}
struct auth_ops svcauth_null = {
.name = "null",
.flavour = RPC_AUTH_NULL,
.accept = svcauth_null_accept,
.release = svcauth_null_release,
};
int
svcauth_unix_accept(struct svc_rqst *rqstp, u32 *authp, int proc)
{
struct svc_buf *argp = &rqstp->rq_argbuf;
struct svc_buf *resp = &rqstp->rq_resbuf;
struct svc_cred *cred = &rqstp->rq_cred;
u32 *bufp = argp->buf, slen, i;
int len = argp->len;
if ((len -= 3) < 0)
return SVC_GARBAGE;
bufp++; /* length */
bufp++; /* time stamp */
slen = XDR_QUADLEN(ntohl(*bufp++)); /* machname length */
if (slen > 64 || (len -= slen + 3) < 0)
goto badcred;
bufp += slen; /* skip machname */
cred->cr_uid = ntohl(*bufp++); /* uid */
cred->cr_gid = ntohl(*bufp++); /* gid */
slen = ntohl(*bufp++); /* gids length */
if (slen > 16 || (len -= slen + 2) < 0)
goto badcred;
for (i = 0; i < NGROUPS && i < slen; i++)
cred->cr_groups[i] = ntohl(*bufp++);
if (i < NGROUPS)
cred->cr_groups[i] = NOGROUP;
bufp += (slen - i);
if (*bufp++ != RPC_AUTH_NULL || *bufp++ != 0) {
*authp = rpc_autherr_badverf;
return SVC_DENIED;
}
argp->buf = bufp;
argp->len = len;
/* Put NULL verifier */
svc_putu32(resp, RPC_AUTH_NULL);
svc_putu32(resp, 0);
return SVC_OK;
badcred:
*authp = rpc_autherr_badcred;
return SVC_DENIED;
}
int
svcauth_unix_release(struct svc_rqst *rqstp)
{
/* Verifier (such as it is) is already in place.
*/
return 0;
}
struct auth_ops svcauth_unix = {
.name = "unix",
.flavour = RPC_AUTH_UNIX,
.accept = svcauth_unix_accept,
.release = svcauth_unix_release,
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment