xref: /openbsd-src/usr.bin/cap_mkdb/cap_mkdb.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: cap_mkdb.c,v 1.22 2015/12/04 19:15:54 jmc Exp $	*/
2 /*	$NetBSD: cap_mkdb.c,v 1.5 1995/09/02 05:47:12 jtc Exp $	*/
3 
4 /*-
5  * Copyright (c) 1992, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/stat.h>
34 
35 #include <db.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <limits.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <ctype.h>
44 #include <unistd.h>
45 
46 #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
47 #define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
48 
49 void	 db_build(char **);
50 void	 dounlink(void);
51 void	 usage(void);
52 int	 igetnext(char **, char **);
53 int	 main(int, char *[]);
54 
55 DB *capdbp;
56 int verbose;
57 char *capname, buf[8 * 1024];
58 
59 HASHINFO openinfo = {
60 	4096,		/* bsize */
61 	16,		/* ffactor */
62 	256,		/* nelem */
63 	2048 * 1024,	/* cachesize */
64 	NULL,		/* hash() */
65 	0		/* lorder */
66 };
67 
68 /*
69  * cap_mkdb creates a capability hash database for quick retrieval of capability
70  * records.  The database contains 2 types of entries: records and references
71  * marked by the first byte in the data.  A record entry contains the actual
72  * capability record whereas a reference contains the name (key) under which
73  * the correct record is stored.
74  */
75 int
76 main(int argc, char *argv[])
77 {
78 	int c;
79 
80 	if (pledge("stdio rpath wpath cpath", NULL) == -1)
81 		err(1, "pledge");
82 
83 	capname = NULL;
84 	while ((c = getopt(argc, argv, "f:iv")) != -1) {
85 		switch(c) {
86 		case 'f':
87 			capname = optarg;
88 			break;
89 		case 'v':
90 			verbose = 1;
91 			break;
92 		case '?':
93 		default:
94 			usage();
95 		}
96 	}
97 	argc -= optind;
98 	argv += optind;
99 
100 	if (*argv == NULL)
101 		usage();
102 
103 	/*
104 	 * The database file is the first argument if no name is specified.
105 	 * Make arrangements to unlink it if we exit badly.
106 	 */
107 	(void)snprintf(buf, sizeof(buf), "%s.db", capname ? capname : *argv);
108 	if ((capname = strdup(buf)) == NULL)
109 		err(1, NULL);
110 	if ((capdbp = dbopen(capname, O_CREAT | O_TRUNC | O_RDWR,
111 	    DEFFILEMODE, DB_HASH, &openinfo)) == NULL)
112 		err(1, "%s", buf);
113 
114 	if (atexit(dounlink))
115 		err(1, "atexit");
116 
117 	db_build(argv);
118 
119 	if (capdbp->close(capdbp) < 0)
120 		err(1, "%s", capname);
121 	capname = NULL;
122 	exit(0);
123 }
124 
125 void
126 dounlink(void)
127 {
128 	if (capname != NULL)
129 		(void)unlink(capname);
130 }
131 
132 /*
133  * Any changes to these definitions should be made also in the getcap(3)
134  * library routines.
135  */
136 #define RECOK	(char)0
137 #define TCERR	(char)1
138 #define SHADOW	(char)2
139 
140 /*
141  * db_build() builds the name and capability databases according to the
142  * details above.
143  */
144 void
145 db_build(char **ifiles)
146 {
147 	DBT key, data;
148 	recno_t reccnt;
149 	size_t len, bplen;
150 	int st;
151 	char *bp, *p, *t, *capbeg, *capend;
152 
153 	cgetusedb(0);		/* disable reading of .db files in getcap(3) */
154 
155 	data.data = NULL;
156 	key.data = NULL;
157 	for (reccnt = 0, bplen = 0; (st = cgetnext(&bp, ifiles)) > 0;) {
158 
159 		/*
160 		 * Allocate enough memory to store the size of the record plus
161 		 * a terminating NULL and one extra byte.
162 		 */
163 		len = strlen(bp);
164 		if (bplen <= len + 2) {
165 			int newbplen = bplen + MAXIMUM(256, len + 2);
166 			void *newdata;
167 
168 			if ((newdata = realloc(data.data, newbplen)) == NULL)
169 				err(1, NULL);
170 			data.data = newdata;
171 			bplen = newbplen;
172 		}
173 
174 		/* Find the end of the name field. */
175 		if ((p = strchr(bp, ':')) == NULL) {
176 			warnx("no name field: %.*s", (int)MINIMUM(len, 20), bp);
177 			continue;
178 		}
179 
180 		/* First byte of stored record indicates status. */
181 		switch(st) {
182 		case 1:
183 			((char *)(data.data))[0] = RECOK;
184 			break;
185 		case 2:
186 			((char *)(data.data))[0] = TCERR;
187 			warnx("Record not tc expanded: %.*s", (int)(p - bp), bp);
188 			break;
189 		}
190 
191 		/* Create the stored record. */
192 		t = (char *)data.data + 1;
193 		/* Copy the cap name and trailing ':' */
194 		len = p - bp + 1;
195 		memcpy(t, bp, len);
196 		t += len;
197 
198 		/* Copy entry, collapsing empty fields. */
199 		capbeg = p + 1;
200 		while (*capbeg) {
201 			/* Skip empty fields. */
202 			if ((len = strspn(capbeg, ": \t\n\r")))
203 				capbeg += len;
204 
205 			/* Find the end of this cap and copy it w/ : */
206 			capend = strchr(capbeg, ':');
207 			if (capend)
208 				len = capend - capbeg + 1;
209 			else
210 				len = strlen(capbeg);
211 			memcpy(t, capbeg, len);
212 			t += len;
213 			capbeg += len;
214 		}
215 		*t = '\0';
216 		data.size = t - (char *)data.data + 1;
217 
218 		/* Store the record under the name field. */
219 		key.data = bp;
220 		key.size = p - bp;
221 
222 		switch(capdbp->put(capdbp, &key, &data, R_NOOVERWRITE)) {
223 		case -1:
224 			err(1, "put");
225 			/* NOTREACHED */
226 		case 1:
227 			warnx("ignored duplicate: %.*s",
228 			    (int)key.size, (char *)key.data);
229 			continue;
230 		}
231 		++reccnt;
232 
233 		/* If only one name, ignore the rest. */
234 		if ((p = strchr(bp, '|')) == NULL)
235 			continue;
236 
237 		/* The rest of the names reference the entire name. */
238 		((char *)(data.data))[0] = SHADOW;
239 		(void) memmove(&((u_char *)(data.data))[1], key.data, key.size);
240 		data.size = key.size + 1;
241 
242 		/* Store references for other names. */
243 		for (p = t = bp;; ++p) {
244 			if (p > t && (*p == ':' || *p == '|')) {
245 				key.size = p - t;
246 				key.data = t;
247 
248 				/*
249 				 * If this is the last entry and contains any
250 				 * spaces, it is a description rather than an
251 				 * alias, so skip it and break.
252 				 */
253 				if (*p != '|' &&
254 				    memchr(key.data, ' ', key.size) != NULL)
255 					break;
256 
257 				switch(capdbp->put(capdbp,
258 				    &key, &data, R_NOOVERWRITE)) {
259 				case -1:
260 					err(1, "put");
261 					/* NOTREACHED */
262 				case 1:
263 					warnx("ignored duplicate: %.*s",
264 					      (int)key.size, (char *)key.data);
265 				}
266 				t = p + 1;
267 			}
268 			if (*p == ':')
269 				break;
270 		}
271 		free(bp);
272 	}
273 
274 	switch(st) {
275 	case -1:
276 		err(1, "file argument");
277 		/* NOTREACHED */
278 	case -2:
279 		errx(1, "potential reference loop detected");
280 		/* NOTREACHED */
281 	}
282 
283 	if (verbose)
284 		(void)printf("cap_mkdb: %d capability records\n", reccnt);
285 }
286 
287 void
288 usage(void)
289 {
290 	(void)fprintf(stderr,
291 	    "usage: cap_mkdb [-v] [-f outfile] file1 [file2 ...]\n");
292 	exit(1);
293 }
294