1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 /* 26 * DESCRIPTION: Contains top level functions to read/write to the DIT. These 27 * are the API between the shim and the mapping system. 28 * Things calling these should have no knowledge of LDAP. Things 29 * called by them should have no knowledge of NIS. 30 * 31 * Error handling here may appear to be limited but, because the 32 * NIS protocol cannot carry meaningful information about why a 33 * N2L operation failed, functions that don't work log 34 * an error and then just return FAILURE. 35 * 36 */ 37 38 /* 39 * Includes. WE WANT TO USE REAL DBM FUNCTIONS SO DO NOT INCLUDE SHIM_HOOKS.H. 40 */ 41 #include <unistd.h> 42 #include <syslog.h> 43 #include <ndbm.h> 44 #include <sys/systeminfo.h> 45 #include <string.h> 46 #include <lber.h> 47 #include <ldap.h> 48 #include <errno.h> 49 #include "ypsym.h" 50 #include "ypdefs.h" 51 #include "shim.h" 52 #include "../ldap_structs.h" 53 #include "../ldap_parse.h" 54 #include "../nisdb_ldap.h" 55 #include "../ldap_util.h" 56 #include "../ldap_op.h" 57 #include "../ldap_attr.h" 58 #include "../nis_parse_ldap_conf.h" 59 #include "../nisdb_mt.h" 60 #include "yptol.h" 61 #include "dit_access_utils.h" 62 #include "stdio.h" 63 64 extern bool delete_map(char *name); 65 extern bool rename_map(char *from, char *to, bool_t secure_map); 66 67 /* Enable standard YP code features defined in ypdefs.h */ 68 USE_YP_MASTER_NAME 69 USE_YP_DOMAIN_NAME 70 USE_YP_SECURE 71 USE_YP_INTERDOMAIN 72 73 /* 74 * Decs 75 */ 76 suc_code add_special_entries(DBM *, map_ctrl *, bool_t *); 77 void free_null_terminated_list(char **list); 78 79 80 /* 81 * FUNCTION: is_yptol_mode(); 82 * 83 * DESCRIPTION: Determines if we should run in N2L or traditional mode based 84 * on the presence of the N2L mapping file. If there are problems 85 * with the file, e.g. unreadable, this will be picked up latter. 86 * 87 * INPUTS: Nothing 88 * 89 * OUTPUTS: TRUE = Run in N2L mode 90 * FALSE = Run in traditional mode. 91 */ 92 bool_t 93 is_yptol_mode() 94 { 95 struct stat filestat; 96 97 if (stat(YP_DEFAULTCONFFILE, &filestat) != -1) 98 return (TRUE); 99 100 return (FALSE); 101 } 102 103 /* 104 * FUNCTION: read_from_dit(); 105 * 106 * DESCRIPTION: Read (i.e. get and map) a single NIS entry from the LDAP DIT. 107 * Also handles retry attempts, on failure, and interpretation of 108 * internal error codes. 109 * 110 * INPUTS: Map name (unqualified) 111 * Domain name 112 * Entry key 113 * Pointer to return location 114 * 115 * OUTPUTS: If successful DBM datum containing result. 116 * On error DBM datum pointing to NULL and, if the cached value 117 * is not to be used, an error code. 118 */ 119 int 120 read_from_dit(char *map, char *domain, datum *key, datum *value) 121 { 122 int count; 123 int res; 124 __nisdb_retry_t *retrieveRetry; 125 126 /* Initialize tsd */ 127 __nisdb_get_tsd()->domainContext = 0; 128 __nisdb_get_tsd()->escapeFlag = '\0'; 129 130 for (count = 0; count < ypDomains.numDomains; count++) { 131 if (0 == ypDomains.domainLabels[count]) 132 continue; 133 if (0 == strcasecmp(domain, ypDomains.domainLabels[count])) { 134 __nisdb_get_tsd()->domainContext = 135 ypDomains.domains[count]; 136 break; 137 } 138 } 139 140 retrieveRetry = &ldapDBTableMapping.retrieveErrorRetry; 141 142 /* Loop 'attempts' times of forever if -1 */ 143 for (count = retrieveRetry->attempts; (0 <= count) || 144 (-1 == retrieveRetry->attempts); count --) { 145 if (TRUE == singleReadFromDIT(map, domain, key, value, &res)) 146 /* It worked, return value irrelevant */ 147 return (0); 148 149 if (LDAP_TIMEOUT == res) { /* Exceeded search timeout */ 150 value->dptr = NULL; 151 return (0); 152 } 153 154 if (is_fatal_error(res)) 155 break; 156 157 /* 158 * Didn't work. If not the special case where no repeats are 159 * done sleep. 160 */ 161 if (0 != retrieveRetry->attempts) 162 (void) poll(NULL, 0, retrieveRetry->timeout*1000); 163 } 164 165 /* Make sure returned pointer is NULL */ 166 value->dptr = NULL; 167 168 /* If we get here access failed work out what to return */ 169 if (ldapDBTableMapping.retrieveError == use_cached) 170 return (0); 171 172 return (res); 173 } 174 175 /* 176 * FUNCTION: write_to_dit(); 177 * 178 * DESCRIPTION: Maps and writes a NIS entry to the LDAP DIT. 179 * Also handles retry attempts, on failure, and interpretation of 180 * internal error codes. 181 * 182 * INPUTS: Pointer to (unqualified) map name 183 * Pointer to domain name 184 * The entries key 185 * What to write 186 * Replace flag indicating 187 * TRUE = Replace (overwrite) any existing entries 188 * FALSE = Return error if there are existing entries 189 * Flag indicating if we should tolerate mapping errors. 190 * 191 * OUTPUTS: SUCCESS = Write was successful 192 * FAILURE = Write failed 193 * 194 */ 195 suc_code 196 write_to_dit(char *map, char *domain, datum key, datum value, 197 bool_t replace, bool_t ignore_map_errs) 198 { 199 int count; 200 int res; 201 __nisdb_retry_t *storeRetry = &ldapDBTableMapping.storeErrorRetry; 202 203 /* Initialize tsd */ 204 __nisdb_get_tsd()->domainContext = 0; 205 __nisdb_get_tsd()->escapeFlag = '\0'; 206 207 for (count = 0; count < ypDomains.numDomains; count++) { 208 if (0 == ypDomains.domainLabels[count]) 209 continue; 210 if (0 == strcasecmp(domain, ypDomains.domainLabels[count])) { 211 __nisdb_get_tsd()->domainContext = 212 ypDomains.domains[count]; 213 break; 214 } 215 } 216 217 storeRetry = &ldapDBTableMapping.storeErrorRetry; 218 219 /* Loop 'attempts' times of forever if -1 */ 220 for (count = storeRetry->attempts; (0 <= count) || 221 (-1 == storeRetry->attempts); count --) { 222 res = singleWriteToDIT(map, domain, &key, &value, replace); 223 if (LDAP_SUCCESS == res) 224 return (SUCCESS); 225 226 if (is_fatal_error(res)) { 227 /* 228 * The mapping failed and will fail again if it is 229 * retried. However there are some cases where an 230 * actual mapping fault (rather than a LDAP problem) 231 * may be ignored. 232 */ 233 if (ignore_map_errs) { 234 switch (res) { 235 case LDAP_INVALID_DN_SYNTAX: 236 case LDAP_OBJECT_CLASS_VIOLATION: 237 case LDAP_NOT_ALLOWED_ON_RDN: 238 case MAP_NAMEFIELD_MATCH_ERROR: 239 case MAP_NO_DN: 240 return (SUCCESS); 241 default: 242 break; 243 } 244 } 245 return (FAILURE); 246 } 247 248 if (ldapDBTableMapping.storeError != sto_retry) 249 return (FAILURE); 250 251 /* 252 * Didn't work. If not the special case where no repeats are 253 * done sleep. 254 */ 255 if (0 != storeRetry->attempts) 256 (void) poll(NULL, 0, storeRetry->timeout*1000); 257 258 } 259 return (FAILURE); 260 } 261 262 /* 263 * FUNCTION : get_ttl_value() 264 * 265 * DESCRIPTION: Get the TTL value, derived from mapping file or DIT, for a 266 * entry. 267 * 268 * GIVEN : Pointer to map 269 * A flag indication if TTL should be max, min or random 270 * 271 * RETURNS : TTL value in seconds. 272 * -1 on failure 273 */ 274 int 275 get_ttl_value(map_ctrl *map, TTL_TYPE type) 276 { 277 __nis_table_mapping_t *table_map; 278 int interval, res; 279 char *myself = "get_ttl_value"; 280 281 /* Get the mapping structure corresponding to `map.domain' */ 282 table_map = mappingFromMap(map->map_name, map->domain, &res); 283 284 if (0 == table_map) { 285 logmsg(MSG_NOTIMECHECK, LOG_ERR, 286 "Get TTL request could not access map %s in domain %s " 287 "(error %d)", map->map_name, map->domain, res); 288 return (-1); 289 } 290 291 switch (type) { 292 case TTL_MAX: 293 return (table_map->initTtlHi); 294 295 case TTL_MIN: 296 return (table_map->initTtlLo); 297 298 default: 299 logmsg(MSG_NOTIMECHECK, LOG_INFO, 300 "%s passed illegal TTL type (%d)", myself, type); 301 /* If unknown TTL type drop through to TTL_RAND */ 302 /* FALLTHROUGH */ 303 304 case TTL_RAND: 305 interval = table_map->initTtlHi - table_map->initTtlLo; 306 if (0 >= interval) 307 return (table_map->initTtlLo); 308 309 /* 310 * Must get a random value. We assume srand48() got 311 * called at initialization. 312 */ 313 return (lrand48() % interval); 314 315 case TTL_RUNNING: 316 return (table_map->ttl); 317 318 319 } 320 } 321 322 /* 323 * FUNCTION : get_mapping_domain_list() 324 * 325 * DESCRIPTION: Gets a list of domain names specified, by nisLDAPdomainContext 326 * attributes, in the mapping file. This is used only for initial 327 * DIT setup. Once the DIT has been set up get_domain_list() is 328 * used instead. 329 * 330 * GIVEN : Pointer returned array. 331 * 332 * RETURNS : Number of element in returned array. 333 * Array of elements this is in static memory 334 * and must not be freed by the caller. 335 */ 336 int 337 get_mapping_domain_list(char ***ptr) 338 { 339 *ptr = ypDomains.domainLabels; 340 return (ypDomains.numDomains); 341 } 342 343 /* 344 * FUNCTION : get_mapping_yppasswdd_domain_list() 345 * 346 * DESCRIPTION: Gets a list of domain names specified, by the 347 * nisLDAPyppasswddDomains attribute, in the mapping file. This 348 * is the list of domains for which passwords should be changed. 349 * 350 * GIVEN : Pointer returned array 351 * 352 * RETURNS : Number of element in returned array. 353 * 0 if no nisLDAPyppasswddDomains attribute is present. 354 * Array of elements this is in static memory 355 * and must not be freed by the caller. 356 */ 357 int 358 get_mapping_yppasswdd_domain_list(char ***ptr) 359 { 360 *ptr = ypDomains.yppasswddDomainLabels; 361 return (ypDomains.numYppasswdd); 362 } 363 364 /* 365 * FUNCTION : free_map_list() 366 * 367 * DESCRIPTION: Frees a map list. 368 * 369 * GIVEN : Pointer to the map list. 370 * 371 * RETURNS : Nothing 372 */ 373 void 374 free_map_list(char **map_list) 375 { 376 free_null_terminated_list(map_list); 377 } 378 379 /* 380 * FUNCTION : get_passwd_list() 381 * 382 * DESCRIPTION: Gets a list of either passwd or passwd.adjunct map files 383 * defined in the mapping file. These are the files which have 384 * 'magic' nisLDAPdatabaseIdMapping entries aliasing them to 385 * passwd or passwd.adjunct. This function is required so that 386 * yppasswdd can work out which maps to synchronize with any 387 * password changes. 388 * 389 * This information is not currently stored by the parser but 390 * we can recover it from the hash table. This makes hard work but 391 * passwords should not be changed very frequently 392 * 393 * GIVEN : Flag indicating if a list is required for passwd or 394 * passwd.adjunct 395 * Domain to return the list for. 396 * 397 * RETURNS : Null terminated list of map names in malloced memory. To be 398 * freed by caller. (Possibly empty if no passwd maps found) 399 * NULL on error 400 */ 401 char ** 402 get_passwd_list(bool_t adjunct, char *domain) 403 { 404 char *myself = "get_passwd_list"; 405 __nis_hash_item_mt *it; 406 int i, size; 407 char *end_ptr; 408 char *target; /* What we are looking for */ 409 int target_len; 410 int domain_len; 411 char **res; /* Result array */ 412 char **res_old; /* Old value of res during realloc */ 413 int array_size; /* Current malloced size */ 414 int res_count = 0; /* Current result count */ 415 416 /* 417 * Always need an array even if just for terminator. Normally one 418 * chunk will be enough. 419 */ 420 res = am(myself, ARRAY_CHUNK * sizeof (char *)); 421 if (NULL == res) 422 return (NULL); 423 array_size = ARRAY_CHUNK; 424 425 /* Set up target */ 426 if (adjunct) 427 target = PASSWD_ADJUNCT_PREFIX; 428 else 429 target = PASSWD_PREFIX; 430 target_len = strlen(target); 431 domain_len = strlen(domain); 432 433 /* Work out hash table length */ 434 size = sizeof (ldapMappingList.keys) / sizeof (ldapMappingList.keys[0]); 435 /* For all hash table entries */ 436 for (i = 0; i < size; i++) { 437 /* Walk linked list for this hash table entry */ 438 for (it = ldapMappingList.keys[i]; NULL != it; it = it->next) { 439 /* Check right map */ 440 if ((target_len + domain_len + 1) > strlen(it->name)) 441 continue; 442 if (0 != strncmp(it->name, target, target_len)) 443 continue; 444 445 /* Check right domain (minus trailing dot) */ 446 if (strlen(domain) >= strlen(it->name)) 447 continue; 448 end_ptr = it->name + strlen(it->name) - 449 strlen(domain) - 1; 450 if (',' != *(end_ptr - 1)) 451 continue; 452 if (0 != strncmp(end_ptr, domain, strlen(domain))) 453 continue; 454 455 /* Check if we need to enlarge array */ 456 if ((res_count + 1) >= array_size) { 457 array_size += ARRAY_CHUNK; 458 res_old = res; 459 res = realloc(res, array_size * 460 sizeof (char *)); 461 if (NULL == res) { 462 res_old[res_count] = NULL; 463 free_passwd_list(res_old); 464 return (NULL); 465 } 466 } 467 468 /* What we really need is strndup() */ 469 res[res_count] = am(myself, end_ptr - it->name + 1); 470 if (NULL == res[res_count]) { 471 free_passwd_list(res); 472 return (NULL); 473 } 474 475 /* Copy from start to end_ptr */ 476 (void) memcpy(res[res_count], it->name, 477 end_ptr-it->name - 1); 478 res_count ++; 479 } 480 } 481 482 /* Terminate array */ 483 res[res_count] = NULL; 484 return (res); 485 } 486 487 /* 488 * FUNCTION : free_passwd_list() 489 * 490 * DESCRIPTION: Frees a password list obtained with get_passwd_list() 491 * 492 * INPUTS : Address of list to free. 493 * 494 * OUTPUTS : Nothing 495 */ 496 void 497 free_passwd_list(char **list) 498 { 499 free_null_terminated_list(list); 500 } 501 502 /* 503 * FUNCTION : free_null_terminated_list() 504 * 505 * DESCRIPTION: Frees a generic null terminated list. 506 * 507 * INPUTS : Address of list to free. 508 * 509 * OUTPUTS : Nothing 510 */ 511 void 512 free_null_terminated_list(char **list) 513 { 514 int index; 515 516 /* Free all the strings */ 517 for (index = 0; NULL != list[index]; index ++) 518 sfree(list[index]); 519 520 /* Free the array */ 521 sfree(list); 522 } 523 524 525 /* 526 * FUNCTION : add_special_entries() 527 * 528 * DESCRIPTION: Adds the special (YP_*) entries to a map. 529 * 530 * Part of dit_access because requires access to the mapping 531 * file in order to work out if secure and interdomain entries 532 * should be created. 533 * 534 * GIVEN : Pointer to an open, temporary, DBM file 535 * Pointer to map information (do not use DBM fields). 536 * Pointer to a location in which to return security flag 537 * 538 * RETURNS : SUCCESS = All entries created 539 * FAILURE = Some entries not created 540 */ 541 suc_code 542 add_special_entries(DBM *db, map_ctrl *map, bool_t *secure_flag) 543 { 544 char local_host[MAX_MASTER_NAME]; 545 __nis_table_mapping_t *table_map; 546 int res; 547 548 /* Last modified time is now */ 549 update_timestamp(db); 550 551 /* Add domain name */ 552 addpair(db, yp_domain_name, map->domain); 553 554 /* For N2L mode local machine is always the master */ 555 sysinfo(SI_HOSTNAME, local_host, sizeof (local_host)); 556 addpair(db, yp_master_name, local_host); 557 558 /* Get the mapping structure corresponding to `map.domain' */ 559 table_map = mappingFromMap(map->map_name, map->domain, &res); 560 if (0 == table_map) 561 return (FAILURE); 562 563 /* Add secure and interdomain flags if required */ 564 if (table_map->securemap_flag) { 565 addpair(db, yp_secure, ""); 566 *secure_flag = TRUE; 567 } else { 568 *secure_flag = FALSE; 569 } 570 if (table_map->usedns_flag) 571 addpair(db, yp_interdomain, ""); 572 573 return (SUCCESS); 574 } 575 576 /* 577 * FUNCTION: update_map_from_dit() 578 * 579 * DESCRIPTION: Core code called to update an entire map. 580 * Information is recovered from LDAP and used to build a duplicate 581 * copy of the live maps. When this is complete the maps are 582 * locked and then overwritten by the new copy. 583 * 584 * INPUTS: map_ctrl containing lots of information about the map and a 585 * pointer to it's lock which will be required. 586 * Flag indicating if progress logging is required. 587 * 588 * OUTPUTS: SUCCESS = Map updated 589 * FAILURE = Map not updated 590 */ 591 suc_code 592 update_map_from_dit(map_ctrl *map, bool_t log_flag) { 593 __nis_table_mapping_t *t; 594 __nis_rule_value_t *rv; 595 __nis_ldap_search_t *ls; 596 __nis_object_dn_t *objectDN = NULL; 597 datum *datval, *datkey; 598 int nr = 0, i, j, nv, numDNs; 599 int statP = SUCCESS, flag; 600 char *objname, **dn; 601 /* Name of temporary entries DBM file */ 602 char *temp_entries; 603 /* Name of temporary TTL DBM file */ 604 char *temp_ttl; 605 /* Temporary DBM handles */ 606 DBM *temp_entries_db; 607 DBM *temp_ttl_db; 608 map_ctrl temp_map; 609 datum key; 610 char *myself = "update_map_from_dit"; 611 bool_t secure_flag; 612 int entry_count = 1; 613 int next_print = PRINT_FREQ; 614 int search_flag = SUCCESS; 615 616 int m; 617 618 /* list of maps whose keys will be transliterated to lowercase */ 619 char *xlate_to_lcase_maps[] = { 620 "hosts.byname", 621 "ipnodes.byname", 622 NULL 623 }; 624 bool_t xlate_to_lcase = FALSE; 625 626 if (!map || !map->map_name || !map->domain) { 627 return (FAILURE); 628 } 629 630 __nisdb_get_tsd()->escapeFlag = '\0'; 631 632 /* 633 * netgroup.byxxx maps are a special case. They are regenerated from 634 * the netgroup map, not the DIT, so handle special case. 635 */ 636 if ((0 == strcmp(map->map_name, NETGROUP_BYHOST)) || 637 0 == (strcmp(map->map_name, NETGROUP_BYUSER))) { 638 return (update_netgroup_byxxx(map)); 639 } 640 641 /* Get the mapping information for the map */ 642 if ((t = mappingFromMap(map->map_name, map->domain, &statP)) == 0) { 643 if (statP == MAP_NO_MAPPING_EXISTS) 644 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 645 "%s: No mapping information available for %s,%s", 646 myself, map->map_name, map->domain); 647 return (FAILURE); 648 } 649 650 /* Allocate and set up names */ 651 if (SUCCESS != alloc_temp_names(map->map_path, 652 &temp_entries, &temp_ttl)) { 653 logmsg(MSG_NOTIMECHECK, LOG_ERR, 654 "%s: Unable to create map names for %s", 655 myself, map->map_path); 656 return (FAILURE); 657 } 658 659 /* Create temp entry and TTL file */ 660 if ((temp_entries_db = dbm_open(temp_entries, O_RDWR | O_CREAT, 0644)) 661 == NULL) { 662 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s", 663 myself, temp_entries); 664 sfree(temp_entries); 665 sfree(temp_ttl); 666 return (FAILURE); 667 } 668 669 if ((temp_ttl_db = dbm_open(temp_ttl, O_RDWR | O_CREAT, 0644)) 670 == NULL) { 671 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s", 672 myself, temp_ttl); 673 dbm_close(temp_entries_db); 674 delete_map(temp_entries); 675 sfree(temp_entries); 676 sfree(temp_ttl); 677 return (FAILURE); 678 } 679 680 /* Initialize domainContext tsd */ 681 __nisdb_get_tsd()->domainContext = 0; 682 for (i = 0; i < ypDomains.numDomains; i++) { 683 if (0 == ypDomains.domainLabels[i]) 684 continue; 685 if (0 == strcasecmp(map->domain, ypDomains.domainLabels[i])) { 686 __nisdb_get_tsd()->domainContext = ypDomains.domains[i]; 687 break; 688 } 689 } 690 691 if (!(objname = getFullMapName(map->map_name, map->domain))) { 692 if (temp_entries_db) 693 dbm_close(temp_entries_db); 694 if (temp_ttl_db) 695 dbm_close(temp_ttl_db); 696 delete_map(temp_entries); 697 sfree(temp_entries); 698 delete_map(temp_ttl); 699 sfree(temp_ttl); 700 return (FAILURE); 701 } 702 703 /* 704 * set xlate_to_lcase to TRUE if map_name is found in 705 * xlate_to_lcase_maps[] 706 */ 707 m = 0; 708 while (xlate_to_lcase_maps[m] != NULL) { 709 if (strncmp(map->map_name, xlate_to_lcase_maps[m], 710 strlen(xlate_to_lcase_maps[m])) == 0) { 711 xlate_to_lcase = TRUE; 712 break; 713 } 714 ++m; 715 } 716 717 /* Try each mapping for the map */ 718 for (flag = 0; t != 0 && search_flag != FAILURE; t = t->next) { 719 720 /* Check if the mapping is the correct one */ 721 if (strcmp(objname, t->objName) != 0) { 722 continue; 723 } 724 725 /* Check if rulesFromLDAP are provided */ 726 if (t->numRulesFromLDAP == 0) { 727 logmsg(MSG_NOTIMECHECK, LOG_ERR, 728 "%s: No rulesFromLDAP available for %s (%s)", 729 myself, t->dbId, map->map_name); 730 continue; 731 } 732 733 /* Set flag to indicate update is enabled */ 734 flag = 1; 735 /* Create ldap request for enumeration */ 736 for (objectDN = t->objectDN; 737 objectDN && objectDN->read.base; 738 objectDN = objectDN->next) { 739 if ((ls = createLdapRequest(t, 0, 0, 1, NULL, 740 objectDN)) == 0) { 741 logmsg(MSG_NOTIMECHECK, LOG_ERR, 742 "%s: Failed to create " 743 "ldapSearch request for " 744 "%s (%s) for base %s", 745 myself, t->dbId, 746 map->map_name, 747 objectDN->read.base); 748 statP = FAILURE; 749 search_flag = FAILURE; 750 break; 751 } 752 753 if (log_flag) { 754 printf("Waiting for LDAP search results.\n"); 755 } 756 757 /* Query LDAP */ 758 nr = (ls->isDN)?0:-1; 759 rv = ldapSearch(ls, &nr, 0, &statP); 760 freeLdapSearch(ls); 761 if (rv == 0) { 762 if (statP == LDAP_NO_SUCH_OBJECT) { 763 /* 764 * No Entry exists in the ldap server. Not 765 * a problem. Maybe there are just no entries 766 * in this map. 767 */ 768 continue; 769 } 770 logmsg(MSG_NOTIMECHECK, LOG_ERR, 771 "%s: ldapSearch error %d " 772 "(%s) for %s (%s) for base %s", 773 myself, statP, ldap_err2string(statP), 774 t->dbId, map->map_name, 775 objectDN->read.base); 776 statP = FAILURE; 777 search_flag = FAILURE; 778 break; 779 } 780 781 if (log_flag) { 782 printf("Processing search results.\n"); 783 } 784 785 /* Obtain list of DNs for logging */ 786 if ((dn = findDNs(myself, rv, nr, 0, &numDNs)) == 0) { 787 statP = FAILURE; 788 search_flag = FAILURE; 789 break; 790 } 791 792 /* For each entry in the result do the following */ 793 for (i = 0; i < nr; i++) { 794 /* Convert LDAP data to NIS equivalents */ 795 statP = buildNISRuleValue(t, &rv[i], 796 map->domain); 797 if (statP == MAP_INDEXLIST_ERROR) 798 continue; 799 if (statP != SUCCESS) { 800 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 801 "%s: Conversion error %d (LDAP to " 802 "name=value pairs) " 803 "for (dn: %s) for " 804 "%s (%s) for base %s", 805 myself, statP, NIL(dn[i]), 806 t->dbId, map->map_name, 807 objectDN->read.base); 808 continue; 809 } 810 811 /* Obtain the datum for value */ 812 datval = ruleValueToDatum(t, &rv[i], &statP); 813 if (datval == 0) { 814 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 815 "%s: Conversion error %d " 816 "(name=value pairs to NIS)" 817 " for (dn: %s) for " 818 "%s (%s) for base %s", 819 myself, statP, NIL(dn[i]), 820 t->dbId, map->map_name, 821 objectDN->read.base); 822 continue; 823 } 824 825 /* Obtain the datum for key */ 826 datkey = getKeyFromRuleValue(t, &rv[i], 827 &nv, &statP, xlate_to_lcase); 828 if (datkey == 0) { 829 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 830 "%s: Unable to obtain NIS " 831 "key from LDAP data (dn:%s) " 832 "for %s (%s) for base %s", 833 myself, NIL(dn[i]), t->dbId, 834 map->map_name, 835 objectDN->read.base); 836 sfree(datval->dptr); 837 sfree(datval); 838 continue; 839 } 840 841 /* Write to the temporary map */ 842 for (j = 0; j < nv; j++, entry_count ++) { 843 if (datkey[j].dsize == 0) 844 continue; 845 errno = 0; 846 /* DBM_INSERT to match */ 847 /* singleReadFromDIT */ 848 if (dbm_store(temp_entries_db, 849 datkey[j], 850 *datval, 851 DBM_INSERT) < 0) { 852 /* 853 * For some cases errno may 854 * still be 0 but dbm_error 855 * isn't informative at all. 856 */ 857 logmsg(MSG_NOTIMECHECK, 858 LOG_WARNING, 859 "%s: dbm store error " 860 "(errno=%d) " 861 "for (key=%s, value=%s) " 862 "for %s (%s) for base %s", 863 myself, 864 errno, 865 datkey[j].dptr, 866 datval->dptr, t->dbId, 867 map->map_name, 868 objectDN->read.base); 869 /* clear the error */ 870 dbm_clearerr(temp_entries_db); 871 } 872 sfree(datkey[j].dptr); 873 874 if (log_flag && (entry_count >= 875 next_print)) { 876 printf("%d entries processed\n", 877 entry_count); 878 next_print *= 2; 879 } 880 881 } 882 sfree(datkey); 883 sfree(datval->dptr); 884 sfree(datval); 885 } 886 887 freeRuleValue(rv, nr); 888 freeDNs(dn, numDNs); 889 } /* End of for over objectDN */ 890 } 891 sfree(objname); 892 893 if (t != 0 || flag == 0 || search_flag == FAILURE) { 894 if (temp_entries_db) 895 dbm_close(temp_entries_db); 896 if (temp_ttl_db) 897 dbm_close(temp_ttl_db); 898 delete_map(temp_entries); 899 sfree(temp_entries); 900 delete_map(temp_ttl); 901 sfree(temp_ttl); 902 return (statP); 903 } 904 /* Set up enough of map_ctrl to call update_entry_ttl */ 905 temp_map.map_name = map->map_name; 906 temp_map.domain = map->domain; 907 temp_map.ttl = temp_ttl_db; 908 909 /* Generate new TTL file */ 910 key = dbm_firstkey(temp_entries_db); 911 while (key.dptr != 0) { 912 if (!is_special_key(&key)) 913 /* 914 * We don't want all the entries to time out at the 915 * same time so create random TTLs. 916 */ 917 if (FAILURE == update_entry_ttl(&temp_map, &key, 918 TTL_RAND)) 919 logmsg(MSG_NOTIMECHECK, LOG_ERR, 920 "%s: Could not update TTL for " 921 "(key=%s) for map %s,%s", 922 myself, NIL(key.dptr), map->map_name, 923 map->domain); 924 key = dbm_nextkey(temp_entries_db); 925 } 926 927 /* Update map TTL */ 928 if (SUCCESS != update_map_ttl(&temp_map)) { 929 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not update map TTL " 930 "for %s,%s", myself, map->map_name, map->domain); 931 } 932 933 /* Set up 'special' nis entries */ 934 add_special_entries(temp_entries_db, map, &secure_flag); 935 936 /* Close temp DBM files */ 937 dbm_close(temp_entries_db); 938 dbm_close(temp_ttl_db); 939 940 /* Lock access to the map for copy */ 941 lock_map_ctrl(map); 942 943 /* Move temp maps to real ones */ 944 rename_map(temp_entries, map->map_path, secure_flag); 945 rename_map(temp_ttl, map->ttl_path, secure_flag); 946 947 /* Free file names */ 948 sfree(temp_entries); 949 sfree(temp_ttl); 950 951 /* Unlock map */ 952 unlock_map_ctrl(map); 953 954 return (SUCCESS); 955 } 956 957 /* 958 * FUNCTION : get_mapping_map_list() 959 * 960 * DESCRIPTION: Gets a list of nis maps for a given domain specified in the 961 * mapping file. This information is not saved so have to go 962 * through the entire hash table. At least this is only done at 963 * initialization time. 964 * 965 * GIVEN : Domain name 966 * 967 * RETURNS : List of map names in malloced memory. MUST BE FREED BY CALLER. 968 */ 969 char ** 970 get_mapping_map_list(char *domain) 971 { 972 char *myself = "get_mapping_map_list"; 973 __nis_hash_item_mt *it; 974 int i, j, size; 975 char *end_ptr; 976 char **res; /* Result array */ 977 char **res_old; /* Old value of res during realloc */ 978 int array_size; /* Current malloced size */ 979 int res_count = 0; /* Current result count */ 980 981 /* 982 * Always need an array even if just for terminator. Normally one 983 * chunk will be enough. 984 */ 985 res = am(myself, ARRAY_CHUNK * sizeof (char *)); 986 if (NULL == res) 987 return (NULL); 988 array_size = ARRAY_CHUNK; 989 990 /* Work out hash table length */ 991 size = sizeof (ldapMappingList.keys) / sizeof (ldapMappingList.keys[0]); 992 /* For all hash table entries */ 993 for (i = 0; i < size; i++) { 994 /* Walk linked list for this hash table entry */ 995 for (it = ldapMappingList.keys[i]; NULL != it; it = it->next) { 996 997 /* Check it's not a split field entry */ 998 if (0 != ((__nis_table_mapping_t *)it)->numSplits) 999 continue; 1000 1001 /* Check right domain (minus trailing dot) */ 1002 if (strlen(domain) >= strlen(it->name)) 1003 continue; 1004 end_ptr = it->name + strlen(it->name) - 1005 strlen(domain) - 1; 1006 if (',' != *(end_ptr - 1)) 1007 continue; 1008 if (0 != strncmp(end_ptr, domain, strlen(domain))) 1009 continue; 1010 1011 /* Check if we need to enlarge array */ 1012 if ((res_count + 1) >= array_size) { 1013 array_size += ARRAY_CHUNK; 1014 res_old = res; 1015 res = realloc(res, array_size * 1016 sizeof (char *)); 1017 if (NULL == res) { 1018 res_old[res_count] = NULL; 1019 free_passwd_list(res_old); 1020 return (NULL); 1021 } 1022 } 1023 1024 /* 1025 * We will need the sequence number when we come to 1026 * sort the entries so for now store a pointer to 1027 * the __nis_hash_item_mt. 1028 */ 1029 res[res_count] = (char *)it; 1030 res_count ++; 1031 } 1032 } 1033 1034 /* Terminate array */ 1035 res[res_count] = NULL; 1036 1037 /* Bubble sort entries into the same order as mapping file */ 1038 for (i = res_count - 2; 0 <= i; i--) { 1039 for (j = 0; j <= i; j++) { 1040 if (((__nis_table_mapping_t *)res[j + 1])->seq_num < 1041 ((__nis_table_mapping_t *)res[j])->seq_num) { 1042 end_ptr = res[j]; 1043 res[j] = res[j+1]; 1044 res[j + 1] = end_ptr; 1045 } 1046 } 1047 } 1048 1049 /* Finally copy the real strings in to each entry */ 1050 for (i = 0; NULL != res[i]; i ++) { 1051 1052 /* Get hash table entry back */ 1053 it = (__nis_hash_item_mt *)res[i]; 1054 1055 end_ptr = it->name + strlen(it->name) - strlen(domain) - 1; 1056 1057 /* What we really need is strndup() */ 1058 res[i] = am(myself, end_ptr - it->name + 1); 1059 if (NULL == res[i]) { 1060 free_map_list(res); 1061 return (NULL); 1062 } 1063 1064 /* Copy from start to end_ptr */ 1065 (void) memcpy(res[i], it->name, end_ptr-it->name - 1); 1066 } 1067 1068 return (res); 1069 } 1070 1071 /* 1072 * FUNCTION : make_nis_container() 1073 * 1074 * DESCRIPTION: Sets up container for map_name in the DIT. 1075 * 1076 * GIVEN : Map name 1077 * The domain name. 1078 * Flag indicating if container should be created. 1079 * 1080 * RETURNS : SUCCESS = It worked 1081 * FAILURE = There was a problem. 1082 */ 1083 suc_code 1084 make_nis_container(char *map_name, char *domain, bool_t init_containers) { 1085 int i, rc, statP = SUCCESS; 1086 __nis_table_mapping_t *t; 1087 char *dn; 1088 char *myself = "make_nis_container"; 1089 1090 if (!map_name || !domain) 1091 return (FAILURE); 1092 1093 if (FALSE == init_containers) { 1094 /* 1095 * If we are not creating containers it is debatable what we 1096 * should do . Maybe we should check for a pre- 1097 * existing container and return failure if it does not exist. 1098 * 1099 * For now we assume the user will not have called us in this 1100 * mode unless they know what they are doing. So return 1101 * success. If they have got it wrong then latter writes will 1102 * fail. 1103 */ 1104 return (SUCCESS); 1105 } 1106 1107 /* Get the mapping information for the map */ 1108 if ((t = mappingFromMap(map_name, domain, &statP)) == 0) { 1109 if (statP == MAP_NO_MAPPING_EXISTS) 1110 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1111 "%s: No mapping information available for %s,%s", 1112 myself, NIL(map_name), NIL(domain)); 1113 return (FAILURE); 1114 } 1115 1116 /* Two times. One for readDN and other for writeDN */ 1117 for (i = 0; i < 2; i++) { 1118 if (i == 0) 1119 dn = t->objectDN->read.base; 1120 else { 1121 if (t->objectDN->write.base == 0) { 1122 logmsg(MSG_NOTIMECHECK, LOG_INFO, 1123 "%s: No baseDN in writespec. Write " 1124 "disabled for %s,%s", 1125 myself, map_name, domain); 1126 break; 1127 } 1128 if (!strcasecmp(dn, t->objectDN->write.base)) 1129 break; 1130 dn = t->objectDN->write.base; 1131 } 1132 1133 if ((rc = makeNISObject(0, dn)) == FAILURE) { 1134 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1135 "%s: Unable to create ldap container (dn: %s) " 1136 "for %s,%s", myself, dn, map_name, domain); 1137 return (FAILURE); 1138 } 1139 } 1140 return (SUCCESS); 1141 } 1142 1143 /* 1144 * FUNCTION : make_nis_domain() 1145 * 1146 * DESCRIPTION: Sets up a nisDomainObject in the DIT 1147 * 1148 * GIVEN: Name of the domain 1149 * Flag indicating if domain should be create or possibly just 1150 * checked for. 1151 */ 1152 suc_code 1153 make_nis_domain(char *domain, bool_t init_containers) { 1154 1155 if (FALSE == init_containers) { 1156 /* 1157 * If we are not creating containers it is debatable what we 1158 * should do with domains. Maybe we should check for a pre- 1159 * existing domain and return failure if it does not exist. 1160 * 1161 * For now we assume the user will not have called us in this 1162 * mode unless they know what they are doing. So return 1163 * success. If they have got it wrong then latter writes will 1164 * fail. 1165 */ 1166 return (SUCCESS); 1167 } 1168 1169 /* Create the domain */ 1170 return (makeNISObject(domain, 0)); 1171 } 1172 1173 /* 1174 * FUNCTION: update_netgroup_byxxx() 1175 * 1176 * DESCRIPTION: Updates the netgroup.byxxx series of maps based on the current 1177 * netgroup file. We invoke revnetgroup so that if any changes 1178 * are made to this utility the same changes are made here. 1179 * 1180 * INPUTS: map_ctrl containing lots of information about the map and a 1181 * pointer to it's lock which will be required. 1182 * 1183 * OUTPUTS: SUCCESS = Map updated 1184 * FAILURE = Map not updated 1185 */ 1186 suc_code 1187 update_netgroup_byxxx(map_ctrl *map) { 1188 /* Name of temporary entries DBM file */ 1189 char *temp_entries; 1190 /* Name of temporary TTL DBM file */ 1191 char *temp_ttl; 1192 /* Temporary DBM handles */ 1193 DBM *temp_entries_db; 1194 DBM *temp_ttl_db; 1195 map_ctrl temp_map; 1196 char *myself = "update_netgroup_byxxx"; 1197 char *cmdbuf; 1198 int cmd_length; 1199 datum key; 1200 map_ctrl *netgroupmap; 1201 int res; 1202 /* Temporary revnetgroup files */ 1203 const char *byhost = NETGROUP_BYHOST "_REV" TEMP_POSTFIX; 1204 const char *byuser = NETGROUP_BYUSER "_REV" TEMP_POSTFIX; 1205 const char *temp_file_name; 1206 1207 1208 /* 1209 * We need to use two different temporary files: one for netgroup.byhost 1210 * and other for netgroup.byuser, since these two maps can be updated 1211 * simultaneously. These temporary files will hold the output of 1212 * revnetgroup [-h|-u] command. They are then used to generate the 1213 * corresponding dbm files and thereafter deleted. 1214 */ 1215 if (0 == strcmp(map->map_name, NETGROUP_BYHOST)) 1216 temp_file_name = byhost; 1217 else 1218 temp_file_name = byuser; 1219 1220 /* Alloc enough cmd buf for revnet cmd */ 1221 cmd_length = strlen("/usr/sbin/makedbm -u ") + 1222 (strlen(map->map_path) - strlen(map->map_name)) + 1223 strlen(NETGROUP_MAP) + 1224 strlen(" | /usr/sbin/revnetgroup -h > ") + 1225 (strlen(map->map_path) - strlen(map->map_name)) + 1226 strlen(temp_file_name) + 1; 1227 cmdbuf = am(myself, cmd_length); 1228 1229 if (NULL == cmdbuf) { 1230 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1231 "%s: Could not alloc cmdbuf.", myself); 1232 return (FAILURE); 1233 } 1234 1235 /* 1236 * If necessary update (and wait for) netgroups map. This is a lot of 1237 * work but if the netgroup map itself is not being accessed it may 1238 * contain information that is not up to date with the DIT. 1239 * 1240 * We use the cmdbuf to store the qualified netgroup map name there will 1241 * be enough space for this but we are not yet creating the cmd. 1242 */ 1243 strlcpy(cmdbuf, map->map_path, strlen(map->map_path) - 1244 strlen(map->map_name) + 1); 1245 strcat(cmdbuf, NETGROUP_MAP); 1246 netgroupmap = (map_ctrl *)shim_dbm_open(cmdbuf, 1247 O_RDWR | O_CREAT, 0644); 1248 if (NULL == netgroupmap) { 1249 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1250 "%s: Could not update %s.", myself, cmdbuf); 1251 sfree(cmdbuf); 1252 return (FAILURE); 1253 } 1254 1255 if (has_map_expired(netgroupmap)) { 1256 lock_map_ctrl(netgroupmap); 1257 update_map_if_required(netgroupmap, TRUE); 1258 unlock_map_ctrl(netgroupmap); 1259 } 1260 shim_dbm_close((DBM *)netgroupmap); 1261 1262 /* Dump netgroup file through revnetgroup to a temp file */ 1263 strcpy(cmdbuf, "/usr/sbin/makedbm -u "); 1264 1265 /* Unmake the netgroup file in same domain as map */ 1266 strncat(cmdbuf, map->map_path, strlen(map->map_path) - 1267 strlen(map->map_name)); 1268 strcat(cmdbuf, NETGROUP_MAP); 1269 1270 if (0 == strcmp(map->map_name, NETGROUP_BYHOST)) { 1271 strcat(cmdbuf, " | /usr/sbin/revnetgroup -h > "); 1272 } else { 1273 strcat(cmdbuf, " | /usr/sbin/revnetgroup -u > "); 1274 } 1275 1276 /* Create temp file file in same domain as map */ 1277 strncat(cmdbuf, map->map_path, strlen(map->map_path) - 1278 strlen(map->map_name)); 1279 strcat(cmdbuf, temp_file_name); 1280 1281 if (0 > system(cmdbuf)) { 1282 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not run \"%s\" " 1283 "(errno=%d)", myself, cmdbuf, errno); 1284 sfree(cmdbuf); 1285 return (FAILURE); 1286 } 1287 sfree(cmdbuf); 1288 1289 /* Allocate and set up names */ 1290 if (SUCCESS != alloc_temp_names(map->map_path, 1291 &temp_entries, &temp_ttl)) { 1292 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1293 "%s: Unable to create map names for %s", 1294 myself, map->map_path); 1295 return (FAILURE); 1296 } 1297 1298 /* Make the temporary DBM file */ 1299 cmdbuf = am(myself, strlen("/usr/sbin/makedbm") + 1300 (strlen(map->map_path) - strlen(map->map_name)) + 1301 strlen(temp_file_name) + 1302 strlen(temp_entries) + 3); 1303 if (NULL == cmdbuf) { 1304 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1305 "%s: Could not allocate cmdbuf.", myself); 1306 sfree(temp_entries); 1307 sfree(temp_ttl); 1308 return (FAILURE); 1309 } 1310 1311 strcpy(cmdbuf, "/usr/sbin/makedbm "); 1312 strncat(cmdbuf, map->map_path, strlen(map->map_path) - 1313 strlen(map->map_name)); 1314 strcat(cmdbuf, temp_file_name); 1315 strcat(cmdbuf, " "); 1316 strcat(cmdbuf, temp_entries); 1317 1318 if (0 > system(cmdbuf)) { 1319 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not run \"%s\" " 1320 "(errno=%d)", myself, cmdbuf, errno); 1321 sfree(cmdbuf); 1322 sfree(temp_entries); 1323 sfree(temp_ttl); 1324 return (FAILURE); 1325 } 1326 1327 /* Already have enough command buffer to rm temporary file */ 1328 strlcpy(cmdbuf, map->map_path, strlen(map->map_path) - 1329 strlen(map->map_name) + 1); 1330 strcat(cmdbuf, temp_file_name); 1331 res = unlink(cmdbuf); 1332 /* If the temp file did not exist no problem. Probably had no entries */ 1333 if ((0 != res) && (ENOENT != errno)) { 1334 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not delete \"%s\" " 1335 "(errno=%d)", myself, cmdbuf, errno); 1336 sfree(temp_entries); 1337 sfree(temp_ttl); 1338 sfree(cmdbuf); 1339 return (FAILURE); 1340 } 1341 sfree(cmdbuf); 1342 1343 if ((temp_entries_db = dbm_open(temp_entries, O_RDWR | O_CREAT, 0644)) 1344 == NULL) { 1345 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s", 1346 myself, temp_entries); 1347 sfree(temp_entries); 1348 sfree(temp_ttl); 1349 return (FAILURE); 1350 } 1351 1352 if ((temp_ttl_db = dbm_open(temp_ttl, O_RDWR | O_CREAT, 0644)) 1353 == NULL) { 1354 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s", 1355 myself, temp_ttl); 1356 dbm_close(temp_entries_db); 1357 sfree(temp_entries); 1358 sfree(temp_ttl); 1359 return (FAILURE); 1360 } 1361 1362 /* 1363 * Set up enough of map_ctrl to call update_entry_ttl. Since there is 1364 * no mapping, and thus not TTL, defined for these maps use the TTL 1365 * values for netgroup map 1366 */ 1367 temp_map.map_name = NETGROUP_MAP; 1368 temp_map.domain = map->domain; 1369 temp_map.ttl = temp_ttl_db; 1370 1371 /* 1372 * Generate new TTL file. Since these maps work only on the whole map 1373 * expiry these will not actually be used but there presence makes it 1374 * easier to handle these maps in the same way as other maps. 1375 */ 1376 key = dbm_firstkey(temp_entries_db); 1377 while (key.dptr != 0) { 1378 if (!is_special_key(&key)) 1379 /* 1380 * For these maps want all timouts to be maximum 1381 */ 1382 if (FAILURE == update_entry_ttl(&temp_map, &key, 1383 TTL_MAX)) 1384 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1385 "%s: Could not update TTL for " 1386 "(key=%s) for map %s,%s", 1387 myself, NIL(key.dptr), map->map_name, 1388 map->domain); 1389 key = dbm_nextkey(temp_entries_db); 1390 } 1391 1392 /* Update map TTL */ 1393 update_map_ttl(&temp_map); 1394 1395 /* Close temp DBM files */ 1396 dbm_close(temp_entries_db); 1397 dbm_close(temp_ttl_db); 1398 1399 /* Lock access to the map for copy */ 1400 lock_map_ctrl(map); 1401 1402 /* Move temp maps to real ones */ 1403 rename_map(temp_entries, map->map_path, FALSE); 1404 rename_map(temp_ttl, map->ttl_path, FALSE); 1405 1406 /* Free file names */ 1407 sfree(temp_entries); 1408 sfree(temp_ttl); 1409 1410 /* Unlock map */ 1411 unlock_map_ctrl(map); 1412 1413 return (SUCCESS); 1414 } 1415