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 *
create_map_ctrl(char * name)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
map_ctrl_init(map_ctrl * map,char * name)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 *
get_map_ctrl(DBM * db)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 *
dup_map_ctrl(map_ctrl * old_map)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
free_map_ctrl(map_ctrl * map)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 *
get_map_name(DBM * db)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
set_key_data(map_ctrl * map,datum * data)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
open_yptol_files(map_ctrl * map)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
insert_map_in_list(char * map_name,int unique_value)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 sfree(new_elt);
530 return (FAILURE);
531 }
532 new_elt->map_id = unique_value;
533
534 if (map_id_list[index] == NULL) {
535 new_elt->next = NULL;
536 } else {
537 new_elt->next = map_id_list[index];
538 }
539 /* insert at begining */
540 map_id_list[index] = new_elt;
541
542 return (SUCCESS);
543 }
544
545 #ifdef NISDB_LDAP_DEBUG
546 /*
547 * FUNCTION : dump_map_id_list()
548 *
549 * DESCRIPTION: display max_map and dump map_id_list[]
550 * not called, here for debug convenience only
551 *
552 * GIVEN : nothing
553 *
554 * RETURNS : nothing
555 */
556 void
dump_map_id_list()557 dump_map_id_list()
558 {
559 int i;
560 map_id_elt_t *cur_elt;
561
562 logmsg(MSG_NOTIMECHECK, LOG_DEBUG,
563 "dump_map_id_list: max_map is: %d, dumping map_idlist ...",
564 max_map);
565
566 for (i = 0; i < MAXHASH; i++) {
567 if (map_id_list[i] == NULL) {
568 logmsg(MSG_NOTIMECHECK, LOG_DEBUG,
569 "no map for index %d", i);
570 } else {
571 logmsg(MSG_NOTIMECHECK, LOG_DEBUG,
572 "index %d has the following maps", i);
573 cur_elt = map_id_list[i];
574 do {
575 logmsg(MSG_NOTIMECHECK, LOG_DEBUG,
576 "%s, unique id: %d",
577 cur_elt->map_name,
578 cur_elt->map_id);
579 cur_elt = cur_elt->next;
580 } while (cur_elt != NULL);
581 }
582 }
583 }
584 #endif
585
586 /*
587 * FUNCTION : free_map_id_list()
588 *
589 * DESCRIPTION: free all previously allocated elements of map_id_list[]
590 * reset max_map to 0
591 *
592 * GIVEN : nothing
593 *
594 * RETURNS : nothing
595 */
596 void
free_map_id_list()597 free_map_id_list()
598 {
599 int i;
600 map_id_elt_t *cur_elt, *next_elt;
601
602 for (i = 0; i < MAXHASH; i++) {
603 if (map_id_list[i] != NULL) {
604 cur_elt = map_id_list[i];
605 do {
606 next_elt = cur_elt->next;
607 if (cur_elt->map_name)
608 sfree(cur_elt->map_name);
609 sfree(cur_elt);
610 cur_elt = next_elt;
611 } while (cur_elt != NULL);
612 map_id_list[i] = NULL;
613 }
614 }
615 max_map = 0;
616 }
617
618 /*
619 * FUNCTION : map_id_list_init()
620 *
621 * DESCRIPTION: initializes map_id_list[] and max_map
622 *
623 * GIVEN : nothing
624 *
625 * RETURNS : 0 if OK
626 * -1 if failure
627 */
628 int
map_id_list_init()629 map_id_list_init()
630 {
631 char **domain_list, **map_list = NULL;
632 int domain_num;
633 int i, j;
634 char *myself = "map_id_list_init";
635 char mapbuf[MAXPATHLEN];
636 int mapbuf_len = sizeof (mapbuf);
637 int map_name_len;
638 int seqnum = 0;
639 int rc = 0;
640
641 for (i = 0; i < MAXHASH; i++) {
642 map_id_list[i] = NULL;
643 }
644
645 domain_num = get_mapping_domain_list(&domain_list);
646 for (i = 0; i < domain_num; i++) {
647 if (map_list) {
648 free_map_list(map_list);
649 map_list = NULL;
650 }
651
652 /* get map list from mapping file */
653 map_list = get_mapping_map_list(domain_list[i]);
654 if (map_list == NULL) {
655 /* no map for this domain in mapping file */
656 logmsg(MSG_NOTIMECHECK, LOG_DEBUG,
657 "%s: get_mapping_map_list()"
658 " found no map for domain %s",
659 myself, domain_list[i]);
660 }
661
662 /* add maps from /var/yp/<domain> */
663 if (add_map_domain_to_list(domain_list[i], &map_list) ==
664 FALSE) {
665 logmsg(MSG_NOTIMECHECK, LOG_ERR,
666 "%s: add_map_domain_to_list() failed", myself);
667 free_map_id_list();
668 if (map_list) free_map_list(map_list);
669 return (-1);
670 }
671
672 if (map_list == NULL || map_list[0] == NULL) {
673 logmsg(MSG_NOTIMECHECK, LOG_DEBUG,
674 "%s: no map in domain %s",
675 myself, domain_list[i]);
676 continue;
677 }
678
679 for (j = 0; map_list[j] != NULL; j++) {
680 /* build long name */
681 map_name_len = ypdbpath_sz + 1 +
682 strlen(domain_list[i]) + 1 +
683 strlen(NTOL_PREFIX) +
684 strlen(map_list[j]) + 1;
685 if (map_name_len > mapbuf_len) {
686 logmsg(MSG_NOTIMECHECK, LOG_ERR,
687 "%s: map name too long for %s",
688 " in domain %s", myself, map_list[j],
689 domain_list[i]);
690 free_map_id_list();
691 if (map_list) free_map_list(map_list);
692 return (-1);
693 }
694 (void) memset(mapbuf, 0, mapbuf_len);
695 (void) snprintf(mapbuf, map_name_len, "%s%c%s%c%s%s",
696 ypdbpath, SEP_CHAR, domain_list[i], SEP_CHAR,
697 NTOL_PREFIX, map_list[j]);
698
699 if (insert_map_in_list(mapbuf, seqnum)
700 == FAILURE) {
701 logmsg(MSG_NOTIMECHECK, LOG_ERR,
702 "%s: failed to insert map %s",
703 " in domain %s", myself, map_list[j]);
704 free_map_id_list();
705 if (map_list) free_map_list(map_list);
706 return (-1);
707 }
708 seqnum++;
709 }
710 }
711
712 max_map = seqnum;
713
714 #ifdef NISDB_LDAP_DEBUG
715 dump_map_id_list();
716 #endif
717
718 /*
719 * If more maps than allocated spaces in shared memory, that's a failure
720 * probably need to free previously allocated memory if failure,
721 * before returning.
722 */
723 if (max_map > MAXHASH) {
724 rc = -1;
725 logmsg(MSG_NOTIMECHECK, LOG_ERR,
726 "%s: too many maps (%d)",
727 myself, max_map);
728 free_map_id_list();
729 }
730 if (map_list) free_map_list(map_list);
731 return (rc);
732 }
733
734 /*
735 * FUNCTION : get_list_max()
736 *
737 * DESCRIPTION: return references to static variables map_id_list
738 * and max_map;
739 *
740 * GIVEN : address for referencing map_id_list
741 * address for referencing max_map
742 *
743 * RETURNS : nothing
744 */
745 void
get_list_max(map_id_elt_t *** list,int * max)746 get_list_max(map_id_elt_t ***list, int *max)
747 {
748 *list = map_id_list;
749 *max = max_map;
750 }
751