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