1 /* NFSv4.1 client for Windows
2 * Copyright © 2012 The Regents of the University of Michigan
4 * Olga Kornievskaia <aglo@umich.edu>
5 * Casey Bodley <cbodley@umich.edu>
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.
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.
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
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 */
29 #define SECURITY_WIN32
31 #include "rpc/auth_sspi.h"
33 static enum clnt_stat
send_null(CLIENT
*client
)
35 struct timeval timeout
= {10, 100};
37 return clnt_call(client
, 0,
38 (xdrproc_t
)xdr_void
, NULL
,
39 (xdrproc_t
)xdr_void
, NULL
, timeout
);
42 static int get_client_for_netaddr(
43 IN
const netaddr4
*netaddr
,
46 IN nfs41_rpc_clnt
*rpc
,
47 OUT OPTIONAL
char *server_name
,
48 OUT CLIENT
**client_out
)
50 int status
= ERROR_NETWORK_UNREACHABLE
;
51 struct netconfig
*nconf
;
55 nconf
= getnetconfigent(netaddr
->netid
);
59 addr
= uaddr2taddr(nconf
, netaddr
->uaddr
);
64 getnameinfo(addr
->buf
, addr
->len
, server_name
, NI_MAXHOST
, NULL
, 0, 0);
65 dprintf(1, "servername is %s\n", server_name
);
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
);
78 freenetconfigent(nconf
);
83 static int get_client_for_multi_addr(
84 IN
const multi_addr4
*addrs
,
87 IN nfs41_rpc_clnt
*rpc
,
88 OUT OPTIONAL
char *server_name
,
89 OUT CLIENT
**client_out
,
90 OUT
uint32_t *addr_index
)
92 int status
= ERROR_NETWORK_UNREACHABLE
;
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
) {
105 int create_rpcsec_auth_client(
106 IN
uint32_t sec_flavor
,
107 IN
char *server_name
,
111 int status
= ERROR_NETWORK_UNREACHABLE
;
113 switch (sec_flavor
) {
114 case RPCSEC_AUTHGSS_KRB5
:
115 client
->cl_auth
= authsspi_create_default(client
, server_name
,
116 RPCSEC_SSPI_SVC_NONE
);
118 case RPCSEC_AUTHGSS_KRB5I
:
119 client
->cl_auth
= authsspi_create_default(client
, server_name
,
120 RPCSEC_SSPI_SVC_INTEGRITY
);
122 case RPCSEC_AUTHGSS_KRB5P
:
123 client
->cl_auth
= authsspi_create_default(client
, server_name
,
124 RPCSEC_SSPI_SVC_PRIVACY
);
127 eprintf("create_rpc_auth_client: unknown rpcsec flavor %d\n",
129 client
->cl_auth
= NULL
;
132 if (client
->cl_auth
== NULL
) {
133 eprintf("nfs41_rpc_clnt_create: failed to create %s\n",
134 secflavorop2name(sec_flavor
));
137 dprintf(1, "nfs41_rpc_clnt_create: successfully created %s\n",
138 secflavorop2name(sec_flavor
));
144 /* Returns a client structure and an associated lock */
145 int nfs41_rpc_clnt_create(
146 IN
const multi_addr4
*addrs
,
151 IN
uint32_t sec_flavor
,
152 OUT nfs41_rpc_clnt
**rpc_out
)
158 char machname
[MAXHOSTNAMELEN
+ 1];
162 rpc
= calloc(1, sizeof(nfs41_rpc_clnt
));
164 status
= GetLastError();
168 if (sec_flavor
== RPCSEC_AUTHGSS_KRB5P
)
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
;
178 status
= get_client_for_multi_addr(addrs
, wsize
, rsize
, needcb
?rpc
:NULL
,
179 rpc
->server_name
, &client
, &addr_index
);
181 clnt_pcreateerror("connecting failed");
182 goto out_free_rpc_cond
;
184 if (send_null(client
) != RPC_SUCCESS
) {
186 eprintf("nfs41_rpc_clnt_create: send_null failed\n");
187 status
= ERROR_NETWORK_UNREACHABLE
;
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");
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
;
205 status
= create_rpcsec_auth_client(sec_flavor
, rpc
->server_name
, client
);
207 eprintf("nfs41_rpc_clnt_create: failed to establish security "
208 "context with %s\n", rpc
->server_name
);
209 status
= ERROR_NETWORK_UNREACHABLE
;
212 dprintf(1, "nfs41_rpc_clnt_create: successfully created %s\n",
213 secflavorop2name(sec_flavor
));
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
;
223 rpc
->is_valid_session
= TRUE
;
227 //initialize rpc client lock
228 InitializeSRWLock(&rpc
->lock
);
234 clnt_destroy(client
);
236 CloseHandle(rpc
->cond
);
242 /* Frees resources allocated in clnt_create */
243 void nfs41_rpc_clnt_free(
244 IN nfs41_rpc_clnt
*rpc
)
246 auth_destroy(rpc
->rpc
->cl_auth
);
247 clnt_destroy(rpc
->rpc
);
248 CloseHandle(rpc
->cond
);
252 static bool_t
rpc_renew_in_progress(nfs41_rpc_clnt
*rpc
, int *value
)
254 bool_t status
= FALSE
;
255 AcquireSRWLockExclusive(&rpc
->lock
);
257 dprintf(1, "nfs41_rpc_renew_in_progress: setting value %d\n", *value
);
258 rpc
->in_recovery
= *value
;
259 if (!rpc
->in_recovery
)
262 status
= rpc
->in_recovery
;
263 dprintf(1, "nfs41_rpc_renew_in_progress: returning value %d\n", status
);
265 ReleaseSRWLockExclusive(&rpc
->lock
);
269 static bool_t
rpc_should_retry(nfs41_rpc_clnt
*rpc
, uint32_t version
)
272 AcquireSRWLockExclusive(&rpc
->lock
);
273 if (rpc
->version
> version
)
275 ReleaseSRWLockExclusive(&rpc
->lock
);
279 static int rpc_reconnect(
280 IN nfs41_rpc_clnt
*rpc
)
282 CLIENT
*client
= NULL
;
286 AcquireSRWLockExclusive(&rpc
->lock
);
288 status
= get_client_for_multi_addr(&rpc
->addrs
, rpc
->wsize
, rpc
->rsize
,
289 rpc
->needcb
?rpc
:NULL
, NULL
, &client
, &addr_index
);
293 if(rpc
->sec_flavor
== RPCSEC_AUTH_SYS
)
294 client
->cl_auth
= rpc
->rpc
->cl_auth
;
296 auth_destroy(rpc
->rpc
->cl_auth
);
297 status
= create_rpcsec_auth_client(rpc
->sec_flavor
, rpc
->server_name
, client
);
299 eprintf("Failed to reestablish security context\n");
300 status
= ERROR_NETWORK_UNREACHABLE
;
304 if (send_null(client
) != RPC_SUCCESS
) {
305 eprintf("rpc_reconnect: send_null failed\n");
306 status
= ERROR_NETWORK_UNREACHABLE
;
310 clnt_destroy(rpc
->rpc
);
312 rpc
->addr_index
= addr_index
;
314 dprintf(1, "nfs41_send_compound: reestablished RPC connection\n");
317 ReleaseSRWLockExclusive(&rpc
->lock
);
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
);
326 eprintf("nfs41_bind_conn_to_session() failed with %s\n",
327 nfs_error_string(status
));
333 clnt_destroy(client
);
337 int nfs41_send_compound(
338 IN nfs41_rpc_clnt
*rpc
,
342 struct timeval timeout
= {90, 100};
343 enum clnt_stat rpc_status
;
344 int status
, count
= 0, one
= 1, zero
= 0;
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
,
354 ReleaseSRWLockShared(&rpc
->lock
);
356 if (rpc_status
!= RPC_SUCCESS
) {
357 eprintf("clnt_call returned rpc_status = %s\n",
358 rpc_error_string(rpc_status
));
364 if (++count
> 3 || !rpc
->is_valid_session
) {
365 status
= ERROR_NETWORK_UNREACHABLE
;
368 if (rpc_should_retry(rpc
, version
))
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
;
378 rpc_renew_in_progress(rpc
, &zero
);
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
);
389 eprintf("Failed to reestablish security context\n");
390 status
= ERROR_NETWORK_UNREACHABLE
;
394 if (rpc_reconnect(rpc
))
395 eprintf("rpc_reconnect: Failed to reconnect!\n");
396 rpc_renew_in_progress(rpc
, &zero
);
399 eprintf("UNHANDLED RPC_ERROR: %d\n", rpc_status
);
400 status
= ERROR_NETWORK_UNREACHABLE
;