GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/smtpd/smtpctl/../makemap.c Lines: 0 228 0.0 %
Date: 2017-11-07 Branches: 0 169 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: makemap.c,v 1.67 2017/07/27 18:48:30 sunil 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/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 <imsg.h>
33
#include <stdio.h>
34
#include <stdlib.h>
35
#include <string.h>
36
#include <unistd.h>
37
#include <limits.h>
38
#include <util.h>
39
40
#include "smtpd.h"
41
#include "log.h"
42
43
#define	PATH_ALIASES	"/etc/mail/aliases"
44
45
static void	 usage(void);
46
static int	 parse_map(DB *, int *, char *);
47
static int	 parse_entry(DB *, int *, char *, size_t, size_t);
48
static int	 parse_mapentry(DB *, int *, char *, size_t, size_t);
49
static int	 parse_setentry(DB *, int *, char *, size_t, size_t);
50
static int	 make_plain(DBT *, char *);
51
static int	 make_aliases(DBT *, char *);
52
static char	*conf_aliases(char *);
53
static int	 dump_db(const char *, DBTYPE);
54
55
struct smtpd	 smtpd;
56
struct smtpd	*env = &smtpd;
57
char		*source;
58
static int	 mode;
59
60
enum output_type {
61
	T_PLAIN,
62
	T_ALIASES,
63
	T_SET
64
} type;
65
66
/*
67
 * Stub functions so that makemap compiles using minimum object files.
68
 */
