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 303 case TTL_RAND: 304 interval = table_map->initTtlHi - table_map->initTtlLo; 305 if (0 >= interval) 306 return (table_map->initTtlLo); 307 308 /* 309 * Must get a random value. We assume srand48() got 310 * called at initialization. 311 */ 312 return (lrand48() % interval); 313 314 case TTL_RUNNING: 315 return (table_map->ttl); 316 317 318 } 319 } 320 321 /* 322 * FUNCTION : get_mapping_domain_list() 323 * 324 * DESCRIPTION: Gets a list of domain names specified, by nisLDAPdomainContext 325 * attributes, in the mapping file. This is used only for initial 326 * DIT setup. Once the DIT has been set up get_domain_list() is 327 * used instead. 328 * 329 * GIVEN : Pointer returned array. 330 * 331 * RETURNS : Number of element in returned array. 332 * Array of elements this is in static memory 333 * and must not be freed by the caller. 334 */ 335 int 336 get_mapping_domain_list(char ***ptr) 337 { 338 *ptr = ypDomains.domainLabels; 339 return (ypDomains.numDomains); 340 } 341 342 /* 343 * FUNCTION : get_mapping_yppasswdd_domain_list() 344 * 345 * DESCRIPTION: Gets a list of domain names specified, by the 346 * nisLDAPyppasswddDomains attribute, in the mapping file. This 347 * is the list of domains for which passwords should be changed. 348 * 349 * GIVEN : Pointer returned array 350 * 351 * RETURNS : Number of element in returned array. 352 * 0 if no nisLDAPyppasswddDomains attribute is present. 353 * Array of elements this is in static memory 354 * and must not be freed by the caller. 355 */ 356 int 357 get_mapping_yppasswdd_domain_list(char ***ptr) 358 { 359 *ptr = ypDomains.yppasswddDomainLabels; 360 return (ypDomains.numYppasswdd); 361 } 362 363 /* 364 * FUNCTION : free_map_list() 365 * 366 * DESCRIPTION: Frees a map list. 367 * 368 * GIVEN : Pointer to the map list. 369 * 370 * RETURNS : Nothing 371 */ 372 void 373 free_map_list(char **map_list) 374 { 375 free_null_terminated_list(map_list); 376 } 377 378 /* 379 * FUNCTION : get_passwd_list() 380 * 381 * DESCRIPTION: Gets a list of either passwd or passwd.adjunct map files 382 * defined in the mapping file. These are the files which have 383 * 'magic' nisLDAPdatabaseIdMapping entries aliasing them to 384 * passwd or passwd.adjunct. This function is required so that 385 * yppasswdd can work out which maps to synchronize with any 386 * password changes. 387 * 388 * This information is not currently stored by the parser but 389 * we can recover it from the hash table. This makes hard work but 390 * passwords should not be changed very frequently 391 * 392 * GIVEN : Flag indicating if a list is required for passwd or 393 * passwd.adjunct 394 * Domain to return the list for. 395 * 396 * RETURNS : Null terminated list of map names in malloced memory. To be 397 * freed by caller. (Possibly empty if no passwd maps found) 398 * NULL on error 399 */ 400 char ** 401 get_passwd_list(bool_t adjunct, char *domain) 402 { 403 char *myself = "get_passwd_list"; 404 __nis_hash_item_mt *it; 405 int i, size; 406 char *end_ptr; 407 char *target; /* What we are looking for */ 408 int target_len; 409 int domain_len; 410 char **res; /* Result array */ 411 char **res_old; /* Old value of res during realloc */ 412 int array_size; /* Current malloced size */ 413 int res_count = 0; /* Current result count */ 414 415 /* 416 * Always need an array even if just for terminator. Normally one 417 * chunk will be enough. 418 */ 419 res = am(myself, ARRAY_CHUNK * sizeof (char *)); 420 if (NULL == res) 421 return (NULL); 422 array_size = ARRAY_CHUNK; 423 424 /* Set up target */ 425 if (adjunct) 426 target = PASSWD_ADJUNCT_PREFIX; 427 else 428 target = PASSWD_PREFIX; 429 target_len = strlen(target); 430 domain_len = strlen(domain); 431 432 /* Work out hash table length */ 433 size = sizeof (ldapMappingList.keys) / sizeof (ldapMappingList.keys[0]); 434 /* For all hash table entries */ 435 for (i = 0; i < size; i++) { 436 /* Walk linked list for this hash table entry */ 437 for (it = ldapMappingList.keys[i]; NULL != it; it = it->next) { 438 /* Check right map */ 439 if ((target_len + domain_len + 1) > strlen(it->name)) 440 continue; 441 if (0 != strncmp(it->name, target, target_len)) 442 continue; 443 444 /* Check right domain (minus trailing dot) */ 445 if (strlen(domain) >= strlen(it->name)) 446 continue; 447 end_ptr = it->name + strlen(it->name) - 448 strlen(domain) - 1; 449 if (',' != *(end_ptr - 1)) 450 continue; 451 if (0 != strncmp(end_ptr, domain, strlen(domain))) 452 continue; 453 454 /* Check if we need to enlarge array */ 455 if ((res_count + 1) >= array_size) { 456 array_size += ARRAY_CHUNK; 457 res_old = res; 458 res = realloc(res, array_size * 459 sizeof (char *)); 460 if (NULL == res) { 461 res_old[res_count] = NULL; 462 free_passwd_list(res_old); 463 return (NULL); 464 } 465 } 466 467 /* What we really need is strndup() */ 468 res[res_count] = am(myself, end_ptr - it->name + 1); 469 if (NULL == res[res_count]) { 470 free_passwd_list(res); 471 return (NULL); 472 } 473 474 /* Copy from start to end_ptr */ 475 (void) memcpy(res[res_count], it->name, 476 end_ptr-it->name - 1); 477 res_count ++; 478 } 479 } 480 481 /* Terminate array */ 482 res[res_count] = NULL; 483 return (res); 484 } 485 486 /* 487 * FUNCTION : free_passwd_list() 488 * 489 * DESCRIPTION: Frees a password list obtained with get_passwd_list() 490 * 491 * INPUTS : Address of list to free. 492 * 493 * OUTPUTS : Nothing 494 */ 495 void 496 free_passwd_list(char **list) 497 { 498 free_null_terminated_list(list); 499 } 500 501 /* 502 * FUNCTION : free_null_terminated_list() 503 * 504 * DESCRIPTION: Frees a generic null terminated list. 505 * 506 * INPUTS : Address of list to free. 507 * 508 * OUTPUTS : Nothing 509 */ 510 void 511 free_null_terminated_list(char **list) 512 { 513 int index; 514 515 /* Free all the strings */ 516 for (index = 0; NULL != list[index]; index ++) 517 sfree(list[index]); 518 519 /* Free the array */ 520 sfree(list); 521 } 522 523 524 /* 525 * FUNCTION : add_special_entries() 526 * 527 * DESCRIPTION: Adds the special (YP_*) entries to a map. 528 * 529 * Part of dit_access because requires access to the mapping 530 * file in order to work out if secure and interdomain entries 531 * should be created. 532 * 533 * GIVEN : Pointer to an open, temporary, DBM file 534 * Pointer to map information (do not use DBM fields). 535 * Pointer to a location in which to return security flag 536 * 537 * RETURNS : SUCCESS = All entries created 538 * FAILURE = Some entries not created 539 */ 540 suc_code 541 add_special_entries(DBM *db, map_ctrl *map, bool_t *secure_flag) 542 { 543 char local_host[MAX_MASTER_NAME]; 544 __nis_table_mapping_t *table_map; 545 int res; 546 547 /* Last modified time is now */ 548 update_timestamp(db); 549 550 /* Add domain name */ 551 addpair(db, yp_domain_name, map->domain); 552 553 /* For N2L mode local machine is always the master */ 554 sysinfo(SI_HOSTNAME, local_host, sizeof (local_host)); 555 addpair(db, yp_master_name, local_host); 556 557 /* Get the mapping structure corresponding to `map.domain' */ 558 table_map = mappingFromMap(map->map_name, map->domain, &res); 559 if (0 == table_map) 560 return (FAILURE); 561 562 /* Add secure and interdomain flags if required */ 563 if (table_map->securemap_flag) { 564 addpair(db, yp_secure, ""); 565 *secure_flag = TRUE; 566 } else { 567 *secure_flag = FALSE; 568 } 569 if (table_map->usedns_flag) 570 addpair(db, yp_interdomain, ""); 571 572 return (SUCCESS); 573 } 574 575 /* 576 * FUNCTION: update_map_from_dit() 577 * 578 * DESCRIPTION: Core code called to update an entire map. 579 * Information is recovered from LDAP and used to build a duplicate 580 * copy of the live maps. When this is complete the maps are 581 * locked and then overwritten by the new copy. 582 * 583 * INPUTS: map_ctrl containing lots of information about the map and a 584 * pointer to it's lock which will be required. 585 * Flag indicating if progress logging is required. 586 * 587 * OUTPUTS: SUCCESS = Map updated 588 * FAILURE = Map not updated 589 */ 590 suc_code 591 update_map_from_dit(map_ctrl *map, bool_t log_flag) { 592 __nis_table_mapping_t *t; 593 __nis_rule_value_t *rv; 594 __nis_ldap_search_t *ls; 595 __nis_object_dn_t *objectDN = NULL; 596 datum *datval, *datkey; 597 int nr = 0, i, j, nv, numDNs; 598 int statP = SUCCESS, flag; 599 char *objname, **dn; 600 /* Name of temporary entries DBM file */ 601 char *temp_entries; 602 /* Name of temporary TTL DBM file */ 603 char *temp_ttl; 604 /* Temporary DBM handles */ 605 DBM *temp_entries_db; 606 DBM *temp_ttl_db; 607 map_ctrl temp_map; 608 datum key; 609 char *myself = "update_map_from_dit"; 610 bool_t secure_flag; 611 int entry_count = 1; 612 int next_print = PRINT_FREQ; 613 int search_flag = SUCCESS; 614 615 int m; 616 617 /* list of maps whose keys will be transliterated to lowercase */ 618 char *xlate_to_lcase_maps[] = { 619 "hosts.byname", 620 "ipnodes.byname", 621 NULL 622 }; 623 bool_t xlate_to_lcase = FALSE; 624 625 if (!map || !map->map_name || !map->domain) { 626 return (FAILURE); 627 } 628 629 __nisdb_get_tsd()->escapeFlag = '\0'; 630 631 /* 632 * netgroup.byxxx maps are a special case. They are regenerated from 633 * the netgroup map, not the DIT, so handle special case. 634 */ 635 if ((0 == strcmp(map->map_name, NETGROUP_BYHOST)) || 636 0 == (strcmp(map->map_name, NETGROUP_BYUSER))) { 637 return (update_netgroup_byxxx(map)); 638 } 639 640 /* Get the mapping information for the map */ 641 if ((t = mappingFromMap(map->map_name, map->domain, &statP)) == 0) { 642 if (statP == MAP_NO_MAPPING_EXISTS) 643 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 644 "%s: No mapping information available for %s,%s", 645 myself, map->map_name, map->domain); 646 return (FAILURE); 647 } 648 649 /* Allocate and set up names */ 650 if (SUCCESS != alloc_temp_names(map->map_path, 651 &temp_entries, &temp_ttl)) { 652 logmsg(MSG_NOTIMECHECK, LOG_ERR, 653 "%s: Unable to create map names for %s", 654 myself, map->map_path); 655 return (FAILURE); 656 } 657 658 /* Create temp entry and TTL file */ 659 if ((temp_entries_db = dbm_open(temp_entries, O_RDWR | O_CREAT, 0644)) 660 == NULL) { 661 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s", 662 myself, temp_entries); 663 sfree(temp_entries); 664 sfree(temp_ttl); 665 return (FAILURE); 666 } 667 668 if ((temp_ttl_db = dbm_open(temp_ttl, O_RDWR | O_CREAT, 0644)) 669 == NULL) { 670 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s", 671 myself, temp_ttl); 672 dbm_close(temp_entries_db); 673 delete_map(temp_entries); 674 sfree(temp_entries); 675 sfree(temp_ttl); 676 return (FAILURE); 677 } 678 679 /* Initialize domainContext tsd */ 680 __nisdb_get_tsd()->domainContext = 0; 681 for (i = 0; i < ypDomains.numDomains; i++) { 682 if (0 == ypDomains.domainLabels[i]) 683 continue; 684 if (0 == strcasecmp(map->domain, ypDomains.domainLabels[i])) { 685 __nisdb_get_tsd()->domainContext = ypDomains.domains[i]; 686 break; 687 } 688 } 689 690 if (!(objname = getFullMapName(map->map_name, map->domain))) { 691 if (temp_entries_db) 692 dbm_close(temp_entries_db); 693 if (temp_ttl_db) 694 dbm_close(temp_ttl_db); 695 delete_map(temp_entries); 696 sfree(temp_entries); 697 delete_map(temp_ttl); 698 sfree(temp_ttl); 699 return (FAILURE); 700 } 701 702 /* 703 * set xlate_to_lcase to TRUE if map_name is found in 704 * xlate_to_lcase_maps[] 705 */ 706 m = 0; 707 while (xlate_to_lcase_maps[m] != NULL) { 708 if (strncmp(map->map_name, xlate_to_lcase_maps[m], 709 strlen(xlate_to_lcase_maps[m])) == 0) { 710 xlate_to_lcase = TRUE; 711 break; 712 } 713 ++m; 714 } 715 716 /* Try each mapping for the map */ 717 for (flag = 0; t != 0 && search_flag != FAILURE; t = t->next) { 718 719 /* Check if the mapping is the correct one */ 720 if (strcmp(objname, t->objName) != 0) { 721 continue; 722 } 723 724 /* Check if rulesFromLDAP are provided */ 725 if (t->numRulesFromLDAP == 0) { 726 logmsg(MSG_NOTIMECHECK, LOG_ERR, 727 "%s: No rulesFromLDAP available for %s (%s)", 728 myself, t->dbId, map->map_name); 729 continue; 730 } 731 732 /* Set flag to indicate update is enabled */ 733 flag = 1; 734 /* Create ldap request for enumeration */ 735 for (objectDN = t->objectDN; 736 objectDN && objectDN->read.base; 737 objectDN = objectDN->next) { 738 if ((ls = createLdapRequest(t, 0, 0, 1, NULL, 739 objectDN)) == 0) { 740 logmsg(MSG_NOTIMECHECK, LOG_ERR, 741 "%s: Failed to create " 742 "ldapSearch request for " 743 "%s (%s) for base %s", 744 myself, t->dbId, 745 map->map_name, 746 objectDN->read.base); 747 statP = FAILURE; 748 search_flag = FAILURE; 749 break; 750 } 751 752 if (log_flag) { 753 printf("Waiting for LDAP search results.\n"); 754 } 755 756 /* Query LDAP */ 757 nr = (ls->isDN)?0:-1; 758 rv = ldapSearch(ls, &nr, 0, &statP); 759 freeLdapSearch(ls); 760 if (rv == 0) { 761 if (statP == LDAP_NO_SUCH_OBJECT) { 762 /* 763 * No Entry exists in the ldap server. Not 764 * a problem. Maybe there are just no entries 765 * in this map. 766 */ 767 continue; 768 } 769 logmsg(MSG_NOTIMECHECK, LOG_ERR, 770 "%s: ldapSearch error %d " 771 "(%s) for %s (%s) for base %s", 772 myself, statP, ldap_err2string(statP), 773 t->dbId, map->map_name, 774 objectDN->read.base); 775 statP = FAILURE; 776 search_flag = FAILURE; 777 break; 778 } 779 780 if (log_flag) { 781 printf("Processing search results.\n"); 782 } 783 784 /* Obtain list of DNs for logging */ 785 if ((dn = findDNs(myself, rv, nr, 0, &numDNs)) == 0) { 786 statP = FAILURE; 787 search_flag = FAILURE; 788 break; 789 } 790 791 /* For each entry in the result do the following */ 792 for (i = 0; i < nr; i++) { 793 /* Convert LDAP data to NIS equivalents */ 794 statP = buildNISRuleValue(t, &rv[i], 795 map->domain); 796 if (statP == MAP_INDEXLIST_ERROR) 797 continue; 798 if (statP != SUCCESS) { 799 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 800 "%s: Conversion error %d (LDAP to " 801 "name=value pairs) " 802 "for (dn: %s) for " 803 "%s (%s) for base %s", 804 myself, statP, NIL(dn[i]), 805 t->dbId, map->map_name, 806 objectDN->read.base); 807 continue; 808 } 809 810 /* Obtain the datum for value */ 811 datval = ruleValueToDatum(t, &rv[i], &statP); 812 if (datval == 0) { 813 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 814 "%s: Conversion error %d " 815 "(name=value pairs to NIS)" 816 " for (dn: %s) for " 817 "%s (%s) for base %s", 818 myself, statP, NIL(dn[i]), 819 t->dbId, map->map_name, 820 objectDN->read.base); 821 continue; 822 } 823 824 /* Obtain the datum for key */ 825 datkey = getKeyFromRuleValue(t, &rv[i], 826 &nv, &statP, xlate_to_lcase); 827 if (datkey == 0) { 828 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 829 "%s: Unable to obtain NIS " 830 "key from LDAP data (dn:%s) " 831 "for %s (%s) for base %s", 832 myself, NIL(dn[i]), t->dbId, 833 map->map_name, 834 objectDN->read.base); 835 sfree(datval->dptr); 836 sfree(datval); 837 continue; 838 } 839 840 /* Write to the temporary map */ 841 for (j = 0; j < nv; j++, entry_count ++) { 842 if (datkey[j].dsize == 0) 843 continue; 844 errno = 0; 845 /* DBM_INSERT to match */ 846 /* singleReadFromDIT */ 847 if (dbm_store(temp_entries_db, 848 datkey[j], 849 *datval, 850 DBM_INSERT) < 0) { 851 /* 852 * For some cases errno may 853 * still be 0 but dbm_error 854 * isn't informative at all. 855 */ 856 logmsg(MSG_NOTIMECHECK, 857 LOG_WARNING, 858 "%s: dbm store error " 859 "(errno=%d) " 860 "for (key=%s, value=%s) " 861 "for %s (%s) for base %s", 862 myself, 863 errno, 864 datkey[j].dptr, 865 datval->dptr, t->dbId, 866 map->map_name, 867 objectDN->read.base); 868 /* clear the error */ 869 dbm_clearerr(temp_entries_db); 870 } 871 sfree(datkey[j].dptr); 872 873 if (log_flag && (entry_count >= 874 next_print)) { 875 printf("%d entries processed\n", 876 entry_count); 877 next_print *= 2; 878 } 879 880 } 881 sfree(datkey); 882 sfree(datval->dptr); 883 sfree(datval); 884 } 885 886 freeRuleValue(rv, nr); 887 freeDNs(dn, numDNs); 888 } /* End of for over objectDN */ 889 } 890 sfree(objname); 891 892 if (t != 0 || flag == 0 || search_flag == FAILURE) { 893 if (temp_entries_db) 894 dbm_close(temp_entries_db); 895 if (temp_ttl_db) 896 dbm_close(temp_ttl_db); 897 delete_map(temp_entries); 898 sfree(temp_entries); 899 delete_map(temp_ttl); 900 sfree(temp_ttl); 901 return (statP); 902 } 903 /* Set up enough of map_ctrl to call update_entry_ttl */ 904 temp_map.map_name = map->map_name; 905 temp_map.domain = map->domain; 906 temp_map.ttl = temp_ttl_db; 907 908 /* Generate new TTL file */ 909 key = dbm_firstkey(temp_entries_db); 910 while (key.dptr != 0) { 911 if (!is_special_key(&key)) 912 /* 913 * We don't want all the entries to time out at the 914 * same time so create random TTLs. 915 */ 916 if (FAILURE == update_entry_ttl(&temp_map, &key, 917 TTL_RAND)) 918 logmsg(MSG_NOTIMECHECK, LOG_ERR, 919 "%s: Could not update TTL for " 920 "(key=%s) for map %s,%s", 921 myself, NIL(key.dptr), map->map_name, 922 map->domain); 923 key = dbm_nextkey(temp_entries_db); 924 } 925 926 /* Update map TTL */ 927 if (SUCCESS != update_map_ttl(&temp_map)) { 928 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not update map TTL " 929 "for %s,%s", myself, map->map_name, map->domain); 930 } 931 932 /* Set up 'special' nis entries */ 933 add_special_entries(temp_entries_db, map, &secure_flag); 934 935 /* Close temp DBM files */ 936 dbm_close(temp_entries_db); 937 dbm_close(temp_ttl_db); 938 939 /* Lock access to the map for copy */ 940 lock_map_ctrl(map); 941 942 /* Move temp maps to real ones */ 943 rename_map(temp_entries, map->map_path, secure_flag); 944 rename_map(temp_ttl, map->ttl_path, secure_flag); 945 946 /* Free file names */ 947 sfree(temp_entries); 948 sfree(temp_ttl); 949 950 /* Unlock map */ 951 unlock_map_ctrl(map); 952 953 return (SUCCESS); 954 } 955 956 /* 957 * FUNCTION : get_mapping_map_list() 958 * 959 * DESCRIPTION: Gets a list of nis maps for a given domain specified in the 960 * mapping file. This information is not saved so have to go 961 * through the entire hash table. At least this is only done at 962 * initialization time. 963 * 964 * GIVEN : Domain name 965 * 966 * RETURNS : List of map names in malloced memory. MUST BE FREED BY CALLER. 967 */ 968 char ** 969 get_mapping_map_list(char *domain) 970 { 971 char *myself = "get_mapping_map_list"; 972 __nis_hash_item_mt *it; 973 int i, j, size; 974 char *end_ptr; 975 char **res; /* Result array */ 976 char **res_old; /* Old value of res during realloc */ 977 int array_size; /* Current malloced size */ 978 int res_count = 0; /* Current result count */ 979 980 /* 981 * Always need an array even if just for terminator. Normally one 982 * chunk will be enough. 983 */ 984 res = am(myself, ARRAY_CHUNK * sizeof (char *)); 985 if (NULL == res) 986 return (NULL); 987 array_size = ARRAY_CHUNK; 988 989 /* Work out hash table length */ 990 size = sizeof (ldapMappingList.keys) / sizeof (ldapMappingList.keys[0]); 991 /* For all hash table entries */ 992 for (i = 0; i < size; i++) { 993 /* Walk linked list for this hash table entry */ 994 for (it = ldapMappingList.keys[i]; NULL != it; it = it->next) { 995 996 /* Check it's not a split field entry */ 997 if (0 != ((__nis_table_mapping_t *)it)->numSplits) 998 continue; 999 1000 /* Check right domain (minus trailing dot) */ 1001 if (strlen(domain) >= strlen(it->name)) 1002 continue; 1003 end_ptr = it->name + strlen(it->name) - 1004 strlen(domain) - 1; 1005 if (',' != *(end_ptr - 1)) 1006 continue; 1007 if (0 != strncmp(end_ptr, domain, strlen(domain))) 1008 continue; 1009 1010 /* Check if we need to enlarge array */ 1011 if ((res_count + 1) >= array_size) { 1012 array_size += ARRAY_CHUNK; 1013 res_old = res; 1014 res = realloc(res, array_size * 1015 sizeof (char *)); 1016 if (NULL == res) { 1017 res_old[res_count] = NULL; 1018 free_passwd_list(res_old); 1019 return (NULL); 1020 } 1021 } 1022 1023 /* 1024 * We will need the sequence number when we come to 1025 * sort the entries so for now store a pointer to 1026 * the __nis_hash_item_mt. 1027 */ 1028 res[res_count] = (char *)it; 1029 res_count ++; 1030 } 1031 } 1032 1033 /* Terminate array */ 1034 res[res_count] = NULL; 1035 1036 /* Bubble sort entries into the same order as mapping file */ 1037 for (i = res_count - 2; 0 <= i; i--) { 1038 for (j = 0; j <= i; j++) { 1039 if (((__nis_table_mapping_t *)res[j + 1])->seq_num < 1040 ((__nis_table_mapping_t *)res[j])->seq_num) { 1041 end_ptr = res[j]; 1042 res[j] = res[j+1]; 1043 res[j + 1] = end_ptr; 1044 } 1045 } 1046 } 1047 1048 /* Finally copy the real strings in to each entry */ 1049 for (i = 0; NULL != res[i]; i ++) { 1050 1051 /* Get hash table entry back */ 1052 it = (__nis_hash_item_mt *)res[i]; 1053 1054 end_ptr = it->name + strlen(it->name) - strlen(domain) - 1; 1055 1056 /* What we really need is strndup() */ 1057 res[i] = am(myself, end_ptr - it->name + 1); 1058 if (NULL == res[i]) { 1059 free_map_list(res); 1060 return (NULL); 1061 } 1062 1063 /* Copy from start to end_ptr */ 1064 (void) memcpy(res[i], it->name, end_ptr-it->name - 1); 1065 } 1066 1067 return (res); 1068 } 1069 1070 /* 1071 * FUNCTION : make_nis_container() 1072 * 1073 * DESCRIPTION: Sets up container for map_name in the DIT. 1074 * 1075 * GIVEN : Map name 1076 * The domain name. 1077 * Flag indicating if container should be created. 1078 * 1079 * RETURNS : SUCCESS = It worked 1080 * FAILURE = There was a problem. 1081 */ 1082 suc_code 1083 make_nis_container(char *map_name, char *domain, bool_t init_containers) { 1084 int i, rc, statP = SUCCESS; 1085 __nis_table_mapping_t *t; 1086 char *dn; 1087 char *myself = "make_nis_container"; 1088 1089 if (!map_name || !domain) 1090 return (FAILURE); 1091 1092 if (FALSE == init_containers) { 1093 /* 1094 * If we are not creating containers it is debatable what we 1095 * should do . Maybe we should check for a pre- 1096 * existing container and return failure if it does not exist. 1097 * 1098 * For now we assume the user will not have called us in this 1099 * mode unless they know what they are doing. So return 1100 * success. If they have got it wrong then latter writes will 1101 * fail. 1102 */ 1103 return (SUCCESS); 1104 } 1105 1106 /* Get the mapping information for the map */ 1107 if ((t = mappingFromMap(map_name, domain, &statP)) == 0) { 1108 if (statP == MAP_NO_MAPPING_EXISTS) 1109 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1110 "%s: No mapping information available for %s,%s", 1111 myself, NIL(map_name), NIL(domain)); 1112 return (FAILURE); 1113 } 1114 1115 /* Two times. One for readDN and other for writeDN */ 1116 for (i = 0; i < 2; i++) { 1117 if (i == 0) 1118 dn = t->objectDN->read.base; 1119 else { 1120 if (t->objectDN->write.base == 0) { 1121 logmsg(MSG_NOTIMECHECK, LOG_INFO, 1122 "%s: No baseDN in writespec. Write " 1123 "disabled for %s,%s", 1124 myself, map_name, domain); 1125 break; 1126 } 1127 if (!strcasecmp(dn, t->objectDN->write.base)) 1128 break; 1129 dn = t->objectDN->write.base; 1130 } 1131 1132 if ((rc = makeNISObject(0, dn)) == FAILURE) { 1133 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1134 "%s: Unable to create ldap container (dn: %s) " 1135 "for %s,%s", myself, dn, map_name, domain); 1136 return (FAILURE); 1137 } 1138 } 1139 return (SUCCESS); 1140 } 1141 1142 /* 1143 * FUNCTION : make_nis_domain() 1144 * 1145 * DESCRIPTION: Sets up a nisDomainObject in the DIT 1146 * 1147 * GIVEN: Name of the domain 1148 * Flag indicating if domain should be create or possibly just 1149 * checked for. 1150 */ 1151 suc_code 1152 make_nis_domain(char *domain, bool_t init_containers) { 1153 1154 if (FALSE == init_containers) { 1155 /* 1156 * If we are not creating containers it is debatable what we 1157 * should do with domains. Maybe we should check for a pre- 1158 * existing domain and return failure if it does not exist. 1159 * 1160 * For now we assume the user will not have called us in this 1161 * mode unless they know what they are doing. So return 1162 * success. If they have got it wrong then latter writes will 1163 * fail. 1164 */ 1165 return (SUCCESS); 1166 } 1167 1168 /* Create the domain */ 1169 return (makeNISObject(domain, 0)); 1170 } 1171 1172 /* 1173 * FUNCTION: update_netgroup_byxxx() 1174 * 1175 * DESCRIPTION: Updates the netgroup.byxxx series of maps based on the current 1176 * netgroup file. We invoke revnetgroup so that if any changes 1177 * are made to this utility the same changes are made here. 1178 * 1179 * INPUTS: map_ctrl containing lots of information about the map and a 1180 * pointer to it's lock which will be required. 1181 * 1182 * OUTPUTS: SUCCESS = Map updated 1183 * FAILURE = Map not updated 1184 */ 1185 suc_code 1186 update_netgroup_byxxx(map_ctrl *map) { 1187 /* Name of temporary entries DBM file */ 1188 char *temp_entries; 1189 /* Name of temporary TTL DBM file */ 1190 char *temp_ttl; 1191 /* Temporary DBM handles */ 1192 DBM *temp_entries_db; 1193 DBM *temp_ttl_db; 1194 map_ctrl temp_map; 1195 char *myself = "update_netgroup_byxxx"; 1196 char *cmdbuf; 1197 int cmd_length; 1198 datum key; 1199 map_ctrl *netgroupmap; 1200 int res; 1201 /* Temporary revnetgroup files */ 1202 const char *byhost = NETGROUP_BYHOST "_REV" TEMP_POSTFIX; 1203 const char *byuser = NETGROUP_BYUSER "_REV" TEMP_POSTFIX; 1204 const char *temp_file_name; 1205 1206 1207 /* 1208 * We need to use two different temporary files: one for netgroup.byhost 1209 * and other for netgroup.byuser, since these two maps can be updated 1210 * simultaneously. These temporary files will hold the output of 1211 * revnetgroup [-h|-u] command. They are then used to generate the 1212 * corresponding dbm files and thereafter deleted. 1213 */ 1214 if (0 == strcmp(map->map_name, NETGROUP_BYHOST)) 1215 temp_file_name = byhost; 1216 else 1217 temp_file_name = byuser; 1218 1219 /* Alloc enough cmd buf for revnet cmd */ 1220 cmd_length = strlen("/usr/sbin/makedbm -u ") + 1221 (strlen(map->map_path) - strlen(map->map_name)) + 1222 strlen(NETGROUP_MAP) + 1223 strlen(" | /usr/sbin/revnetgroup -h > ") + 1224 (strlen(map->map_path) - strlen(map->map_name)) + 1225 strlen(temp_file_name) + 1; 1226 cmdbuf = am(myself, cmd_length); 1227 1228 if (NULL == cmdbuf) { 1229 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1230 "%s: Could not alloc cmdbuf.", myself); 1231 return (FAILURE); 1232 } 1233 1234 /* 1235 * If necessary update (and wait for) netgroups map. This is a lot of 1236 * work but if the netgroup map itself is not being accessed it may 1237 * contain information that is not up to date with the DIT. 1238 * 1239 * We use the cmdbuf to store the qualified netgroup map name there will 1240 * be enough space for this but we are not yet creating the cmd. 1241 */ 1242 strlcpy(cmdbuf, map->map_path, strlen(map->map_path) - 1243 strlen(map->map_name) + 1); 1244 strcat(cmdbuf, NETGROUP_MAP); 1245 netgroupmap = (map_ctrl *)shim_dbm_open(cmdbuf, 1246 O_RDWR | O_CREAT, 0644); 1247 if (NULL == netgroupmap) { 1248 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1249 "%s: Could not update %s.", myself, cmdbuf); 1250 sfree(cmdbuf); 1251 return (FAILURE); 1252 } 1253 1254 if (has_map_expired(netgroupmap)) { 1255 lock_map_ctrl(netgroupmap); 1256 update_map_if_required(netgroupmap, TRUE); 1257 unlock_map_ctrl(netgroupmap); 1258 } 1259 shim_dbm_close((DBM *)netgroupmap); 1260 1261 /* Dump netgroup file through revnetgroup to a temp file */ 1262 strcpy(cmdbuf, "/usr/sbin/makedbm -u "); 1263 1264 /* Unmake the netgroup file in same domain as map */ 1265 strncat(cmdbuf, map->map_path, strlen(map->map_path) - 1266 strlen(map->map_name)); 1267 strcat(cmdbuf, NETGROUP_MAP); 1268 1269 if (0 == strcmp(map->map_name, NETGROUP_BYHOST)) { 1270 strcat(cmdbuf, " | /usr/sbin/revnetgroup -h > "); 1271 } else { 1272 strcat(cmdbuf, " | /usr/sbin/revnetgroup -u > "); 1273 } 1274 1275 /* Create temp file file in same domain as map */ 1276 strncat(cmdbuf, map->map_path, strlen(map->map_path) - 1277 strlen(map->map_name)); 1278 strcat(cmdbuf, temp_file_name); 1279 1280 if (0 > system(cmdbuf)) { 1281 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not run \"%s\" " 1282 "(errno=%d)", myself, cmdbuf, errno); 1283 sfree(cmdbuf); 1284 return (FAILURE); 1285 } 1286 sfree(cmdbuf); 1287 1288 /* Allocate and set up names */ 1289 if (SUCCESS != alloc_temp_names(map->map_path, 1290 &temp_entries, &temp_ttl)) { 1291 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1292 "%s: Unable to create map names for %s", 1293 myself, map->map_path); 1294 return (FAILURE); 1295 } 1296 1297 /* Make the temporary DBM file */ 1298 cmdbuf = am(myself, strlen("/usr/sbin/makedbm") + 1299 (strlen(map->map_path) - strlen(map->map_name)) + 1300 strlen(temp_file_name) + 1301 strlen(temp_entries) + 3); 1302 if (NULL == cmdbuf) { 1303 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1304 "%s: Could not allocate cmdbuf.", myself); 1305 sfree(temp_entries); 1306 sfree(temp_ttl); 1307 return (FAILURE); 1308 } 1309 1310 strcpy(cmdbuf, "/usr/sbin/makedbm "); 1311 strncat(cmdbuf, map->map_path, strlen(map->map_path) - 1312 strlen(map->map_name)); 1313 strcat(cmdbuf, temp_file_name); 1314 strcat(cmdbuf, " "); 1315 strcat(cmdbuf, temp_entries); 1316 1317 if (0 > system(cmdbuf)) { 1318 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not run \"%s\" " 1319 "(errno=%d)", myself, cmdbuf, errno); 1320 sfree(cmdbuf); 1321 sfree(temp_entries); 1322 sfree(temp_ttl); 1323 return (FAILURE); 1324 } 1325 1326 /* Already have enough command buffer to rm temporary file */ 1327 strlcpy(cmdbuf, map->map_path, strlen(map->map_path) - 1328 strlen(map->map_name) + 1); 1329 strcat(cmdbuf, temp_file_name); 1330 res = unlink(cmdbuf); 1331 /* If the temp file did not exist no problem. Probably had no entries */ 1332 if ((0 != res) && (ENOENT != errno)) { 1333 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not delete \"%s\" " 1334 "(errno=%d)", myself, cmdbuf, errno); 1335 sfree(temp_entries); 1336 sfree(temp_ttl); 1337 sfree(cmdbuf); 1338 return (FAILURE); 1339 } 1340 sfree(cmdbuf); 1341 1342 if ((temp_entries_db = dbm_open(temp_entries, O_RDWR | O_CREAT, 0644)) 1343 == NULL) { 1344 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s", 1345 myself, temp_entries); 1346 sfree(temp_entries); 1347 sfree(temp_ttl); 1348 return (FAILURE); 1349 } 1350 1351 if ((temp_ttl_db = dbm_open(temp_ttl, O_RDWR | O_CREAT, 0644)) 1352 == NULL) { 1353 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s", 1354 myself, temp_ttl); 1355 dbm_close(temp_entries_db); 1356 sfree(temp_entries); 1357 sfree(temp_ttl); 1358 return (FAILURE); 1359 } 1360 1361 /* 1362 * Set up enough of map_ctrl to call update_entry_ttl. Since there is 1363 * no mapping, and thus not TTL, defined for these maps use the TTL 1364 * values for netgroup map 1365 */ 1366 temp_map.map_name = NETGROUP_MAP; 1367 temp_map.domain = map->domain; 1368 temp_map.ttl = temp_ttl_db; 1369 1370 /* 1371 * Generate new TTL file. Since these maps work only on the whole map 1372 * expiry these will not actually be used but there presence makes it 1373 * easier to handle these maps in the same way as other maps. 1374 */ 1375 key = dbm_firstkey(temp_entries_db); 1376 while (key.dptr != 0) { 1377 if (!is_special_key(&key)) 1378 /* 1379 * For these maps want all timouts to be maximum 1380 */ 1381 if (FAILURE == update_entry_ttl(&temp_map, &key, 1382 TTL_MAX)) 1383 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1384 "%s: Could not update TTL for " 1385 "(key=%s) for map %s,%s", 1386 myself, NIL(key.dptr), map->map_name, 1387 map->domain); 1388 key = dbm_nextkey(temp_entries_db); 1389 } 1390 1391 /* Update map TTL */ 1392 update_map_ttl(&temp_map); 1393 1394 /* Close temp DBM files */ 1395 dbm_close(temp_entries_db); 1396 dbm_close(temp_ttl_db); 1397 1398 /* Lock access to the map for copy */ 1399 lock_map_ctrl(map); 1400 1401 /* Move temp maps to real ones */ 1402 rename_map(temp_entries, map->map_path, FALSE); 1403 rename_map(temp_ttl, map->ttl_path, FALSE); 1404 1405 /* Free file names */ 1406 sfree(temp_entries); 1407 sfree(temp_ttl); 1408 1409 /* Unlock map */ 1410 unlock_map_ctrl(map); 1411 1412 return (SUCCESS); 1413 } 1414