Commit | Line | Data |
---|---|---|
805e021f CE |
1 | /* |
2 | * vi:set cin noet sw=4 tw=70: | |
3 | * Copyright 2006, International Business Machines Corporation and others. | |
4 | * All Rights Reserved. | |
5 | * | |
6 | * This software has been released under the terms of the IBM Public | |
7 | * License. For details, see the LICENSE file in the top-level source | |
8 | * directory or online at http://www.openafs.org/dl/license10.html | |
9 | */ | |
10 | ||
11 | /* | |
12 | * Filesystem export operations for Linux | |
13 | */ | |
14 | #include <afsconfig.h> | |
15 | #include "afs/param.h" | |
16 | ||
17 | ||
18 | #if !defined(AFS_NONFSTRANS) || defined(AFS_AIX_IAUTH_ENV) | |
19 | #include <linux/module.h> /* early to avoid printf->printk mapping */ | |
20 | #include <linux/fs.h> | |
21 | #include "afs/sysincludes.h" | |
22 | #include "afsincludes.h" | |
23 | #include "nfsclient.h" | |
24 | #include <linux/sunrpc/svc.h> | |
25 | #include <linux/sunrpc/svcauth.h> | |
26 | ||
27 | static unsigned long authtab_addr = 0; | |
28 | #if defined(module_param) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) | |
29 | module_param(authtab_addr, long, 0); | |
30 | #else | |
31 | MODULE_PARM(authtab_addr, "l"); | |
32 | #endif | |
33 | MODULE_PARM_DESC(authtab_addr, "Address of the authtab array."); | |
34 | ||
35 | extern struct auth_ops *authtab[] __attribute__((weak)); | |
36 | static struct auth_ops **afs_authtab; | |
37 | static struct auth_ops *afs_new_authtab[RPC_AUTH_MAXFLAVOR]; | |
38 | static struct auth_ops *afs_orig_authtab[RPC_AUTH_MAXFLAVOR]; | |
39 | ||
40 | static int whine_memory = 0; | |
41 | ||
42 | afs_lock_t afs_xnfssrv; | |
43 | ||
44 | struct nfs_server_thread { | |
45 | struct nfs_server_thread *next; /* next in chain */ | |
46 | pid_t pid; /* pid of this thread */ | |
47 | int active; /* this thread is servicing an RPC */ | |
48 | struct sockaddr_in client_addr; /* latest client of this thread */ | |
49 | int client_addrlen; | |
50 | afs_int32 uid; /* AFS UID/PAG for this thread */ | |
51 | afs_int32 code; /* What should InitReq return? */ | |
52 | int flavor; /* auth flavor */ | |
53 | uid_t client_uid; /* UID claimed by client */ | |
54 | gid_t client_gid; /* GID claimed by client */ | |
55 | gid_t client_g0, client_g1; /* groups claimed by client */ | |
56 | }; | |
57 | ||
58 | static struct nfs_server_thread *nfssrv_list = 0; | |
59 | ||
60 | static struct nfs_server_thread *find_nfs_thread(int create) | |
61 | { | |
62 | struct nfs_server_thread *this; | |
63 | ||
64 | /* Check that this is an nfsd kernel thread */ | |
65 | if (current->files != init_task.files || strcmp(current->comm, "nfsd")) | |
66 | return 0; | |
67 | ||
68 | ObtainWriteLock(&afs_xnfssrv, 804); | |
69 | for (this = nfssrv_list; this; this = this->next) | |
70 | if (this->pid == current->pid) | |
71 | break; | |
72 | if (!this && create) { | |
73 | this = afs_osi_Alloc(sizeof(struct nfs_server_thread)); | |
74 | if (this) { | |
75 | this->next = nfssrv_list; | |
76 | this->pid = current->pid; | |
77 | this->client_addrlen = 0; | |
78 | nfssrv_list = this; | |
79 | printk("afs: added nfsd task %d/%d\n", | |
80 | current->tgid, current->pid); | |
81 | } else if (!whine_memory) { | |
82 | whine_memory = 1; | |
83 | printk("afs: failed to allocate memory for nfsd task %d/%d\n", | |
84 | current->tgid, current->pid); | |
85 | } | |
86 | } | |
87 | ReleaseWriteLock(&afs_xnfssrv); | |
88 | return this; | |
89 | } | |
90 | ||
91 | static int | |
92 | svcauth_afs_accept(struct svc_rqst *rqstp, u32 *authp) | |
93 | { | |
94 | struct nfs_server_thread *ns; | |
95 | struct afs_exporter *outexp; | |
96 | afs_ucred_t *credp; | |
97 | struct sockaddr_in *addr; | |
98 | int code; | |
99 | ||
100 | code = afs_orig_authtab[rqstp->rq_authop->flavour]->accept(rqstp, authp); | |
101 | if (code != SVC_OK) | |
102 | return code; | |
103 | ||
104 | AFS_GLOCK(); | |
105 | ns = find_nfs_thread(1); | |
106 | if (!ns) { | |
107 | AFS_GUNLOCK(); | |
108 | /* XXX maybe we should fail this with rpc_system_err? */ | |
109 | return SVC_OK; | |
110 | } | |
111 | #if HAVE_LINUX_SVC_ADDR_IN | |
112 | addr = svc_addr_in(rqstp); | |
113 | #else | |
114 | addr = &rqstp->rq_addr; | |
115 | #endif | |
116 | ||
117 | ns->active = 1; | |
118 | ns->flavor = rqstp->rq_authop->flavour; | |
119 | ns->code = EACCES; | |
120 | ns->client_addr = *addr; | |
121 | ns->client_addrlen = rqstp->rq_addrlen; | |
122 | ns->client_uid = afs_cr_uid(&rqstp->rq_cred); | |
123 | ns->client_gid = afs_cr_gid(&rqstp->rq_cred); | |
124 | if (afs_cr_group_info(&rqstp->rq_cred)->ngroups > 0) | |
125 | ns->client_g0 = GROUP_AT(afs_cr_group_info(&rqstp->rq_cred), 0); | |
126 | else | |
127 | ns->client_g0 = -1; | |
128 | if (afs_cr_group_info(&rqstp->rq_cred)->ngroups > 1) | |
129 | ns->client_g1 = GROUP_AT(afs_cr_group_info(&rqstp->rq_cred), 1); | |
130 | else | |
131 | ns->client_g1 = -1; | |
132 | ||
133 | if (addr->sin_family != AF_INET) { | |
134 | printk("afs: NFS request from non-IPv4 client (family %d len %d)\n", | |
135 | addr->sin_family, rqstp->rq_addrlen); | |
136 | goto done; | |
137 | } | |
138 | ||
139 | credp = crget(); | |
140 | afs_set_cr_uid(credp, afs_cr_uid(&rqstp->rq_cred)); | |
141 | afs_set_cr_gid(credp, afs_cr_gid(&rqstp->rq_cred)); | |
142 | get_group_info(afs_cr_group_info(&rqstp->rq_cred)); | |
143 | afs_set_cr_group_info(credp, afs_cr_group_info(&rqstp->rq_cred)); | |
144 | ||
145 | /* avoid creating wildcard entries by mapping anonymous | |
146 | * clients to afs_nobody */ | |
147 | if (afs_cr_uid(credp) == -1) | |
148 | afs_set_cr_uid(credp, -2); | |
149 | code = afs_nfsclient_reqhandler(0, &credp, addr->sin_addr.s_addr, | |
150 | &ns->uid, &outexp); | |
151 | if (!code && outexp) EXP_RELE(outexp); | |
152 | if (!code) ns->code = 0; | |
153 | if (code) | |
154 | printk("afs: svcauth_afs_accept: afs_nfsclient_reqhandler: %d\n", code); | |
155 | crfree(credp); | |
156 | ||
157 | done: | |
158 | AFS_GUNLOCK(); | |
159 | return SVC_OK; | |
160 | } | |
161 | ||
162 | ||
163 | #if 0 | |
164 | /* This doesn't work, because they helpfully NULL out rqstp->authop | |
165 | * before calling us, so we have no way to tell what the original | |
166 | * auth flavor was. | |
167 | */ | |
168 | static int | |
169 | svcauth_afs_release(struct svc_rqst *rqstp) | |
170 | { | |
171 | struct nfs_server_thread *ns; | |
172 | ||
173 | AFS_GLOCK(); | |
174 | ns = find_nfs_thread(0); | |
175 | if (ns) ns->active = 0; | |
176 | AFS_GUNLOCK(); | |
177 | ||
178 | return afs_orig_authtab[rqstp->rq_authop->flavour]->release(rqstp); | |
179 | } | |
180 | #endif | |
181 | ||
182 | ||
183 | int osi_linux_nfs_initreq(struct vrequest *av, afs_ucred_t *cr, int *code) | |
184 | { | |
185 | struct nfs_server_thread *ns; | |
186 | ||
187 | ns = find_nfs_thread(0); | |
188 | if (!ns || !ns->active) | |
189 | return 0; | |
190 | ||
191 | *code = ns->code; | |
192 | if (!ns->code) { | |
193 | afs_cr_ruid(cr) = NFSXLATOR_CRED; | |
194 | av->uid = ns->uid; | |
195 | } | |
196 | return 1; | |
197 | } | |
198 | ||
199 | void osi_linux_nfssrv_init(void) | |
200 | { | |
201 | int i; | |
202 | ||
203 | nfssrv_list = 0; | |
204 | AFS_RWLOCK_INIT(&afs_xnfssrv, "afs_xnfssrv"); | |
205 | ||
206 | if (authtab && !IS_ERR(authtab)) | |
207 | afs_authtab = authtab; | |
208 | else if (authtab_addr) afs_authtab = (struct auth_ops **)authtab_addr; | |
209 | else { | |
210 | printk("Warning: Unable to find the address of authtab\n"); | |
211 | printk("NFS Translator hooks will not be installed\n"); | |
212 | printk("To correct, specify authtab_addr=<authtab>\n"); | |
213 | afs_authtab = 0; | |
214 | return; | |
215 | } | |
216 | ||
217 | for (i = 0; i < RPC_AUTH_MAXFLAVOR; i++) { | |
218 | afs_orig_authtab[i] = afs_authtab[i]; | |
219 | if (!afs_orig_authtab[i] || afs_orig_authtab[i]->flavour != i || | |
220 | !try_module_get(afs_orig_authtab[i]->owner)) { | |
221 | afs_orig_authtab[i] = 0; | |
222 | continue; | |
223 | } | |
224 | ||
225 | afs_new_authtab[i] = afs_osi_Alloc(sizeof(struct auth_ops)); | |
226 | osi_Assert(afs_new_authtab[i] != NULL); | |
227 | *(afs_new_authtab[i]) = *(afs_orig_authtab[i]); | |
228 | afs_new_authtab[i]->owner = THIS_MODULE; | |
229 | afs_new_authtab[i]->accept = svcauth_afs_accept; | |
230 | /* afs_new_authtab[i]->release = svcauth_afs_release; */ | |
231 | svc_auth_unregister(i); | |
232 | svc_auth_register(i, afs_new_authtab[i]); | |
233 | } | |
234 | } | |
235 | ||
236 | void osi_linux_nfssrv_shutdown(void) | |
237 | { | |
238 | struct nfs_server_thread *next; | |
239 | int i; | |
240 | ||
241 | if (afs_authtab) { | |
242 | for (i = 0; i < RPC_AUTH_MAXFLAVOR; i++) { | |
243 | if (!afs_orig_authtab[i]) | |
244 | continue; | |
245 | svc_auth_unregister(i); | |
246 | svc_auth_register(i, afs_orig_authtab[i]); | |
247 | module_put(afs_orig_authtab[i]->owner); | |
248 | afs_osi_Free(afs_new_authtab[i], sizeof(struct auth_ops)); | |
249 | } | |
250 | } | |
251 | ||
252 | AFS_GLOCK(); | |
253 | ObtainWriteLock(&afs_xnfssrv, 805); | |
254 | while (nfssrv_list) { | |
255 | next = nfssrv_list->next; | |
256 | afs_osi_Free(nfssrv_list, sizeof(struct nfs_server_thread)); | |
257 | nfssrv_list = next; | |
258 | } | |
259 | ReleaseWriteLock(&afs_xnfssrv); | |
260 | AFS_GUNLOCK(); | |
261 | } | |
262 | #endif /* AFS_NONFSTRANS */ | |
263 |