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