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 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 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 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 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 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 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 } 513 static void user_cache_free(struct list_entry *entry) 514 { 515 free(list_container(entry, struct idmap_user, entry)); 516 } 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 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 } 549 static void group_cache_free(struct list_entry *entry) 550 { 551 free(list_container(entry, struct idmap_group, entry)); 552 } 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 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 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 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 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 */ 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 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 */ 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 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 */ 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 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 */ 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 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 */ 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 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 */ 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 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