xref: /openbsd/usr.bin/cap_mkdb/cap_mkdb.c (revision 097a140d)
1 /*	$OpenBSD: cap_mkdb.c,v 1.25 2020/09/15 07:41:38 kevlo 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 
53 DB *capdbp;
54 int verbose;
55 char *capname, buf[8 * 1024];
56 
57 HASHINFO openinfo = {
58 	4096,		/* bsize */
59 	16,		/* ffactor */
60 	256,		/* nelem */
61 	2048 * 1024,	/* cachesize */
62 	NULL,		/* hash() */
63 	0		/* lorder */
64 };
65 
66 /*
67  * cap_mkdb creates a capability hash database for quick retrieval of capability
68  * records.  The database contains 2 types of entries: records and references
69  * marked by the first byte in the data.  A record entry contains the actual
70  * capability record whereas a reference contains the name (key) under which
71  * the correct record is stored.
72  */
73 int
74 main(int argc, char *argv[])
75 {
76 	int c;
77 
78 	if (pledge("stdio rpath wpath cpath", NULL) == -1)
79 		err(1, "pledge");
80 
81 	capname = NULL;
82 	while ((c = getopt(argc, argv, "f:iv")) != -1) {
83 		switch(c) {
84 		case 'f':
85 			capname = optarg;
86 			break;
87 		case 'v':
88 			verbose = 1;
89 			break;
90 		case '?':
91 		default:
92 			usage();
93 		}
94 	}
95 	argc -= optind;
96 	argv += optind;
97 
98 	if (*argv == NULL)
99 		usage();
100 
101 	/*
102 	 * The database file is the first argument if no name is specified.
103 	 * Make arrangements to unlink it if we exit badly.
104 	 */
105 	(void)snprintf(buf, sizeof(buf), "%s.db", capname ? capname : *argv);
106 	if ((capname = strdup(buf)) == NULL)
107 		err(1, NULL);
108 	if ((capdbp = dbopen(capname, O_CREAT | O_TRUNC | O_RDWR,
109 	    DEFFILEMODE, DB_HASH, &openinfo)) == NULL)
110 		err(1, "%s", buf);
111 
112 	if (atexit(dounlink) != 0)
113 		err(1, "atexit");
114 
115 	db_build(argv);
116 
117 	if (capdbp->close(capdbp) < 0)
118 		err(1, "%s", capname);
119 	capname = NULL;
120 	exit(0);
121 }
122 
123 void
124 dounlink(void)
125 {
126 	if (capname != NULL)
127 		(void)unlink(capname);
128 }
129 
130 /*
131  * Any changes to these definitions should be made also in the getcap(3)
132  * library routines.
133  */
134 #define RECOK	(char)0
135 #define TCERR	(char)1
136 #define SHADOW	(char)2
137 
138 /*
139  * db_build() builds the name and capability databases according to the
140  * details above.
141  */
142 void
143 db_build(char **ifiles)
144 {
145 	DBT key, data;
146 	recno_t reccnt;
147 	size_t len, bplen;
148 	int st;
149 	char *bp, *p, *t, *capbeg, *capend;
150 
151 	cgetusedb(0);		/* disable reading of .db files in getcap(3) */
152 
153 	data.data = NULL;
154 	key.data = NULL;
155 	for (reccnt = 0, bplen = 0; (st = cgetnext(&bp, ifiles)) > 0;) {
156 
157 		/*
158 		 * Allocate enough memory to store the size of the record plus
159 		 * a terminating NULL and one extra byte.
160 		 */
161 		len = strlen(bp);
162 		if (bplen <= len + 2) {
163 			int newbplen = bplen + MAXIMUM(256, len + 2);
164 			void *newdata;
165 
166 			if ((newdata = realloc(data.data, newbplen)) == NULL)
167 				err(1, NULL);
168 			data.data = newdata;
169 			bplen = newbplen;
170 		}
171 
172 		/* Find the end of the name field. */
173 		if ((p = strchr(bp, ':')) == NULL) {
174 			warnx("no name field: %.*s", (int)MINIMUM(len, 20), bp);
175 			continue;
176 		}
177 
178 		/* First byte of stored record indicates status. */
179 		switch(st) {
180 		case 1:
181 			((char *)(data.data))[0] = RECOK;
182 			break;
183 		case 2:
184 			((char *)(data.data))[0] = TCERR;
185 			warnx("Record not tc expanded: %.*s", (int)(p - bp), bp);
186 			break;
187 		}
188 
189 		/* Create the stored record. */
190 		t = (char *)data.data + 1;
191 		/* Copy the cap name and trailing ':' */
192 		len = p - bp + 1;
193 		memcpy(t, bp, len);
194 		t += len;
195 
196 		/* Copy entry, collapsing empty fields. */
197 		capbeg = p + 1;
198 		while (*capbeg) {
199 			/* Skip empty fields. */
200 			if ((len = strspn(capbeg, ": \t\n\r")))
201 				capbeg += len;
202 
203 			/* Find the end of this cap and copy it w/ : */
204 			capend = strchr(capbeg, ':');
205 			if (capend)
206 				len = capend - capbeg + 1;
207 			else
208 				len = strlen(capbeg);
209 			memcpy(t, capbeg, len);
210 			t += len;
211 			capbeg += len;
212 		}
213 		*t = '\0';
214 		data.size = t - (char *)data.data + 1;
215 
216 		/* Store the record under the name field. */
217 		key.data = bp;
218 		key.size = p - bp;
219 
220 		switch(capdbp->put(capdbp, &key, &data, R_NOOVERWRITE)) {
221 		case -1:
222 			err(1, "put");
223 			/* NOTREACHED */
224 		case 1:
225 			warnx("ignored duplicate: %.*s",
226 			    (int)key.size, (char *)key.data);
227 			continue;
228 		}
229 		++reccnt;
230 
231 		/* If only one name, ignore the rest. */
232 		if ((p = strchr(bp, '|')) == NULL)
233 			continue;
234 
235 		/* The rest of the names reference the entire name. */
236 		((char *)(data.data))[0] = SHADOW;
237 		(void) memmove(&((u_char *)(data.data))[1], key.data, key.size);
238 		data.size = key.size + 1;
239 
240 		/* Store references for other names. */
241 		for (p = t = bp;; ++p) {
242 			if (p > t && (*p == ':' || *p == '|')) {
243 				key.size = p - t;
244 				key.data = t;
245 
246 				/*
247 				 * If this is the last entry and contains any
248 				 * spaces, it is a description rather than an
249 				 * alias, so skip it and break.
250 				 */
251 				if (*p != '|' &&
252 				    memchr(key.data, ' ', key.size) != NULL)
253 					break;
254 
255 				switch(capdbp->put(capdbp,
256 				    &key, &data, R_NOOVERWRITE)) {
257 				case -1:
258 					err(1, "put");
259 					/* NOTREACHED */
260 				case 1:
261 					warnx("ignored duplicate: %.*s",
262 					      (int)key.size, (char *)key.data);
263 				}
264 				t = p + 1;
265 			}
266 			if (*p == ':')
267 				break;
268 		}
269 		free(bp);
270 	}
271 
272 	switch(st) {
273 	case -1:
274 		err(1, "file argument");
275 		/* NOTREACHED */
276 	case -2:
277 		errx(1, "potential reference loop detected");
278 		/* NOTREACHED */
279 	}
280 
281 	if (verbose)
282 		(void)printf("cap_mkdb: %d capability records\n", reccnt);
283 }
284 
285 void
286 usage(void)
287 {
288 	(void)fprintf(stderr,
289 	    "usage: cap_mkdb [-v] [-f outfile] file1 [file2 ...]\n");
290 	exit(1);
291 }
292