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