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 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 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 */ 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 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 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 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 StringCchCopyA(config_path, ARRAYSIZE(config_path), "C:\\ReactOS\\system32\\drivers\\etc\\ms-nfs41-idmap.conf"); 385 } 386 #endif 387 388 /* load configuration from file */ 389 #ifdef __REACTOS__ 390 status = config_load(config, config_path); 391 #else 392 status = config_load(config, CONFIG_FILENAME); 393 #endif 394 if (status) { 395 #ifdef __REACTOS__ 396 eprintf("config_load('%s') failed with %d\n", config_path, status); 397 #else 398 eprintf("config_load('%s') failed with %d\n", CONFIG_FILENAME, status); 399 #endif 400 goto out; 401 } 402 out: 403 return status; 404 } 405 406 407 /* generic cache */ 408 typedef struct list_entry* (*entry_alloc_fn)(); 409 typedef void (*entry_free_fn)(struct list_entry*); 410 typedef void (*entry_copy_fn)(struct list_entry*, const struct list_entry*); 411 412 struct cache_ops { 413 entry_alloc_fn entry_alloc; 414 entry_free_fn entry_free; 415 entry_copy_fn entry_copy; 416 }; 417 418 struct idmap_cache { 419 struct list_entry head; 420 const struct cache_ops *ops; 421 SRWLOCK lock; 422 }; 423 424 425 static void cache_init( 426 struct idmap_cache *cache, 427 const struct cache_ops *ops) 428 { 429 list_init(&cache->head); 430 cache->ops = ops; 431 InitializeSRWLock(&cache->lock); 432 } 433 434 static void cache_cleanup( 435 struct idmap_cache *cache) 436 { 437 struct list_entry *entry, *tmp; 438 list_for_each_tmp(entry, tmp, &cache->head) 439 cache->ops->entry_free(entry); 440 list_init(&cache->head); 441 } 442 443 static int cache_insert( 444 struct idmap_cache *cache, 445 const struct idmap_lookup *lookup, 446 const struct list_entry *src) 447 { 448 struct list_entry *entry; 449 int status = NO_ERROR; 450 451 AcquireSRWLockExclusive(&cache->lock); 452 453 /* search for an existing match */ 454 entry = list_search(&cache->head, lookup->value, lookup->compare); 455 if (entry) { 456 /* overwrite the existing entry with the new results */ 457 cache->ops->entry_copy(entry, src); 458 goto out; 459 } 460 461 /* initialize a new entry and add it to the list */ 462 entry = cache->ops->entry_alloc(); 463 if (entry == NULL) { 464 status = GetLastError(); 465 goto out; 466 } 467 cache->ops->entry_copy(entry, src); 468 list_add_head(&cache->head, entry); 469 out: 470 ReleaseSRWLockExclusive(&cache->lock); 471 return status; 472 } 473 474 static int cache_lookup( 475 struct idmap_cache *cache, 476 const struct idmap_lookup *lookup, 477 struct list_entry *entry_out) 478 { 479 struct list_entry *entry; 480 int status = ERROR_NOT_FOUND; 481 482 AcquireSRWLockShared(&cache->lock); 483 484 entry = list_search(&cache->head, lookup->value, lookup->compare); 485 if (entry) { 486 /* make a copy for use outside of the lock */ 487 cache->ops->entry_copy(entry_out, entry); 488 status = NO_ERROR; 489 } 490 491 ReleaseSRWLockShared(&cache->lock); 492 return status; 493 } 494 495 496 /* user cache */ 497 struct idmap_user { 498 struct list_entry entry; 499 char username[VAL_LEN]; 500 char principal[VAL_LEN]; 501 uid_t uid; 502 gid_t gid; 503 time_t last_updated; 504 }; 505 506 static struct list_entry* user_cache_alloc() 507 { 508 struct idmap_user *user = calloc(1, sizeof(struct idmap_user)); 509 return user == NULL ? NULL : &user->entry; 510 } 511 static void user_cache_free(struct list_entry *entry) 512 { 513 free(list_container(entry, struct idmap_user, entry)); 514 } 515 static void user_cache_copy( 516 struct list_entry *lhs, 517 const struct list_entry *rhs) 518 { 519 struct idmap_user *dst = list_container(lhs, struct idmap_user, entry); 520 const struct idmap_user *src = list_container(rhs, const struct idmap_user, entry); 521 StringCchCopyA(dst->username, VAL_LEN, src->username); 522 StringCchCopyA(dst->principal, VAL_LEN, src->principal); 523 dst->uid = src->uid; 524 dst->gid = src->gid; 525 dst->last_updated = src->last_updated; 526 } 527 static const struct cache_ops user_cache_ops = { 528 user_cache_alloc, 529 user_cache_free, 530 user_cache_copy 531 }; 532 533 534 /* group cache */ 535 struct idmap_group { 536 struct list_entry entry; 537 char name[VAL_LEN]; 538 gid_t gid; 539 time_t last_updated; 540 }; 541 542 static struct list_entry* group_cache_alloc() 543 { 544 struct idmap_group *group = calloc(1, sizeof(struct idmap_group)); 545 return group == NULL ? NULL : &group->entry; 546 } 547 static void group_cache_free(struct list_entry *entry) 548 { 549 free(list_container(entry, struct idmap_group, entry)); 550 } 551 static void group_cache_copy( 552 struct list_entry *lhs, 553 const struct list_entry *rhs) 554 { 555 struct idmap_group *dst = list_container(lhs, struct idmap_group, entry); 556 const struct idmap_group *src = list_container(rhs, const struct idmap_group, entry); 557 StringCchCopyA(dst->name, VAL_LEN, src->name); 558 dst->gid = src->gid; 559 dst->last_updated = src->last_updated; 560 } 561 static const struct cache_ops group_cache_ops = { 562 group_cache_alloc, 563 group_cache_free, 564 group_cache_copy 565 }; 566 567 568 /* ldap context */ 569 struct idmap_context { 570 struct idmap_config config; 571 struct idmap_cache users; 572 struct idmap_cache groups; 573 LDAP *ldap; 574 }; 575 576 577 static int idmap_filter( 578 struct idmap_config *config, 579 const struct idmap_lookup *lookup, 580 char *filter, 581 size_t filter_len) 582 { 583 UINT_PTR i; 584 int status = NO_ERROR; 585 586 switch (lookup->type) { 587 case TYPE_INT: 588 i = (UINT_PTR)lookup->value; 589 if (FAILED(StringCchPrintfA(filter, filter_len, 590 "(&(objectClass=%s)(%s=%u))", 591 config->classes[lookup->klass], 592 config->attributes[lookup->attr], (UINT)i))) { 593 status = ERROR_BUFFER_OVERFLOW; 594 eprintf("ldap filter buffer overflow: '%s=%u'\n", 595 config->attributes[lookup->attr], (UINT)i); 596 } 597 break; 598 599 case TYPE_STR: 600 if (FAILED(StringCchPrintfA(filter, filter_len, 601 "(&(objectClass=%s)(%s=%s))", 602 config->classes[lookup->klass], 603 config->attributes[lookup->attr], lookup->value))) { 604 status = ERROR_BUFFER_OVERFLOW; 605 eprintf("ldap filter buffer overflow: '%s=%s'\n", 606 config->attributes[lookup->attr], lookup->value); 607 } 608 break; 609 610 default: 611 status = ERROR_INVALID_PARAMETER; 612 break; 613 } 614 return status; 615 } 616 617 static int idmap_query_attrs( 618 struct idmap_context *context, 619 const struct idmap_lookup *lookup, 620 const unsigned attributes, 621 const unsigned optional, 622 PCHAR *values[], 623 const int len) 624 { 625 char filter[FILTER_LEN]; 626 struct idmap_config *config = &context->config; 627 LDAPMessage *res = NULL, *entry; 628 int i, status; 629 630 /* format the ldap filter */ 631 status = idmap_filter(config, lookup, filter, FILTER_LEN); 632 if (status) 633 goto out; 634 635 /* send the ldap query */ 636 status = ldap_search_st(context->ldap, config->base, 637 LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, &res); 638 if (status) { 639 eprintf("ldap search for '%s' failed with %d: %s\n", 640 filter, status, ldap_err2stringA(status)); 641 status = LdapMapErrorToWin32(status); 642 goto out; 643 } 644 645 entry = ldap_first_entry(context->ldap, res); 646 if (entry == NULL) { 647 status = LDAP_NO_RESULTS_RETURNED; 648 eprintf("ldap search for '%s' failed with %d: %s\n", 649 filter, status, ldap_err2stringA(status)); 650 status = LdapMapErrorToWin32(status); 651 goto out; 652 } 653 654 /* fetch the attributes */ 655 for (i = 0; i < len; i++) { 656 if (ATTR_ISSET(attributes, i)) { 657 values[i] = ldap_get_values(context->ldap, 658 entry, config->attributes[i]); 659 660 /* fail if required attributes are missing */ 661 if (values[i] == NULL && !ATTR_ISSET(optional, i)) { 662 status = LDAP_NO_SUCH_ATTRIBUTE; 663 eprintf("ldap entry for '%s' missing required " 664 "attribute '%s', returning %d: %s\n", 665 filter, config->attributes[i], 666 status, ldap_err2stringA(status)); 667 status = LdapMapErrorToWin32(status); 668 goto out; 669 } 670 } 671 } 672 out: 673 if (res) ldap_msgfree(res); 674 return status; 675 } 676 677 static int idmap_lookup_user( 678 struct idmap_context *context, 679 const struct idmap_lookup *lookup, 680 struct idmap_user *user) 681 { 682 PCHAR* values[NUM_ATTRIBUTES] = { NULL }; 683 const unsigned attributes = ATTR_FLAG(ATTR_USER_NAME) 684 | ATTR_FLAG(ATTR_PRINCIPAL) 685 | ATTR_FLAG(ATTR_UID) 686 | ATTR_FLAG(ATTR_GID); 687 /* principal is optional; we'll cache it if we have it */ 688 const unsigned optional = ATTR_FLAG(ATTR_PRINCIPAL); 689 int i, status; 690 691 /* check the user cache for an existing entry */ 692 status = cache_lookup(&context->users, lookup, &user->entry); 693 if (status == NO_ERROR) { 694 /* don't return expired entries; query new attributes 695 * and overwrite the entry with cache_insert() */ 696 if (time(NULL) - user->last_updated < context->config.cache_ttl) 697 goto out; 698 } 699 700 /* send the query to the ldap server */ 701 status = idmap_query_attrs(context, lookup, 702 attributes, optional, values, NUM_ATTRIBUTES); 703 if (status) 704 goto out_free_values; 705 706 /* parse attributes */ 707 if (FAILED(StringCchCopyA(user->username, VAL_LEN, 708 *values[ATTR_USER_NAME]))) { 709 eprintf("ldap attribute %s='%s' longer than %u characters\n", 710 context->config.attributes[ATTR_USER_NAME], 711 *values[ATTR_USER_NAME], VAL_LEN); 712 status = ERROR_BUFFER_OVERFLOW; 713 goto out_free_values; 714 } 715 if (FAILED(StringCchCopyA(user->principal, VAL_LEN, 716 values[ATTR_PRINCIPAL] ? *values[ATTR_PRINCIPAL] : ""))) { 717 eprintf("ldap attribute %s='%s' longer than %u characters\n", 718 context->config.attributes[ATTR_PRINCIPAL], 719 values[ATTR_PRINCIPAL] ? *values[ATTR_PRINCIPAL] : "", VAL_LEN); 720 status = ERROR_BUFFER_OVERFLOW; 721 goto out_free_values; 722 } 723 if (!parse_uint(*values[ATTR_UID], &user->uid)) { 724 eprintf("failed to parse ldap attribute %s='%s'\n", 725 context->config.attributes[ATTR_UID], *values[ATTR_UID]); 726 status = ERROR_INVALID_PARAMETER; 727 goto out_free_values; 728 } 729 if (!parse_uint(*values[ATTR_GID], &user->gid)) { 730 eprintf("failed to parse ldap attribute %s='%s'\n", 731 context->config.attributes[ATTR_GID], *values[ATTR_GID]); 732 status = ERROR_INVALID_PARAMETER; 733 goto out_free_values; 734 } 735 user->last_updated = time(NULL); 736 737 if (context->config.cache_ttl) { 738 /* insert the entry into the cache */ 739 cache_insert(&context->users, lookup, &user->entry); 740 } 741 out_free_values: 742 for (i = 0; i < NUM_ATTRIBUTES; i++) 743 ldap_value_free(values[i]); 744 out: 745 return status; 746 } 747 748 static int idmap_lookup_group( 749 struct idmap_context *context, 750 const struct idmap_lookup *lookup, 751 struct idmap_group *group) 752 { 753 PCHAR* values[NUM_ATTRIBUTES] = { NULL }; 754 const unsigned attributes = ATTR_FLAG(ATTR_GROUP_NAME) 755 | ATTR_FLAG(ATTR_GID); 756 int i, status; 757 758 /* check the group cache for an existing entry */ 759 status = cache_lookup(&context->groups, lookup, &group->entry); 760 if (status == NO_ERROR) { 761 /* don't return expired entries; query new attributes 762 * and overwrite the entry with cache_insert() */ 763 if (time(NULL) - group->last_updated < context->config.cache_ttl) 764 goto out; 765 } 766 767 /* send the query to the ldap server */ 768 status = idmap_query_attrs(context, lookup, 769 attributes, 0, values, NUM_ATTRIBUTES); 770 if (status) 771 goto out_free_values; 772 773 /* parse attributes */ 774 if (FAILED(StringCchCopyA(group->name, VAL_LEN, 775 *values[ATTR_GROUP_NAME]))) { 776 eprintf("ldap attribute %s='%s' longer than %u characters\n", 777 context->config.attributes[ATTR_GROUP_NAME], 778 *values[ATTR_GROUP_NAME], VAL_LEN); 779 status = ERROR_BUFFER_OVERFLOW; 780 goto out_free_values; 781 } 782 if (!parse_uint(*values[ATTR_GID], &group->gid)) { 783 eprintf("failed to parse ldap attribute %s='%s'\n", 784 context->config.attributes[ATTR_GID], *values[ATTR_GID]); 785 status = ERROR_INVALID_PARAMETER; 786 goto out_free_values; 787 } 788 group->last_updated = time(NULL); 789 790 if (context->config.cache_ttl) { 791 /* insert the entry into the cache */ 792 cache_insert(&context->groups, lookup, &group->entry); 793 } 794 out_free_values: 795 for (i = 0; i < NUM_ATTRIBUTES; i++) 796 ldap_value_free(values[i]); 797 out: 798 return status; 799 } 800 801 802 /* public idmap interface */ 803 int nfs41_idmap_create( 804 struct idmap_context **context_out) 805 { 806 struct idmap_context *context; 807 int status = NO_ERROR; 808 809 context = calloc(1, sizeof(struct idmap_context)); 810 if (context == NULL) { 811 status = GetLastError(); 812 goto out; 813 } 814 815 /* initialize the caches */ 816 cache_init(&context->users, &user_cache_ops); 817 cache_init(&context->groups, &group_cache_ops); 818 819 /* load ldap configuration from file */ 820 status = config_init(&context->config); 821 if (status) { 822 eprintf("config_init() failed with %d\n", status); 823 goto out_err_free; 824 } 825 826 /* initialize ldap and configure options */ 827 context->ldap = ldap_init(context->config.hostname, context->config.port); 828 if (context->ldap == NULL) { 829 status = LdapGetLastError(); 830 eprintf("ldap_init(%s) failed with %d: %s\n", 831 context->config.hostname, status, ldap_err2stringA(status)); 832 status = LdapMapErrorToWin32(status); 833 goto out_err_free; 834 } 835 836 status = ldap_set_option(context->ldap, LDAP_OPT_PROTOCOL_VERSION, 837 (void *)&context->config.version); 838 if (status != LDAP_SUCCESS) { 839 eprintf("ldap_set_option(version=%d) failed with %d\n", 840 context->config.version, status); 841 status = LdapMapErrorToWin32(status); 842 goto out_err_free; 843 } 844 845 if (context->config.timeout) { 846 status = ldap_set_option(context->ldap, LDAP_OPT_TIMELIMIT, 847 (void *)&context->config.timeout); 848 if (status != LDAP_SUCCESS) { 849 eprintf("ldap_set_option(timeout=%d) failed with %d\n", 850 context->config.timeout, status); 851 status = LdapMapErrorToWin32(status); 852 goto out_err_free; 853 } 854 } 855 856 *context_out = context; 857 out: 858 return status; 859 860 out_err_free: 861 nfs41_idmap_free(context); 862 goto out; 863 } 864 865 void nfs41_idmap_free( 866 struct idmap_context *context) 867 { 868 /* clean up the connection */ 869 if (context->ldap) 870 ldap_unbind(context->ldap); 871 872 cache_cleanup(&context->users); 873 cache_cleanup(&context->groups); 874 free(context); 875 } 876 877 878 /* username -> uid, gid */ 879 static int username_cmp(const struct list_entry *list, const void *value) 880 { 881 const struct idmap_user *entry = list_container(list, 882 const struct idmap_user, entry); 883 const char *username = (const char*)value; 884 return strcmp(entry->username, username); 885 } 886 887 int nfs41_idmap_name_to_ids( 888 struct idmap_context *context, 889 const char *username, 890 uid_t *uid_out, 891 gid_t *gid_out) 892 { 893 struct idmap_lookup lookup = { ATTR_USER_NAME, 894 CLASS_USER, TYPE_STR, username_cmp }; 895 struct idmap_user user; 896 int status; 897 898 if (context == NULL) 899 return ERROR_FILE_NOT_FOUND; 900 901 dprintf(IDLVL, "--> nfs41_idmap_name_to_ids('%s')\n", username); 902 903 lookup.value = username; 904 905 /* look up the user entry */ 906 status = idmap_lookup_user(context, &lookup, &user); 907 if (status) { 908 dprintf(IDLVL, "<-- nfs41_idmap_name_to_ids('%s') " 909 "failed with %d\n", username, status); 910 goto out; 911 } 912 913 *uid_out = user.uid; 914 *gid_out = user.gid; 915 dprintf(IDLVL, "<-- nfs41_idmap_name_to_ids('%s') " 916 "returning uid=%u, gid=%u\n", username, user.uid, user.gid); 917 out: 918 return status; 919 } 920 921 /* uid -> username */ 922 static int uid_cmp(const struct list_entry *list, const void *value) 923 { 924 const struct idmap_user *entry = list_container(list, 925 const struct idmap_user, entry); 926 const UINT_PTR uid = (const UINT_PTR)value; 927 return (UINT)uid - entry->uid; 928 } 929 930 int nfs41_idmap_uid_to_name( 931 struct idmap_context *context, 932 uid_t uid, 933 char *name, 934 size_t len) 935 { 936 UINT_PTR uidp = uid; /* convert to pointer size to pass as void* */ 937 struct idmap_lookup lookup = { ATTR_UID, CLASS_USER, TYPE_INT, uid_cmp }; 938 struct idmap_user user; 939 int status; 940 941 dprintf(IDLVL, "--> nfs41_idmap_uid_to_name(%u)\n", uid); 942 943 lookup.value = (const void*)uidp; 944 945 /* look up the user entry */ 946 status = idmap_lookup_user(context, &lookup, &user); 947 if (status) { 948 dprintf(IDLVL, "<-- nfs41_idmap_uid_to_name(%u) " 949 "failed with %d\n", uid, status); 950 goto out; 951 } 952 953 if (FAILED(StringCchCopyA(name, len, user.username))) { 954 status = ERROR_BUFFER_OVERFLOW; 955 eprintf("username buffer overflow: '%s' > %u\n", 956 user.username, len); 957 goto out; 958 } 959 960 dprintf(IDLVL, "<-- nfs41_idmap_uid_to_name(%u) " 961 "returning '%s'\n", uid, name); 962 out: 963 return status; 964 } 965 966 /* principal -> uid, gid */ 967 static int principal_cmp(const struct list_entry *list, const void *value) 968 { 969 const struct idmap_user *entry = list_container(list, 970 const struct idmap_user, entry); 971 const char *principal = (const char*)value; 972 return strcmp(entry->principal, principal); 973 } 974 975 int nfs41_idmap_principal_to_ids( 976 struct idmap_context *context, 977 const char *principal, 978 uid_t *uid_out, 979 gid_t *gid_out) 980 { 981 struct idmap_lookup lookup = { ATTR_PRINCIPAL, 982 CLASS_USER, TYPE_STR, principal_cmp }; 983 struct idmap_user user; 984 int status; 985 986 dprintf(IDLVL, "--> nfs41_idmap_principal_to_ids('%s')\n", principal); 987 988 lookup.value = principal; 989 990 /* look up the user entry */ 991 status = idmap_lookup_user(context, &lookup, &user); 992 if (status) { 993 dprintf(IDLVL, "<-- nfs41_idmap_principal_to_ids('%s') " 994 "failed with %d\n", principal, status); 995 goto out; 996 } 997 998 *uid_out = user.uid; 999 *gid_out = user.gid; 1000 dprintf(IDLVL, "<-- nfs41_idmap_principal_to_ids('%s') " 1001 "returning uid=%u, gid=%u\n", principal, user.uid, user.gid); 1002 out: 1003 return status; 1004 } 1005 1006 /* group -> gid */ 1007 static int group_cmp(const struct list_entry *list, const void *value) 1008 { 1009 const struct idmap_group *entry = list_container(list, 1010 const struct idmap_group, entry); 1011 const char *group = (const char*)value; 1012 return strcmp(entry->name, group); 1013 } 1014 1015 int nfs41_idmap_group_to_gid( 1016 struct idmap_context *context, 1017 const char *name, 1018 gid_t *gid_out) 1019 { 1020 struct idmap_lookup lookup = { ATTR_GROUP_NAME, 1021 CLASS_GROUP, TYPE_STR, group_cmp }; 1022 struct idmap_group group; 1023 int status; 1024 1025 dprintf(IDLVL, "--> nfs41_idmap_group_to_gid('%s')\n", name); 1026 1027 lookup.value = name; 1028 1029 /* look up the group entry */ 1030 status = idmap_lookup_group(context, &lookup, &group); 1031 if (status) { 1032 dprintf(IDLVL, "<-- nfs41_idmap_group_to_gid('%s') " 1033 "failed with %d\n", name, status); 1034 goto out; 1035 } 1036 1037 *gid_out = group.gid; 1038 dprintf(IDLVL, "<-- nfs41_idmap_group_to_gid('%s') " 1039 "returning %u\n", name, group.gid); 1040 out: 1041 return status; 1042 } 1043 1044 /* gid -> group */ 1045 static int gid_cmp(const struct list_entry *list, const void *value) 1046 { 1047 const struct idmap_group *entry = list_container(list, 1048 const struct idmap_group, entry); 1049 const UINT_PTR gid = (const UINT_PTR)value; 1050 return (UINT)gid - entry->gid; 1051 } 1052 1053 int nfs41_idmap_gid_to_group( 1054 struct idmap_context *context, 1055 gid_t gid, 1056 char *name, 1057 size_t len) 1058 { 1059 UINT_PTR gidp = gid; /* convert to pointer size to pass as void* */ 1060 struct idmap_lookup lookup = { ATTR_GID, CLASS_GROUP, TYPE_INT, gid_cmp }; 1061 struct idmap_group group; 1062 int status; 1063 1064 dprintf(IDLVL, "--> nfs41_idmap_gid_to_group(%u)\n", gid); 1065 1066 lookup.value = (const void*)gidp; 1067 1068 /* look up the group entry */ 1069 status = idmap_lookup_group(context, &lookup, &group); 1070 if (status) { 1071 dprintf(IDLVL, "<-- nfs41_idmap_gid_to_group(%u) " 1072 "failed with %d\n", gid, status); 1073 goto out; 1074 } 1075 1076 if (FAILED(StringCchCopyA(name, len, group.name))) { 1077 status = ERROR_BUFFER_OVERFLOW; 1078 eprintf("group name buffer overflow: '%s' > %u\n", 1079 group.name, len); 1080 goto out; 1081 } 1082 1083 dprintf(IDLVL, "<-- nfs41_idmap_gid_to_group(%u) " 1084 "returning '%s'\n", gid, name); 1085 out: 1086 return status; 1087 } 1088