1 /* $OpenBSD: makemap.c,v 1.43 2013/01/31 18:34:43 eric Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> 5 * Copyright (c) 2008-2009 Jacek Masiulaniec <jacekm@dobremiasto.net> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/types.h> 21 #include <sys/stat.h> 22 #include <sys/tree.h> 23 #include <sys/queue.h> 24 #include <sys/param.h> 25 #include <sys/socket.h> 26 27 #include <db.h> 28 #include <ctype.h> 29 #include <err.h> 30 #include <errno.h> 31 #include <event.h> 32 #include <fcntl.h> 33 #include <imsg.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <util.h> 38 #include <unistd.h> 39 40 #include "smtpd.h" 41 #include "log.h" 42 43 #define PATH_ALIASES "/etc/mail/aliases" 44 45 extern char *__progname; 46 47 __dead void usage(void); 48 static int parse_map(char *); 49 static int parse_entry(char *, size_t, size_t); 50 static int parse_mapentry(char *, size_t, size_t); 51 static int parse_setentry(char *, size_t, size_t); 52 static int make_plain(DBT *, char *); 53 static int make_aliases(DBT *, char *); 54 static char *conf_aliases(char *); 55 56 DB *db; 57 char *source; 58 char *oflag; 59 int dbputs; 60 61 struct smtpd smtpd; 62 struct smtpd *env = &smtpd; 63 64 enum program { 65 P_MAKEMAP, 66 P_NEWALIASES 67 } mode; 68 69 enum output_type { 70 T_PLAIN, 71 T_ALIASES, 72 T_SET 73 } type; 74 75 /* 76 * Stub functions so that makemap compiles using minimum object files. 77 */ 78 void 79 purge_config(uint8_t what) 80 { 81 bzero(env, sizeof(struct smtpd)); 82 } 83 84 int 85 main(int argc, char *argv[]) 86 { 87 struct stat sb; 88 char dbname[MAXPATHLEN]; 89 char *opts; 90 char *conf; 91 int ch; 92 DBTYPE dbtype = DB_HASH; 93 char *p; 94 mode_t omode; 95 96 log_init(1); 97 98 mode = strcmp(__progname, "newaliases") ? P_MAKEMAP : P_NEWALIASES; 99 conf = CONF_FILE; 100 type = T_PLAIN; 101 opts = "ho:t:d:"; 102 if (mode == P_NEWALIASES) 103 opts = "f:h"; 104 105 while ((ch = getopt(argc, argv, opts)) != -1) { 106 switch (ch) { 107 case 'd': 108 if (strcmp(optarg, "hash") == 0) 109 dbtype = DB_HASH; 110 else if (strcmp(optarg, "btree") == 0) 111 dbtype = DB_BTREE; 112 else if (strcmp(optarg, "dbm") == 0) 113 dbtype = DB_RECNO; 114 else 115 errx(1, "unsupported DB type '%s'", optarg); 116 break; 117 case 'f': 118 conf = optarg; 119 break; 120 case 'o': 121 oflag = optarg; 122 break; 123 case 't': 124 if (strcmp(optarg, "aliases") == 0) 125 type = T_ALIASES; 126 else if (strcmp(optarg, "set") == 0) 127 type = T_SET; 128 else 129 errx(1, "unsupported type '%s'", optarg); 130 break; 131 default: 132 usage(); 133 } 134 } 135 argc -= optind; 136 argv += optind; 137 138 /* sendmail-compat makemap ... re-execute using proper interface */ 139 if (argc == 2) { 140 if (oflag) 141 usage(); 142 143 p = strstr(argv[1], ".db"); 144 if (p == NULL || strcmp(p, ".db") != 0) { 145 if (! bsnprintf(dbname, sizeof dbname, "%s.db", 146 argv[1])) 147 errx(1, "database name too long"); 148 } 149 else { 150 if (strlcpy(dbname, argv[1], sizeof dbname) 151 >= sizeof dbname) 152 errx(1, "database name too long"); 153 } 154 155 execlp("makemap", "makemap", "-d", argv[0], "-o", dbname, "-", 156 NULL); 157 err(1, "execlp"); 158 } 159 160 if (mode == P_NEWALIASES) { 161 if (geteuid()) 162 errx(1, "need root privileges"); 163 if (argc != 0) 164 usage(); 165 type = T_ALIASES; 166 source = conf_aliases(conf); 167 } else { 168 if (argc != 1) 169 usage(); 170 source = argv[0]; 171 } 172 173 if (oflag == NULL && asprintf(&oflag, "%s.db", source) == -1) 174 err(1, "asprintf"); 175 176 if (strcmp(source, "-") != 0) 177 if (stat(source, &sb) == -1) 178 err(1, "stat: %s", source); 179 180 if (! bsnprintf(dbname, sizeof(dbname), "%s.XXXXXXXXXXX", oflag)) 181 errx(1, "path too long"); 182 omode = umask(7077); 183 if (mkstemp(dbname) == -1) 184 err(1, "mkstemp"); 185 umask(omode); 186 187 db = dbopen(dbname, O_EXLOCK|O_RDWR|O_SYNC, 0644, dbtype, NULL); 188 if (db == NULL) { 189 warn("dbopen: %s", dbname); 190 goto bad; 191 } 192 193 if (strcmp(source, "-") != 0) 194 if (fchmod(db->fd(db), sb.st_mode) == -1 || 195 fchown(db->fd(db), sb.st_uid, sb.st_gid) == -1) { 196 warn("couldn't carry ownership and perms to %s", 197 dbname); 198 goto bad; 199 } 200 201 if (! parse_map(source)) 202 goto bad; 203 204 if (db->close(db) == -1) { 205 warn("dbclose: %s", dbname); 206 goto bad; 207 } 208 209 if (rename(dbname, oflag) == -1) { 210 warn("rename"); 211 goto bad; 212 } 213 214 if (mode == P_NEWALIASES) 215 printf("%s: %d aliases\n", source, dbputs); 216 else if (dbputs == 0) 217 warnx("warning: empty map created: %s", oflag); 218 219 return 0; 220 bad: 221 unlink(dbname); 222 return 1; 223 } 224 225 int 226 parse_map(char *filename) 227 { 228 FILE *fp; 229 char *line; 230 size_t len; 231 size_t lineno = 0; 232 char delim[] = { '\\', 0, 0 }; 233 234 if (strcmp(filename, "-") == 0) 235 fp = fdopen(0, "r"); 236 else 237 fp = fopen(filename, "r"); 238 if (fp == NULL) { 239 warn("%s", filename); 240 return 0; 241 } 242 243 if (!isatty(fileno(fp)) && flock(fileno(fp), LOCK_SH|LOCK_NB) == -1) { 244 if (errno == EWOULDBLOCK) 245 warnx("%s is locked", filename); 246 else 247 warn("%s: flock", filename); 248 fclose(fp); 249 return 0; 250 } 251 252 while ((line = fparseln(fp, &len, &lineno, delim, 0)) != NULL) { 253 if (! parse_entry(line, len, lineno)) { 254 free(line); 255 fclose(fp); 256 return 0; 257 } 258 free(line); 259 } 260 261 fclose(fp); 262 return 1; 263 } 264 265 int 266 parse_entry(char *line, size_t len, size_t lineno) 267 { 268 switch (type) { 269 case T_PLAIN: 270 case T_ALIASES: 271 return parse_mapentry(line, len, lineno); 272 case T_SET: 273 return parse_setentry(line, len, lineno); 274 } 275 return 0; 276 } 277 278 int 279 parse_mapentry(char *line, size_t len, size_t lineno) 280 { 281 DBT key; 282 DBT val; 283 char *keyp; 284 char *valp; 285 286 keyp = line; 287 while (isspace((int)*keyp)) 288 keyp++; 289 if (*keyp == '\0' || *keyp == '#') 290 return 1; 291 292 valp = keyp; 293 strsep(&valp, " \t:"); 294 if (valp == NULL || valp == keyp) 295 goto bad; 296 while (*valp == ':' || isspace((int)*valp)) 297 valp++; 298 if (*valp == '\0' || *valp == '#') 299 goto bad; 300 301 /* Check for dups. */ 302 key.data = keyp; 303 key.size = strlen(keyp) + 1; 304 305 xlowercase(key.data, key.data, strlen(key.data) + 1); 306 if (db->get(db, &key, &val, 0) == 0) { 307 warnx("%s:%zd: duplicate entry for %s", source, lineno, keyp); 308 return 0; 309 } 310 311 if (type == T_PLAIN) { 312 if (! make_plain(&val, valp)) 313 goto bad; 314 } 315 else if (type == T_ALIASES) { 316 if (! make_aliases(&val, valp)) 317 goto bad; 318 } 319 320 if (db->put(db, &key, &val, 0) == -1) { 321 warn("dbput"); 322 return 0; 323 } 324 325 dbputs++; 326 327 free(val.data); 328 329 return 1; 330 331 bad: 332 warnx("%s:%zd: invalid entry", source, lineno); 333 return 0; 334 } 335 336 int 337 parse_setentry(char *line, size_t len, size_t lineno) 338 { 339 DBT key; 340 DBT val; 341 char *keyp; 342 343 keyp = line; 344 while (isspace((int)*keyp)) 345 keyp++; 346 if (*keyp == '\0' || *keyp == '#') 347 return 1; 348 349 val.data = "<set>"; 350 val.size = strlen(val.data) + 1; 351 352 /* Check for dups. */ 353 key.data = keyp; 354 key.size = strlen(keyp) + 1; 355 xlowercase(key.data, key.data, strlen(key.data) + 1); 356 if (db->get(db, &key, &val, 0) == 0) { 357 warnx("%s:%zd: duplicate entry for %s", source, lineno, keyp); 358 return 0; 359 } 360 361 if (db->put(db, &key, &val, 0) == -1) { 362 warn("dbput"); 363 return 0; 364 } 365 366 dbputs++; 367 368 return 1; 369 } 370 371 int 372 make_plain(DBT *val, char *text) 373 { 374 val->data = xstrdup(text, "make_plain"); 375 val->size = strlen(text) + 1; 376 377 return (val->size); 378 } 379 380 int 381 make_aliases(DBT *val, char *text) 382 { 383 struct expandnode xn; 384 char *subrcpt; 385 char *endp; 386 char *origtext; 387 388 val->data = NULL; 389 val->size = 0; 390 391 origtext = xstrdup(text, "make_aliases"); 392 393 while ((subrcpt = strsep(&text, ",")) != NULL) { 394 /* subrcpt: strip initial whitespace. */ 395 while (isspace((int)*subrcpt)) 396 ++subrcpt; 397 if (*subrcpt == '\0') 398 goto error; 399 400 /* subrcpt: strip trailing whitespace. */ 401 endp = subrcpt + strlen(subrcpt) - 1; 402 while (subrcpt < endp && isspace((int)*endp)) 403 *endp-- = '\0'; 404 405 if (! text_to_expandnode(&xn, subrcpt)) 406 goto error; 407 } 408 409 val->data = origtext; 410 val->size = strlen(origtext) + 1; 411 return (val->size); 412 413 error: 414 free(origtext); 415 416 return 0; 417 } 418 419 char * 420 conf_aliases(char *cfgpath) 421 { 422 struct table *table; 423 char *path; 424 char *p; 425 426 if (parse_config(env, cfgpath, 0)) 427 exit(1); 428 429 table = table_findbyname("aliases"); 430 if (table == NULL) 431 return (PATH_ALIASES); 432 433 path = xstrdup(table->t_config, "conf_aliases"); 434 p = strstr(path, ".db"); 435 if (p == NULL || strcmp(p, ".db") != 0) { 436 return (path); 437 } 438 *p = '\0'; 439 return (path); 440 } 441 442 void 443 usage(void) 444 { 445 if (mode == P_NEWALIASES) 446 fprintf(stderr, "usage: %s [-f file]\n", __progname); 447 else 448 fprintf(stderr, "usage: %s [-d dbtype] [-o dbfile] " 449 "[-t type] file\n", __progname); 450 exit(1); 451 } 452