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 2008 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 int search_flag = SUCCESS; 617 618 if (!map || !map->map_name || !map->domain) { 619 return (FAILURE); 620 } 621 622 __nisdb_get_tsd()->escapeFlag = '\0'; 623 624 /* 625 * netgroup.byxxx maps are a special case. They are regenerated from 626 * the netgroup map, not the DIT, so handle special case. 627 */ 628 if ((0 == strcmp(map->map_name, NETGROUP_BYHOST)) || 629 0 == (strcmp(map->map_name, NETGROUP_BYUSER))) { 630 return (update_netgroup_byxxx(map)); 631 } 632 633 /* Get the mapping information for the map */ 634 if ((t = mappingFromMap(map->map_name, map->domain, &statP)) == 0) { 635 if (statP == MAP_NO_MAPPING_EXISTS) 636 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 637 "%s: No mapping information available for %s,%s", 638 myself, map->map_name, map->domain); 639 return (FAILURE); 640 } 641 642 /* Allocate and set up names */ 643 if (SUCCESS != alloc_temp_names(map->map_path, 644 &temp_entries, &temp_ttl)) { 645 logmsg(MSG_NOTIMECHECK, LOG_ERR, 646 "%s: Unable to create map names for %s", 647 myself, map->map_path); 648 return (FAILURE); 649 } 650 651 /* Create temp entry and TTL file */ 652 if ((temp_entries_db = dbm_open(temp_entries, O_RDWR | O_CREAT, 0644)) 653 == NULL) { 654 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s", 655 myself, temp_entries); 656 sfree(temp_entries); 657 sfree(temp_ttl); 658 return (FAILURE); 659 } 660 661 if ((temp_ttl_db = dbm_open(temp_ttl, O_RDWR | O_CREAT, 0644)) 662 == NULL) { 663 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s", 664 myself, temp_ttl); 665 dbm_close(temp_entries_db); 666 delete_map(temp_entries); 667 sfree(temp_entries); 668 sfree(temp_ttl); 669 return (FAILURE); 670 } 671 672 /* Initialize domainContext tsd */ 673 __nisdb_get_tsd()->domainContext = 0; 674 for (i = 0; i < ypDomains.numDomains; i++) { 675 if (0 == ypDomains.domainLabels[i]) 676 continue; 677 if (0 == strcasecmp(map->domain, ypDomains.domainLabels[i])) { 678 __nisdb_get_tsd()->domainContext = ypDomains.domains[i]; 679 break; 680 } 681 } 682 683 if (!(objname = getFullMapName(map->map_name, map->domain))) { 684 if (temp_entries_db) 685 dbm_close(temp_entries_db); 686 if (temp_ttl_db) 687 dbm_close(temp_ttl_db); 688 delete_map(temp_entries); 689 sfree(temp_entries); 690 delete_map(temp_ttl); 691 sfree(temp_ttl); 692 return (FAILURE); 693 } 694 695 /* Try each mapping for the map */ 696 for (flag = 0; t != 0 && search_flag != FAILURE; t = t->next) { 697 698 /* Check if the mapping is the correct one */ 699 if (strcmp(objname, t->objName) != 0) { 700 continue; 701 } 702 703 /* Check if rulesFromLDAP are provided */ 704 if (t->numRulesFromLDAP == 0) { 705 logmsg(MSG_NOTIMECHECK, LOG_ERR, 706 "%s: No rulesFromLDAP available for %s (%s)", 707 myself, t->dbId, map->map_name); 708 continue; 709 } 710 711 /* Set flag to indicate update is enabled */ 712 flag = 1; 713 /* Create ldap request for enumeration */ 714 for (objectDN = t->objectDN; 715 objectDN && objectDN->read.base; 716 objectDN = objectDN->next) { 717 if ((ls = createLdapRequest(t, 0, 0, 1, NULL, 718 objectDN)) == 0) { 719 logmsg(MSG_NOTIMECHECK, LOG_ERR, 720 "%s: Failed to create " 721 "ldapSearch request for " 722 "%s (%s) for base %s", 723 myself, t->dbId, 724 map->map_name, 725 objectDN->read.base); 726 statP = FAILURE; 727 search_flag = FAILURE; 728 break; 729 } 730 731 if (log_flag) { 732 printf("Waiting for LDAP search results.\n"); 733 } 734 735 /* Query LDAP */ 736 nr = (ls->isDN)?0:-1; 737 rv = ldapSearch(ls, &nr, 0, &statP); 738 freeLdapSearch(ls); 739 if (rv == 0) { 740 if (statP == LDAP_NO_SUCH_OBJECT) { 741 /* 742 * No Entry exists in the ldap server. Not 743 * a problem. Maybe there are just no entries 744 * in this map. 745 */ 746 continue; 747 } 748 logmsg(MSG_NOTIMECHECK, LOG_ERR, 749 "%s: ldapSearch error %d " 750 "(%s) for %s (%s) for base %s", 751 myself, statP, ldap_err2string(statP), 752 t->dbId, map->map_name, 753 objectDN->read.base); 754 statP = FAILURE; 755 search_flag = FAILURE; 756 break; 757 } 758 759 if (log_flag) { 760 printf("Processing search results.\n"); 761 } 762 763 /* Obtain list of DNs for logging */ 764 if ((dn = findDNs(myself, rv, nr, 0, &numDNs)) == 0) { 765 statP = FAILURE; 766 search_flag = FAILURE; 767 break; 768 } 769 770 /* For each entry in the result do the following */ 771 for (i = 0; i < nr; i++) { 772 /* Convert LDAP data to NIS equivalents */ 773 statP = buildNISRuleValue(t, &rv[i], 774 map->domain); 775 if (statP == MAP_INDEXLIST_ERROR) 776 continue; 777 if (statP != SUCCESS) { 778 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 779 "%s: Conversion error %d (LDAP to " 780 "name=value pairs) " 781 "for (dn: %s) for " 782 "%s (%s) for base %s", 783 myself, statP, NIL(dn[i]), 784 t->dbId, map->map_name, 785 objectDN->read.base); 786 continue; 787 } 788 789 /* Obtain the datum for value */ 790 datval = ruleValueToDatum(t, &rv[i], &statP); 791 if (datval == 0) { 792 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 793 "%s: Conversion error %d " 794 "(name=value pairs to NIS)" 795 " for (dn: %s) for " 796 "%s (%s) for base %s", 797 myself, statP, NIL(dn[i]), 798 t->dbId, map->map_name, 799 objectDN->read.base); 800 continue; 801 } 802 803 /* Obtain the datum for key */ 804 datkey = getKeyFromRuleValue(t, &rv[i], 805 &nv, &statP); 806 if (datkey == 0) { 807 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 808 "%s: Unable to obtain NIS " 809 "key from LDAP data (dn:%s) " 810 "for %s (%s) for base %s", 811 myself, NIL(dn[i]), t->dbId, 812 map->map_name, 813 objectDN->read.base); 814 sfree(datval->dptr); 815 sfree(datval); 816 continue; 817 } 818 819 /* Write to the temporary map */ 820 for (j = 0; j < nv; j++, entry_count ++) { 821 if (datkey[j].dsize == 0) 822 continue; 823 errno = 0; 824 /* DBM_INSERT to match */ 825 /* singleReadFromDIT */ 826 if (dbm_store(temp_entries_db, 827 datkey[j], 828 *datval, 829 DBM_INSERT) < 0) { 830 /* 831 * For some cases errno may 832 * still be 0 but dbm_error 833 * isn't informative at all. 834 */ 835 logmsg(MSG_NOTIMECHECK, 836 LOG_WARNING, 837 "%s: dbm store error " 838 "(errno=%d) " 839 "for (key=%s, value=%s) " 840 "for %s (%s) for base %s", 841 myself, 842 errno, 843 datkey[j].dptr, 844 datval->dptr, t->dbId, 845 map->map_name, 846 objectDN->read.base); 847 /* clear the error */ 848 dbm_clearerr(temp_entries_db); 849 } 850 sfree(datkey[j].dptr); 851 852 if (log_flag && (entry_count >= 853 next_print)) { 854 printf("%d entries processed\n", 855 entry_count); 856 next_print *= 2; 857 } 858 859 } 860 sfree(datkey); 861 sfree(datval->dptr); 862 sfree(datval); 863 } 864 865 freeRuleValue(rv, nr); 866 freeDNs(dn, numDNs); 867 } /* End of for over objectDN */ 868 } 869 sfree(objname); 870 871 if (t != 0 || flag == 0 || search_flag == FAILURE) { 872 if (temp_entries_db) 873 dbm_close(temp_entries_db); 874 if (temp_ttl_db) 875 dbm_close(temp_ttl_db); 876 delete_map(temp_entries); 877 sfree(temp_entries); 878 delete_map(temp_ttl); 879 sfree(temp_ttl); 880 return (statP); 881 } 882 /* Set up enough of map_ctrl to call update_entry_ttl */ 883 temp_map.map_name = map->map_name; 884 temp_map.domain = map->domain; 885 temp_map.ttl = temp_ttl_db; 886 887 /* Generate new TTL file */ 888 key = dbm_firstkey(temp_entries_db); 889 while (key.dptr != 0) { 890 if (!is_special_key(&key)) 891 /* 892 * We don't want all the entries to time out at the 893 * same time so create random TTLs. 894 */ 895 if (FAILURE == update_entry_ttl(&temp_map, &key, 896 TTL_RAND)) 897 logmsg(MSG_NOTIMECHECK, LOG_ERR, 898 "%s: Could not update TTL for " 899 "(key=%s) for map %s,%s", 900 myself, NIL(key.dptr), map->map_name, 901 map->domain); 902 key = dbm_nextkey(temp_entries_db); 903 } 904 905 /* Update map TTL */ 906 if (SUCCESS != update_map_ttl(&temp_map)) { 907 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not update map TTL " 908 "for %s,%s", myself, map->map_name, map->domain); 909 } 910 911 /* Set up 'special' nis entries */ 912 add_special_entries(temp_entries_db, map, &secure_flag); 913 914 /* Close temp DBM files */ 915 dbm_close(temp_entries_db); 916 dbm_close(temp_ttl_db); 917 918 /* Lock access to the map for copy */ 919 lock_map_ctrl(map); 920 921 /* Move temp maps to real ones */ 922 rename_map(temp_entries, map->map_path, secure_flag); 923 rename_map(temp_ttl, map->ttl_path, secure_flag); 924 925 /* Free file names */ 926 sfree(temp_entries); 927 sfree(temp_ttl); 928 929 /* Unlock map */ 930 unlock_map_ctrl(map); 931 932 return (SUCCESS); 933 } 934 935 /* 936 * FUNCTION : get_mapping_map_list() 937 * 938 * DESCRIPTION: Gets a list of nis maps for a given domain specified in the 939 * mapping file. This information is not saved so have to go 940 * through the entire hash table. At least this is only done at 941 * initialization time. 942 * 943 * GIVEN : Domain name 944 * 945 * RETURNS : List of map names in malloced memory. MUST BE FREED BY CALLER. 946 */ 947 char ** 948 get_mapping_map_list(char *domain) 949 { 950 char *myself = "get_mapping_map_list"; 951 __nis_hash_item_mt *it; 952 int i, j, size; 953 char *end_ptr; 954 char **res; /* Result array */ 955 char **res_old; /* Old value of res during realloc */ 956 int array_size; /* Current malloced size */ 957 int res_count = 0; /* Current result count */ 958 959 /* 960 * Always need an array even if just for terminator. Normally one 961 * chunk will be enough. 962 */ 963 res = am(myself, ARRAY_CHUNK * sizeof (char *)); 964 if (NULL == res) 965 return (NULL); 966 array_size = ARRAY_CHUNK; 967 968 /* Work out hash table length */ 969 size = sizeof (ldapMappingList.keys) / sizeof (ldapMappingList.keys[0]); 970 /* For all hash table entries */ 971 for (i = 0; i < size; i++) { 972 /* Walk linked list for this hash table entry */ 973 for (it = ldapMappingList.keys[i]; NULL != it; it = it->next) { 974 975 /* Check it's not a split field entry */ 976 if (0 != ((__nis_table_mapping_t *)it)->numSplits) 977 continue; 978 979 /* Check right domain (minus trailing dot) */ 980 if (strlen(domain) >= strlen(it->name)) 981 continue; 982 end_ptr = it->name + strlen(it->name) - 983 strlen(domain) - 1; 984 if (',' != *(end_ptr - 1)) 985 continue; 986 if (0 != strncmp(end_ptr, domain, strlen(domain))) 987 continue; 988 989 /* Check if we need to enlarge array */ 990 if ((res_count + 1) >= array_size) { 991 array_size += ARRAY_CHUNK; 992 res_old = res; 993 res = realloc(res, array_size * 994 sizeof (char *)); 995 if (NULL == res) { 996 res_old[res_count] = NULL; 997 free_passwd_list(res_old); 998 return (NULL); 999 } 1000 } 1001 1002 /* 1003 * We will need the sequence number when we come to 1004 * sort the entries so for now store a pointer to 1005 * the __nis_hash_item_mt. 1006 */ 1007 res[res_count] = (char *)it; 1008 res_count ++; 1009 } 1010 } 1011 1012 /* Terminate array */ 1013 res[res_count] = NULL; 1014 1015 /* Bubble sort entries into the same order as mapping file */ 1016 for (i = res_count - 2; 0 <= i; i--) { 1017 for (j = 0; j <= i; j++) { 1018 if (((__nis_table_mapping_t *)res[j + 1])->seq_num < 1019 ((__nis_table_mapping_t *)res[j])->seq_num) { 1020 end_ptr = res[j]; 1021 res[j] = res[j+1]; 1022 res[j + 1] = end_ptr; 1023 } 1024 } 1025 } 1026 1027 /* Finally copy the real strings in to each entry */ 1028 for (i = 0; NULL != res[i]; i ++) { 1029 1030 /* Get hash table entry back */ 1031 it = (__nis_hash_item_mt *)res[i]; 1032 1033 end_ptr = it->name + strlen(it->name) - strlen(domain) - 1; 1034 1035 /* What we really need is strndup() */ 1036 res[i] = am(myself, end_ptr - it->name + 1); 1037 if (NULL == res[i]) { 1038 free_map_list(res); 1039 return (NULL); 1040 } 1041 1042 /* Copy from start to end_ptr */ 1043 (void) memcpy(res[i], it->name, end_ptr-it->name - 1); 1044 } 1045 1046 return (res); 1047 } 1048 1049 /* 1050 * FUNCTION : make_nis_container() 1051 * 1052 * DESCRIPTION: Sets up container for map_name in the DIT. 1053 * 1054 * GIVEN : Map name 1055 * The domain name. 1056 * Flag indicating if container should be created. 1057 * 1058 * RETURNS : SUCCESS = It worked 1059 * FAILURE = There was a problem. 1060 */ 1061 suc_code 1062 make_nis_container(char *map_name, char *domain, bool_t init_containers) { 1063 int i, rc, statP = SUCCESS; 1064 __nis_table_mapping_t *t; 1065 char *dn; 1066 char *myself = "make_nis_container"; 1067 1068 if (!map_name || !domain) 1069 return (FAILURE); 1070 1071 if (FALSE == init_containers) { 1072 /* 1073 * If we are not creating containers it is debatable what we 1074 * should do . Maybe we should check for a pre- 1075 * existing container and return failure if it does not exist. 1076 * 1077 * For now we assume the user will not have called us in this 1078 * mode unless they know what they are doing. So return 1079 * success. If they have got it wrong then latter writes will 1080 * fail. 1081 */ 1082 return (SUCCESS); 1083 } 1084 1085 /* Get the mapping information for the map */ 1086 if ((t = mappingFromMap(map_name, domain, &statP)) == 0) { 1087 if (statP == MAP_NO_MAPPING_EXISTS) 1088 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1089 "%s: No mapping information available for %s,%s", 1090 myself, NIL(map_name), NIL(domain)); 1091 return (FAILURE); 1092 } 1093 1094 /* Two times. One for readDN and other for writeDN */ 1095 for (i = 0; i < 2; i++) { 1096 if (i == 0) 1097 dn = t->objectDN->read.base; 1098 else { 1099 if (t->objectDN->write.base == 0) { 1100 logmsg(MSG_NOTIMECHECK, LOG_INFO, 1101 "%s: No baseDN in writespec. Write " 1102 "disabled for %s,%s", 1103 myself, map_name, domain); 1104 break; 1105 } 1106 if (!strcasecmp(dn, t->objectDN->write.base)) 1107 break; 1108 dn = t->objectDN->write.base; 1109 } 1110 1111 if ((rc = makeNISObject(0, dn)) == FAILURE) { 1112 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1113 "%s: Unable to create ldap container (dn: %s) " 1114 "for %s,%s", myself, dn, map_name, domain); 1115 return (FAILURE); 1116 } 1117 } 1118 return (SUCCESS); 1119 } 1120 1121 /* 1122 * FUNCTION : make_nis_domain() 1123 * 1124 * DESCRIPTION: Sets up a nisDomainObject in the DIT 1125 * 1126 * GIVEN: Name of the domain 1127 * Flag indicating if domain should be create or possibly just 1128 * checked for. 1129 */ 1130 suc_code 1131 make_nis_domain(char *domain, bool_t init_containers) { 1132 1133 if (FALSE == init_containers) { 1134 /* 1135 * If we are not creating containers it is debatable what we 1136 * should do with domains. Maybe we should check for a pre- 1137 * existing domain and return failure if it does not exist. 1138 * 1139 * For now we assume the user will not have called us in this 1140 * mode unless they know what they are doing. So return 1141 * success. If they have got it wrong then latter writes will 1142 * fail. 1143 */ 1144 return (SUCCESS); 1145 } 1146 1147 /* Create the domain */ 1148 return (makeNISObject(domain, 0)); 1149 } 1150 1151 /* 1152 * FUNCTION: update_netgroup_byxxx() 1153 * 1154 * DESCRIPTION: Updates the netgroup.byxxx series of maps based on the current 1155 * netgroup file. We invoke revnetgroup so that if any changes 1156 * are made to this utility the same changes are made here. 1157 * 1158 * INPUTS: map_ctrl containing lots of information about the map and a 1159 * pointer to it's lock which will be required. 1160 * 1161 * OUTPUTS: SUCCESS = Map updated 1162 * FAILURE = Map not updated 1163 */ 1164 suc_code 1165 update_netgroup_byxxx(map_ctrl *map) { 1166 /* Name of temporary entries DBM file */ 1167 char *temp_entries; 1168 /* Name of temporary TTL DBM file */ 1169 char *temp_ttl; 1170 /* Temporary DBM handles */ 1171 DBM *temp_entries_db; 1172 DBM *temp_ttl_db; 1173 map_ctrl temp_map; 1174 char *myself = "update_netgroup_byxxx"; 1175 char *cmdbuf; 1176 int cmd_length; 1177 datum key; 1178 map_ctrl *netgroupmap; 1179 int res; 1180 /* Temporary revnetgroup files */ 1181 const char *byhost = NETGROUP_BYHOST "_REV" TEMP_POSTFIX; 1182 const char *byuser = NETGROUP_BYUSER "_REV" TEMP_POSTFIX; 1183 const char *temp_file_name; 1184 1185 1186 /* 1187 * We need to use two different temporary files: one for netgroup.byhost 1188 * and other for netgroup.byuser, since these two maps can be updated 1189 * simultaneously. These temporary files will hold the output of 1190 * revnetgroup [-h|-u] command. They are then used to generate the 1191 * corresponding dbm files and thereafter deleted. 1192 */ 1193 if (0 == strcmp(map->map_name, NETGROUP_BYHOST)) 1194 temp_file_name = byhost; 1195 else 1196 temp_file_name = byuser; 1197 1198 /* Alloc enough cmd buf for revnet cmd */ 1199 cmd_length = strlen("/usr/sbin/makedbm -u ") + 1200 (strlen(map->map_path) - strlen(map->map_name)) + 1201 strlen(NETGROUP_MAP) + 1202 strlen(" | /usr/sbin/revnetgroup -h > ") + 1203 (strlen(map->map_path) - strlen(map->map_name)) + 1204 strlen(temp_file_name) + 1; 1205 cmdbuf = am(myself, cmd_length); 1206 1207 if (NULL == cmdbuf) { 1208 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1209 "%s: Could not alloc cmdbuf.", myself); 1210 return (FAILURE); 1211 } 1212 1213 /* 1214 * If necessary update (and wait for) netgroups map. This is a lot of 1215 * work but if the netgroup map itself is not being accessed it may 1216 * contain information that is not up to date with the DIT. 1217 * 1218 * We use the cmdbuf to store the qualified netgroup map name there will 1219 * be enough space for this but we are not yet creating the cmd. 1220 */ 1221 strlcpy(cmdbuf, map->map_path, strlen(map->map_path) - 1222 strlen(map->map_name) + 1); 1223 strcat(cmdbuf, NETGROUP_MAP); 1224 netgroupmap = (map_ctrl *)shim_dbm_open(cmdbuf, 1225 O_RDWR | O_CREAT, 0644); 1226 if (NULL == netgroupmap) { 1227 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1228 "%s: Could not update %s.", myself, cmdbuf); 1229 sfree(cmdbuf); 1230 return (FAILURE); 1231 } 1232 1233 if (has_map_expired(netgroupmap)) { 1234 lock_map_ctrl(netgroupmap); 1235 update_map_if_required(netgroupmap, TRUE); 1236 unlock_map_ctrl(netgroupmap); 1237 } 1238 shim_dbm_close((DBM *)netgroupmap); 1239 1240 /* Dump netgroup file through revnetgroup to a temp file */ 1241 strcpy(cmdbuf, "/usr/sbin/makedbm -u "); 1242 1243 /* Unmake the netgroup file in same domain as map */ 1244 strncat(cmdbuf, map->map_path, strlen(map->map_path) - 1245 strlen(map->map_name)); 1246 strcat(cmdbuf, NETGROUP_MAP); 1247 1248 if (0 == strcmp(map->map_name, NETGROUP_BYHOST)) { 1249 strcat(cmdbuf, " | /usr/sbin/revnetgroup -h > "); 1250 } else { 1251 strcat(cmdbuf, " | /usr/sbin/revnetgroup -u > "); 1252 } 1253 1254 /* Create temp file file in same domain as map */ 1255 strncat(cmdbuf, map->map_path, strlen(map->map_path) - 1256 strlen(map->map_name)); 1257 strcat(cmdbuf, temp_file_name); 1258 1259 if (0 > system(cmdbuf)) { 1260 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not run \"%s\" " 1261 "(errno=%d)", myself, cmdbuf, errno); 1262 sfree(cmdbuf); 1263 return (FAILURE); 1264 } 1265 sfree(cmdbuf); 1266 1267 /* Allocate and set up names */ 1268 if (SUCCESS != alloc_temp_names(map->map_path, 1269 &temp_entries, &temp_ttl)) { 1270 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1271 "%s: Unable to create map names for %s", 1272 myself, map->map_path); 1273 return (FAILURE); 1274 } 1275 1276 /* Make the temporary DBM file */ 1277 cmdbuf = am(myself, strlen("/usr/sbin/makedbm") + 1278 (strlen(map->map_path) - strlen(map->map_name)) + 1279 strlen(temp_file_name) + 1280 strlen(temp_entries) + 3); 1281 if (NULL == cmdbuf) { 1282 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1283 "%s: Could not allocate cmdbuf.", myself); 1284 sfree(temp_entries); 1285 sfree(temp_ttl); 1286 return (FAILURE); 1287 } 1288 1289 strcpy(cmdbuf, "/usr/sbin/makedbm "); 1290 strncat(cmdbuf, map->map_path, strlen(map->map_path) - 1291 strlen(map->map_name)); 1292 strcat(cmdbuf, temp_file_name); 1293 strcat(cmdbuf, " "); 1294 strcat(cmdbuf, temp_entries); 1295 1296 if (0 > system(cmdbuf)) { 1297 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not run \"%s\" " 1298 "(errno=%d)", myself, cmdbuf, errno); 1299 sfree(cmdbuf); 1300 sfree(temp_entries); 1301 sfree(temp_ttl); 1302 return (FAILURE); 1303 } 1304 1305 /* Already have enough command buffer to rm temporary file */ 1306 strlcpy(cmdbuf, map->map_path, strlen(map->map_path) - 1307 strlen(map->map_name) + 1); 1308 strcat(cmdbuf, temp_file_name); 1309 res = unlink(cmdbuf); 1310 /* If the temp file did not exist no problem. Probably had no entries */ 1311 if ((0 != res) && (ENOENT != errno)) { 1312 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not delete \"%s\" " 1313 "(errno=%d)", myself, cmdbuf, errno); 1314 sfree(temp_entries); 1315 sfree(temp_ttl); 1316 sfree(cmdbuf); 1317 return (FAILURE); 1318 } 1319 sfree(cmdbuf); 1320 1321 if ((temp_entries_db = dbm_open(temp_entries, O_RDWR | O_CREAT, 0644)) 1322 == NULL) { 1323 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s", 1324 myself, temp_entries); 1325 sfree(temp_entries); 1326 sfree(temp_ttl); 1327 return (FAILURE); 1328 } 1329 1330 if ((temp_ttl_db = dbm_open(temp_ttl, O_RDWR | O_CREAT, 0644)) 1331 == NULL) { 1332 logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s", 1333 myself, temp_ttl); 1334 dbm_close(temp_entries_db); 1335 sfree(temp_entries); 1336 sfree(temp_ttl); 1337 return (FAILURE); 1338 } 1339 1340 /* 1341 * Set up enough of map_ctrl to call update_entry_ttl. Since there is 1342 * no mapping, and thus not TTL, defined for these maps use the TTL 1343 * values for netgroup map 1344 */ 1345 temp_map.map_name = NETGROUP_MAP; 1346 temp_map.domain = map->domain; 1347 temp_map.ttl = temp_ttl_db; 1348 1349 /* 1350 * Generate new TTL file. Since these maps work only on the whole map 1351 * expiry these will not actually be used but there presence makes it 1352 * easier to handle these maps in the same way as other maps. 1353 */ 1354 key = dbm_firstkey(temp_entries_db); 1355 while (key.dptr != 0) { 1356 if (!is_special_key(&key)) 1357 /* 1358 * For these maps want all timouts to be maximum 1359 */ 1360 if (FAILURE == update_entry_ttl(&temp_map, &key, 1361 TTL_MAX)) 1362 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1363 "%s: Could not update TTL for " 1364 "(key=%s) for map %s,%s", 1365 myself, NIL(key.dptr), map->map_name, 1366 map->domain); 1367 key = dbm_nextkey(temp_entries_db); 1368 } 1369 1370 /* Update map TTL */ 1371 update_map_ttl(&temp_map); 1372 1373 /* Close temp DBM files */ 1374 dbm_close(temp_entries_db); 1375 dbm_close(temp_ttl_db); 1376 1377 /* Lock access to the map for copy */ 1378 lock_map_ctrl(map); 1379 1380 /* Move temp maps to real ones */ 1381 rename_map(temp_entries, map->map_path, FALSE); 1382 rename_map(temp_ttl, map->ttl_path, FALSE); 1383 1384 /* Free file names */ 1385 sfree(temp_entries); 1386 sfree(temp_ttl); 1387 1388 /* Unlock map */ 1389 unlock_map_ctrl(map); 1390 1391 return (SUCCESS); 1392 } 1393