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 /* 27 * DESCRIPTION: Contains functions relating to the creation and manipulation 28 * of map_ctrl structures. These are used to hold information 29 * specific to one NIS map. 30 * 31 * Because each of these contains a significant amount of state 32 * information about an individual map they are created (on the 33 * heap) when a map is opened and destroyed when it is closed. 34 * The overhead of doing this is less than maintaining a pool 35 * of map_ctrls. 36 * 37 * If two processes access the same map two map_ctrls will be 38 * created with similar contents (but differing DBM pointers). 39 * Both will have the same hash value so when one is locked 40 * access to the other will also be prevented. 41 */ 42 43 #include <unistd.h> 44 #include <stdlib.h> 45 #include <syslog.h> 46 #include <ndbm.h> 47 #include <string.h> 48 #include <sys/param.h> 49 #include "ypsym.h" 50 #include "ypdefs.h" 51 #include "shim.h" 52 #include "yptol.h" 53 #include "../ldap_util.h" 54 55 extern int hash(char *s); 56 extern bool_t add_map_domain_to_list(char *domain, char ***map_list); 57 58 /* 59 * Static variables for locking mechanism in 60 * N2L mode. 61 * map_id_list: hash table for map lists 62 * max_map: max number of maps in map_id_list 63 * it is also used as the map ID for 64 * unknown maps, see get_map_id() 65 * in usr/src/cmd/ypcmd/shared/lockmap.c 66 */ 67 static map_id_elt_t *map_id_list[MAXHASH]; 68 static int max_map = 0; 69 70 /* Switch on parts of ypdefs.h */ 71 USE_DBM 72 USE_YPDBPATH 73 74 /* 75 * FUNCTION: create_map_ctrl(); 76 * 77 * DESCRIPTION: Create and a new map_ctrl in a non opened state. 78 * 79 * INPUTS: Fully qualified map name 80 * 81 * OUTPUTS: Pointer to map_ctrl 82 * NULL on failure. 83 * 84 */ 85 map_ctrl * 86 create_map_ctrl(char *name) 87 { 88 char *myself = "create_map_ctrl"; 89 map_ctrl *map; 90 91 map = (map_ctrl *)am(myself, sizeof (map_ctrl)); 92 if (NULL == map) { 93 logmsg(MSG_NOTIMECHECK, LOG_ERR, "Could not alloc map_ctrl"); 94 return (NULL); 95 } 96 97 /* Clear new map (in case we have to free it) */ 98 map->entries = NULL; 99 map->hash_val = 0; 100 map->map_name = NULL; 101 map->domain = NULL; 102 map->map_path = NULL; 103 map->ttl = NULL; 104 map->ttl_path = NULL; 105 map->trad_map_path = NULL; 106 map->key_data.dptr = NULL; 107 map->open_mode = 0; 108 map->open_flags = 0; 109 110 /* 111 * Initialize the fields of the map_ctrl. By doing this once here we 112 * can save a lot of work as map entries are accessed. 113 */ 114 if (SUCCESS != map_ctrl_init(map, name)) { 115 logmsg(MSG_NOTIMECHECK, LOG_ERR, 116 "Could not initialize map_ctrl for %s", name); 117 free_map_ctrl(map); 118 return (NULL); 119 } 120 121 return (map); 122 } 123 124 /* 125 * FUNCTION : map_ctrl_init() 126 * 127 * DESCRIPTION: Initializes the fields of a map_ctrl structure. 128 * 129 * By doing this once (when the map_ctrl is created) we avoid 130 * numerous other function having to repeat this string 131 * manipulation. 132 * 133 * GIVEN : Pointer to the structure 134 * Fully qualified name of the map 135 * 136 * RETURNS : SUCCESS = map_ctrl fully set up. 137 * FAILURE = map_ctrl not set up CALLER MUST FREE. 138 */ 139 suc_code 140 map_ctrl_init(map_ctrl *map, char *name) 141 { 142 char *myself = "map_ctrl_init"; 143 char *p, *q; 144 145 /* Save map path for future reference */ 146 map->map_path = (char *)strdup(name); 147 if (NULL == map->map_path) { 148 logmsg(MSG_NOMEM, LOG_ERR, 149 "Could not duplicate map path %s", map); 150 return (FAILURE); 151 } 152 153 /* Work out map's unqualified name from path */ 154 p = strrchr(name, SEP_CHAR); 155 if (NULL == p) { 156 /* Must be at least a domain and name */ 157 logmsg(MSG_NOTIMECHECK, LOG_ERR, 158 "Could not find separator in map path %s", map); 159 return (FAILURE); 160 } 161 q = p + 1; 162 163 /* Check for and remove N2L prefix */ 164 if (yptol_mode) { 165 /* 166 * Check for and remove N2L prefix. If not found not a problem 167 * we open some old style maps during DIT initialization. 168 */ 169 if (0 == strncmp(q, NTOL_PREFIX, strlen(NTOL_PREFIX))) 170 q += strlen(NTOL_PREFIX); 171 } else { 172 if (0 == strncmp(q, NTOL_PREFIX, strlen(NTOL_PREFIX))) 173 logmsg(MSG_NOTIMECHECK, LOG_ERR, 174 "Working in non N2L mode and path %s " 175 "contains N2L prefix", name); 176 } 177 178 /* Save unqualified map name */ 179 map->map_name = strdup(q); 180 if (NULL == map->map_name) { 181 logmsg(MSG_NOMEM, LOG_ERR, 182 "Could not duplicate map name %s", q); 183 return (FAILURE); 184 } 185 186 /* Work out map's domain name from path */ 187 for (q = p-1; (SEP_CHAR != *q) && (q > name); q--); 188 189 if (q <= name) { 190 /* Didn't find separator */ 191 logmsg(MSG_NOTIMECHECK, LOG_ERR, 192 "Could not find domain in map path %s", name); 193 return (FAILURE); 194 } 195 196 map->domain = (char *)am(myself, p - q); 197 if (NULL == map->domain) { 198 logmsg(MSG_NOMEM, LOG_ERR, 199 "Could not alloc memory for domain in path %s", name); 200 return (FAILURE); 201 } 202 (void) strncpy(map->domain, q + 1, p-q-1); 203 map->domain[p-q-1] = '\0'; 204 205 /* Work out extra names required by N2L */ 206 if (yptol_mode) { 207 /* 208 * Work out what old style NIS path would have been. This is 209 * used to check for date of DBM file so add the DBM 210 * extension. 211 */ 212 map->trad_map_path = (char *)am(myself, strlen(map->map_name) + 213 + strlen(dbm_pag) + (p - name) + 2); 214 if (NULL == map->trad_map_path) { 215 logmsg(MSG_NOMEM, LOG_ERR, 216 "Could not alocate memory for " 217 "traditional map path derived from %s", name); 218 return (FAILURE); 219 } 220 221 strncpy(map->trad_map_path, name, p - name + 1); 222 map->trad_map_path[p - name + 1] = '\0'; 223 strcat(map->trad_map_path, map->map_name); 224 strcat(map->trad_map_path, dbm_pag); 225 226 /* Generate qualified TTL file name */ 227 map->ttl_path = (char *)am(myself, strlen(map->map_path) + 228 strlen(TTL_POSTFIX) + 1); 229 if (NULL == map->ttl_path) { 230 logmsg(MSG_NOMEM, LOG_ERR, 231 "Could not alocate memory for " 232 "ttl path derived from %s", name); 233 return (FAILURE); 234 } 235 236 strcpy(map->ttl_path, map->map_path); 237 strcat(map->ttl_path, TTL_POSTFIX); 238 } 239 240 /* Work out hash value */ 241 map->hash_val = hash(name); 242 243 /* Set up magic number */ 244 map->magic = MAP_MAGIC; 245 246 /* Null out pointers */ 247 map->entries = NULL; 248 map->ttl = NULL; 249 250 /* No key data yet */ 251 map->key_data.dptr = NULL; 252 map->key_data.dsize = 0; 253 254 return (SUCCESS); 255 } 256 257 /* 258 * FUNCTION: get_map_crtl(); 259 * 260 * DESCRIPTION: Find an existing map_ctrl for a map of a given DBM * (i.e. 261 * handle) . If none exists return an error. 262 * 263 * INPUTS: Map handle 264 * 265 * OUTPUTS: Pointer to map_ctrl 266 * NULL on failure. 267 * 268 */ 269 map_ctrl * 270 get_map_ctrl(DBM *db) 271 { 272 /* Check that this really is a map_ctrl not a DBM */ 273 if (((map_ctrl *)db)->magic != MAP_MAGIC) { 274 logmsg(MSG_NOTIMECHECK, LOG_ERR, 275 "SHIM called with DBM ptr not map_crtl ptr"); 276 return (NULL); 277 } 278 279 /* Since this is an opaque pointer just cast it */ 280 return ((map_ctrl *)db); 281 } 282 283 /* 284 * FUNCTION: dup_map_ctrl() 285 * 286 * DESCRIPTION: Duplicates a map_ctrl structure 287 * 288 * GIVEN : Map_ctrl to duplicate 289 * 290 * RETURNS : Pointer to a new malloced map_ctrl. CALLER MUST FREE 291 * NULL on failure. 292 */ 293 map_ctrl * 294 dup_map_ctrl(map_ctrl *old_map) 295 { 296 map_ctrl *new_map; 297 298 /* 299 * Could save a little bit of time by duplicating the static parts 300 * of the old map but on balance it is cleaner to just make a new one 301 * from scratch 302 */ 303 new_map = create_map_ctrl(old_map->map_path); 304 305 if (NULL == new_map) 306 return (NULL); 307 308 /* If old map had open handles duplicate them */ 309 if (NULL != old_map->entries) { 310 new_map->open_flags = old_map->open_flags; 311 new_map->open_mode = old_map->open_mode; 312 if (FAILURE == open_yptol_files(new_map)) { 313 free_map_ctrl(new_map); 314 return (NULL); 315 } 316 } 317 318 return (new_map); 319 } 320 321 /* 322 * FUNCTION: free_map_crtl(); 323 * 324 * DESCRIPTION: Free contents of a map_ctr structure and closed any open 325 * DBM files. 326 * 327 * INPUTS: Pointer to pointer to a map_ctrl. 328 * 329 * OUTPUTS: Nothing 330 * 331 */ 332 void 333 free_map_ctrl(map_ctrl *map) 334 { 335 336 if (NULL != map->entries) { 337 dbm_close(map->entries); 338 map->entries = NULL; 339 } 340 341 if (NULL != map->map_name) { 342 sfree(map->map_name); 343 map->map_name = NULL; 344 } 345 346 if (NULL != map->map_path) { 347 sfree(map->map_path); 348 map->map_path = NULL; 349 } 350 351 if (NULL != map->domain) { 352 sfree(map->domain); 353 map->domain = NULL; 354 } 355 356 if (yptol_mode) { 357 if (NULL != map->ttl) { 358 dbm_close(map->ttl); 359 map->ttl = NULL; 360 } 361 362 if (NULL != map->trad_map_path) { 363 sfree(map->trad_map_path); 364 map->trad_map_path = NULL; 365 } 366 367 if (NULL != map->ttl_path) { 368 sfree(map->ttl_path); 369 map->ttl_path = NULL; 370 } 371 372 if (NULL != map->key_data.dptr) { 373 sfree(map->key_data.dptr); 374 map->key_data.dptr = NULL; 375 map->key_data.dsize = 0; 376 } 377 } 378 379 map->magic = 0; 380 381 /* Since map_ctrls are now always in malloced memory */ 382 sfree(map); 383 384 } 385 386 /* 387 * FUNCTION : get_map_name() 388 * 389 * DESCRIPTION: Get the name of a map from its map_ctrl. This could be done 390 * as a simple dereference but this function hides the internal 391 * implementation of map_ctrl from higher layers. 392 * 393 * GIVEN : A map_ctrl pointer 394 * 395 * RETURNS : A pointer to the map_ctrl. Higher levels treat this as an 396 * opaque DBM pointer. 397 * NULL on failure. 398 */ 399 char * 400 get_map_name(DBM *db) 401 { 402 map_ctrl *map = (map_ctrl *)db; 403 404 if (NULL == map) 405 return (NULL); 406 407 return (map->map_name); 408 } 409 410 /* 411 * FUNCTION : set_key_data() 412 * 413 * DESCRIPTION: Sets up the key data freeing any that already exists. 414 * 415 * GIVEN : Pointer to the map_ctrl to set up. 416 * Datum containing the key. The dptr of this will be set to 417 * point to the key data. 418 * 419 * RETURNS : Nothing 420 */ 421 void 422 set_key_data(map_ctrl *map, datum *data) 423 { 424 char *myself = "set_key_data"; 425 426 /* 427 * Free up any existing key data. Because each dbm file can only have 428 * one enumeration going at a time this is safe. 429 */ 430 if (NULL != map->key_data.dptr) { 431 sfree(map->key_data.dptr); 432 map->key_data.dptr = NULL; 433 map->key_data.dsize = 0; 434 } 435 436 /* If nothing in key just return */ 437 if (NULL == data->dptr) 438 return; 439 440 /* Something is in the key so must duplicate out of static memory */ 441 map->key_data.dptr = (char *)am(myself, data->dsize); 442 if (NULL == map->key_data.dptr) { 443 logmsg(MSG_NOMEM, LOG_ERR, "Cannot alloc memory for key data"); 444 } else { 445 memcpy(map->key_data.dptr, data->dptr, data->dsize); 446 map->key_data.dsize = data->dsize; 447 } 448 449 /* Set datum to point to malloced version of the data */ 450 data->dptr = map->key_data.dptr; 451 452 return; 453 454 } 455 456 /* 457 * FUNCTION : open_yptol_files() 458 * 459 * DESCRIPTION: Opens both yptol files for a map. This is called both when a 460 * map is opened and when it is reopened as a result of an update 461 * operation. Must be called with map locked. 462 * 463 * GIVEN : Initialized map_ctrl 464 * 465 * RETURNS : SUCCESS = Maps opened 466 * FAILURE = Maps not opened (and mess tidied up) 467 */ 468 suc_code 469 open_yptol_files(map_ctrl *map) 470 { 471 472 /* Open entries map */ 473 map->entries = dbm_open(map->map_path, map->open_flags, map->open_mode); 474 475 if (NULL == map->entries) { 476 /* Maybe we were asked to open a non-existent map. No problem */ 477 return (FAILURE); 478 } 479 480 if (yptol_mode) { 481 /* Open TTLs map. Must always be writable */ 482 map->ttl = dbm_open(map->ttl_path, O_RDWR | O_CREAT, 0644); 483 if (NULL == map->ttl) { 484 logmsg(MSG_NOTIMECHECK, LOG_ERR, 485 "Cannot open TTL file %s", map->ttl_path); 486 dbm_close(map->entries); 487 map->entries = NULL; 488 return (FAILURE); 489 } 490 } 491 492 return (SUCCESS); 493 } 494 495 /* 496 * FUNCTION : insert_map_in_list() 497 * 498 * DESCRIPTION: add a map in map_id_list[] 499 * 500 * GIVEN : map name 501 * map unique ID 502 * 503 * RETURNS : SUCCESS = map added 504 * FAILURE = map not added 505 */ 506 suc_code 507 insert_map_in_list(char *map_name, int unique_value) 508 { 509 int index; 510 bool_t yptol_nl_sav = yptol_newlock; 511 map_id_elt_t *new_elt; 512 513 /* 514 * Index in the hash table is computed from the original 515 * hash function: make sure yptol_newlock is set to false. 516 */ 517 yptol_newlock = FALSE; 518 index = hash(map_name); 519 yptol_newlock = yptol_nl_sav; 520 521 new_elt = (map_id_elt_t *)calloc(1, sizeof (map_id_elt_t)); 522 if (new_elt == NULL) { 523 return (FAILURE); 524 } 525 new_elt->map_name = strdup(map_name); 526 if (new_elt->map_name == NULL) { /* strdup() failed */ 527 sfree(new_elt); 528 return (FAILURE); 529 } 530 new_elt->map_id = unique_value; 531 532 if (map_id_list[index] == NULL) { 533 new_elt->next = NULL; 534 } else { 535 new_elt->next = map_id_list[index]; 536 } 537 /* insert at begining */ 538 map_id_list[index] = new_elt; 539 540 return (SUCCESS); 541 } 542 543 #ifdef NISDB_LDAP_DEBUG 544 /* 545 * FUNCTION : dump_map_id_list() 546 * 547 * DESCRIPTION: display max_map and dump map_id_list[] 548 * not called, here for debug convenience only 549 * 550 * GIVEN : nothing 551 * 552 * RETURNS : nothing 553 */ 554 void 555 dump_map_id_list() 556 { 557 int i; 558 map_id_elt_t *cur_elt; 559 560 logmsg(MSG_NOTIMECHECK, LOG_DEBUG, 561 "dump_map_id_list: max_map is: %d, dumping map_idlist ...", 562 max_map); 563 564 for (i = 0; i < MAXHASH; i++) { 565 if (map_id_list[i] == NULL) { 566 logmsg(MSG_NOTIMECHECK, LOG_DEBUG, 567 "no map for index %d", i); 568 } else { 569 logmsg(MSG_NOTIMECHECK, LOG_DEBUG, 570 "index %d has the following maps", i); 571 cur_elt = map_id_list[i]; 572 do { 573 logmsg(MSG_NOTIMECHECK, LOG_DEBUG, 574 "%s, unique id: %d", 575 cur_elt->map_name, 576 cur_elt->map_id); 577 cur_elt = cur_elt->next; 578 } while (cur_elt != NULL); 579 } 580 } 581 } 582 #endif 583 584 /* 585 * FUNCTION : free_map_id_list() 586 * 587 * DESCRIPTION: free all previously allocated elements of map_id_list[] 588 * reset max_map to 0 589 * 590 * GIVEN : nothing 591 * 592 * RETURNS : nothing 593 */ 594 void 595 free_map_id_list() 596 { 597 int i; 598 map_id_elt_t *cur_elt, *next_elt; 599 600 for (i = 0; i < MAXHASH; i++) { 601 if (map_id_list[i] != NULL) { 602 cur_elt = map_id_list[i]; 603 do { 604 next_elt = cur_elt->next; 605 if (cur_elt->map_name) 606 sfree(cur_elt->map_name); 607 sfree(cur_elt); 608 cur_elt = next_elt; 609 } while (cur_elt != NULL); 610 map_id_list[i] = NULL; 611 } 612 } 613 max_map = 0; 614 } 615 616 /* 617 * FUNCTION : map_id_list_init() 618 * 619 * DESCRIPTION: initializes map_id_list[] and max_map 620 * 621 * GIVEN : nothing 622 * 623 * RETURNS : 0 if OK 624 * -1 if failure 625 */ 626 int 627 map_id_list_init() 628 { 629 char **domain_list, **map_list = NULL; 630 int domain_num; 631 int i, j; 632 char *myself = "map_id_list_init"; 633 char mapbuf[MAXPATHLEN]; 634 int mapbuf_len = sizeof (mapbuf); 635 int map_name_len; 636 int seqnum = 0; 637 int rc = 0; 638 639 for (i = 0; i < MAXHASH; i++) { 640 map_id_list[i] = NULL; 641 } 642 643 domain_num = get_mapping_domain_list(&domain_list); 644 for (i = 0; i < domain_num; i++) { 645 if (map_list) { 646 free_map_list(map_list); 647 map_list = NULL; 648 } 649 650 /* get map list from mapping file */ 651 map_list = get_mapping_map_list(domain_list[i]); 652 if (map_list == NULL) { 653 /* no map for this domain in mapping file */ 654 logmsg(MSG_NOTIMECHECK, LOG_DEBUG, 655 "%s: get_mapping_map_list()" 656 " found no map for domain %s", 657 myself, domain_list[i]); 658 } 659 660 /* add maps from /var/yp/<domain> */ 661 if (add_map_domain_to_list(domain_list[i], &map_list) == 662 FALSE) { 663 logmsg(MSG_NOTIMECHECK, LOG_ERR, 664 "%s: add_map_domain_to_list() failed", myself); 665 free_map_id_list(); 666 if (map_list) free_map_list(map_list); 667 return (-1); 668 } 669 670 if (map_list == NULL || map_list[0] == NULL) { 671 logmsg(MSG_NOTIMECHECK, LOG_DEBUG, 672 "%s: no map in domain %s", 673 myself, domain_list[i]); 674 continue; 675 } 676 677 for (j = 0; map_list[j] != NULL; j++) { 678 /* build long name */ 679 map_name_len = ypdbpath_sz + 1 + 680 strlen(domain_list[i]) + 1 + 681 strlen(NTOL_PREFIX) + 682 strlen(map_list[j]) + 1; 683 if (map_name_len > mapbuf_len) { 684 logmsg(MSG_NOTIMECHECK, LOG_ERR, 685 "%s: map name too long for %s", 686 " in domain %s", myself, map_list[j], 687 domain_list[i]); 688 free_map_id_list(); 689 if (map_list) free_map_list(map_list); 690 return (-1); 691 } 692 (void) memset(mapbuf, 0, mapbuf_len); 693 (void) snprintf(mapbuf, map_name_len, "%s%c%s%c%s%s", 694 ypdbpath, SEP_CHAR, domain_list[i], SEP_CHAR, 695 NTOL_PREFIX, map_list[j]); 696 697 if (insert_map_in_list(mapbuf, seqnum) 698 == FAILURE) { 699 logmsg(MSG_NOTIMECHECK, LOG_ERR, 700 "%s: failed to insert map %s", 701 " in domain %s", myself, map_list[j]); 702 free_map_id_list(); 703 if (map_list) free_map_list(map_list); 704 return (-1); 705 } 706 seqnum++; 707 } 708 } 709 710 max_map = seqnum; 711 712 #ifdef NISDB_LDAP_DEBUG 713 dump_map_id_list(); 714 #endif 715 716 /* 717 * If more maps than allocated spaces in shared memory, that's a failure 718 * probably need to free previously allocated memory if failure, 719 * before returning. 720 */ 721 if (max_map > MAXHASH) { 722 rc = -1; 723 logmsg(MSG_NOTIMECHECK, LOG_ERR, 724 "%s: too many maps (%d)", 725 myself, max_map); 726 free_map_id_list(); 727 } 728 if (map_list) free_map_list(map_list); 729 return (rc); 730 } 731 732 /* 733 * FUNCTION : get_list_max() 734 * 735 * DESCRIPTION: return references to static variables map_id_list 736 * and max_map; 737 * 738 * GIVEN : address for referencing map_id_list 739 * address for referencing max_map 740 * 741 * RETURNS : nothing 742 */ 743 void 744 get_list_max(map_id_elt_t ***list, int *max) 745 { 746 *list = map_id_list; 747 *max = max_map; 748 } 749