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 *
create_map_ctrl(char * name)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
map_ctrl_init(map_ctrl * map,char * name)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 *
get_map_ctrl(DBM * db)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 *
dup_map_ctrl(map_ctrl * old_map)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
free_map_ctrl(map_ctrl * map)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 *
get_map_name(DBM * db)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
set_key_data(map_ctrl * map,datum * data)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
open_yptol_files(map_ctrl * map)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
insert_map_in_list(char * map_name,int unique_value)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
dump_map_id_list()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
free_map_id_list()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
map_id_list_init()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
get_list_max(map_id_elt_t *** list,int * max)744 get_list_max(map_id_elt_t ***list, int *max)
745 {
746 *list = map_id_list;
747 *max = max_map;
748 }
749