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 <strsafe.h>
24 #include <winldap.h>
25 #include <stdlib.h> /* for strtoul() */
26 #include <errno.h>
27 #include <time.h>
28
29 #include "idmap.h"
30 #include "nfs41_const.h"
31 #include "list.h"
32 #include "daemon_debug.h"
33
34
35 #define IDLVL 2 /* dprintf level for idmap logging */
36
37 #define FILTER_LEN 1024
38 #define NAME_LEN 32
39 #define VAL_LEN 257
40
41
42 enum ldap_class {
43 CLASS_USER,
44 CLASS_GROUP,
45
46 NUM_CLASSES
47 };
48
49 enum ldap_attr {
50 ATTR_USER_NAME,
51 ATTR_GROUP_NAME,
52 ATTR_PRINCIPAL,
53 ATTR_UID,
54 ATTR_GID,
55
56 NUM_ATTRIBUTES
57 };
58
59 #define ATTR_FLAG(attr) (1 << (attr))
60 #define ATTR_ISSET(mask, attr) (((mask) & ATTR_FLAG(attr)) != 0)
61
62
63 /* ldap/cache lookups */
64 struct idmap_lookup {
65 enum ldap_attr attr;
66 enum ldap_class klass;
67 #ifdef __REACTOS__
68 uint32_t type;
69 #else
70 enum config_type type;
71 #endif
72 list_compare_fn compare;
73 const void *value;
74 };
75
76
77 #ifndef __REACTOS__
78 /* configuration */
79 static const char CONFIG_FILENAME[] = "C:\\ReactOS\\System32\\drivers\\etc\\ms-nfs41-idmap.conf";
80 #endif
81
82 struct idmap_config {
83 /* ldap server information */
84 char hostname[NFS41_HOSTNAME_LEN+1];
85 UINT port;
86 UINT version;
87 UINT timeout;
88
89 /* ldap schema information */
90 char classes[NUM_CLASSES][NAME_LEN];
91 char attributes[NUM_ATTRIBUTES][NAME_LEN];
92 char base[VAL_LEN];
93
94 /* caching configuration */
95 INT cache_ttl;
96 };
97
98
99 enum config_type {
100 TYPE_STR,
101 TYPE_INT
102 };
103
104 struct config_option {
105 const char *key;
106 const char *def;
107 enum config_type type;
108 size_t offset;
109 size_t max_len;
110 };
111
112 /* helper macros for declaring config_options */
113 #define OPT_INT(key,def,field) \
114 { key, def, TYPE_INT, FIELD_OFFSET(struct idmap_config, field), 0 }
115 #define OPT_STR(key,def,field,len) \
116 { key, def, TYPE_STR, FIELD_OFFSET(struct idmap_config, field), len }
117 #define OPT_CLASS(key,def,index) \
118 { key, def, TYPE_STR, FIELD_OFFSET(struct idmap_config, classes[index]), NAME_LEN }
119 #define OPT_ATTR(key,def,index) \
120 { key, def, TYPE_STR, FIELD_OFFSET(struct idmap_config, attributes[index]), NAME_LEN }
121
122 /* table of recognized config options, including type and default value */
123 static const struct config_option g_options[] = {
124 /* server information */
125 OPT_STR("ldap_hostname", "localhost", hostname, NFS41_HOSTNAME_LEN+1),
126 OPT_INT("ldap_port", "389", port),
127 OPT_INT("ldap_version", "3", version),
128 OPT_INT("ldap_timeout", "0", timeout),
129
130 /* schema information */
131 OPT_STR("ldap_base", "cn=localhost", base, VAL_LEN),
132 OPT_CLASS("ldap_class_users", "user", CLASS_USER),
133 OPT_CLASS("ldap_class_groups", "group", CLASS_GROUP),
134 OPT_ATTR("ldap_attr_username", "cn", ATTR_USER_NAME),
135 OPT_ATTR("ldap_attr_groupname", "cn", ATTR_GROUP_NAME),
136 OPT_ATTR("ldap_attr_gssAuthName", "gssAuthName", ATTR_PRINCIPAL),
137 OPT_ATTR("ldap_attr_uidNumber", "uidNumber", ATTR_UID),
138 OPT_ATTR("ldap_attr_gidNumber", "gidNumber", ATTR_GID),
139
140 /* caching configuration */
141 OPT_INT("cache_ttl", "60", cache_ttl),
142 };
143
144
145 /* parse each line into key-value pairs
146 * accepts 'key = value' or 'key = "value"',
147 * ignores whitespace anywhere outside the ""s */
148 struct config_pair {
149 const char *key, *value;
150 size_t key_len, value_len;
151 };
152
config_parse_pair(char * line,struct config_pair * pair)153 static int config_parse_pair(
154 char *line,
155 struct config_pair *pair)
156 {
157 char *pos = line;
158 int status = NO_ERROR;
159
160 /* terminate at comment */
161 pos = strchr(line, '#');
162 if (pos) *pos = 0;
163
164 /* skip whitespace before key */
165 pos = line;
166 while (isspace(*pos)) pos++;
167 pair->key = pos;
168
169 pos = strchr(pos, '=');
170 if (pos == NULL) {
171 eprintf("missing '='\n");
172 status = ERROR_INVALID_PARAMETER;
173 goto out;
174 }
175
176 /* skip whitespace after key */
177 pair->key_len = pos - pair->key;
178 while (pair->key_len && isspace(pair->key[pair->key_len-1]))
179 pair->key_len--;
180
181 if (pair->key_len <= 0) {
182 eprintf("empty key\n");
183 status = ERROR_INVALID_PARAMETER;
184 goto out;
185 }
186
187 /* skip whitespace after = */
188 pos++;
189 while (isspace(*pos)) pos++;
190
191 if (*pos == 0) {
192 eprintf("end of line looking for value\n");
193 status = ERROR_INVALID_PARAMETER;
194 goto out;
195 }
196
197 if (*pos == '\"') {
198 /* value is between the "s */
199 pair->value = pos + 1;
200 pos = strchr(pair->value, '\"');
201 if (pos == NULL) {
202 eprintf("no matching '\"'\n");
203 status = ERROR_INVALID_PARAMETER;
204 goto out;
205 }
206 pair->value_len = pos - pair->value;
207 } else {
208 pair->value = pos;
209 pair->value_len = strlen(pair->value);
210
211 /* skip whitespace after value */
212 while (pair->value_len && isspace(pair->value[pair->value_len-1]))
213 pair->value_len--;
214 }
215
216 /* on success, null terminate the key and value */
217 ((char*)pair->key)[pair->key_len] = 0;
218 ((char*)pair->value)[pair->value_len] = 0;
219 out:
220 return status;
221 }
222
parse_uint(const char * str,UINT * id_out)223 static BOOL parse_uint(
224 const char *str,
225 UINT *id_out)
226 {
227 PCHAR endp;
228 const UINT id = strtoul(str, &endp, 10);
229
230 /* must convert the whole string */
231 if ((endp - str) < (ptrdiff_t)strlen(str))
232 return FALSE;
233
234 /* result must fit in 32 bits */
235 if (id == ULONG_MAX && errno == ERANGE)
236 return FALSE;
237
238 *id_out = id;
239 return TRUE;
240 }
241
242 /* parse default values from g_options[] into idmap_config */
config_defaults(struct idmap_config * config)243 static int config_defaults(
244 struct idmap_config *config)
245 {
246 const struct config_option *option;
247 const int count = ARRAYSIZE(g_options);
248 char *dst;
249 int i, status = NO_ERROR;
250
251 for (i = 0; i < count; i++) {
252 option = &g_options[i];
253 dst = (char*)config + option->offset;
254
255 if (option->type == TYPE_INT) {
256 if (!parse_uint(option->def, (UINT*)dst)) {
257 status = ERROR_INVALID_PARAMETER;
258 eprintf("failed to parse default value of %s=\"%s\": "
259 "expected a number\n", option->key, option->def);
260 break;
261 }
262 } else {
263 if (FAILED(StringCchCopyA(dst, option->max_len, option->def))) {
264 status = ERROR_BUFFER_OVERFLOW;
265 eprintf("failed to parse default value of %s=\"%s\": "
266 "buffer overflow > %u\n", option->key, option->def,
267 option->max_len);
268 break;
269 }
270 }
271 }
272 return status;
273 }
274
config_find_option(const struct config_pair * pair,const struct config_option ** option)275 static int config_find_option(
276 const struct config_pair *pair,
277 const struct config_option **option)
278 {
279 int i, count = ARRAYSIZE(g_options);
280 int status = ERROR_NOT_FOUND;
281
282 /* find the config_option by key */
283 for (i = 0; i < count; i++) {
284 if (stricmp(pair->key, g_options[i].key) == 0) {
285 *option = &g_options[i];
286 status = NO_ERROR;
287 break;
288 }
289 }
290 return status;
291 }
292
config_load(struct idmap_config * config,const char * filename)293 static int config_load(
294 struct idmap_config *config,
295 const char *filename)
296 {
297 char buffer[1024], *pos;
298 FILE *file;
299 struct config_pair pair;
300 const struct config_option *option;
301 int line = 0;
302 int status = NO_ERROR;
303
304 /* open the file */
305 file = fopen(filename, "r");
306 if (file == NULL) {
307 eprintf("config_load() failed to open file '%s'\n", filename);
308 goto out;
309 }
310
311 /* read each line */
312 while (fgets(buffer, sizeof(buffer), file)) {
313 line++;
314
315 /* skip whitespace */
316 pos = buffer;
317 while (isspace(*pos)) pos++;
318
319 /* skip comments and empty lines */
320 if (*pos == '#' || *pos == 0)
321 continue;
322
323 /* parse line into a key=value pair */
324 status = config_parse_pair(buffer, &pair);
325 if (status) {
326 eprintf("error on line %d: %s\n", line, buffer);
327 break;
328 }
329
330 /* find the config_option by key */
331 status = config_find_option(&pair, &option);
332 if (status) {
333 eprintf("unrecognized option '%s' on line %d: %s\n",
334 pair.key, line, buffer);
335 status = ERROR_INVALID_PARAMETER;
336 break;
337 }
338
339 if (option->type == TYPE_INT) {
340 if (!parse_uint(pair.value, (UINT*)((char*)config + option->offset))) {
341 status = ERROR_INVALID_PARAMETER;
342 eprintf("expected a number on line %d: %s=\"%s\"\n",
343 line, pair.key, pair.value);
344 break;
345 }
346 } else {
347 if (FAILED(StringCchCopyNA((char*)config + option->offset,
348 option->max_len, pair.value, pair.value_len))) {
349 status = ERROR_BUFFER_OVERFLOW;
350 eprintf("overflow on line %d: %s=\"%s\"\n",
351 line, pair.key, pair.value);
352 break;
353 }
354 }
355 }
356
357 fclose(file);
358 out:
359 return status;
360 }
361
config_init(struct idmap_config * config)362 static int config_init(
363 struct idmap_config *config)
364 {
365 int status;
366 #ifdef __REACTOS__
367 char config_path[MAX_PATH];
368 #endif
369
370 /* load default values */
371 status = config_defaults(config);
372 if (status) {
373 eprintf("config_defaults() failed with %d\n", status);
374 goto out;
375 }
376
377 #ifdef __REACTOS__
378 if (GetSystemDirectoryA(config_path, ARRAYSIZE(config_path)))
379 {
380 StringCchCatA(config_path, ARRAYSIZE(config_path), "\\drivers\\etc\\ms-nfs41-idmap.conf");
381 }
382 else
383 {
384 status = GetLastError();
385 eprintf("GetSystemDirectoryA failed with %ld\n", GetLastError());
386 goto out;
387 }
388 #endif
389
390 /* load configuration from file */
391 #ifdef __REACTOS__
392 status = config_load(config, config_path);
393 #else
394 status = config_load(config, CONFIG_FILENAME);
395 #endif
396 if (status) {
397 #ifdef __REACTOS__
398 eprintf("config_load('%s') failed with %d\n", config_path, status);
399 #else
400 eprintf("config_load('%s') failed with %d\n", CONFIG_FILENAME, status);
401 #endif
402 goto out;
403 }
404 out:
405 return status;
406 }
407
408
409 /* generic cache */
410 typedef struct list_entry* (*entry_alloc_fn)();
411 typedef void (*entry_free_fn)(struct list_entry*);
412 typedef void (*entry_copy_fn)(struct list_entry*, const struct list_entry*);
413
414 struct cache_ops {
415 entry_alloc_fn entry_alloc;
416 entry_free_fn entry_free;
417 entry_copy_fn entry_copy;
418 };
419
420 struct idmap_cache {
421 struct list_entry head;
422 const struct cache_ops *ops;
423 SRWLOCK lock;
424 };
425
426
cache_init(struct idmap_cache * cache,const struct cache_ops * ops)427 static void cache_init(
428 struct idmap_cache *cache,
429 const struct cache_ops *ops)
430 {
431 list_init(&cache->head);
432 cache->ops = ops;
433 InitializeSRWLock(&cache->lock);
434 }
435
cache_cleanup(struct idmap_cache * cache)436 static void cache_cleanup(
437 struct idmap_cache *cache)
438 {
439 struct list_entry *entry, *tmp;
440 list_for_each_tmp(entry, tmp, &cache->head)
441 cache->ops->entry_free(entry);
442 list_init(&cache->head);
443 }
444
cache_insert(struct idmap_cache * cache,const struct idmap_lookup * lookup,const struct list_entry * src)445 static int cache_insert(
446 struct idmap_cache *cache,
447 const struct idmap_lookup *lookup,
448 const struct list_entry *src)
449 {
450 struct list_entry *entry;
451 int status = NO_ERROR;
452
453 AcquireSRWLockExclusive(&cache->lock);
454
455 /* search for an existing match */
456 entry = list_search(&cache->head, lookup->value, lookup->compare);
457 if (entry) {
458 /* overwrite the existing entry with the new results */
459 cache->ops->entry_copy(entry, src);
460 goto out;
461 }
462
463 /* initialize a new entry and add it to the list */
464 entry = cache->ops->entry_alloc();
465 if (entry == NULL) {
466 status = GetLastError();
467 goto out;
468 }
469 cache->ops->entry_copy(entry, src);
470 list_add_head(&cache->head, entry);
471 out:
472 ReleaseSRWLockExclusive(&cache->lock);
473 return status;
474 }
475
cache_lookup(struct idmap_cache * cache,const struct idmap_lookup * lookup,struct list_entry * entry_out)476 static int cache_lookup(
477 struct idmap_cache *cache,
478 const struct idmap_lookup *lookup,
479 struct list_entry *entry_out)
480 {
481 struct list_entry *entry;
482 int status = ERROR_NOT_FOUND;
483
484 AcquireSRWLockShared(&cache->lock);
485
486 entry = list_search(&cache->head, lookup->value, lookup->compare);
487 if (entry) {
488 /* make a copy for use outside of the lock */
489 cache->ops->entry_copy(entry_out, entry);
490 status = NO_ERROR;
491 }
492
493 ReleaseSRWLockShared(&cache->lock);
494 return status;
495 }
496
497
498 /* user cache */
499 struct idmap_user {
500 struct list_entry entry;
501 char username[VAL_LEN];
502 char principal[VAL_LEN];
503 uid_t uid;
504 gid_t gid;
505 time_t last_updated;
506 };
507
user_cache_alloc()508 static struct list_entry* user_cache_alloc()
509 {
510 struct idmap_user *user = calloc(1, sizeof(struct idmap_user));
511 return user == NULL ? NULL : &user->entry;
512 }
user_cache_free(struct list_entry * entry)513 static void user_cache_free(struct list_entry *entry)
514 {
515 free(list_container(entry, struct idmap_user, entry));
516 }
user_cache_copy(struct list_entry * lhs,const struct list_entry * rhs)517 static void user_cache_copy(
518 struct list_entry *lhs,
519 const struct list_entry *rhs)
520 {
521 struct idmap_user *dst = list_container(lhs, struct idmap_user, entry);
522 const struct idmap_user *src = list_container(rhs, const struct idmap_user, entry);
523 StringCchCopyA(dst->username, VAL_LEN, src->username);
524 StringCchCopyA(dst->principal, VAL_LEN, src->principal);
525 dst->uid = src->uid;
526 dst->gid = src->gid;
527 dst->last_updated = src->last_updated;
528 }
529 static const struct cache_ops user_cache_ops = {
530 user_cache_alloc,
531 user_cache_free,
532 user_cache_copy
533 };
534
535
536 /* group cache */
537 struct idmap_group {
538 struct list_entry entry;
539 char name[VAL_LEN];
540 gid_t gid;
541 time_t last_updated;
542 };
543
group_cache_alloc()544 static struct list_entry* group_cache_alloc()
545 {
546 struct idmap_group *group = calloc(1, sizeof(struct idmap_group));
547 return group == NULL ? NULL : &group->entry;
548 }
group_cache_free(struct list_entry * entry)549 static void group_cache_free(struct list_entry *entry)
550 {
551 free(list_container(entry, struct idmap_group, entry));
552 }
group_cache_copy(struct list_entry * lhs,const struct list_entry * rhs)553 static void group_cache_copy(
554 struct list_entry *lhs,
555 const struct list_entry *rhs)
556 {
557 struct idmap_group *dst = list_container(lhs, struct idmap_group, entry);
558 const struct idmap_group *src = list_container(rhs, const struct idmap_group, entry);
559 StringCchCopyA(dst->name, VAL_LEN, src->name);
560 dst->gid = src->gid;
561 dst->last_updated = src->last_updated;
562 }
563 static const struct cache_ops group_cache_ops = {
564 group_cache_alloc,
565 group_cache_free,
566 group_cache_copy
567 };
568
569
570 /* ldap context */
571 struct idmap_context {
572 struct idmap_config config;
573 struct idmap_cache users;
574 struct idmap_cache groups;
575 LDAP *ldap;
576 };
577
578
idmap_filter(struct idmap_config * config,const struct idmap_lookup * lookup,char * filter,size_t filter_len)579 static int idmap_filter(
580 struct idmap_config *config,
581 const struct idmap_lookup *lookup,
582 char *filter,
583 size_t filter_len)
584 {
585 UINT_PTR i;
586 int status = NO_ERROR;
587
588 switch (lookup->type) {
589 case TYPE_INT:
590 i = (UINT_PTR)lookup->value;
591 if (FAILED(StringCchPrintfA(filter, filter_len,
592 "(&(objectClass=%s)(%s=%u))",
593 config->classes[lookup->klass],
594 config->attributes[lookup->attr], (UINT)i))) {
595 status = ERROR_BUFFER_OVERFLOW;
596 eprintf("ldap filter buffer overflow: '%s=%u'\n",
597 config->attributes[lookup->attr], (UINT)i);
598 }
599 break;
600
601 case TYPE_STR:
602 if (FAILED(StringCchPrintfA(filter, filter_len,
603 "(&(objectClass=%s)(%s=%s))",
604 config->classes[lookup->klass],
605 config->attributes[lookup->attr], lookup->value))) {
606 status = ERROR_BUFFER_OVERFLOW;
607 eprintf("ldap filter buffer overflow: '%s=%s'\n",
608 config->attributes[lookup->attr], lookup->value);
609 }
610 break;
611
612 default:
613 status = ERROR_INVALID_PARAMETER;
614 break;
615 }
616 return status;
617 }
618
idmap_query_attrs(struct idmap_context * context,const struct idmap_lookup * lookup,const unsigned attributes,const unsigned optional,PCHAR * values[],const int len)619 static int idmap_query_attrs(
620 struct idmap_context *context,
621 const struct idmap_lookup *lookup,
622 const unsigned attributes,
623 const unsigned optional,
624 PCHAR *values[],
625 const int len)
626 {
627 char filter[FILTER_LEN];
628 struct idmap_config *config = &context->config;
629 LDAPMessage *res = NULL, *entry;
630 int i, status;
631
632 /* format the ldap filter */
633 status = idmap_filter(config, lookup, filter, FILTER_LEN);
634 if (status)
635 goto out;
636
637 /* send the ldap query */
638 status = ldap_search_st(context->ldap, config->base,
639 LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, &res);
640 if (status) {
641 eprintf("ldap search for '%s' failed with %d: %s\n",
642 filter, status, ldap_err2stringA(status));
643 status = LdapMapErrorToWin32(status);
644 goto out;
645 }
646
647 entry = ldap_first_entry(context->ldap, res);
648 if (entry == NULL) {
649 status = LDAP_NO_RESULTS_RETURNED;
650 eprintf("ldap search for '%s' failed with %d: %s\n",
651 filter, status, ldap_err2stringA(status));
652 status = LdapMapErrorToWin32(status);
653 goto out;
654 }
655
656 /* fetch the attributes */
657 for (i = 0; i < len; i++) {
658 if (ATTR_ISSET(attributes, i)) {
659 values[i] = ldap_get_values(context->ldap,
660 entry, config->attributes[i]);
661
662 /* fail if required attributes are missing */
663 if (values[i] == NULL && !ATTR_ISSET(optional, i)) {
664 status = LDAP_NO_SUCH_ATTRIBUTE;
665 eprintf("ldap entry for '%s' missing required "
666 "attribute '%s', returning %d: %s\n",
667 filter, config->attributes[i],
668 status, ldap_err2stringA(status));
669 status = LdapMapErrorToWin32(status);
670 goto out;
671 }
672 }
673 }
674 out:
675 if (res) ldap_msgfree(res);
676 return status;
677 }
678
idmap_lookup_user(struct idmap_context * context,const struct idmap_lookup * lookup,struct idmap_user * user)679 static int idmap_lookup_user(
680 struct idmap_context *context,
681 const struct idmap_lookup *lookup,
682 struct idmap_user *user)
683 {
684 PCHAR* values[NUM_ATTRIBUTES] = { NULL };
685 const unsigned attributes = ATTR_FLAG(ATTR_USER_NAME)
686 | ATTR_FLAG(ATTR_PRINCIPAL)
687 | ATTR_FLAG(ATTR_UID)
688 | ATTR_FLAG(ATTR_GID);
689 /* principal is optional; we'll cache it if we have it */
690 const unsigned optional = ATTR_FLAG(ATTR_PRINCIPAL);
691 int i, status;
692
693 /* check the user cache for an existing entry */
694 status = cache_lookup(&context->users, lookup, &user->entry);
695 if (status == NO_ERROR) {
696 /* don't return expired entries; query new attributes
697 * and overwrite the entry with cache_insert() */
698 if (time(NULL) - user->last_updated < context->config.cache_ttl)
699 goto out;
700 }
701
702 /* send the query to the ldap server */
703 status = idmap_query_attrs(context, lookup,
704 attributes, optional, values, NUM_ATTRIBUTES);
705 if (status)
706 goto out_free_values;
707
708 /* parse attributes */
709 if (FAILED(StringCchCopyA(user->username, VAL_LEN,
710 *values[ATTR_USER_NAME]))) {
711 eprintf("ldap attribute %s='%s' longer than %u characters\n",
712 context->config.attributes[ATTR_USER_NAME],
713 *values[ATTR_USER_NAME], VAL_LEN);
714 status = ERROR_BUFFER_OVERFLOW;
715 goto out_free_values;
716 }
717 if (FAILED(StringCchCopyA(user->principal, VAL_LEN,
718 values[ATTR_PRINCIPAL] ? *values[ATTR_PRINCIPAL] : ""))) {
719 eprintf("ldap attribute %s='%s' longer than %u characters\n",
720 context->config.attributes[ATTR_PRINCIPAL],
721 values[ATTR_PRINCIPAL] ? *values[ATTR_PRINCIPAL] : "", VAL_LEN);
722 status = ERROR_BUFFER_OVERFLOW;
723 goto out_free_values;
724 }
725 if (!parse_uint(*values[ATTR_UID], &user->uid)) {
726 eprintf("failed to parse ldap attribute %s='%s'\n",
727 context->config.attributes[ATTR_UID], *values[ATTR_UID]);
728 status = ERROR_INVALID_PARAMETER;
729 goto out_free_values;
730 }
731 if (!parse_uint(*values[ATTR_GID], &user->gid)) {
732 eprintf("failed to parse ldap attribute %s='%s'\n",
733 context->config.attributes[ATTR_GID], *values[ATTR_GID]);
734 status = ERROR_INVALID_PARAMETER;
735 goto out_free_values;
736 }
737 user->last_updated = time(NULL);
738
739 if (context->config.cache_ttl) {
740 /* insert the entry into the cache */
741 cache_insert(&context->users, lookup, &user->entry);
742 }
743 out_free_values:
744 for (i = 0; i < NUM_ATTRIBUTES; i++)
745 ldap_value_free(values[i]);
746 out:
747 return status;
748 }
749
idmap_lookup_group(struct idmap_context * context,const struct idmap_lookup * lookup,struct idmap_group * group)750 static int idmap_lookup_group(
751 struct idmap_context *context,
752 const struct idmap_lookup *lookup,
753 struct idmap_group *group)
754 {
755 PCHAR* values[NUM_ATTRIBUTES] = { NULL };
756 const unsigned attributes = ATTR_FLAG(ATTR_GROUP_NAME)
757 | ATTR_FLAG(ATTR_GID);
758 int i, status;
759
760 /* check the group cache for an existing entry */
761 status = cache_lookup(&context->groups, lookup, &group->entry);
762 if (status == NO_ERROR) {
763 /* don't return expired entries; query new attributes
764 * and overwrite the entry with cache_insert() */
765 if (time(NULL) - group->last_updated < context->config.cache_ttl)
766 goto out;
767 }
768
769 /* send the query to the ldap server */
770 status = idmap_query_attrs(context, lookup,
771 attributes, 0, values, NUM_ATTRIBUTES);
772 if (status)
773 goto out_free_values;
774
775 /* parse attributes */
776 if (FAILED(StringCchCopyA(group->name, VAL_LEN,
777 *values[ATTR_GROUP_NAME]))) {
778 eprintf("ldap attribute %s='%s' longer than %u characters\n",
779 context->config.attributes[ATTR_GROUP_NAME],
780 *values[ATTR_GROUP_NAME], VAL_LEN);
781 status = ERROR_BUFFER_OVERFLOW;
782 goto out_free_values;
783 }
784 if (!parse_uint(*values[ATTR_GID], &group->gid)) {
785 eprintf("failed to parse ldap attribute %s='%s'\n",
786 context->config.attributes[ATTR_GID], *values[ATTR_GID]);
787 status = ERROR_INVALID_PARAMETER;
788 goto out_free_values;
789 }
790 group->last_updated = time(NULL);
791
792 if (context->config.cache_ttl) {
793 /* insert the entry into the cache */
794 cache_insert(&context->groups, lookup, &group->entry);
795 }
796 out_free_values:
797 for (i = 0; i < NUM_ATTRIBUTES; i++)
798 ldap_value_free(values[i]);
799 out:
800 return status;
801 }
802
803
804 /* public idmap interface */
nfs41_idmap_create(struct idmap_context ** context_out)805 int nfs41_idmap_create(
806 struct idmap_context **context_out)
807 {
808 struct idmap_context *context;
809 int status = NO_ERROR;
810
811 context = calloc(1, sizeof(struct idmap_context));
812 if (context == NULL) {
813 status = GetLastError();
814 goto out;
815 }
816
817 /* initialize the caches */
818 cache_init(&context->users, &user_cache_ops);
819 cache_init(&context->groups, &group_cache_ops);
820
821 /* load ldap configuration from file */
822 status = config_init(&context->config);
823 if (status) {
824 eprintf("config_init() failed with %d\n", status);
825 goto out_err_free;
826 }
827
828 /* initialize ldap and configure options */
829 context->ldap = ldap_init(context->config.hostname, context->config.port);
830 if (context->ldap == NULL) {
831 status = LdapGetLastError();
832 eprintf("ldap_init(%s) failed with %d: %s\n",
833 context->config.hostname, status, ldap_err2stringA(status));
834 status = LdapMapErrorToWin32(status);
835 goto out_err_free;
836 }
837
838 status = ldap_set_option(context->ldap, LDAP_OPT_PROTOCOL_VERSION,
839 (void *)&context->config.version);
840 if (status != LDAP_SUCCESS) {
841 eprintf("ldap_set_option(version=%d) failed with %d\n",
842 context->config.version, status);
843 status = LdapMapErrorToWin32(status);
844 goto out_err_free;
845 }
846
847 if (context->config.timeout) {
848 status = ldap_set_option(context->ldap, LDAP_OPT_TIMELIMIT,
849 (void *)&context->config.timeout);
850 if (status != LDAP_SUCCESS) {
851 eprintf("ldap_set_option(timeout=%d) failed with %d\n",
852 context->config.timeout, status);
853 status = LdapMapErrorToWin32(status);
854 goto out_err_free;
855 }
856 }
857
858 *context_out = context;
859 out:
860 return status;
861
862 out_err_free:
863 nfs41_idmap_free(context);
864 goto out;
865 }
866
nfs41_idmap_free(struct idmap_context * context)867 void nfs41_idmap_free(
868 struct idmap_context *context)
869 {
870 /* clean up the connection */
871 if (context->ldap)
872 ldap_unbind(context->ldap);
873
874 cache_cleanup(&context->users);
875 cache_cleanup(&context->groups);
876 free(context);
877 }
878
879
880 /* username -> uid, gid */
username_cmp(const struct list_entry * list,const void * value)881 static int username_cmp(const struct list_entry *list, const void *value)
882 {
883 const struct idmap_user *entry = list_container(list,
884 const struct idmap_user, entry);
885 const char *username = (const char*)value;
886 return strcmp(entry->username, username);
887 }
888
nfs41_idmap_name_to_ids(struct idmap_context * context,const char * username,uid_t * uid_out,gid_t * gid_out)889 int nfs41_idmap_name_to_ids(
890 struct idmap_context *context,
891 const char *username,
892 uid_t *uid_out,
893 gid_t *gid_out)
894 {
895 struct idmap_lookup lookup = { ATTR_USER_NAME,
896 CLASS_USER, TYPE_STR, username_cmp };
897 struct idmap_user user;
898 int status;
899
900 if (context == NULL)
901 return ERROR_FILE_NOT_FOUND;
902
903 dprintf(IDLVL, "--> nfs41_idmap_name_to_ids('%s')\n", username);
904
905 lookup.value = username;
906
907 /* look up the user entry */
908 status = idmap_lookup_user(context, &lookup, &user);
909 if (status) {
910 dprintf(IDLVL, "<-- nfs41_idmap_name_to_ids('%s') "
911 "failed with %d\n", username, status);
912 goto out;
913 }
914
915 *uid_out = user.uid;
916 *gid_out = user.gid;
917 dprintf(IDLVL, "<-- nfs41_idmap_name_to_ids('%s') "
918 "returning uid=%u, gid=%u\n", username, user.uid, user.gid);
919 out:
920 return status;
921 }
922
923 /* uid -> username */
uid_cmp(const struct list_entry * list,const void * value)924 static int uid_cmp(const struct list_entry *list, const void *value)
925 {
926 const struct idmap_user *entry = list_container(list,
927 const struct idmap_user, entry);
928 const UINT_PTR uid = (const UINT_PTR)value;
929 return (UINT)uid - entry->uid;
930 }
931
nfs41_idmap_uid_to_name(struct idmap_context * context,uid_t uid,char * name,size_t len)932 int nfs41_idmap_uid_to_name(
933 struct idmap_context *context,
934 uid_t uid,
935 char *name,
936 size_t len)
937 {
938 UINT_PTR uidp = uid; /* convert to pointer size to pass as void* */
939 struct idmap_lookup lookup = { ATTR_UID, CLASS_USER, TYPE_INT, uid_cmp };
940 struct idmap_user user;
941 int status;
942
943 dprintf(IDLVL, "--> nfs41_idmap_uid_to_name(%u)\n", uid);
944
945 lookup.value = (const void*)uidp;
946
947 /* look up the user entry */
948 status = idmap_lookup_user(context, &lookup, &user);
949 if (status) {
950 dprintf(IDLVL, "<-- nfs41_idmap_uid_to_name(%u) "
951 "failed with %d\n", uid, status);
952 goto out;
953 }
954
955 if (FAILED(StringCchCopyA(name, len, user.username))) {
956 status = ERROR_BUFFER_OVERFLOW;
957 eprintf("username buffer overflow: '%s' > %u\n",
958 user.username, len);
959 goto out;
960 }
961
962 dprintf(IDLVL, "<-- nfs41_idmap_uid_to_name(%u) "
963 "returning '%s'\n", uid, name);
964 out:
965 return status;
966 }
967
968 /* principal -> uid, gid */
principal_cmp(const struct list_entry * list,const void * value)969 static int principal_cmp(const struct list_entry *list, const void *value)
970 {
971 const struct idmap_user *entry = list_container(list,
972 const struct idmap_user, entry);
973 const char *principal = (const char*)value;
974 return strcmp(entry->principal, principal);
975 }
976
nfs41_idmap_principal_to_ids(struct idmap_context * context,const char * principal,uid_t * uid_out,gid_t * gid_out)977 int nfs41_idmap_principal_to_ids(
978 struct idmap_context *context,
979 const char *principal,
980 uid_t *uid_out,
981 gid_t *gid_out)
982 {
983 struct idmap_lookup lookup = { ATTR_PRINCIPAL,
984 CLASS_USER, TYPE_STR, principal_cmp };
985 struct idmap_user user;
986 int status;
987
988 dprintf(IDLVL, "--> nfs41_idmap_principal_to_ids('%s')\n", principal);
989
990 lookup.value = principal;
991
992 /* look up the user entry */
993 status = idmap_lookup_user(context, &lookup, &user);
994 if (status) {
995 dprintf(IDLVL, "<-- nfs41_idmap_principal_to_ids('%s') "
996 "failed with %d\n", principal, status);
997 goto out;
998 }
999
1000 *uid_out = user.uid;
1001 *gid_out = user.gid;
1002 dprintf(IDLVL, "<-- nfs41_idmap_principal_to_ids('%s') "
1003 "returning uid=%u, gid=%u\n", principal, user.uid, user.gid);
1004 out:
1005 return status;
1006 }
1007
1008 /* group -> gid */
group_cmp(const struct list_entry * list,const void * value)1009 static int group_cmp(const struct list_entry *list, const void *value)
1010 {
1011 const struct idmap_group *entry = list_container(list,
1012 const struct idmap_group, entry);
1013 const char *group = (const char*)value;
1014 return strcmp(entry->name, group);
1015 }
1016
nfs41_idmap_group_to_gid(struct idmap_context * context,const char * name,gid_t * gid_out)1017 int nfs41_idmap_group_to_gid(
1018 struct idmap_context *context,
1019 const char *name,
1020 gid_t *gid_out)
1021 {
1022 struct idmap_lookup lookup = { ATTR_GROUP_NAME,
1023 CLASS_GROUP, TYPE_STR, group_cmp };
1024 struct idmap_group group;
1025 int status;
1026
1027 dprintf(IDLVL, "--> nfs41_idmap_group_to_gid('%s')\n", name);
1028
1029 lookup.value = name;
1030
1031 /* look up the group entry */
1032 status = idmap_lookup_group(context, &lookup, &group);
1033 if (status) {
1034 dprintf(IDLVL, "<-- nfs41_idmap_group_to_gid('%s') "
1035 "failed with %d\n", name, status);
1036 goto out;
1037 }
1038
1039 *gid_out = group.gid;
1040 dprintf(IDLVL, "<-- nfs41_idmap_group_to_gid('%s') "
1041 "returning %u\n", name, group.gid);
1042 out:
1043 return status;
1044 }
1045
1046 /* gid -> group */
gid_cmp(const struct list_entry * list,const void * value)1047 static int gid_cmp(const struct list_entry *list, const void *value)
1048 {
1049 const struct idmap_group *entry = list_container(list,
1050 const struct idmap_group, entry);
1051 const UINT_PTR gid = (const UINT_PTR)value;
1052 return (UINT)gid - entry->gid;
1053 }
1054
nfs41_idmap_gid_to_group(struct idmap_context * context,gid_t gid,char * name,size_t len)1055 int nfs41_idmap_gid_to_group(
1056 struct idmap_context *context,
1057 gid_t gid,
1058 char *name,
1059 size_t len)
1060 {
1061 UINT_PTR gidp = gid; /* convert to pointer size to pass as void* */
1062 struct idmap_lookup lookup = { ATTR_GID, CLASS_GROUP, TYPE_INT, gid_cmp };
1063 struct idmap_group group;
1064 int status;
1065
1066 dprintf(IDLVL, "--> nfs41_idmap_gid_to_group(%u)\n", gid);
1067
1068 lookup.value = (const void*)gidp;
1069
1070 /* look up the group entry */
1071 status = idmap_lookup_group(context, &lookup, &group);
1072 if (status) {
1073 dprintf(IDLVL, "<-- nfs41_idmap_gid_to_group(%u) "
1074 "failed with %d\n", gid, status);
1075 goto out;
1076 }
1077
1078 if (FAILED(StringCchCopyA(name, len, group.name))) {
1079 status = ERROR_BUFFER_OVERFLOW;
1080 eprintf("group name buffer overflow: '%s' > %u\n",
1081 group.name, len);
1082 goto out;
1083 }
1084
1085 dprintf(IDLVL, "<-- nfs41_idmap_gid_to_group(%u) "
1086 "returning '%s'\n", gid, name);
1087 out:
1088 return status;
1089 }
1090