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
26 #include <iphlpapi.h> /* for GetAdaptersAddresses() */
27 #include <wincrypt.h> /* for Crypt*() functions */
28 #include <winsock2.h> /* for hostent struct */
31 #include "delegation.h"
32 #include "daemon_debug.h"
33 #include "nfs41_ops.h"
36 uint32_t nfs41_exchange_id_flags(
39 uint32_t flags
= EXCHGID4_FLAG_SUPP_MOVED_REFER
;
41 flags
|= EXCHGID4_FLAG_USE_PNFS_DS
;
43 flags
|= EXCHGID4_FLAG_USE_NON_PNFS
| EXCHGID4_FLAG_USE_PNFS_MDS
;
47 static int pnfs_client_init(
48 IN nfs41_client
*client
)
50 enum pnfs_status pnfsstat
;
51 int status
= NO_ERROR
;
53 /* initialize the pnfs layout and device lists for metadata clients */
54 pnfsstat
= pnfs_layout_list_create(&client
->layouts
);
56 status
= ERROR_NOT_ENOUGH_MEMORY
;
59 pnfsstat
= pnfs_file_device_list_create(&client
->devices
);
61 status
= ERROR_NOT_ENOUGH_MEMORY
;
68 pnfs_layout_list_free(client
->layouts
);
69 client
->layouts
= NULL
;
73 static int update_server(
74 IN nfs41_client
*client
,
75 IN
const char *server_scope
,
76 IN
const server_owner4
*owner
)
81 /* find a server matching the owner.major_id and scope */
82 status
= nfs41_server_find_or_create(owner
->so_major_id
,
83 server_scope
, nfs41_rpc_netaddr(client
->rpc
), &server
);
87 /* if the server is the same, we now have an extra reference. if
88 * the servers are different, we still need to deref the old server.
89 * so both cases can be treated the same */
91 nfs41_server_deref(client
->server
);
92 client
->server
= server
;
97 static int update_exchangeid_res(
98 IN nfs41_client
*client
,
99 IN
const nfs41_exchange_id_res
*exchangeid
)
101 client
->clnt_id
= exchangeid
->clientid
;
102 client
->seq_id
= exchangeid
->sequenceid
;
103 client
->roles
= exchangeid
->flags
& EXCHGID4_FLAG_MASK_PNFS
;
104 return update_server(client
, exchangeid
->server_scope
,
105 &exchangeid
->server_owner
);
108 int nfs41_client_create(
109 IN nfs41_rpc_clnt
*rpc
,
110 IN
const client_owner4
*owner
,
112 IN
const nfs41_exchange_id_res
*exchangeid
,
113 OUT nfs41_client
**client_out
)
116 nfs41_client
*client
;
118 client
= calloc(1, sizeof(nfs41_client
));
119 if (client
== NULL
) {
120 status
= GetLastError();
124 memcpy(&client
->owner
, owner
, sizeof(client_owner4
));
126 client
->is_data
= is_data
;
128 status
= update_exchangeid_res(client
, exchangeid
);
132 list_init(&client
->state
.opens
);
133 list_init(&client
->state
.delegations
);
134 InitializeCriticalSection(&client
->state
.lock
);
136 //initialize a lock used to protect access to client id and client id seq#
137 InitializeSRWLock(&client
->exid_lock
);
139 InitializeConditionVariable(&client
->recovery
.cond
);
140 InitializeCriticalSection(&client
->recovery
.lock
);
142 status
= pnfs_client_init(client
);
144 eprintf("pnfs_client_init() failed with %d\n", status
);
147 *client_out
= client
;
151 nfs41_client_free(client
); /* also calls nfs41_rpc_clnt_free() */
154 nfs41_rpc_clnt_free(rpc
);
158 static void dprint_roles(
162 dprintf(level
, "roles: %s%s%s\n",
163 (roles
& EXCHGID4_FLAG_USE_NON_PNFS
) ? "USE_NON_PNFS " : "",
164 (roles
& EXCHGID4_FLAG_USE_PNFS_MDS
) ? "USE_PNFS_MDS " : "",
165 (roles
& EXCHGID4_FLAG_USE_PNFS_DS
) ? "USE_PNFS_DS" : "");
168 int nfs41_client_renew(
169 IN nfs41_client
*client
)
171 nfs41_exchange_id_res exchangeid
= { 0 };
174 status
= nfs41_exchange_id(client
->rpc
, &client
->owner
,
175 nfs41_exchange_id_flags(client
->is_data
), &exchangeid
);
177 eprintf("nfs41_exchange_id() failed with %d\n", status
);
178 status
= ERROR_BAD_NET_RESP
;
182 if (client
->is_data
) { /* require USE_PNFS_DS */
183 if ((exchangeid
.flags
& EXCHGID4_FLAG_USE_PNFS_DS
) == 0) {
184 eprintf("client expected USE_PNFS_DS\n");
185 status
= ERROR_BAD_NET_RESP
;
188 } else { /* require USE_NON_PNFS or USE_PNFS_MDS */
189 if ((exchangeid
.flags
& EXCHGID4_FLAG_USE_NON_PNFS
) == 0 &&
190 (exchangeid
.flags
& EXCHGID4_FLAG_USE_PNFS_MDS
) == 0) {
191 eprintf("client expected USE_NON_PNFS OR USE_PNFS_MDS\n");
192 status
= ERROR_BAD_NET_RESP
;
197 dprint_roles(2, exchangeid
.flags
);
199 AcquireSRWLockExclusive(&client
->exid_lock
);
200 status
= update_exchangeid_res(client
, &exchangeid
);
201 ReleaseSRWLockExclusive(&client
->exid_lock
);
206 void nfs41_client_free(
207 IN nfs41_client
*client
)
209 dprintf(2, "nfs41_client_free(%llu)\n", client
->clnt_id
);
210 nfs41_client_delegation_free(client
);
211 if (client
->session
) nfs41_session_free(client
->session
);
212 nfs41_destroy_clientid(client
->rpc
, client
->clnt_id
);
213 if (client
->server
) nfs41_server_deref(client
->server
);
214 nfs41_rpc_clnt_free(client
->rpc
);
215 if (client
->layouts
) pnfs_layout_list_free(client
->layouts
);
216 if (client
->devices
) pnfs_file_device_list_free(client
->devices
);
217 DeleteCriticalSection(&client
->state
.lock
);
218 DeleteCriticalSection(&client
->recovery
.lock
);
223 /* client_owner generation
224 * we choose to use MAC addresses to generate a client_owner value that
225 * is unique to a machine and persists over restarts. because the client
226 * can have multiple network adapters/addresses, we take each adapter into
227 * account. the specification suggests that "for privacy reasons, it is
228 * best to perform some one-way function," so we apply an md5 hash to the
229 * sorted list of MAC addresses */
232 * RFC 5661: 2.4. Client Identifiers and Client Owners
233 * http://tools.ietf.org/html/rfc5661#section-2.4
235 * MSDN: GetAdaptersAddresses Function
236 * http://msdn.microsoft.com/en-us/library/aa365915%28VS.85%29.aspx
238 * MSDN: Example C Program: Creating an MD5 Hash from File Content
239 * http://msdn.microsoft.com/en-us/library/aa382380%28VS.85%29.aspx */
242 /* use an rbtree to sort mac address entries */
244 RB_ENTRY(mac_entry
) rbnode
;
249 int mac_cmp(struct mac_entry
*lhs
, struct mac_entry
*rhs
)
251 const int diff
= rhs
->length
- lhs
->length
;
252 return diff
? diff
: strncmp((const char*)lhs
->address
,
253 (const char*)rhs
->address
, lhs
->length
);
255 RB_HEAD(mac_tree
, mac_entry
);
256 RB_GENERATE(mac_tree
, mac_entry
, rbnode
, mac_cmp
)
258 static void mac_entry_insert(
259 IN
struct mac_tree
*root
,
263 struct mac_entry
*entry
;
265 entry
= calloc(1, sizeof(struct mac_entry
));
269 entry
->address
= address
;
270 entry
->length
= length
;
272 if (RB_INSERT(mac_tree
, root
, entry
))
276 static int adapter_valid(
277 IN
const IP_ADAPTER_ADDRESSES
*addr
)
279 /* ignore generic interfaces whose address is not unique */
280 switch (addr
->IfType
) {
281 case IF_TYPE_SOFTWARE_LOOPBACK
:
285 /* must have an address */
286 if (addr
->PhysicalAddressLength
== 0)
289 /* must support ip */
290 return addr
->Ipv4Enabled
|| addr
->Ipv6Enabled
;
296 static DWORD
hash_mac_addrs(
299 PIP_ADAPTER_ADDRESSES addr
, addrs
= NULL
;
300 struct mac_tree rbtree
= RB_INITIALIZER(rbtree
);
301 struct mac_entry
*entry
, *node
;
305 /* start with enough room for DEFAULT_MINIMUM_ENTITIES */
306 len
= DEFAULT_MINIMUM_ENTITIES
* sizeof(IP_ADAPTER_ADDRESSES
);
309 PIP_ADAPTER_ADDRESSES tmp
;
310 /* reallocate the buffer until we can fit all of it */
311 tmp
= realloc(addrs
, len
);
313 status
= GetLastError();
317 status
= GetAdaptersAddresses(AF_UNSPEC
,
318 GAA_FLAG_INCLUDE_ALL_INTERFACES
| GAA_FLAG_SKIP_ANYCAST
|
319 GAA_FLAG_SKIP_DNS_SERVER
| GAA_FLAG_SKIP_FRIENDLY_NAME
|
320 GAA_FLAG_SKIP_MULTICAST
| GAA_FLAG_SKIP_UNICAST
,
322 } while (status
== ERROR_BUFFER_OVERFLOW
);
325 eprintf("GetAdaptersAddresses() failed with %d\n", status
);
329 /* get the mac address of each adapter */
330 for (addr
= addrs
; addr
; addr
= addr
->Next
)
331 if (adapter_valid(addr
))
332 mac_entry_insert(&rbtree
, addr
->PhysicalAddress
,
333 addr
->PhysicalAddressLength
);
335 /* require at least one valid address */
336 if (RB_EMPTY(&rbtree
)) {
337 status
= ERROR_FILE_NOT_FOUND
;
338 eprintf("GetAdaptersAddresses() did not return "
339 "any valid mac addresses, failing with %d.\n", status
);
343 RB_FOREACH_SAFE(entry
, mac_tree
, &rbtree
, node
) {
344 RB_REMOVE(mac_tree
, &rbtree
, entry
);
346 if (!CryptHashData(hash
, entry
->address
, entry
->length
, 0)) {
347 status
= GetLastError();
348 eprintf("CryptHashData() failed with %d\n", status
);
349 /* don't break here, we need to free the rest */
358 int nfs41_client_owner(
360 IN
uint32_t sec_flavor
,
361 OUT client_owner4
*owner
)
367 const ULONGLONG time_created
= GetTickCount64();
369 char username
[UNLEN
+ 1];
370 DWORD len
= UNLEN
+ 1;
372 if (!GetUserNameA(username
, &len
)) {
373 status
= GetLastError();
374 eprintf("GetUserName() failed with %d\n", status
);
378 /* owner.verifier = "time created" */
379 memcpy(owner
->co_verifier
, &time_created
, sizeof(time_created
));
381 /* set up the md5 hash generator */
382 if (!CryptAcquireContext(&context
, NULL
, NULL
,
383 PROV_RSA_FULL
, CRYPT_VERIFYCONTEXT
)) {
384 status
= GetLastError();
385 eprintf("CryptAcquireContext() failed with %d\n", status
);
388 if (!CryptCreateHash(context
, CALG_MD5
, 0, 0, &hash
)) {
389 status
= GetLastError();
390 eprintf("CryptCreateHash() failed with %d\n", status
);
394 if (!CryptHashData(hash
, (const BYTE
*)&sec_flavor
, (DWORD
)sizeof(sec_flavor
), 0)) {
395 status
= GetLastError();
396 eprintf("CryptHashData() failed with %d\n", status
);
400 if (!CryptHashData(hash
, (const BYTE
*)username
, (DWORD
)strlen(username
), 0)) {
401 status
= GetLastError();
402 eprintf("CryptHashData() failed with %d\n", status
);
406 if (!CryptHashData(hash
, (const BYTE
*)name
, (DWORD
)strlen(name
), 0)) {
407 status
= GetLastError();
408 eprintf("CryptHashData() failed with %d\n", status
);
412 /* add the mac address from each applicable adapter to the hash */
413 status
= hash_mac_addrs(hash
);
415 eprintf("hash_mac_addrs() failed with %d\n", status
);
419 /* extract the hash size (should always be 16 for md5) */
420 buffer
= (PBYTE
)&owner
->co_ownerid_len
;
421 length
= (DWORD
)sizeof(DWORD
);
422 if (!CryptGetHashParam(hash
, HP_HASHSIZE
, buffer
, &length
, 0)) {
423 status
= GetLastError();
424 eprintf("CryptGetHashParam(size) failed with %d\n", status
);
427 /* extract the hash buffer */
428 buffer
= owner
->co_ownerid
;
429 length
= owner
->co_ownerid_len
;
430 if (!CryptGetHashParam(hash
, HP_HASHVAL
, buffer
, &length
, 0)) {
431 status
= GetLastError();
432 eprintf("CryptGetHashParam(val) failed with %d\n", status
);
437 CryptDestroyHash(hash
);
439 CryptReleaseContext(context
, 0);