1 /* $OpenBSD: makemap.c,v 1.32 2011/05/16 21:27:38 jasper Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.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 *env = NULL; 62 63 enum program { 64 P_MAKEMAP, 65 P_NEWALIASES 66 } mode; 67 68 enum output_type { 69 T_PLAIN, 70 T_ALIASES, 71 T_SET 72 } type; 73 74 /* 75 * Stub functions so that makemap compiles using minimum object files. 76 */ 77 void 78 purge_config(u_int8_t what) 79 { 80 bzero(env, sizeof(struct smtpd)); 81 } 82 83 int 84 ssl_load_certfile(const char *name, u_int8_t flags) 85 { 86 return (0); 87 } 88 89 int 90 main(int argc, char *argv[]) 91 { 92 struct stat sb; 93 char dbname[MAXPATHLEN]; 94 char *opts; 95 char *conf; 96 int ch; 97 struct smtpd smtpd; 98 99 env = &smtpd; 100 101 log_init(1); 102 103 mode = strcmp(__progname, "newaliases") ? P_MAKEMAP : P_NEWALIASES; 104 conf = CONF_FILE; 105 type = T_PLAIN; 106 opts = "ho:t:"; 107 if (mode == P_NEWALIASES) 108 opts = "f:h"; 109 110 while ((ch = getopt(argc, argv, opts)) != -1) { 111 switch (ch) { 112 case 'f': 113 conf = optarg; 114 break; 115 case 'o': 116 oflag = optarg; 117 break; 118 case 't': 119 if (strcmp(optarg, "aliases") == 0) 120 type = T_ALIASES; 121 else if (strcmp(optarg, "set") == 0) 122 type = T_SET; 123 else 124 errx(1, "unsupported type '%s'", optarg); 125 break; 126 default: 127 usage(); 128 } 129 } 130 argc -= optind; 131 argv += optind; 132 133 if (mode == P_NEWALIASES) { 134 if (geteuid()) 135 errx(1, "need root privileges"); 136 if (argc != 0) 137 usage(); 138 type = T_ALIASES; 139 source = conf_aliases(conf); 140 } else { 141 if (argc != 1) 142 usage(); 143 source = argv[0]; 144 } 145 146 if (oflag == NULL && asprintf(&oflag, "%s.db", source) == -1) 147 err(1, "asprintf"); 148 149 if (stat(source, &sb) == -1) 150 err(1, "stat: %s", source); 151 152 if (! bsnprintf(dbname, sizeof(dbname), "%s.XXXXXXXXXXX", oflag)) 153 errx(1, "path too long"); 154 if (mkstemp(dbname) == -1) 155 err(1, "mkstemp"); 156 157 db = dbopen(dbname, O_EXLOCK|O_RDWR|O_SYNC, 0644, DB_HASH, NULL); 158 if (db == NULL) { 159 warn("dbopen: %s", dbname); 160 goto bad; 161 } 162 163 if (fchmod(db->fd(db), sb.st_mode) == -1 || 164 fchown(db->fd(db), sb.st_uid, sb.st_gid) == -1) { 165 warn("couldn't carry ownership and perms to %s", dbname); 166 goto bad; 167 } 168 169 if (! parse_map(source)) 170 goto bad; 171 172 if (db->close(db) == -1) { 173 warn("dbclose: %s", dbname); 174 goto bad; 175 } 176 177 if (rename(dbname, oflag) == -1) { 178 warn("rename"); 179 goto bad; 180 } 181 182 if (mode == P_NEWALIASES) 183 printf("%s: %d aliases\n", source, dbputs); 184 else if (dbputs == 0) 185 warnx("warning: empty map created: %s", oflag); 186 187 return 0; 188 bad: 189 unlink(dbname); 190 return 1; 191 } 192 193 int 194 parse_map(char *filename) 195 { 196 FILE *fp; 197 char *line; 198 size_t len; 199 size_t lineno = 0; 200 char delim[] = { '\\', 0, 0 }; 201 202 fp = fopen(filename, "r"); 203 if (fp == NULL) { 204 warn("%s", filename); 205 return 0; 206 } 207 208 if (flock(fileno(fp), LOCK_SH|LOCK_NB) == -1) { 209 if (errno == EWOULDBLOCK) 210 warnx("%s is locked", filename); 211 else 212 warn("%s: flock", filename); 213 fclose(fp); 214 return 0; 215 } 216 217 while ((line = fparseln(fp, &len, &lineno, delim, 0)) != NULL) { 218 if (! parse_entry(line, len, lineno)) { 219 free(line); 220 fclose(fp); 221 return 0; 222 } 223 free(line); 224 } 225 226 fclose(fp); 227 return 1; 228 } 229 230 int 231 parse_entry(char *line, size_t len, size_t lineno) 232 { 233 switch (type) { 234 case T_PLAIN: 235 case T_ALIASES: 236 return parse_mapentry(line, len, lineno); 237 case T_SET: 238 return parse_setentry(line, len, lineno); 239 } 240 return 0; 241 } 242 243 int 244 parse_mapentry(char *line, size_t len, size_t lineno) 245 { 246 DBT key; 247 DBT val; 248 char *keyp; 249 char *valp; 250 251 keyp = line; 252 while (isspace((int)*keyp)) 253 keyp++; 254 if (*keyp == '\0' || *keyp == '#') 255 return 1; 256 257 valp = keyp; 258 strsep(&valp, " \t:"); 259 if (valp == NULL || valp == keyp) 260 goto bad; 261 while (*valp == ':' || isspace((int)*valp)) 262 valp++; 263 if (*valp == '\0' || *valp == '#') 264 goto bad; 265 266 /* Check for dups. */ 267 key.data = keyp; 268 key.size = strlen(keyp) + 1; 269 if (db->get(db, &key, &val, 0) == 0) { 270 warnx("%s:%zd: duplicate entry for %s", source, lineno, keyp); 271 return 0; 272 } 273 274 if (type == T_PLAIN) { 275 if (! make_plain(&val, valp)) 276 goto bad; 277 } 278 else if (type == T_ALIASES) { 279 lowercase(key.data, key.data, strlen(key.data) + 1); 280 if (! make_aliases(&val, valp)) 281 goto bad; 282 } 283 284 if (db->put(db, &key, &val, 0) == -1) { 285 warn("dbput"); 286 return 0; 287 } 288 289 dbputs++; 290 291 free(val.data); 292 293 return 1; 294 295 bad: 296 warnx("%s:%zd: invalid entry", source, lineno); 297 return 0; 298 } 299 300 int 301 parse_setentry(char *line, size_t len, size_t lineno) 302 { 303 DBT key; 304 DBT val; 305 char *keyp; 306 307 keyp = line; 308 while (isspace((int)*keyp)) 309 keyp++; 310 if (*keyp == '\0' || *keyp == '#') 311 return 1; 312 313 val.data = "<set>"; 314 val.size = strlen(val.data) + 1; 315 316 /* Check for dups. */ 317 key.data = keyp; 318 key.size = strlen(keyp) + 1; 319 if (db->get(db, &key, &val, 0) == 0) { 320 warnx("%s:%zd: duplicate entry for %s", source, lineno, keyp); 321 return 0; 322 } 323 324 if (db->put(db, &key, &val, 0) == -1) { 325 warn("dbput"); 326 return 0; 327 } 328 329 dbputs++; 330 331 return 1; 332 } 333 334 int 335 make_plain(DBT *val, char *text) 336 { 337 val->data = strdup(text); 338 if (val->data == NULL) 339 err(1, "malloc"); 340 341 val->size = strlen(text) + 1; 342 343 return (val->size); 344 } 345 346 int 347 make_aliases(DBT *val, char *text) 348 { 349 struct expandnode expnode; 350 char *subrcpt; 351 char *endp; 352 char *origtext; 353 354 val->data = NULL; 355 val->size = 0; 356 357 origtext = strdup(text); 358 if (origtext == NULL) 359 fatal("strdup"); 360 361 while ((subrcpt = strsep(&text, ",")) != NULL) { 362 /* subrcpt: strip initial whitespace. */ 363 while (isspace((int)*subrcpt)) 364 ++subrcpt; 365 if (*subrcpt == '\0') 366 goto error; 367 368 /* subrcpt: strip trailing whitespace. */ 369 endp = subrcpt + strlen(subrcpt) - 1; 370 while (subrcpt < endp && isspace((int)*endp)) 371 *endp-- = '\0'; 372 373 bzero(&expnode, sizeof(struct expandnode)); 374 if (! alias_parse(&expnode, subrcpt)) 375 goto error; 376 } 377 378 val->data = origtext; 379 val->size = strlen(origtext) + 1; 380 return (val->size); 381 382 error: 383 free(origtext); 384 385 return 0; 386 } 387 388 char * 389 conf_aliases(char *cfgpath) 390 { 391 struct map *map; 392 char *path; 393 char *p; 394 395 if (parse_config(env, cfgpath, 0)) 396 exit(1); 397 398 map = map_findbyname("aliases"); 399 if (map == NULL) 400 return (PATH_ALIASES); 401 402 path = strdup(map->m_config); 403 if (path == NULL) 404 err(1, NULL); 405 p = strstr(path, ".db"); 406 if (p == NULL || p[3] != '\0') 407 errx(1, "%s: %s: no .db suffix present", cfgpath, path); 408 *p = '\0'; 409 410 return (path); 411 } 412 413 void 414 usage(void) 415 { 416 if (mode == P_NEWALIASES) 417 fprintf(stderr, "usage: %s [-f file]\n", __progname); 418 else 419 fprintf(stderr, "usage: %s [-o dbfile] [-t type] file\n", 420 __progname); 421 exit(1); 422 } 423