ReactOS  0.4.12-dev-18-gf469aca
nfs41_client.c
Go to the documentation of this file.
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 
37  IN bool_t is_data)
38 {
40  if (is_data)
42  else
44  return flags;
45 }
46 
47 static int pnfs_client_init(
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 
73 static int update_server(
75  IN const char *server_scope,
76  IN const server_owner4 *owner)
77 {
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 
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 
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;
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 
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 
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 
208 {
209  dprintf(2, "nfs41_client_free(%llu)\n", client->clnt_id);
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 
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);
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 
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) {
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 
298 {
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 {
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 
359  IN const char *name,
361  OUT client_owner4 *owner)
362 {
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,
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 }
#define realloc
Definition: debug_ros.c:6
pnfs_status
Definition: pnfs.h:58
static DWORD hash_mac_addrs(IN HCRYPTHASH hash)
Definition: nfs41_client.c:296
int nfs41_destroy_clientid(IN nfs41_rpc_clnt *rpc, IN uint64_t clientid)
Definition: nfs41_ops.c:242
VOID WINAPI AcquireSRWLockExclusive(PSRWLOCK Lock)
Definition: sync.c:54
GLint level
Definition: gl.h:1546
#define IN
Definition: typedefs.h:38
SRWLOCK exid_lock
Definition: nfs41.h:198
static rfbScreenInfoPtr server
Definition: vnc.c:74
#define RB_EMPTY(head)
Definition: tree.h:323
#define IF_TYPE_TUNNEL
Definition: ipifcons.h:151
Definition: http.c:6587
ACPI_SIZE strlen(const char *String)
Definition: utclib.c:269
int32_t bool_t
Definition: types.h:101
#define RB_INSERT(name, x, y)
Definition: tree.h:726
#define ERROR_BUFFER_OVERFLOW
Definition: winerror.h:185
#define DWORD
Definition: msvc.h:34
enum pnfs_status pnfs_file_device_list_create(OUT struct pnfs_file_device_list **devices_out)
Definition: pnfs_device.c:124
#define free
Definition: debug_ros.c:5
uint32_t nfs41_exchange_id_flags(IN bool_t is_data)
Definition: nfs41_client.c:36
uint8_t entry
Definition: isohybrid.c:63
static void mac_entry_insert(IN struct mac_tree *root, IN PBYTE address, IN ULONG length)
Definition: nfs41_client.c:258
void nfs41_client_delegation_free(IN nfs41_client *client)
Definition: delegation.c:828
#define ERROR_NOT_ENOUGH_MEMORY
Definition: dderror.h:7
client_owner4 owner
Definition: nfs41.h:194
DWORD WINAPI GetLastError(VOID)
Definition: except.c:1059
GLuint buffer
Definition: glext.h:5915
void eprintf(LPCSTR format,...)
Definition: daemon_debug.c:86
#define CryptAcquireContext
Definition: wincrypt.h:4162
static int pnfs_client_init(IN nfs41_client *client)
Definition: nfs41_client.c:47
void nfs41_rpc_clnt_free(IN nfs41_rpc_clnt *rpc)
Definition: nfs41_rpc.c:243
#define RB_FOREACH_SAFE(x, name, head, y)
Definition: tree.h:745
#define NO_ERROR
Definition: dderror.h:5
ULONGLONG WINAPI GetTickCount64(VOID)
Definition: GetTickCount64.c:9
int nfs41_server_find_or_create(IN const char *server_owner_major_id, IN const char *server_scope, IN const netaddr4 *addr, OUT nfs41_server **server_out)
Definition: nfs41_server.c:221
#define HP_HASHSIZE
Definition: wincrypt.h:2184
struct node node
#define ERROR_BAD_NET_RESP
Definition: winerror.h:150
BOOL WINAPI CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam, BYTE *pbData, DWORD *pdwDataLen, DWORD dwFlags)
Definition: crypt.c:1615
#define GAA_FLAG_SKIP_FRIENDLY_NAME
int hash
Definition: main.c:58
static int adapter_valid(IN const IP_ADAPTER_ADDRESSES *addr)
Definition: nfs41_client.c:276
#define dprintf
Definition: regdump.c:33
#define IF_TYPE_SOFTWARE_LOOPBACK
Definition: ipifcons.h:44
void nfs41_session_free(IN nfs41_session *session)
nfs41_rpc_clnt * rpc
Definition: nfs41.h:201
VOID WINAPI InitializeCriticalSection(OUT LPCRITICAL_SECTION lpCriticalSection)
Definition: synch.c:697
smooth NULL
Definition: ftsmooth.c:416
static __inline netaddr4 * nfs41_rpc_netaddr(IN nfs41_rpc_clnt *rpc)
Definition: nfs41.h:500
#define AF_UNSPEC
Definition: winsock.h:344
#define ERROR_FILE_NOT_FOUND
Definition: disk.h:79
static WCHAR username[]
Definition: url.c:32
void nfs41_client_free(IN nfs41_client *client)
Definition: nfs41_client.c:206
void WINAPI DeleteCriticalSection(PCRITICAL_SECTION)
GLenum GLuint GLenum GLsizei length
Definition: glext.h:5579
uint64_t ULONGLONG
Definition: typedefs.h:65
void pnfs_file_device_list_free(IN struct pnfs_file_device_list *devices)
Definition: pnfs_device.c:144
int strncmp(const char *String1, const char *String2, ACPI_SIZE Count)
Definition: utclib.c:534
static FILE * client
Definition: client.c:41
static FILE * out
Definition: regtests2xml.c:44
unsigned long DWORD
Definition: ntddk_ex.h:95
GLuint address
Definition: glext.h:9393
#define RB_GENERATE(name, type, field, cmp)
Definition: tree.h:400
GLbitfield flags
Definition: glext.h:7161
Definition: nfs41_client.c:243
BOOL WINAPI CryptDestroyHash(HCRYPTHASH hHash)
Definition: crypt.c:895
int nfs41_exchange_id(IN nfs41_rpc_clnt *rpc, IN client_owner4 *owner, IN uint32_t flags_in, OUT nfs41_exchange_id_res *res_out)
Definition: nfs41_ops.c:36
int mac_cmp(struct mac_entry *lhs, struct mac_entry *rhs)
Definition: nfs41_client.c:249
int nfs41_client_owner(IN const char *name, IN uint32_t sec_flavor, OUT client_owner4 *owner)
Definition: nfs41_client.c:358
GLenum const GLvoid * addr
Definition: glext.h:9621
enum pnfs_status pnfs_layout_list_create(OUT struct pnfs_layout_list **layouts_out)
Definition: pnfs_layout.c:121
static void dprint_roles(IN int level, IN uint32_t roles)
Definition: nfs41_client.c:158
struct client_state state
Definition: nfs41.h:215
VOID WINAPI ReleaseSRWLockExclusive(PSRWLOCK Lock)
Definition: sync.c:82
void pnfs_layout_list_free(IN struct pnfs_layout_list *layouts)
Definition: pnfs_layout.c:139
#define memcpy(s1, s2, n)
Definition: mkisofs.h:878
GLenum GLsizei len
Definition: glext.h:6722
BOOL WINAPI GetUserNameA(LPSTR lpszName, LPDWORD lpSize)
Definition: misc.c:246
BOOL WINAPI CryptCreateHash(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey, DWORD dwFlags, HCRYPTHASH *phHash)
Definition: crypt.c:745
#define CALG_MD5
Definition: wincrypt.h:1805
VOID WINAPI InitializeSRWLock(PSRWLOCK Lock)
Definition: sync.c:75
unsigned char BYTE
Definition: ntddk_ex.h:96
#define CRYPT_VERIFYCONTEXT
Definition: wincrypt.h:2069
ULONG_PTR HCRYPTHASH
Definition: wincrypt.h:50
struct __nfs41_client::@27 recovery
#define DEFAULT_MINIMUM_ENTITIES
Definition: iptypes.h:29
static int update_server(IN nfs41_client *client, IN const char *server_scope, IN const server_owner4 *owner)
Definition: nfs41_client.c:73
ULONG_PTR HCRYPTPROV
Definition: wincrypt.h:46
#define UNLEN
Definition: sspi.c:28
VOID WINAPI InitializeConditionVariable(PCONDITION_VARIABLE ConditionVariable)
Definition: sync.c:68
#define RB_INITIALIZER(root)
Definition: tree.h:301
BOOL WINAPI CryptHashData(HCRYPTHASH hHash, const BYTE *pbData, DWORD dwDataLen, DWORD dwFlags)
Definition: crypt.c:1776
#define GAA_FLAG_SKIP_UNICAST
UINT32 uint32_t
Definition: types.h:75
RB_HEAD(mac_tree, mac_entry)
#define RB_REMOVE(name, x, y)
Definition: tree.h:727
BOOL WINAPI CryptReleaseContext(HCRYPTPROV hProv, ULONG_PTR dwFlags)
Definition: crypt.c:651
Definition: name.c:36
#define calloc
Definition: rosglue.h:14
#define OUT
Definition: typedefs.h:39
__WINE_SERVER_LIST_INLINE void list_init(struct list *list)
Definition: list.h:149
unsigned int ULONG
Definition: retypes.h:1
void nfs41_server_deref(IN nfs41_server *server)
Definition: nfs41_server.c:155
sec_flavor
Definition: nfs41_ops.h:861
#define HP_HASHVAL
Definition: wincrypt.h:2183
int 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)
Definition: nfs41_client.c:108
Definition: _hash_fun.h:40
static SERVICE_STATUS status
Definition: service.c:31
int nfs41_client_renew(IN nfs41_client *client)
Definition: nfs41_client.c:168
BYTE * PBYTE
Definition: pedump.c:66
static PIP_ADAPTER_ADDRESSES
Definition: iphlpapi.c:76
#define RB_ENTRY(type)
Definition: tree.h:310
static int update_exchangeid_res(IN nfs41_client *client, IN const nfs41_exchange_id_res *exchangeid)
Definition: nfs41_client.c:97
#define PROV_RSA_FULL
Definition: wincrypt.h:2039
bool_t is_data
Definition: nfs41.h:202
Definition: ps.c:97