GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/smtpd/smtpd/../mda_variables.c Lines: 0 158 0.0 %
Date: 2017-11-13 Branches: 0 142 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: mda_variables.c,v 1.1 2017/05/26 21:30:00 gilles Exp $	*/
2
3
/*
4
 * Copyright (c) 2011-2017 Gilles Chehade <gilles@poolp.org>
5
 * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
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/queue.h>
22
#include <sys/tree.h>
23
#include <sys/socket.h>
24
25
#include <netinet/in.h>
26
27
#include <imsg.h>
28
#include <stdio.h>
29
#include <stdlib.h>
30
#include <string.h>
31
#include <unistd.h>
32
#include <limits.h>
33
34
#include "smtpd.h"
35
#include "log.h"
36
37
#define	EXPAND_DEPTH	10
38
39
size_t mda_expand_format(char *, size_t, const struct envelope *,
40
    const struct userinfo *);
41
static size_t mda_expand_token(char *, size_t, const char *,
42
    const struct envelope *, const struct userinfo *);
43
static int mod_lowercase(char *, size_t);
44
static int mod_uppercase(char *, size_t);
45
static int mod_strip(char *, size_t);
46
47
static struct modifiers {
48
	char	*name;
49
	int	(*f)(char *buf, size_t len);
50
} token_modifiers[] = {
51
	{ "lowercase",	mod_lowercase },
52
	{ "uppercase",	mod_uppercase },
53
	{ "strip",	mod_strip },
54
	{ "raw",	NULL },		/* special case, must stay last */
55
};
56
57
#define	MAXTOKENLEN	128
58
59
static size_t
60
mda_expand_token(char *dest, size_t len, const char *token,
61
    const struct envelope *ep, const struct userinfo *ui)
