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 <windows.h>
23 #include <stdio.h>
24 #include <time.h>
25 #include <winsock2.h>
26 #include <iphlpapi.h> /* for GetAdaptersAddresses() */
27 #include <wincrypt.h> /* for Crypt*() functions */
28 #include <winsock2.h> /* for hostent struct */
29
30 #include "tree.h"
31 #include "delegation.h"
32 #include "daemon_debug.h"
33 #include "nfs41_ops.h"
34
35
nfs41_exchange_id_flags(IN bool_t is_data)36 uint32_t nfs41_exchange_id_flags(
37 IN bool_t is_data)
38 {
39 uint32_t flags = EXCHGID4_FLAG_SUPP_MOVED_REFER;
40 if (is_data)
41 flags |= EXCHGID4_FLAG_USE_PNFS_DS;
42 else
43 flags |= EXCHGID4_FLAG_USE_NON_PNFS | EXCHGID4_FLAG_USE_PNFS_MDS;
44 return flags;
45 }
46
pnfs_client_init(IN nfs41_client * client)47 static int pnfs_client_init(
48 IN nfs41_client *client)
49 {
50 enum pnfs_status pnfsstat;
51 int status = NO_ERROR;
52
53 /* initialize the pnfs layout and device lists for metadata clients */
54 pnfsstat = pnfs_layout_list_create(&client->layouts);
55 if (pnfsstat) {
56 status = ERROR_NOT_ENOUGH_MEMORY;
57 goto out;
58 }
59 pnfsstat = pnfs_file_device_list_create(&client->devices);
60 if (pnfsstat) {
61 status = ERROR_NOT_ENOUGH_MEMORY;
62 goto out_err_layouts;
63 }
64 out:
65 return status;
66
67 out_err_layouts:
68 pnfs_layout_list_free(client->layouts);
69 client->layouts = NULL;
70 goto out;
71 }
72
update_server(IN nfs41_client * client,IN const char * server_scope,IN const server_owner4 * owner)73 static int update_server(
74 IN nfs41_client *client,
75 IN const char *server_scope,
76 IN const server_owner4 *owner)
77 {
78 nfs41_server *server;
79 int status;
80
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);
84 if (status)
85 goto out;
86
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 */
90 if (client->server)
91 nfs41_server_deref(client->server);
92 client->server = server;
93 out:
94 return status;
95 }
96
update_exchangeid_res(IN nfs41_client * client,IN const nfs41_exchange_id_res * exchangeid)97 static int update_exchangeid_res(
98 IN nfs41_client *client,
99 IN const nfs41_exchange_id_res *exchangeid)
100 {
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);
106 }
107
nfs41_client_create(IN nfs41_rpc_clnt * rpc,IN const client_owner4 * owner,IN bool_t is_data,IN const nfs41_exchange_id_res * exchangeid,OUT nfs41_client ** client_out)108 int nfs41_client_create(
109 IN nfs41_rpc_clnt *rpc,
110 IN const client_owner4 *owner,
111 IN bool_t is_data,
112 IN const nfs41_exchange_id_res *exchangeid,
113 OUT nfs41_client **client_out)
114 {
115 int status;
116 nfs41_client *client;
117
118 client = calloc(1, sizeof(nfs41_client));
119 if (client == NULL) {
120 status = GetLastError();
121 goto out_err_rpc;
122 }
123
124 memcpy(&client->owner, owner, sizeof(client_owner4));
125 client->rpc = rpc;
126 client->is_data = is_data;
127
128 status = update_exchangeid_res(client, exchangeid);
129 if (status)
130 goto out_err_client;
131
132 list_init(&client->state.opens);
133 list_init(&client->state.delegations);
134 InitializeCriticalSection(&client->state.lock);
135
136 //initialize a lock used to protect access to client id and client id seq#
137 InitializeSRWLock(&client->exid_lock);
138
139 InitializeConditionVariable(&client->recovery.cond);
140 InitializeCriticalSection(&client->recovery.lock);
141
142 status = pnfs_client_init(client);
143 if (status) {
144 eprintf("pnfs_client_init() failed with %d\n", status);
145 goto out_err_client;
146 }
147 *client_out = client;
148 out:
149 return status;
150 out_err_client:
151 nfs41_client_free(client); /* also calls nfs41_rpc_clnt_free() */
152 goto out;
153 out_err_rpc:
154 nfs41_rpc_clnt_free(rpc);
155 goto out;
156 }
157
dprint_roles(IN int level,IN uint32_t roles)158 static void dprint_roles(
159 IN int level,
160 IN uint32_t roles)
161 {
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" : "");
166 }
167
nfs41_client_renew(IN nfs41_client * client)168 int nfs41_client_renew(
169 IN nfs41_client *client)
170 {
171 nfs41_exchange_id_res exchangeid = { 0 };
172 int status;
173
174 status = nfs41_exchange_id(client->rpc, &client->owner,
175 nfs41_exchange_id_flags(client->is_data), &exchangeid);
176 if (status) {
177 eprintf("nfs41_exchange_id() failed with %d\n", status);
178 status = ERROR_BAD_NET_RESP;
179 goto out;
180 }
181
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;
186 goto out;
187 }
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;
193 goto out;
194 }
195 }
196
197 dprint_roles(2, exchangeid.flags);
198
199 AcquireSRWLockExclusive(&client->exid_lock);
200 status = update_exchangeid_res(client, &exchangeid);
201 ReleaseSRWLockExclusive(&client->exid_lock);
202 out:
203 return status;
204 }
205
nfs41_client_free(IN nfs41_client * client)206 void nfs41_client_free(
207 IN nfs41_client *client)
208 {
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);
219 free(client);
220 }
221
222
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 */
230
231 /* References:
232 * RFC 5661: 2.4. Client Identifiers and Client Owners
233 * http://tools.ietf.org/html/rfc5661#section-2.4
234 *
235 * MSDN: GetAdaptersAddresses Function
236 * http://msdn.microsoft.com/en-us/library/aa365915%28VS.85%29.aspx
237 *
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 */
240
241
242 /* use an rbtree to sort mac address entries */
243 struct mac_entry {
244 RB_ENTRY(mac_entry) rbnode;
245 PBYTE address;
246 ULONG length;
247 };
248
mac_cmp(struct mac_entry * lhs,struct mac_entry * rhs)249 int mac_cmp(struct mac_entry *lhs, struct mac_entry *rhs)
250 {
251 const int diff = rhs->length - lhs->length;
252 return diff ? diff : strncmp((const char*)lhs->address,
253 (const char*)rhs->address, lhs->length);
254 }
255 RB_HEAD(mac_tree, mac_entry);
RB_GENERATE(mac_tree,mac_entry,rbnode,mac_cmp)256 RB_GENERATE(mac_tree, mac_entry, rbnode, mac_cmp)
257
258 static void mac_entry_insert(
259 IN struct mac_tree *root,
260 IN PBYTE address,
261 IN ULONG length)
262 {
263 struct mac_entry *entry;
264
265 entry = calloc(1, sizeof(struct mac_entry));
266 if (entry == NULL)
267 return;
268
269 entry->address = address;
270 entry->length = length;
271
272 if (RB_INSERT(mac_tree, root, entry))
273 free(entry);
274 }
275
adapter_valid(IN const IP_ADAPTER_ADDRESSES * addr)276 static int adapter_valid(
277 IN const IP_ADAPTER_ADDRESSES *addr)
278 {
279 /* ignore generic interfaces whose address is not unique */
280 switch (addr->IfType) {
281 case IF_TYPE_SOFTWARE_LOOPBACK:
282 case IF_TYPE_TUNNEL:
283 return 0;
284 }
285 /* must have an address */
286 if (addr->PhysicalAddressLength == 0)
287 return 0;
288 #ifndef __REACTOS__
289 /* must support ip */
290 return addr->Ipv4Enabled || addr->Ipv6Enabled;
291 #else
292 return 1;
293 #endif
294 }
295
hash_mac_addrs(IN HCRYPTHASH hash)296 static DWORD hash_mac_addrs(
297 IN HCRYPTHASH hash)
298 {
299 PIP_ADAPTER_ADDRESSES addr, addrs = NULL;
300 struct mac_tree rbtree = RB_INITIALIZER(rbtree);
301 struct mac_entry *entry, *node;
302 ULONG len;
303 DWORD status;
304
305 /* start with enough room for DEFAULT_MINIMUM_ENTITIES */
306 len = DEFAULT_MINIMUM_ENTITIES * sizeof(IP_ADAPTER_ADDRESSES);
307
308 do {
309 PIP_ADAPTER_ADDRESSES tmp;
310 /* reallocate the buffer until we can fit all of it */
311 tmp = realloc(addrs, len);
312 if (tmp == NULL) {
313 status = GetLastError();
314 goto out;
315 }
316 addrs = tmp;
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,
321 NULL, addrs, &len);
322 } while (status == ERROR_BUFFER_OVERFLOW);
323
324 if (status) {
325 eprintf("GetAdaptersAddresses() failed with %d\n", status);
326 goto out;
327 }
328
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);
334
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);
340 goto out;
341 }
342
343 RB_FOREACH_SAFE(entry, mac_tree, &rbtree, node) {
344 RB_REMOVE(mac_tree, &rbtree, entry);
345
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 */
350 }
351 free(entry);
352 }
353 out:
354 free(addrs);
355 return status;
356 }
357
nfs41_client_owner(IN const char * name,IN uint32_t sec_flavor,OUT client_owner4 * owner)358 int nfs41_client_owner(
359 IN const char *name,
360 IN uint32_t sec_flavor,
361 OUT client_owner4 *owner)
362 {
363 HCRYPTPROV context;
364 HCRYPTHASH hash;
365 PBYTE buffer;
366 DWORD length;
367 const ULONGLONG time_created = GetTickCount64();
368 int status;
369 char username[UNLEN + 1];
370 DWORD len = UNLEN + 1;
371
372 if (!GetUserNameA(username, &len)) {
373 status = GetLastError();
374 eprintf("GetUserName() failed with %d\n", status);
375 goto out;
376 }
377
378 /* owner.verifier = "time created" */
379 memcpy(owner->co_verifier, &time_created, sizeof(time_created));
380
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);
386 goto out;
387 }
388 if (!CryptCreateHash(context, CALG_MD5, 0, 0, &hash)) {
389 status = GetLastError();
390 eprintf("CryptCreateHash() failed with %d\n", status);
391 goto out_context;
392 }
393
394 if (!CryptHashData(hash, (const BYTE*)&sec_flavor, (DWORD)sizeof(sec_flavor), 0)) {
395 status = GetLastError();
396 eprintf("CryptHashData() failed with %d\n", status);
397 goto out_hash;
398 }
399
400 if (!CryptHashData(hash, (const BYTE*)username, (DWORD)strlen(username), 0)) {
401 status = GetLastError();
402 eprintf("CryptHashData() failed with %d\n", status);
403 goto out_hash;
404 }
405
406 if (!CryptHashData(hash, (const BYTE*)name, (DWORD)strlen(name), 0)) {
407 status = GetLastError();
408 eprintf("CryptHashData() failed with %d\n", status);
409 goto out_hash;
410 }
411
412 /* add the mac address from each applicable adapter to the hash */
413 status = hash_mac_addrs(hash);
414 if (status) {
415 eprintf("hash_mac_addrs() failed with %d\n", status);
416 goto out_hash;
417 }
418
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);
425 goto out_hash;
426 }
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);
433 goto out_hash;
434 }
435
436 out_hash:
437 CryptDestroyHash(hash);
438 out_context:
439 CryptReleaseContext(context, 0);
440 out:
441 return status;
442 }
443