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 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
27/*	  All Rights Reserved  	*/
28
29/*
30 * Portions of this source code were derived from Berkeley 4.3 BSD
31 * under license from the Regents of the University of California.
32 */
33
34#pragma ident	"%Z%%M%	%I%	%E% SMI"
35
36#ifndef	lint
37static	char sccsid[] = "%Z%%M% %I%     %E% SMI";
38#endif
39
40#include <stdlib.h>
41#include <dirent.h>
42#include <strings.h>
43#include "ypsym.h"
44#include "ypdefs.h"
45USE_YPDBPATH
46USE_DBM
47#include "shim.h"
48#include "../ldap_util.h"
49
50/*
51 * This constructs a file name from a passed domain name, a passed map name,
52 * and a globally known YP data base path prefix.
53 *
54 * Has to be in shim because it needs the N2L prefix
55 *
56 * RETURNS :	TRUE = A name was successfully created
57 *		FALSE = A name could not be created
58 */
59
60bool_t
61ypmkfilename(domain, map, path)
62	char *domain;
63	char *map;
64	char *path;
65{
66	int length;
67
68	/* Do not allow any path as a domain name. */
69	if (strchr(domain, '/') != NULL)
70		return (FALSE);
71
72	length = strlen(domain) + strlen(map) + ypdbpath_sz + 3;
73	if (yptol_mode)
74		length += strlen(NTOL_PREFIX) + 1;
75
76	if ((MAXNAMLEN + 1) < length) {
77		(void) fprintf(stderr, "ypserv:  Map name string too long.\n");
78		return (FALSE);
79	}
80
81	strcpy(path, ypdbpath);
82	strcat(path, "/");
83	strcat(path, domain);
84	strcat(path, "/");
85
86	/* If in N2L mode add N2L prefix */
87	if (yptol_mode)
88		strcat(path, NTOL_PREFIX);
89	strcat(path, map);
90
91	return (TRUE);
92}
93
94/*
95 * check whether a map is already in an array/list
96 *
97 * RETURNS: TRUE if yes
98 *          FALSE if not
99 */
100bool_t
101on_maplist(char *mapname, char **list) {
102	int i = 0;
103
104	if (list == NULL) {
105		return (FALSE);
106	}
107
108	while (list[i] != NULL) {
109		if (strcmp(mapname, list[i++]) == 0) {
110			return (TRUE);
111		}
112	}
113
114	return (FALSE);
115}
116
117/*
118 * add a map at the end of an array/list
119 *
120 * list_len: if -1, we do not know list length
121 *
122 * RETURNS: TRUE if map was added
123 *          FALSE if not
124 */
125bool_t
126add_in_maplist(char *mapname, char ***list, int *list_len) {
127	int i = 0;
128	char **list_tmp;
129
130	if (list == NULL) {
131		return (FALSE);
132	}
133
134	list_tmp = *list;
135
136	if (list_tmp == NULL) {
137		*list_len = 0;
138	} else {
139		/* find 1st free element */
140		while (list_tmp[i] != NULL) {
141			/*
142			 * increment in loop so that
143			 * list_tmp[i] == NULL
144			 * when exiting
145			 */
146			i++;
147		}
148	}
149
150	/* if we don't know list length, assume we reach its end */
151	if (*list_len == -1) {
152		*list_len = i;
153	}
154
155	/* do we need to reallocate ? */
156	if (i+1 >= *list_len) {
157		list_tmp = (char **)realloc(list_tmp,
158				    (*list_len + ARRAY_CHUNK) *
159					sizeof (char *));
160		if (list_tmp == NULL) {
161			return (FALSE);
162		}
163		*list = list_tmp;
164		*list_len += ARRAY_CHUNK;
165	}
166
167	/* add in list */
168	(*list)[i] = strdup(mapname);
169	if ((*list)[i] == NULL) {
170		/* strdup() failed */
171		return (FALSE);
172	}
173	(*list)[++i] = NULL;
174
175	return (TRUE);
176}
177
178/*
179 * This checks to see whether a domain name is present at the local node as a
180 * subdirectory of ypdbpath
181 *
182 * Was originally in cmd/ypcmd/shared/ancil.c as ypcheck_domain(domain).
183 * Now ypcheck_domain(domain) calls this function.
184 */
185bool
186ypcheck_domain_yptol(char *domain)
187{
188	char path[MAXNAMLEN + 1];
189	struct stat filestat;
190	bool present = FALSE;
191
192	strcpy(path, ypdbpath);
193	strcat(path, "/");
194	if (strlcat(path, domain, MAXNAMLEN + 1) >=  MAXNAMLEN + 1)
195		return (present);
196
197	if (stat(path, &filestat) != -1) {
198		if (S_ISDIR(filestat.st_mode))
199			present = TRUE;
200	}
201	return (present);
202}
203
204/*
205 * This performs an existence check on the dbm data base files <name>.pag and
206 * <name>.dir.  pname is a ptr to the filename.  This should be an absolute
207 * path.
208 * Returns TRUE if the map exists and is accessible; else FALSE.
209 *
210 * Note:  The file name should be a "base" form, without a file "extension" of
211 * .dir or .pag appended.  See ypmkfilename for a function which will generate
212 * the name correctly.  Errors in the stat call will be reported at this level,
213 * however, the non-existence of a file is not considered an error, and so will
214 * not be reported.
215 *
216 * Was originally in cmd/ypcmd/shared/utils.c as ypcheck_map_existence().
217 * Now ypcheck_map_existence() calls this function.
218 */
219bool
220ypcheck_map_existence_yptol(char *pname)
221{
222	char dbfile[MAXNAMLEN + sizeof (TTL_POSTFIX) + 1];
223	struct stat64 filestat;
224	int len;
225
226	if (!pname || ((len = (int)strlen(pname)) == 0) ||
227	    (len + sizeof (dbm_pag) + sizeof (TTL_POSTFIX)) >
228	    sizeof (dbfile)) {
229		return (FALSE);
230	}
231
232	errno = 0;
233
234	/* Check for existance of .dir file */
235	(void) strcpy(dbfile, pname);
236	(void) strcat(dbfile, dbm_dir);
237
238	if (stat64(dbfile, &filestat) == -1) {
239		if (errno != ENOENT) {
240			(void) fprintf(stderr,
241			    "ypserv:  Stat error on map file %s.\n",
242			    dbfile);
243		}
244		return (FALSE);
245	}
246
247	/* Check for existance of .pag file */
248	(void) strcpy(dbfile, pname);
249	(void) strcat(dbfile, dbm_pag);
250
251	if (stat64(dbfile, &filestat) == -1) {
252		if (errno != ENOENT) {
253			(void) fprintf(stderr,
254			    "ypserv:  Stat error on map file %s.\n",
255			    dbfile);
256		}
257		return (FALSE);
258	}
259
260	if (yptol_mode) {
261		/* Check for existance of TTL .dir file */
262		(void) strcpy(dbfile, pname);
263		(void) strcat(dbfile, TTL_POSTFIX);
264		(void) strcat(dbfile, dbm_dir);
265
266		if (stat64(dbfile, &filestat) == -1) {
267			if (errno != ENOENT) {
268				(void) fprintf(stderr,
269				    "ypserv:  Stat error on map file %s.\n",
270				    dbfile);
271			}
272			return (FALSE);
273		}
274
275		/* Check for existance of TTL .pag file */
276		(void) strcpy(dbfile, pname);
277		(void) strcat(dbfile, TTL_POSTFIX);
278		(void) strcat(dbfile, dbm_pag);
279
280		if (stat64(dbfile, &filestat) == -1) {
281			if (errno != ENOENT) {
282				(void) fprintf(stderr,
283				    "ypserv:  Stat error on map file %s.\n",
284				    dbfile);
285			}
286			return (FALSE);
287		}
288	}
289
290	return (TRUE);
291}
292
293/*
294 * This adds maps in a domain to a given list,
295 * from maps in /var/yp/<domain>
296 * Inspired from yplist_maps() in cmd/ypcmd/ypserv_ancil.c
297 *
298 * domain is the relevant domain name
299 * map_list is the list of maps in an array of map names,
300 *    which may or may not be empty
301 *
302 * RETURNS :    TRUE = everything went fine
303 *              FALSE = an error occured
304 */
305bool_t
306add_map_domain_to_list(char *domain, char ***map_list)
307{
308	char domdir[MAXNAMLEN + 1];
309	char path[MAXNAMLEN + 1];
310	int domdir_len = sizeof (domdir);
311	DIR *dirp;
312	struct dirent *dp;
313	int name_len;
314	int dbm_pag_len = sizeof (dbm_pag);
315	char *ext;
316	char *mapname;
317	int map_list_len = -1;
318
319	if (map_list == NULL) {
320		return (FALSE);
321	}
322
323	/* no domain, not a problem */
324	if (domain == NULL) {
325		return (TRUE);
326	}
327
328	/* not a valid domain, not a problem */
329	if (!ypcheck_domain_yptol(domain)) {
330		return (TRUE);
331	}
332
333	if (snprintf(domdir, domdir_len, "%s/%s", ypdbpath, domain)
334	    > domdir_len) {
335		return (FALSE);
336	}
337
338	if ((dirp = opendir(domdir)) == NULL) {
339		return (FALSE);
340	}
341
342	for (dp = readdir(dirp); dp != NULL;
343	    dp = readdir(dirp)) {
344		/*
345		 * If it's possible that the file name is one of the two files
346		 * implementing a map, remove the extension (dbm_pag or dbm_dir)
347		 */
348		name_len = (int)strlen(dp->d_name);
349
350		if (name_len < dbm_pag_len - 1) {
351			continue;		/* Too Short */
352		}
353
354		ext = &(dp->d_name[name_len - (dbm_pag_len - 1)]);
355
356		if (strcmp(ext, dbm_pag) != 0) {
357			continue;		/* No dbm file extension */
358		}
359
360		*ext = '\0';
361
362		/*
363		 * In yptol mode look at LDAP_ prefixed maps. In non yptol mode
364		 * ignore them.
365		 */
366		if (yptol_mode) {
367			if (0 != strncmp(dp->d_name, NTOL_PREFIX,
368			    strlen(NTOL_PREFIX))) {
369				continue;
370			}
371
372			/*
373			 * Already have an LDAP_ prefix. Don't want to add it
374			 * twice.
375			 */
376			mapname = dp->d_name + strlen(NTOL_PREFIX);
377		} else {
378			if (0 == strncmp(dp->d_name, NTOL_PREFIX,
379			    strlen(NTOL_PREFIX))) {
380				continue;
381			}
382			mapname = dp->d_name;
383		}
384
385		if (ypmkfilename(domain, mapname, path) == FALSE) {
386			(void) closedir(dirp);
387			return (FALSE);
388		}
389
390		/*
391		 * At this point, path holds the map file base name (no dbm
392		 * file extension), and mapname holds the map name.
393		 */
394		if (ypcheck_map_existence_yptol(path) &&
395		    !on_maplist(mapname, *map_list)) {
396			if (add_in_maplist(mapname, map_list, &map_list_len) ==
397			    FALSE) {
398				(void) closedir(dirp);
399				return (FALSE);
400			}
401		}
402	}
403
404	(void) closedir(dirp);
405	return (TRUE);
406}
407