62
{
63
	char		rtoken[MAXTOKENLEN];
64
	char		tmp[EXPAND_BUFFER];
65
	const char     *string;
66
	char	       *lbracket, *rbracket, *content, *sep, *mods;
67
	ssize_t		i;
68
	ssize_t		begoff, endoff;
69
	const char     *errstr = NULL;
70
	int		replace = 1;
71
	int		raw = 0;
72
73
	begoff = 0;
74
	endoff = EXPAND_BUFFER;
75
	mods = NULL;
76
77
	if (strlcpy(rtoken, token, sizeof rtoken) >= sizeof rtoken)
78
		return 0;
79
80
	/* token[x[:y]] -> extracts optional x and y, converts into offsets */
81
	if ((lbracket = strchr(rtoken, '[')) &&
82
	    (rbracket = strchr(rtoken, ']'))) {
83
		/* ] before [ ... or empty */
84
		if (rbracket < lbracket || rbracket - lbracket <= 1)
85
			return 0;
86
87
		*lbracket = *rbracket = '\0';
88
		 content  = lbracket + 1;
89
90
		 if ((sep = strchr(content, ':')) == NULL)
91
			 endoff = begoff = strtonum(content, -EXPAND_BUFFER,
92
			     EXPAND_BUFFER, &errstr);
93
		 else {
94
			 *sep = '\0';
95
			 if (content != sep)
96
				 begoff = strtonum(content, -EXPAND_BUFFER,
97
				     EXPAND_BUFFER, &errstr);
98
			 if (*(++sep)) {
99
				 if (errstr == NULL)
100
					 endoff = strtonum(sep, -EXPAND_BUFFER,
101
					     EXPAND_BUFFER, &errstr);
102
			 }
103
		 }
104
		 if (errstr)
105
			 return 0;
106
107
		 /* token:mod_1,mod_2,mod_n -> extract modifiers */
108
		 mods = strchr(rbracket + 1, ':');
109
	} else {
110
		if ((mods = strchr(rtoken, ':')) != NULL)
111
			*mods++ = '\0';
112
	}
113
114
	/* token -> expanded token */
115
	if (!strcasecmp("sender", rtoken)) {
116
		if (snprintf(tmp, sizeof tmp, "%s@%s",
117
			ep->sender.user, ep->sender.domain) >= (int)sizeof tmp)
118
			return 0;
119
		string = tmp;
120
	}
121
	else if (!strcasecmp("dest", rtoken)) {
122
		if (snprintf(tmp, sizeof tmp, "%s@%s",
123
			ep->dest.user, ep->dest.domain) >= (int)sizeof tmp)
124
			return 0;
125
		string = tmp;
126
	}
127
	else if (!strcasecmp("rcpt", rtoken)) {
128
		if (snprintf(tmp, sizeof tmp, "%s@%s",
129
			ep->rcpt.user, ep->rcpt.domain) >= (int)sizeof tmp)
130
			return 0;
131
		string = tmp;
132
	}
133
	else if (!strcasecmp("sender.user", rtoken))
134
		string = ep->sender.user;
135
	else if (!strcasecmp("sender.domain", rtoken))
136
		string = ep->sender.domain;
137
	else if (!strcasecmp("user.username", rtoken))
138
		string = ui->username;
139
	else if (!strcasecmp("user.directory", rtoken)) {
140
		string = ui->directory;
141
		replace = 0;
142
	}
143
	else if (!strcasecmp("dest.user", rtoken))
144
		string = ep->dest.user;
145
	else if (!strcasecmp("dest.domain", rtoken))
146
		string = ep->dest.domain;
147
	else if (!strcasecmp("rcpt.user", rtoken))
148
		string = ep->rcpt.user;
149
	else if (!strcasecmp("rcpt.domain", rtoken))
150
		string = ep->rcpt.domain;
151
	else
152
		return 0;
153
154
	if (string != tmp) {
155
		if (strlcpy(tmp, string, sizeof tmp) >= sizeof tmp)
156
			return 0;
157
		string = tmp;
158
	}
159
160
	/*  apply modifiers */
161
	if (mods != NULL) {
162
		do {
163
			if ((sep = strchr(mods, '|')) != NULL)
164
				*sep++ = '\0';
165
			for (i = 0; (size_t)i < nitems(token_modifiers); ++i) {
166
				if (!strcasecmp(token_modifiers[i].name, mods)) {
167
					if (token_modifiers[i].f == NULL) {
168
						raw = 1;
169
						break;
170
					}
171
					if (!token_modifiers[i].f(tmp, sizeof tmp))
172
						return 0; /* modifier error */
173
					break;
174
				}
175
			}
176
			if ((size_t)i == nitems(token_modifiers))
177
				return 0; /* modifier not found */
178
		} while ((mods = sep) != NULL);
179
	}
180
181
	if (!raw && replace)
182
		for (i = 0; (size_t)i < strlen(tmp); ++i)
183
			if (strchr(MAILADDR_ESCAPE, tmp[i]))
184
				tmp[i] = ':';
185
186
	/* expanded string is empty */
187
	i = strlen(string);
188
	if (i == 0)
189
		return 0;
190
191
	/* begin offset beyond end of string */
192
	if (begoff >= i)
193
		return 0;
194
195
	/* end offset beyond end of string, make it end of string */
196
	if (endoff >= i)
197
		endoff = i - 1;
198
199
	/* negative begin offset, make it relative to end of string */
200
	if (begoff < 0)
201
		begoff += i;
202
	/* negative end offset, make it relative to end of string,
203
	 * note that end offset is inclusive.
204
	 */
205
	if (endoff < 0)
206
		endoff += i - 1;
207
208
	/* check that final offsets are valid */
209
	if (begoff < 0 || endoff < 0 || endoff < begoff)
210
		return 0;
211
	endoff += 1; /* end offset is inclusive */
212
213
	/* check that substring does not exceed destination buffer length */
214
	i = endoff - begoff;
215
	if ((size_t)i + 1 >= len)
216
		return 0;
217
218
	string += begoff;
219
	for (; i; i--) {
220
		*dest = (replace && *string == '/') ? ':' : *string;
221
		dest++;
222
		string++;
223
	}
224
225
	return endoff - begoff;
226
}
227
228
229
size_t
230
mda_expand_format(char *buf, size_t len, const struct envelope *ep,
231
    const struct userinfo *ui)
