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