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