[OOPS] That was not meant to be pushed/commited
[reactos.git] / base / services / nfsd / nfs41_rpc.c
1 /* NFSv4.1 client for Windows
2 * Copyright © 2012 The Regents of the University of Michigan
3 *
4 * Olga Kornievskaia <aglo@umich.edu>
5 * Casey Bodley <cbodley@umich.edu>
6 *
7 * This library is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or (at
10 * your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful, but
13 * without any warranty; without even the implied warranty of merchantability
14 * or fitness for a particular purpose. See the GNU Lesser General Public
15 * License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 */
21
22 #include "nfs41_ops.h"
23 #include "daemon_debug.h"
24 #include "nfs41_xdr.h"
25 #include "nfs41_callback.h"
26 #include "nfs41_driver.h" /* for AUTH_SYS, AUTHGSS_KRB5s defines */
27
28 #include "rpc/rpc.h"
29 #define SECURITY_WIN32
30 #include <security.h>
31 #include "rpc/auth_sspi.h"
32
33 static enum clnt_stat send_null(CLIENT *client)
34 {
35 struct timeval timeout = {10, 100};
36
37 return clnt_call(client, 0,
38 (xdrproc_t)xdr_void, NULL,
39 (xdrproc_t)xdr_void, NULL, timeout);
40 }
41
42 static int get_client_for_netaddr(
43 IN const netaddr4 *netaddr,
44 IN uint32_t wsize,
45 IN uint32_t rsize,
46 IN nfs41_rpc_clnt *rpc,
47 OUT OPTIONAL char *server_name,
48 OUT CLIENT **client_out)
49 {
50 int status = ERROR_NETWORK_UNREACHABLE;
51 struct netconfig *nconf;
52 struct netbuf *addr;
53 CLIENT *client;
54
55 nconf = getnetconfigent(netaddr->netid);
56 if (nconf == NULL)
57 goto out;
58
59 addr = uaddr2taddr(nconf, netaddr->uaddr);
60 if (addr == NULL)
61 goto out_free_conf;
62
63 if (server_name) {
64 getnameinfo(addr->buf, addr->len, server_name, NI_MAXHOST, NULL, 0, 0);
65 dprintf(1, "servername is %s\n", server_name);
66 }
67 dprintf(1, "callback function %p args %p\n", nfs41_handle_callback, rpc);
68 client = clnt_tli_create(RPC_ANYFD, nconf, addr, NFS41_RPC_PROGRAM,
69 NFS41_RPC_VERSION, wsize, rsize, rpc ? proc_cb_compound_res : NULL,
70 rpc ? nfs41_handle_callback : NULL, rpc ? rpc : NULL);
71 if (client) {
72 *client_out = client;
73 status = NO_ERROR;
74 }
75
76 freenetbuf(addr);
77 out_free_conf:
78 freenetconfigent(nconf);
79 out:
80 return status;
81 }
82
83 static int get_client_for_multi_addr(
84 IN const multi_addr4 *addrs,
85 IN uint32_t wsize,
86 IN uint32_t rsize,
87 IN nfs41_rpc_clnt *rpc,
88 OUT OPTIONAL char *server_name,
89 OUT CLIENT **client_out,
90 OUT uint32_t *addr_index)
91 {
92 int status = ERROR_NETWORK_UNREACHABLE;
93 uint32_t i;
94 for (i = 0; i < addrs->count; i++) {
95 status = get_client_for_netaddr(&addrs->arr[i],
96 wsize, rsize, rpc, server_name, client_out);
97 if (status == NO_ERROR) {
98 *addr_index = i;
99 break;
100 }
101 }
102 return status;
103 }
104
105 int create_rpcsec_auth_client(
106 IN uint32_t sec_flavor,
107 IN char *server_name,
108 CLIENT *client
109 )
110 {
111 int status = ERROR_NETWORK_UNREACHABLE;
112
113 switch (sec_flavor) {
114 case RPCSEC_AUTHGSS_KRB5:
115 client->cl_auth = authsspi_create_default(client, server_name,
116 RPCSEC_SSPI_SVC_NONE);
117 break;
118 case RPCSEC_AUTHGSS_KRB5I:
119 client->cl_auth = authsspi_create_default(client, server_name,
120 RPCSEC_SSPI_SVC_INTEGRITY);
121 break;
122 case RPCSEC_AUTHGSS_KRB5P:
123 client->cl_auth = authsspi_create_default(client, server_name,
124 RPCSEC_SSPI_SVC_PRIVACY);
125 break;
126 default:
127 eprintf("create_rpc_auth_client: unknown rpcsec flavor %d\n",
128 sec_flavor);
129 client->cl_auth = NULL;
130 }
131
132 if (client->cl_auth == NULL) {
133 eprintf("nfs41_rpc_clnt_create: failed to create %s\n",
134 secflavorop2name(sec_flavor));
135 goto out;
136 } else
137 dprintf(1, "nfs41_rpc_clnt_create: successfully created %s\n",
138 secflavorop2name(sec_flavor));
139 status = 0;
140 out:
141 return status;
142 }
143
144 /* Returns a client structure and an associated lock */
145 int nfs41_rpc_clnt_create(
146 IN const multi_addr4 *addrs,
147 IN uint32_t wsize,
148 IN uint32_t rsize,
149 IN uint32_t uid,
150 IN uint32_t gid,
151 IN uint32_t sec_flavor,
152 OUT nfs41_rpc_clnt **rpc_out)
153 {
154 CLIENT *client;
155 nfs41_rpc_clnt *rpc;
156 uint32_t addr_index;
157 int status;
158 char machname[MAXHOSTNAMELEN + 1];
159 gid_t gids[1];
160 bool_t needcb = 1;
161
162 rpc = calloc(1, sizeof(nfs41_rpc_clnt));
163 if (rpc == NULL) {
164 status = GetLastError();
165 goto out;
166 }
167 #ifdef NO_CB_4_KRB5P
168 if (sec_flavor == RPCSEC_AUTHGSS_KRB5P)
169 needcb = 0;
170 #endif
171 rpc->needcb = needcb;
172 rpc->cond = CreateEvent(NULL, TRUE, FALSE, NULL);
173 if (rpc->cond == NULL) {
174 status = GetLastError();
175 eprintf("CreateEvent failed %d\n", status);
176 goto out_free_rpc_clnt;
177 }
178 status = get_client_for_multi_addr(addrs, wsize, rsize, needcb?rpc:NULL,
179 rpc->server_name, &client, &addr_index);
180 if (status) {
181 clnt_pcreateerror("connecting failed");
182 goto out_free_rpc_cond;
183 }
184 if (send_null(client) != RPC_SUCCESS) {
185 // XXX Do what here?
186 eprintf("nfs41_rpc_clnt_create: send_null failed\n");
187 status = ERROR_NETWORK_UNREACHABLE;
188 goto out_err_client;
189 }
190
191 rpc->sec_flavor = sec_flavor;
192 if (sec_flavor == RPCSEC_AUTH_SYS) {
193 if (gethostname(machname, sizeof(machname)) == -1) {
194 eprintf("nfs41_rpc_clnt_create: gethostname failed\n");
195 goto out_err_client;
196 }
197 machname[sizeof(machname) - 1] = '\0';
198 client->cl_auth = authsys_create(machname, uid, gid, 0, gids);
199 if (client->cl_auth == NULL) {
200 eprintf("nfs41_rpc_clnt_create: failed to create rpc authsys\n");
201 status = ERROR_NETWORK_UNREACHABLE;
202 goto out_err_client;
203 }
204 } else {
205 status = create_rpcsec_auth_client(sec_flavor, rpc->server_name, client);
206 if (status) {
207 eprintf("nfs41_rpc_clnt_create: failed to establish security "
208 "context with %s\n", rpc->server_name);
209 status = ERROR_NETWORK_UNREACHABLE;
210 goto out_err_client;
211 } else
212 dprintf(1, "nfs41_rpc_clnt_create: successfully created %s\n",
213 secflavorop2name(sec_flavor));
214 }
215 rpc->rpc = client;
216
217 /* keep a copy of the address and buffer sizes for reconnect */
218 memcpy(&rpc->addrs, addrs, sizeof(multi_addr4));
219 /* save the index of the address we connected to */
220 rpc->addr_index = addr_index;
221 rpc->wsize = wsize;
222 rpc->rsize = rsize;
223 rpc->is_valid_session = TRUE;
224 rpc->uid = uid;
225 rpc->gid = gid;
226
227 //initialize rpc client lock
228 InitializeSRWLock(&rpc->lock);
229
230 *rpc_out = rpc;
231 out:
232 return status;
233 out_err_client:
234 clnt_destroy(client);
235 out_free_rpc_cond:
236 CloseHandle(rpc->cond);
237 out_free_rpc_clnt:
238 free(rpc);
239 goto out;
240 }
241
242 /* Frees resources allocated in clnt_create */
243 void nfs41_rpc_clnt_free(
244 IN nfs41_rpc_clnt *rpc)
245 {
246 auth_destroy(rpc->rpc->cl_auth);
247 clnt_destroy(rpc->rpc);
248 CloseHandle(rpc->cond);
249 free(rpc);
250 }
251
252 static bool_t rpc_renew_in_progress(nfs41_rpc_clnt *rpc, int *value)
253 {
254 bool_t status = FALSE;
255 AcquireSRWLockExclusive(&rpc->lock);
256 if (value) {
257 dprintf(1, "nfs41_rpc_renew_in_progress: setting value %d\n", *value);
258 rpc->in_recovery = *value;
259 if (!rpc->in_recovery)
260 SetEvent(rpc->cond);
261 } else {
262 status = rpc->in_recovery;
263 dprintf(1, "nfs41_rpc_renew_in_progress: returning value %d\n", status);
264 }
265 ReleaseSRWLockExclusive(&rpc->lock);
266 return status;
267 }
268
269 static bool_t rpc_should_retry(nfs41_rpc_clnt *rpc, uint32_t version)
270 {
271 bool_t status = 0;
272 AcquireSRWLockExclusive(&rpc->lock);
273 if (rpc->version > version)
274 status = 1;
275 ReleaseSRWLockExclusive(&rpc->lock);
276 return status;
277 }
278
279 static int rpc_reconnect(
280 IN nfs41_rpc_clnt *rpc)
281 {
282 CLIENT *client = NULL;
283 uint32_t addr_index;
284 int status;
285
286 AcquireSRWLockExclusive(&rpc->lock);
287
288 status = get_client_for_multi_addr(&rpc->addrs, rpc->wsize, rpc->rsize,
289 rpc->needcb?rpc:NULL, NULL, &client, &addr_index);
290 if (status)
291 goto out_unlock;
292
293 if(rpc->sec_flavor == RPCSEC_AUTH_SYS)
294 client->cl_auth = rpc->rpc->cl_auth;
295 else {
296 auth_destroy(rpc->rpc->cl_auth);
297 status = create_rpcsec_auth_client(rpc->sec_flavor, rpc->server_name, client);
298 if (status) {
299 eprintf("Failed to reestablish security context\n");
300 status = ERROR_NETWORK_UNREACHABLE;
301 goto out_err_client;
302 }
303 }
304 if (send_null(client) != RPC_SUCCESS) {
305 eprintf("rpc_reconnect: send_null failed\n");
306 status = ERROR_NETWORK_UNREACHABLE;
307 goto out_err_client;
308 }
309
310 clnt_destroy(rpc->rpc);
311 rpc->rpc = client;
312 rpc->addr_index = addr_index;
313 rpc->version++;
314 dprintf(1, "nfs41_send_compound: reestablished RPC connection\n");
315
316 out_unlock:
317 ReleaseSRWLockExclusive(&rpc->lock);
318
319 /* after releasing the rpc lock, send a BIND_CONN_TO_SESSION if
320 * we need to associate the connection with the backchannel */
321 if (status == NO_ERROR && rpc->needcb &&
322 rpc->client && rpc->client->session) {
323 status = nfs41_bind_conn_to_session(rpc,
324 rpc->client->session->session_id, CDFC4_BACK_OR_BOTH);
325 if (status)
326 eprintf("nfs41_bind_conn_to_session() failed with %s\n",
327 nfs_error_string(status));
328 status = NFS4_OK;
329 }
330 return status;
331
332 out_err_client:
333 clnt_destroy(client);
334 goto out_unlock;
335 }
336
337 int nfs41_send_compound(
338 IN nfs41_rpc_clnt *rpc,
339 IN char *inbuf,
340 OUT char *outbuf)
341 {
342 struct timeval timeout = {90, 100};
343 enum clnt_stat rpc_status;
344 int status, count = 0, one = 1, zero = 0;
345 uint32_t version;
346
347 try_again:
348 AcquireSRWLockShared(&rpc->lock);
349 version = rpc->version;
350 rpc_status = clnt_call(rpc->rpc, 1,
351 (xdrproc_t)nfs_encode_compound, inbuf,
352 (xdrproc_t)nfs_decode_compound, outbuf,
353 timeout);
354 ReleaseSRWLockShared(&rpc->lock);
355
356 if (rpc_status != RPC_SUCCESS) {
357 eprintf("clnt_call returned rpc_status = %s\n",
358 rpc_error_string(rpc_status));
359 switch(rpc_status) {
360 case RPC_CANTRECV:
361 case RPC_CANTSEND:
362 case RPC_TIMEDOUT:
363 case RPC_AUTHERROR:
364 if (++count > 3 || !rpc->is_valid_session) {
365 status = ERROR_NETWORK_UNREACHABLE;
366 break;
367 }
368 if (rpc_should_retry(rpc, version))
369 goto try_again;
370 while (rpc_renew_in_progress(rpc, NULL)) {
371 status = WaitForSingleObject(rpc->cond, INFINITE);
372 if (status != WAIT_OBJECT_0) {
373 dprintf(1, "rpc_renew_in_progress: WaitForSingleObject failed\n");
374 print_condwait_status(1, status);
375 status = ERROR_LOCK_VIOLATION;
376 goto out;
377 }
378 rpc_renew_in_progress(rpc, &zero);
379 goto try_again;
380 }
381 rpc_renew_in_progress(rpc, &one);
382 if (rpc_status == RPC_AUTHERROR && rpc->sec_flavor != RPCSEC_AUTH_SYS) {
383 AcquireSRWLockExclusive(&rpc->lock);
384 auth_destroy(rpc->rpc->cl_auth);
385 status = create_rpcsec_auth_client(rpc->sec_flavor,
386 rpc->server_name, rpc->rpc);
387 ReleaseSRWLockExclusive(&rpc->lock);
388 if (status) {
389 eprintf("Failed to reestablish security context\n");
390 status = ERROR_NETWORK_UNREACHABLE;
391 goto out;
392 }
393 } else
394 if (rpc_reconnect(rpc))
395 eprintf("rpc_reconnect: Failed to reconnect!\n");
396 rpc_renew_in_progress(rpc, &zero);
397 goto try_again;
398 default:
399 eprintf("UNHANDLED RPC_ERROR: %d\n", rpc_status);
400 status = ERROR_NETWORK_UNREACHABLE;
401 goto out;
402 }
403 goto out;
404 }
405
406 status = 0;
407 out:
408 return status;
409 }