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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * DESCRIPTION:	Contains functions relating to the creation and manipulation
31  *		of map_ctrl structures. These are used to hold information
32  *		specific to one NIS map.
33  *
34  *		Because each of these contains a significant amount of state
35  *		information about an individual map they are created (on the
36  *		heap) when a map is opened and destroyed when it is closed.
37  *		The overhead of doing this is less than maintaining a pool
38  *		of map_ctrls.
39  *
40  *		If two processes access the same map two map_ctrls will be
41  *		created with similar contents (but differing DBM pointers).
42  *		Both will have the same hash value so when one is locked
43  *		access to the other will also be prevented.
44  */
45 
46 #include <unistd.h>
47 #include <syslog.h>
48 #include <ndbm.h>
49 #include <string.h>
50 #include "ypsym.h"
51 #include "ypdefs.h"
52 #include "shim.h"
53 #include "yptol.h"
54 #include "../ldap_util.h"
55 
56 /* Switch on parts of ypdefs.h */
57 USE_DBM
58 
59 /*
60  * FUNCTION: 	create_map_ctrl();
61  *
62  * DESCRIPTION: Create and a new map_ctrl in a non opened state.
63  *
64  * INPUTS:	Fully qualified map name
65  *
66  * OUTPUTS:	Pointer to map_ctrl
67  *		NULL on failure.
68  *
69  */
70 map_ctrl *
71 create_map_ctrl(char *name)
72 {
73 	char *myself = "create_map_ctrl";
74 	map_ctrl *map;
75 
76 	map = (map_ctrl *)am(myself, sizeof (map_ctrl));
77 	if (NULL == map) {
78 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "Could not alloc map_ctrl");
79 		return (NULL);
80 	}
81 
82 	/* Clear new map (in case we have to free it) */
83 	map->entries = NULL;
84 	map->hash_val = 0;
85 	map->map_name = NULL;
86 	map->domain = NULL;
87 	map->map_path = NULL;
88 	map->ttl = NULL;
89 	map->ttl_path = NULL;
90 	map->trad_map_path = NULL;
91 	map->key_data.dptr = NULL;
92 	map->open_mode = 0;
93 	map->open_flags = 0;
94 
95 	/*
96 	 * Initialize the fields of the map_ctrl. By doing this once here we
97 	 * can save a lot of work as map entries are accessed.
98 	 */
99 	if (SUCCESS != map_ctrl_init(map, name)) {
100 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
101 				"Could not initialize map_ctrl for %s", name);
102 		free_map_ctrl(map);
103 		return (NULL);
104 	}
105 
106 	return (map);
107 }
108 
109 /*
110  * FUNCTION :	map_ctrl_init()
111  *
112  * DESCRIPTION:	Initializes the fields of a map_ctrl structure.
113  *
114  *		By doing this once (when the map_ctrl is created) we avoid
115  *		numerous other function having to repeat this string
116  *		manipulation.
117  *
118  * GIVEN :	Pointer to the structure
119  *		Fully qualified name of the map
120  *
121  * RETURNS :	SUCCESS = map_ctrl fully set up.
122  *		FAILURE = map_ctrl not set up CALLER MUST FREE.
123  */
124 suc_code
125 map_ctrl_init(map_ctrl *map, char *name)
126 {
127 	char *myself = "map_ctrl_init";
128 	char *p, *q;
129 
130 	/* Save map path for future reference */
131 	map->map_path = (char *)strdup(name);
132 	if (NULL ==  map->map_path) {
133 		logmsg(MSG_NOMEM, LOG_ERR,
134 				"Could not duplicate map path %s", map);
135 		return (FAILURE);
136 	}
137 
138 	/* Work out map's unqualified name from path */
139 	p = strrchr(name, SEP_CHAR);
140 	if (NULL == p) {
141 		/* Must be at least a domain and name */
142 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
143 			"Could not find separator in map path %s", map);
144 		return (FAILURE);
145 	}
146 	q = p + 1;
147 
148 	/* Check for and remove N2L prefix */
149 	if (yptol_mode) {
150 		/*
151 		 * Check for and remove N2L prefix. If not found not a problem
152 		 * we open some old style maps during DIT initialization.
153 		 */
154 		if (0 == strncmp(q, NTOL_PREFIX, strlen(NTOL_PREFIX)))
155 			q += strlen(NTOL_PREFIX);
156 	} else {
157 		if (0 == strncmp(q, NTOL_PREFIX, strlen(NTOL_PREFIX)))
158 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
159 				"Working in non N2L mode and path %s "
160 				"contains N2L prefix", name);
161 	}
162 
163 	/* Save unqualified map name */
164 	map->map_name = strdup(q);
165 	if (NULL == map->map_name) {
166 		logmsg(MSG_NOMEM, LOG_ERR,
167 				"Could not duplicate map name %s", q);
168 		return (FAILURE);
169 	}
170 
171 	/* Work out map's domain name from path */
172 	for (q = p-1; (SEP_CHAR != *q) && (q > name); q--);
173 
174 	if (q <= name) {
175 		/* Didn't find separator */
176 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
177 				"Could not find domain in map path %s", name);
178 		return (FAILURE);
179 	}
180 
181 	map->domain = (char *)am(myself, p - q);
182 	if (NULL == map->domain) {
183 		logmsg(MSG_NOMEM, LOG_ERR,
184 			"Could not alloc memory for domain in path %s", name);
185 		return (FAILURE);
186 	}
187 	strncpy(map->domain, q + 1, p-q-1);
188 	map->domain[p-q-1] = '\0';
189 
190 	/* Work out extra names required by N2L */
191 	if (yptol_mode) {
192 		/*
193 		 * Work out what old style NIS path would have been. This is
194 		 * used to check for date of DBM file so add the DBM
195 		 * extension.
196 		 */
197 		map->trad_map_path = (char *)am(myself, strlen(map->map_name) +
198 					+ strlen(dbm_pag) + (p - name) + 2);
199 		if (NULL == map->trad_map_path) {
200 			logmsg(MSG_NOMEM, LOG_ERR,
201 				"Could not alocate memory for "
202 				"traditional map path derived from %s", name);
203 			return (FAILURE);
204 		}
205 
206 		strncpy(map->trad_map_path, name, p - name + 1);
207 		map->trad_map_path[p - name + 1] = '\0';
208 		strcat(map->trad_map_path, map->map_name);
209 		strcat(map->trad_map_path, dbm_pag);
210 
211 		/* Generate qualified TTL file name */
212 		map->ttl_path = (char *)am(myself, strlen(map->map_path) +
213 						strlen(TTL_POSTFIX) + 1);
214 		if (NULL == map->ttl_path) {
215 			logmsg(MSG_NOMEM, LOG_ERR,
216 				"Could not alocate memory for "
217 				"ttl path derived from %s", name);
218 			return (FAILURE);
219 		}
220 
221 		strcpy(map->ttl_path, map->map_path);
222 		strcat(map->ttl_path, TTL_POSTFIX);
223 	}
224 
225 	/* Work out hash value */
226 	map->hash_val = hash(name);
227 
228 	/* Set up magic number */
229 	map->magic = MAP_MAGIC;
230 
231 	/* Null out pointers */
232 	map->entries = NULL;
233 	map->ttl = NULL;
234 
235 	/* No key data yet */
236 	map->key_data.dptr = NULL;
237 	map->key_data.dsize = 0;
238 
239 	return (SUCCESS);
240 }
241 
242 /*
243  * FUNCTION: 	get_map_crtl();
244  *
245  * DESCRIPTION: Find an existing map_ctrl for a map of a given DBM * (i.e.
246  *		handle) . If none exists return an error.
247  *
248  * INPUTS:	Map handle
249  *
250  * OUTPUTS:	Pointer to map_ctrl
251  *		NULL on failure.
252  *
253  */
254 map_ctrl *
255 get_map_ctrl(DBM *db)
256 {
257 	/* Check that this really is a map_ctrl not a DBM */
258 	if (((map_ctrl *)db)->magic != MAP_MAGIC) {
259 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
260 				"SHIM called with DBM ptr not map_crtl ptr");
261 		return (NULL);
262 	}
263 
264 	/* Since this is an opaque pointer just cast it */
265 	return ((map_ctrl *)db);
266 }
267 
268 /*
269  * FUNCTION:	dup_map_ctrl()
270  *
271  * DESCRIPTION:	Duplicates a map_ctrl structure
272  *
273  * GIVEN :	Map_ctrl to duplicate
274  *
275  * RETURNS :	Pointer to a new malloced map_ctrl. CALLER MUST FREE
276  *		NULL on failure.
277  */
278 map_ctrl *
279 dup_map_ctrl(map_ctrl *old_map)
280 {
281 	map_ctrl *new_map;
282 
283 	/*
284 	 * Could save a little bit of time by duplicating the static parts
285 	 * of the old map but on balance it is cleaner to just make a new one
286 	 * from scratch
287 	 */
288 	new_map = create_map_ctrl(old_map->map_path);
289 
290 	if (NULL == new_map)
291 		return (NULL);
292 
293 	/* If old map had open handles duplicate them */
294 	if (NULL != old_map->entries) {
295 		new_map->open_flags = old_map->open_flags;
296 		new_map->open_mode = old_map->open_mode;
297 		if (FAILURE == open_yptol_files(new_map)) {
298 			free_map_ctrl(new_map);
299 			return (NULL);
300 		}
301 	}
302 
303 	return (new_map);
304 }
305 
306 /*
307  * FUNCTION: 	free_map_crtl();
308  *
309  * DESCRIPTION: Free contents of a map_ctr structure and closed any open
310  *		DBM files.
311  *
312  * INPUTS:	Pointer to pointer to a map_ctrl.
313  *
314  * OUTPUTS:	Nothing
315  *
316  */
317 void
318 free_map_ctrl(map_ctrl *map)
319 {
320 
321 	if (NULL != map->entries) {
322 		dbm_close(map->entries);
323 		map->entries = NULL;
324 	}
325 
326 	if (NULL != map->map_name) {
327 		sfree(map->map_name);
328 		map->map_name = NULL;
329 	}
330 
331 	if (NULL != map->map_path) {
332 		sfree(map->map_path);
333 		map->map_path = NULL;
334 	}
335 
336 	if (NULL != map->domain) {
337 		sfree(map->domain);
338 		map->domain = NULL;
339 	}
340 
341 	if (yptol_mode) {
342 		if (NULL != map->ttl) {
343 			dbm_close(map->ttl);
344 			map->ttl = NULL;
345 		}
346 
347 		if (NULL != map->trad_map_path) {
348 			sfree(map->trad_map_path);
349 			map->trad_map_path = NULL;
350 		}
351 
352 		if (NULL != map->ttl_path) {
353 			sfree(map->ttl_path);
354 			map->ttl_path = NULL;
355 		}
356 
357 		if (NULL != map->key_data.dptr) {
358 			sfree(map->key_data.dptr);
359 			map->key_data.dptr = NULL;
360 			map->key_data.dsize = 0;
361 		}
362 	}
363 
364 	map->magic = 0;
365 
366 	/* Since map_ctrls are now always in malloced memory */
367 	sfree(map);
368 
369 }
370 
371 /*
372  * FUNCTION :	get_map_name()
373  *
374  * DESCRIPTION:	Get the name of a map from its map_ctrl. This could be done
375  *		as a simple dereference but this function hides the internal
376  *		implementation of map_ctrl from higher layers.
377  *
378  * GIVEN :	A map_ctrl pointer
379  *
380  * RETURNS :	A pointer to the map_ctrl. Higher levels treat this as an
381  *		opaque DBM pointer.
382  *		NULL on failure.
383  */
384 char *
385 get_map_name(DBM *db)
386 {
387 	map_ctrl *map = (map_ctrl *)db;
388 
389 	if (NULL == map)
390 		return (NULL);
391 
392 	return (map->map_name);
393 }
394 
395 /*
396  * FUNCTION :	set_key_data()
397  *
398  * DESCRIPTION:	Sets up the key data freeing any that already exists.
399  *
400  * GIVEN :	Pointer to the map_ctrl to set up.
401  *		Datum containing the key. The dptr of this will be set to
402  *		point to the key data.
403  *
404  * RETURNS :	Nothing
405  */
406 void
407 set_key_data(map_ctrl *map, datum *data)
408 {
409 	char *myself = "set_key_data";
410 
411 	/*
412 	 * Free up any existing key data. Because each dbm file can only have
413 	 * one enumeration going at a time this is safe.
414 	 */
415 	if (NULL != map->key_data.dptr) {
416 		sfree(map->key_data.dptr);
417 		map->key_data.dptr = NULL;
418 		map->key_data.dsize = 0;
419 	}
420 
421 	/* If nothing in key just return */
422 	if (NULL == data->dptr)
423 		return;
424 
425 	/* Something is in the key so must duplicate out of static memory */
426 	map->key_data.dptr = (char *)am(myself, data->dsize);
427 	if (NULL == map->key_data.dptr) {
428 		logmsg(MSG_NOMEM, LOG_ERR, "Cannot alloc memory for key data");
429 	} else {
430 		memcpy(map->key_data.dptr, data->dptr, data->dsize);
431 		map->key_data.dsize = data->dsize;
432 	}
433 
434 	/* Set datum to point to malloced version of the data */
435 	data->dptr = map->key_data.dptr;
436 
437 	return;
438 
439 }
440 
441 /*
442  * FUNCTION :	open_yptol_files()
443  *
444  * DESCRIPTION:	Opens both yptol files for a map. This is called both when a
445  *		map is opened and when it is reopened as a result of an update
446  *		operation. Must be called with map locked.
447  *
448  * GIVEN :	Initialized map_ctrl
449  *
450  * RETURNS :	SUCCESS = Maps opened
451  *		FAILURE = Maps not opened (and mess tidied up)
452  */
453 suc_code
454 open_yptol_files(map_ctrl *map)
455 {
456 
457 	/* Open entries map */
458 	map->entries = dbm_open(map->map_path, map->open_flags, map->open_mode);
459 
460 	if (NULL == map->entries) {
461 		/* Maybe we were asked to open a non-existent map. No problem */
462 		return (FAILURE);
463 	}
464 
465 	if (yptol_mode) {
466 		/* Open TTLs map. Must always be writable */
467 		map->ttl = dbm_open(map->ttl_path, O_RDWR | O_CREAT, 0644);
468 		if (NULL == map->ttl) {
469 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
470 				"Cannot open TTL file %s", map->ttl_path);
471 			dbm_close(map->entries);
472 			map->entries = NULL;
473 			return (FAILURE);
474 		}
475 	}
476 
477 	return (SUCCESS);
478 }
479