cap_mkdb.c revision 1.16
1/* $OpenBSD: cap_mkdb.c,v 1.16 2009/10/27 23:59:36 deraadt 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/param.h> 34#include <sys/stat.h> 35 36#include <db.h> 37#include <err.h> 38#include <errno.h> 39#include <fcntl.h> 40#include <limits.h> 41#include <stdio.h> 42#include <stdlib.h> 43#include <string.h> 44#include <ctype.h> 45#include <unistd.h> 46 47void db_build(char **); 48void dounlink(void); 49void usage(void); 50int igetnext(char **, char **); 51int main(int, char *[]); 52 53DB *capdbp; 54int info, verbose; 55char *capname, buf[8 * 1024]; 56 57HASHINFO 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 */ 73int 74main(int argc, char *argv[]) 75{ 76 int c; 77 78 capname = NULL; 79 while ((c = getopt(argc, argv, "f:iv")) != -1) { 80 switch(c) { 81 case 'f': 82 capname = optarg; 83 break; 84 case 'v': 85 verbose = 1; 86 break; 87 case 'i': 88 info = 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)) 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 123void 124dounlink(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 */ 142void 143db_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, *out, ch; 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; 156 (st = (info ? igetnext(&bp, ifiles) : cgetnext(&bp, ifiles))) > 0;) { 157 158 /* 159 * Allocate enough memory to store four times the size of the 160 * record (so an existing ':' can be expanded to '\072' for 161 * terminfo) plus a terminating NULL and one extra byte. 162 */ 163 len = strlen(bp); 164 if (bplen <= 4 * len + 2) { 165 int newbplen = bplen + MAX(256, 4 * 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, info ? ',' : ':')) == NULL) { 176 warnx("no name field: %.*s", (int)MIN(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 if (info) { 193 /* 194 * The record separator is :, so it is necessary to 195 * change commas into colons. However, \, should be 196 * left alone, unless the \ is the last part of ^\. 197 */ 198 data.size = len + 2; 199 out = ((char *) data.data) + 1; 200 t = bp; 201 while (t < bp + len) { 202 switch (ch = *t++) { 203 case '^': 204 case '\\': 205 *out++ = ch; 206 if (*t != '\0') 207 *out++ = *t++; 208 break; 209 case ':': 210 memcpy(out, "\\072", 4); 211 out += 4; 212 data.size += 3; /* : already counted */ 213 break; 214 case ',': 215 *out++ = ':'; 216 break; 217 default: 218 *out++ = ch; 219 break; 220 } 221 } 222 *out++ = '\0'; 223 if (memchr((char *)data.data + 1, '\0', data.size - 2)) { 224 warnx("NUL in entry: %.*s", (int)MIN(len, 20), bp); 225 continue; 226 } 227 } else { 228 char *capbeg, *capend; 229 230 t = (char *)data.data + 1; 231 /* Copy the cap name and trailing ':' */ 232 len = p - bp + 1; 233 memcpy(t, bp, len); 234 t += len; 235 236 /* Copy entry, collapsing empty fields. */ 237 capbeg = p + 1; 238 while (*capbeg) { 239 /* Skip empty fields. */ 240 if ((len = strspn(capbeg, ": \t\n\r"))) 241 capbeg += len; 242 243 /* Find the end of this cap and copy it w/ : */ 244 capend = strchr(capbeg, ':'); 245 if (capend) 246 len = capend - capbeg + 1; 247 else 248 len = strlen(capbeg); 249 memcpy(t, capbeg, len); 250 t += len; 251 capbeg += len; 252 } 253 *t = '\0'; 254 data.size = t - (char *)data.data + 1; 255 } 256 257 /* Store the record under the name field. */ 258 key.data = bp; 259 key.size = p - bp; 260 261 switch(capdbp->put(capdbp, &key, &data, R_NOOVERWRITE)) { 262 case -1: 263 err(1, "put"); 264 /* NOTREACHED */ 265 case 1: 266 warnx("ignored duplicate: %.*s", 267 (int)key.size, (char *)key.data); 268 continue; 269 } 270 ++reccnt; 271 272 /* If only one name, ignore the rest. */ 273 if ((p = strchr(bp, '|')) == NULL) 274 continue; 275 276 /* The rest of the names reference the entire name. */ 277 ((char *)(data.data))[0] = SHADOW; 278 (void) memmove(&((u_char *)(data.data))[1], key.data, key.size); 279 data.size = key.size + 1; 280 281 /* Store references for other names. */ 282 for (p = t = bp;; ++p) { 283 if (p > t && (*p == (info ? ',' : ':') || *p == '|')) { 284 key.size = p - t; 285 key.data = t; 286 switch(capdbp->put(capdbp, 287 &key, &data, R_NOOVERWRITE)) { 288 case -1: 289 err(1, "put"); 290 /* NOTREACHED */ 291 case 1: 292 warnx("ignored duplicate: %.*s", 293 (int)key.size, (char *)key.data); 294 } 295 t = p + 1; 296 } 297 if (*p == (info ? ',' : ':')) 298 break; 299 } 300 free(bp); 301 } 302 303 switch(st) { 304 case -1: 305 err(1, "file argument"); 306 /* NOTREACHED */ 307 case -2: 308 errx(1, "potential reference loop detected"); 309 /* NOTREACHED */ 310 } 311 312 if (verbose) 313 (void)printf("cap_mkdb: %d capability records\n", reccnt); 314} 315 316void 317usage(void) 318{ 319 (void)fprintf(stderr, 320 "usage: cap_mkdb [-iv] [-f outfile] file1 [file2 ...]\n"); 321 exit(1); 322} 323