diff options
Diffstat (limited to 'net/sunrpc/svcauth.c')
-rw-r--r-- | net/sunrpc/svcauth.c | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/net/sunrpc/svcauth.c b/net/sunrpc/svcauth.c new file mode 100644 index 000000000000..bde8147ef2db --- /dev/null +++ b/net/sunrpc/svcauth.c | |||
@@ -0,0 +1,216 @@ | |||
1 | /* | ||
2 | * linux/net/sunrpc/svcauth.c | ||
3 | * | ||
4 | * The generic interface for RPC authentication on the server side. | ||
5 | * | ||
6 | * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> | ||
7 | * | ||
8 | * CHANGES | ||
9 | * 19-Apr-2000 Chris Evans - Security fix | ||
10 | */ | ||
11 | |||
12 | #include <linux/types.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/sunrpc/types.h> | ||
16 | #include <linux/sunrpc/xdr.h> | ||
17 | #include <linux/sunrpc/svcsock.h> | ||
18 | #include <linux/sunrpc/svcauth.h> | ||
19 | #include <linux/err.h> | ||
20 | #include <linux/hash.h> | ||
21 | |||
22 | #define RPCDBG_FACILITY RPCDBG_AUTH | ||
23 | |||
24 | |||
25 | /* | ||
26 | * Table of authenticators | ||
27 | */ | ||
28 | extern struct auth_ops svcauth_null; | ||
29 | extern struct auth_ops svcauth_unix; | ||
30 | |||
31 | static DEFINE_SPINLOCK(authtab_lock); | ||
32 | static struct auth_ops *authtab[RPC_AUTH_MAXFLAVOR] = { | ||
33 | [0] = &svcauth_null, | ||
34 | [1] = &svcauth_unix, | ||
35 | }; | ||
36 | |||
37 | int | ||
38 | svc_authenticate(struct svc_rqst *rqstp, u32 *authp) | ||
39 | { | ||
40 | rpc_authflavor_t flavor; | ||
41 | struct auth_ops *aops; | ||
42 | |||
43 | *authp = rpc_auth_ok; | ||
44 | |||
45 | flavor = ntohl(svc_getu32(&rqstp->rq_arg.head[0])); | ||
46 | |||
47 | dprintk("svc: svc_authenticate (%d)\n", flavor); | ||
48 | |||
49 | spin_lock(&authtab_lock); | ||
50 | if (flavor >= RPC_AUTH_MAXFLAVOR || !(aops = authtab[flavor]) | ||
51 | || !try_module_get(aops->owner)) { | ||
52 | spin_unlock(&authtab_lock); | ||
53 | *authp = rpc_autherr_badcred; | ||
54 | return SVC_DENIED; | ||
55 | } | ||
56 | spin_unlock(&authtab_lock); | ||
57 | |||
58 | rqstp->rq_authop = aops; | ||
59 | return aops->accept(rqstp, authp); | ||
60 | } | ||
61 | |||
62 | int svc_set_client(struct svc_rqst *rqstp) | ||
63 | { | ||
64 | return rqstp->rq_authop->set_client(rqstp); | ||
65 | } | ||
66 | |||
67 | /* A request, which was authenticated, has now executed. | ||
68 | * Time to finalise the the credentials and verifier | ||
69 | * and release and resources | ||
70 | */ | ||
71 | int svc_authorise(struct svc_rqst *rqstp) | ||
72 | { | ||
73 | struct auth_ops *aops = rqstp->rq_authop; | ||
74 | int rv = 0; | ||
75 | |||
76 | rqstp->rq_authop = NULL; | ||
77 | |||
78 | if (aops) { | ||
79 | rv = aops->release(rqstp); | ||
80 | module_put(aops->owner); | ||
81 | } | ||
82 | return rv; | ||
83 | } | ||
84 | |||
85 | int | ||
86 | svc_auth_register(rpc_authflavor_t flavor, struct auth_ops *aops) | ||
87 | { | ||
88 | int rv = -EINVAL; | ||
89 | spin_lock(&authtab_lock); | ||
90 | if (flavor < RPC_AUTH_MAXFLAVOR && authtab[flavor] == NULL) { | ||
91 | authtab[flavor] = aops; | ||
92 | rv = 0; | ||
93 | } | ||
94 | spin_unlock(&authtab_lock); | ||
95 | return rv; | ||
96 | } | ||
97 | |||
98 | void | ||
99 | svc_auth_unregister(rpc_authflavor_t flavor) | ||
100 | { | ||
101 | spin_lock(&authtab_lock); | ||
102 | if (flavor < RPC_AUTH_MAXFLAVOR) | ||
103 | authtab[flavor] = NULL; | ||
104 | spin_unlock(&authtab_lock); | ||
105 | } | ||
106 | EXPORT_SYMBOL(svc_auth_unregister); | ||
107 | |||
108 | /************************************************** | ||
109 | * cache for domain name to auth_domain | ||
110 | * Entries are only added by flavours which will normally | ||
111 | * have a structure that 'inherits' from auth_domain. | ||
112 | * e.g. when an IP -> domainname is given to auth_unix, | ||
113 | * and the domain name doesn't exist, it will create a | ||
114 | * auth_unix_domain and add it to this hash table. | ||
115 | * If it finds the name does exist, but isn't AUTH_UNIX, | ||
116 | * it will complain. | ||
117 | */ | ||
118 | |||
119 | /* | ||
120 | * Auth auth_domain cache is somewhat different to other caches, | ||
121 | * largely because the entries are possibly of different types: | ||
122 | * each auth flavour has it's own type. | ||
123 | * One consequence of this that DefineCacheLookup cannot | ||
124 | * allocate a new structure as it cannot know the size. | ||
125 | * Notice that the "INIT" code fragment is quite different | ||
126 | * from other caches. When auth_domain_lookup might be | ||
127 | * creating a new domain, the new domain is passed in | ||
128 | * complete and it is used as-is rather than being copied into | ||
129 | * another structure. | ||
130 | */ | ||
131 | #define DN_HASHBITS 6 | ||
132 | #define DN_HASHMAX (1<<DN_HASHBITS) | ||
133 | #define DN_HASHMASK (DN_HASHMAX-1) | ||
134 | |||
135 | static struct cache_head *auth_domain_table[DN_HASHMAX]; | ||
136 | |||
137 | static void auth_domain_drop(struct cache_head *item, struct cache_detail *cd) | ||
138 | { | ||
139 | struct auth_domain *dom = container_of(item, struct auth_domain, h); | ||
140 | if (cache_put(item,cd)) | ||
141 | authtab[dom->flavour]->domain_release(dom); | ||
142 | } | ||
143 | |||
144 | |||
145 | struct cache_detail auth_domain_cache = { | ||
146 | .hash_size = DN_HASHMAX, | ||
147 | .hash_table = auth_domain_table, | ||
148 | .name = "auth.domain", | ||
149 | .cache_put = auth_domain_drop, | ||
150 | }; | ||
151 | |||
152 | void auth_domain_put(struct auth_domain *dom) | ||
153 | { | ||
154 | auth_domain_drop(&dom->h, &auth_domain_cache); | ||
155 | } | ||
156 | |||
157 | static inline int auth_domain_hash(struct auth_domain *item) | ||
158 | { | ||
159 | return hash_str(item->name, DN_HASHBITS); | ||
160 | } | ||
161 | static inline int auth_domain_match(struct auth_domain *tmp, struct auth_domain *item) | ||
162 | { | ||
163 | return strcmp(tmp->name, item->name) == 0; | ||
164 | } | ||
165 | |||
166 | struct auth_domain * | ||
167 | auth_domain_lookup(struct auth_domain *item, int set) | ||
168 | { | ||
169 | struct auth_domain *tmp = NULL; | ||
170 | struct cache_head **hp, **head; | ||
171 | head = &auth_domain_cache.hash_table[auth_domain_hash(item)]; | ||
172 | |||
173 | if (set) | ||
174 | write_lock(&auth_domain_cache.hash_lock); | ||
175 | else | ||
176 | read_lock(&auth_domain_cache.hash_lock); | ||
177 | for (hp=head; *hp != NULL; hp = &tmp->h.next) { | ||
178 | tmp = container_of(*hp, struct auth_domain, h); | ||
179 | if (!auth_domain_match(tmp, item)) | ||
180 | continue; | ||
181 | if (!set) { | ||
182 | cache_get(&tmp->h); | ||
183 | goto out_noset; | ||
184 | } | ||
185 | *hp = tmp->h.next; | ||
186 | tmp->h.next = NULL; | ||
187 | auth_domain_drop(&tmp->h, &auth_domain_cache); | ||
188 | goto out_set; | ||
189 | } | ||
190 | /* Didn't find anything */ | ||
191 | if (!set) | ||
192 | goto out_nada; | ||
193 | auth_domain_cache.entries++; | ||
194 | out_set: | ||
195 | item->h.next = *head; | ||
196 | *head = &item->h; | ||
197 | cache_get(&item->h); | ||
198 | write_unlock(&auth_domain_cache.hash_lock); | ||
199 | cache_fresh(&auth_domain_cache, &item->h, item->h.expiry_time); | ||
200 | cache_get(&item->h); | ||
201 | return item; | ||
202 | out_nada: | ||
203 | tmp = NULL; | ||
204 | out_noset: | ||
205 | read_unlock(&auth_domain_cache.hash_lock); | ||
206 | return tmp; | ||
207 | } | ||
208 | |||
209 | struct auth_domain *auth_domain_find(char *name) | ||
210 | { | ||
211 | struct auth_domain *rv, ad; | ||
212 | |||
213 | ad.name = name; | ||
214 | rv = auth_domain_lookup(&ad, 0); | ||
215 | return rv; | ||
216 | } | ||