232
{
233
	char		tmpbuf[EXPAND_BUFFER], *ptmp, *pbuf, *ebuf;
234
	char		exptok[EXPAND_BUFFER];
235
	size_t		exptoklen;
236
	char		token[MAXTOKENLEN];
237
	size_t		ret, tmpret;
238
239
	if (len < sizeof tmpbuf) {
240
		log_warnx("mda_expand_format: tmp buffer < rule buffer");
241
		return 0;
242
	}
243
244
	memset(tmpbuf, 0, sizeof tmpbuf);
245
	pbuf = buf;
246
	ptmp = tmpbuf;
247
	ret = tmpret = 0;
248
249
	/* special case: ~/ only allowed expanded at the beginning */
250
	if (strncmp(pbuf, "~/", 2) == 0) {
251
		tmpret = snprintf(ptmp, sizeof tmpbuf, "%s/", ui->directory);
252
		if (tmpret >= sizeof tmpbuf) {
253
			log_warnx("warn: user directory for %s too large",
254
			    ui->directory);
255
			return 0;
256
		}
257
		ret  += tmpret;
258
		ptmp += tmpret;
259
		pbuf += 2;
260
	}
261
262
263
	/* expansion loop */
264
	for (; *pbuf && ret < sizeof tmpbuf; ret += tmpret) {
265
		if (*pbuf == '%' && *(pbuf + 1) == '%') {
266
			*ptmp++ = *pbuf++;
267
			pbuf  += 1;
268
			tmpret = 1;
269
			continue;
270
		}
271
272
		if (*pbuf != '%' || *(pbuf + 1) != '{') {
273
			*ptmp++ = *pbuf++;
274
			tmpret = 1;
275
			continue;
276
		}
277
278
		/* %{...} otherwise fail */
279
		if (*(pbuf+1) != '{' || (ebuf = strchr(pbuf+1, '}')) == NULL)
280
			return 0;
281
282
		/* extract token from %{token} */
283
		if ((size_t)(ebuf - pbuf) - 1 >= sizeof token)
284
			return 0;
285
286
		memcpy(token, pbuf+2, ebuf-pbuf-1);
287
		if (strchr(token, '}') == NULL)
288
			return 0;
289
		*strchr(token, '}') = '\0';
290
291
		exptoklen = mda_expand_token(exptok, sizeof exptok, token, ep,
292
		    ui);
293
		if (exptoklen == 0)
294
			return 0;
295
296
		/* writing expanded token at ptmp will overflow tmpbuf */
297
		if (sizeof (tmpbuf) - (ptmp - tmpbuf) <= exptoklen)
298
			return 0;
299
300
		memcpy(ptmp, exptok, exptoklen);
301
		pbuf   = ebuf + 1;
302
		ptmp  += exptoklen;
303
		tmpret = exptoklen;
304
	}
305
	if (ret >= sizeof tmpbuf)
306
		return 0;
307
308
	if ((ret = strlcpy(buf, tmpbuf, len)) >= len)
309
		return 0;
310
311
	return ret;
312
}
313
314
static int
315
mod_lowercase(char *buf, size_t len)
316
{
317
	char tmp[EXPAND_BUFFER];
318
319
	if (!lowercase(tmp, buf, sizeof tmp))
320
		return 0;
321
	if (strlcpy(buf, tmp, len) >= len)
322
		return 0;
323
	return 1;
324
}
325
326
static int
327
mod_uppercase(char *buf, size_t len)
328
{
329
	char tmp[EXPAND_BUFFER];
330
331
	if (!uppercase(tmp, buf, sizeof tmp))
332
		return 0;
333
	if (strlcpy(buf, tmp, len) >= len)
334
		return 0;
335
	return 1;
336
}
337
338
static int
339
mod_strip(char *buf, size_t len)
340
{
341
	char *tag, *at;
342
	unsigned int i;
343
344
	/* gilles+hackers -> gilles */
345
	if ((tag = strchr(buf, *env->sc_subaddressing_delim)) != NULL) {
346
		/* gilles+hackers@poolp.org -> gilles@poolp.org */
347
		if ((at = strchr(tag, '@')) != NULL) {
348
			for (i = 0; i <= strlen(at); ++i)
349
				tag[i] = at[i];
350
		} else
351
			*tag = '\0';
352
	}
353
	return 1;
354
}