xref: /openbsd-src/usr.sbin/smtpd/makemap.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
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