69
void
70
purge_config(uint8_t what)
71
{
72
	memset(env, 0, sizeof(struct smtpd));
73
}
74
75
int
76
fork_proc_backend(const char *backend, const char *conf, const char *procname)
77
{
78
	return (-1);
79
}
80
81
int
82
makemap(int prog_mode, int argc, char *argv[])
83
{
84
	struct stat	 sb;
85
	char		 dbname[PATH_MAX];
86
	DB		*db;
87
	const char	*opts;
88
	char		*conf, *oflag = NULL;
89
	int		 ch, dbputs = 0, Uflag = 0;
90
	DBTYPE		 dbtype = DB_HASH;
91
	char		*p;
92
	int		 fd = -1;
93
94
	log_init(1, LOG_MAIL);
95
96
	mode = prog_mode;
97
	conf = CONF_FILE;
98
	type = T_PLAIN;
99
	opts = "b:C:d:ho:O:t:U";
100
	if (mode == P_NEWALIASES)
101
		opts = "f:h";
102
103
	while ((ch = getopt(argc, argv, opts)) != -1) {
104
		switch (ch) {
105
		case 'b':
106
			if (optarg && strcmp(optarg, "i") == 0)
107
				mode = P_NEWALIASES;
108
			break;
109
		case 'C':
110
			break; /* for compatibility */
111
		case 'd':
112
			if (strcmp(optarg, "hash") == 0)
113
				dbtype = DB_HASH;
114
			else if (strcmp(optarg, "btree") == 0)
115
				dbtype = DB_BTREE;
116
			else
117
				errx(1, "unsupported DB type '%s'", optarg);
118
			break;
119
		case 'f':
120
			conf = optarg;
121
			break;
122
		case 'o':
123
			oflag = optarg;
124
			break;
125
		case 'O':
126
			if (strncmp(optarg, "AliasFile=", 10) != 0)
127
				break;
128
			type = T_ALIASES;
129
			p = strchr(optarg, '=');
130
			source = ++p;
131
			break;
132
		case 't':
133
			if (strcmp(optarg, "aliases") == 0)
134
				type = T_ALIASES;
135
			else if (strcmp(optarg, "set") == 0)
136
				type = T_SET;
137
			else
138
				errx(1, "unsupported type '%s'", optarg);
139
			break;
140
		case 'U':
141
			Uflag = 1;
142
			break;
143
		default:
144
			usage();
145
		}
146
	}
147
	argc -= optind;
148
	argv += optind;
149
150
	/* sendmail-compat makemap ... re-execute using proper interface */
151
	if (argc == 2) {
152
		if (oflag)
153
			usage();
154
155
		p = strstr(argv[1], ".db");
156
		if (p == NULL || strcmp(p, ".db") != 0) {
157
			if (!bsnprintf(dbname, sizeof dbname, "%s.db",
158
				argv[1]))
159
				errx(1, "database name too long");
160
		}
161
		else {
162
			if (strlcpy(dbname, argv[1], sizeof dbname)
163
			    >= sizeof dbname)
164
				errx(1, "database name too long");
165
		}
166
167
		execlp("makemap", "makemap", "-d", argv[0], "-o", dbname, "-",
168
		    (char *)NULL);
169
		err(1, "execlp");
170
	}
171
172
	if (mode == P_NEWALIASES) {
173
		if (geteuid())
174
			errx(1, "need root privileges");
175
		if (argc != 0)
176
			usage();
177
		type = T_ALIASES;
178
		if (source == NULL)
179
			source = conf_aliases(conf);
180
	} else {
181
		if (argc != 1)
182
			usage();
183
		source = argv[0];
184
	}
185
186
	if (Uflag)
187
		return dump_db(source, dbtype);
188
189
	if (oflag == NULL && asprintf(&oflag, "%s.db", source) == -1)
190
		err(1, "asprintf");
191
192
	if (strcmp(source, "-") != 0)
193
		if (stat(source, &sb) == -1)
194
			err(1, "stat: %s", source);
195
196
	if (!bsnprintf(dbname, sizeof(dbname), "%s.XXXXXXXXXXX", oflag))
197
		errx(1, "path too long");
198
	if ((fd = mkstemp(dbname)) == -1)
199
		err(1, "mkstemp");
200
201
	db = dbopen(dbname, O_TRUNC|O_RDWR, 0644, dbtype, NULL);
202
	if (db == NULL) {
203
		warn("dbopen: %s", dbname);
204
		goto bad;
205
	}
206
207
	if (strcmp(source, "-") != 0)
208
		if (fchmod(db->fd(db), sb.st_mode) == -1 ||
209
		    fchown(db->fd(db), sb.st_uid, sb.st_gid) == -1) {
210
			warn("couldn't carry ownership and perms to %s",
211
			    dbname);
212
			goto bad;
213
		}
214
215
	if (!parse_map(db, &dbputs, source))
216
		goto bad;
217
218
	if (db->close(db) == -1) {
219
		warn("dbclose: %s", dbname);
220
		goto bad;
221
	}
222
223
	/* force to disk before renaming over an existing file */
224
	if (fsync(fd) == -1) {
225
		warn("fsync: %s", dbname);
226
		goto bad;
227
	}
228
	if (close(fd) == -1) {
229
		fd = -1;
230
		warn("close: %s", dbname);
231
		goto bad;
232
	}
233
	fd = -1;
234
235
	if (rename(dbname, oflag) == -1) {
236
		warn("rename");
237
		goto bad;
238
	}
239
240
	if (mode == P_NEWALIASES)
241
		printf("%s: %d aliases\n", source, dbputs);
242
	else if (dbputs == 0)
243
		warnx("warning: empty map created: %s", oflag);
244
245
	return 0;
246
bad:
247
	if (fd != -1)
248
		close(fd);
249
	unlink(dbname);
250
	return 1;
251
}
252
253
static int
254
parse_map(DB *db, int *dbputs, char *filename)
255
{
256
	FILE	*fp;
257
	char	*line;
258
	size_t	 len;
259
	size_t	 lineno = 0;
260
261
	if (strcmp(filename, "-") == 0)
262
		fp = fdopen(0, "r");
263
	else
264
		fp = fopen(filename, "r");
265
	if (fp == NULL) {
266
		warn("%s", filename);
267
		return 0;
268
	}
269
270
	if (!isatty(fileno(fp)) && flock(fileno(fp), LOCK_SH|LOCK_NB) == -1) {
271
		if (errno == EWOULDBLOCK)
272
			warnx("%s is locked", filename);
273
		else
274
			warn("%s: flock", filename);
275
		fclose(fp);
276
		return 0;
277
	}
278
279
	while ((line = fparseln(fp, &len, &lineno,
280
	    NULL, FPARSELN_UNESCCOMM)) != NULL) {
281
		if (!parse_entry(db, dbputs, line, len, lineno)) {
282
			free(line);
283
			fclose(fp);
284
			return 0;
285
		}
286
		free(line);
287
	}
288
289
	fclose(fp);
290
	return 1;
291
}
292
293
static int
294
parse_entry(DB *db, int *dbputs, char *line, size_t len, size_t lineno)
295
{
296
	switch (type) {
297
	case T_PLAIN:
298
	case T_ALIASES:
299
		return parse_mapentry(db, dbputs, line, len, lineno);
300
	case T_SET:
301
		return parse_setentry(db, dbputs, line, len, lineno);
302
	}
303
	return 0;
304
}
305
306
static int
307
parse_mapentry(DB *db, int *dbputs, char *line, size_t len, size_t lineno)
308
{
309
	DBT	 key;
310
	DBT	 val;
311
	char	*keyp;
312
	char	*valp;
313
314
	keyp = line;
315
	while (isspace((unsigned char)*keyp))
316
		keyp++;
317
	if (*keyp == '\0')
318
		return 1;
319
320
	valp = keyp;
321
	strsep(&valp, " \t:");
322
	if (valp == NULL || valp == keyp)
323
		goto bad;
324
	while (*valp == ':' || isspace((unsigned char)*valp))
325
		valp++;
326
	if (*valp == '\0')
327
		goto bad;
328
329
	/* Check for dups. */
330
	key.data = keyp;
331
	key.size = strlen(keyp) + 1;
332
333
	xlowercase(key.data, key.data, strlen(key.data) + 1);
334
	if (db->get(db, &key, &val, 0) == 0) {
335
		warnx("%s:%zd: duplicate entry for %s", source, lineno, keyp);
336
		return 0;
337
	}
338
339
	if (type == T_PLAIN) {
340
		if (!make_plain(&val, valp))
341
			goto bad;
342
	}
343
	else if (type == T_ALIASES) {
344
		if (!make_aliases(&val, valp))
345
			goto bad;
346
	}
347
348
	if (db->put(db, &key, &val, 0) == -1) {
349
		warn("dbput");
350
		return 0;
351
	}
352
353
	(*dbputs)++;
354
355
	free(val.data);
356
357
	return 1;
358
359
bad:
360
	warnx("%s:%zd: invalid entry", source, lineno);
361
	return 0;
362
}
363
364
static int
365
parse_setentry(DB *db, int *dbputs, char *line, size_t len, size_t lineno)
366
{
367
	DBT	 key;
368
	DBT	 val;
369
	char	*keyp;
370
371
	keyp = line;
372
	while (isspace((unsigned char)*keyp))
373
		keyp++;
374
	if (*keyp == '\0')
375
		return 1;
376
377
	val.data  = "<set>";
378
	val.size = strlen(val.data) + 1;
379
380
	/* Check for dups. */
381
	key.data = keyp;
382
	key.size = strlen(keyp) + 1;
383
	xlowercase(key.data, key.data, strlen(key.data) + 1);
384
	if (db->get(db, &key, &val, 0) == 0) {
385
		warnx("%s:%zd: duplicate entry for %s", source, lineno, keyp);
386
		return 0;
387
	}
388
389
	if (db->put(db, &key, &val, 0) == -1) {
390
		warn("dbput");
391
		return 0;
392
	}
393
394
	(*dbputs)++;
395
396
	return 1;
397
}
398
399
static int
400
make_plain(DBT *val, char *text)
401
{
402
	val->data = xstrdup(text, "make_plain");
403
	val->size = strlen(text) + 1;
404
405
	return (val->size);
406
}
407
408
static int
409
make_aliases(DBT *val, char *text)
410
{
411
	struct expandnode	xn;
412
	char		       *subrcpt;
413
	char		       *origtext;
414
415
	val->data = NULL;
416
	val->size = 0;
417
418
	origtext = xstrdup(text, "make_aliases");
419
420
	while ((subrcpt = strsep(&text, ",")) != NULL) {
421
		/* subrcpt: strip initial and trailing whitespace. */
422
		subrcpt = strip(subrcpt);
423
		if (*subrcpt == '\0')
424
			goto error;
425
426
		if (!text_to_expandnode(&xn, subrcpt))
427
			goto error;
428
	}
429
430
	val->data = origtext;
431
	val->size = strlen(origtext) + 1;
432
	return (val->size);
433
434
error:
435
	free(origtext);
436
437
	return 0;
438
}
439
440
static char *
441
conf_aliases(char *cfgpath)
442
{
443
	struct table	*table;
444
	char		*path;
445
	char		*p;
446
447
	if (parse_config(env, cfgpath, 0))
448
		exit(1);
449
450
	table = table_find("aliases", NULL);
451
	if (table == NULL)
452
		return (PATH_ALIASES);
453
454
	path = xstrdup(table->t_config, "conf_aliases");
455
	p = strstr(path, ".db");
456
	if (p == NULL || strcmp(p, ".db") != 0) {
457
		return (path);
458
	}
459
	*p = '\0';
460
	return (path);
461
}
462
463
static int
464
dump_db(const char *dbname, DBTYPE dbtype)
465
{
466
	DB	*db;
467
	DBT	 key, val;
468
	char	*keystr, *valstr;
469
	int	 r;
470
471
	db = dbopen(dbname, O_RDONLY, 0644, dbtype, NULL);
472
	if (db == NULL)
473
		err(1, "dbopen: %s", dbname);
474
475
	for (r = db->seq(db, &key, &val, R_FIRST); r == 0;
476
	    r = db->seq(db, &key, &val, R_NEXT)) {
477
		keystr = key.data;
478
		valstr = val.data;
479
		if (keystr[key.size - 1] == '\0')
480
			key.size--;
481
		if (valstr[val.size - 1] == '\0')
482
			val.size--;
483
		printf("%.*s\t%.*s\n", (int)key.size, keystr,
484
		    (int)val.size, valstr);
485
	}
486
	if (r == -1)
487
		err(1, "db->seq: %s", dbname);
488
489
	if (db->close(db) == -1)
490
		err(1, "dbclose: %s", dbname);
491
492
	return 0;
493
}
494
495
static void
496
usage(void)
497
{
498
	if (mode == P_NEWALIASES)
499
		fprintf(stderr, "usage: newaliases [-f file]\n");
500
	else
501
		fprintf(stderr, "usage: makemap [-U] [-d dbtype] [-o dbfile] "
502
		    "[-t type] file\n");
503
	exit(1);
504
}