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