Bug Summary

File:src/usr.sbin/smtpd/smtpd/../mda_variables.c
Warning:line 254, column 8
Although the value stored to 'tmpret' is used in the enclosing expression, the value is never actually read from 'tmpret'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name mda_variables.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.sbin/smtpd/smtpd/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.sbin/smtpd/smtpd/.. -D IO_TLS -D QUEUE_PROFILING -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/smtpd/smtpd/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.sbin/smtpd/smtpd/../mda_variables.c
1/* $OpenBSD: mda_variables.c,v 1.7 2021/06/14 17:58:15 eric 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 <stdlib.h>
21#include <string.h>
22
23#include "smtpd.h"
24#include "log.h"
25
26#define EXPAND_DEPTH10 10
27
28ssize_t mda_expand_format(char *, size_t, const struct deliver *,
29 const struct userinfo *, const char *);
30static ssize_t mda_expand_token(char *, size_t, const char *,
31 const struct deliver *, const struct userinfo *, const char *);
32static int mod_lowercase(char *, size_t);
33static int mod_uppercase(char *, size_t);
34static int mod_strip(char *, size_t);
35
36static struct modifiers {
37 char *name;
38 int (*f)(char *buf, size_t len);
39} token_modifiers[] = {
40 { "lowercase", mod_lowercase },
41 { "uppercase", mod_uppercase },
42 { "strip", mod_strip },
43 { "raw", NULL((void *)0) }, /* special case, must stay last */
44};
45
46#define MAXTOKENLEN128 128
47
48static ssize_t
49mda_expand_token(char *dest, size_t len, const char *token,
50 const struct deliver *dlv, const struct userinfo *ui, const char *mda_command)
51{
52 char rtoken[MAXTOKENLEN128];
53 char tmp[EXPAND_BUFFER1024];
54 const char *string;
55 char *lbracket, *rbracket, *content, *sep, *mods;
56 ssize_t i;
57 ssize_t begoff, endoff;
58 const char *errstr = NULL((void *)0);
59 int replace = 1;
60 int raw = 0;
61
62 begoff = 0;
63 endoff = EXPAND_BUFFER1024;
64 mods = NULL((void *)0);
65
66 if (strlcpy(rtoken, token, sizeof rtoken) >= sizeof rtoken)
67 return -1;
68
69 /* token[x[:y]] -> extracts optional x and y, converts into offsets */
70 if ((lbracket = strchr(rtoken, '[')) &&
71 (rbracket = strchr(rtoken, ']'))) {
72 /* ] before [ ... or empty */
73 if (rbracket < lbracket || rbracket - lbracket <= 1)
74 return -1;
75
76 *lbracket = *rbracket = '\0';
77 content = lbracket + 1;
78
79 if ((sep = strchr(content, ':')) == NULL((void *)0))
80 endoff = begoff = strtonum(content, -EXPAND_BUFFER1024,
81 EXPAND_BUFFER1024, &errstr);
82 else {
83 *sep = '\0';
84 if (content != sep)
85 begoff = strtonum(content, -EXPAND_BUFFER1024,
86 EXPAND_BUFFER1024, &errstr);
87 if (*(++sep)) {
88 if (errstr == NULL((void *)0))
89 endoff = strtonum(sep, -EXPAND_BUFFER1024,
90 EXPAND_BUFFER1024, &errstr);
91 }
92 }
93 if (errstr)
94 return -1;
95
96 /* token:mod_1,mod_2,mod_n -> extract modifiers */
97 mods = strchr(rbracket + 1, ':');
98 } else {
99 if ((mods = strchr(rtoken, ':')) != NULL((void *)0))
100 *mods++ = '\0';
101 }
102
103 /* token -> expanded token */
104 if (!strcasecmp("sender", rtoken)) {
105 if (snprintf(tmp, sizeof tmp, "%s@%s",
106 dlv->sender.user, dlv->sender.domain) >= (int)sizeof tmp)
107 return -1;
108 if (strcmp(tmp, "@") == 0)
109 (void)strlcpy(tmp, "", sizeof tmp);
110 string = tmp;
111 }
112 else if (!strcasecmp("rcpt", rtoken)) {
113 if (snprintf(tmp, sizeof tmp, "%s@%s",
114 dlv->rcpt.user, dlv->rcpt.domain) >= (int)sizeof tmp)
115 return -1;
116 if (strcmp(tmp, "@") == 0)
117 (void)strlcpy(tmp, "", sizeof tmp);
118 string = tmp;
119 }
120 else if (!strcasecmp("dest", rtoken)) {
121 if (snprintf(tmp, sizeof tmp, "%s@%s",
122 dlv->dest.user, dlv->dest.domain) >= (int)sizeof tmp)
123 return -1;
124 if (strcmp(tmp, "@") == 0)
125 (void)strlcpy(tmp, "", sizeof tmp);
126 string = tmp;
127 }
128 else if (!strcasecmp("sender.user", rtoken))
129 string = dlv->sender.user;
130 else if (!strcasecmp("sender.domain", rtoken))
131 string = dlv->sender.domain;
132 else if (!strcasecmp("user.username", rtoken))
133 string = ui->username;
134 else if (!strcasecmp("user.directory", rtoken)) {
135 string = ui->directory;
136 replace = 0;
137 }
138 else if (!strcasecmp("rcpt.user", rtoken))
139 string = dlv->rcpt.user;
140 else if (!strcasecmp("rcpt.domain", rtoken))
141 string = dlv->rcpt.domain;
142 else if (!strcasecmp("dest.user", rtoken))
143 string = dlv->dest.user;
144 else if (!strcasecmp("dest.domain", rtoken))
145 string = dlv->dest.domain;
146 else if (!strcasecmp("mda", rtoken)) {
147 string = mda_command;
148 replace = 0;
149 }
150 else if (!strcasecmp("mbox.from", rtoken)) {
151 if (snprintf(tmp, sizeof tmp, "%s@%s",
152 dlv->sender.user, dlv->sender.domain) >= (int)sizeof tmp)
153 return -1;
154 if (strcmp(tmp, "@") == 0)
155 (void)strlcpy(tmp, "MAILER-DAEMON", sizeof tmp);
156 string = tmp;
157 }
158 else
159 return -1;
160
161 if (string != tmp) {
162 if (strlcpy(tmp, string, sizeof tmp) >= sizeof tmp)
163 return -1;
164 string = tmp;
165 }
166
167 /* apply modifiers */
168 if (mods != NULL((void *)0)) {
169 do {
170 if ((sep = strchr(mods, '|')) != NULL((void *)0))
171 *sep++ = '\0';
172 for (i = 0; (size_t)i < nitems(token_modifiers)(sizeof((token_modifiers)) / sizeof((token_modifiers)[0])); ++i) {
173 if (!strcasecmp(token_modifiers[i].name, mods)) {
174 if (token_modifiers[i].f == NULL((void *)0)) {
175 raw = 1;
176 break;
177 }
178 if (!token_modifiers[i].f(tmp, sizeof tmp))
179 return -1; /* modifier error */
180 break;
181 }
182 }
183 if ((size_t)i == nitems(token_modifiers)(sizeof((token_modifiers)) / sizeof((token_modifiers)[0])))
184 return -1; /* modifier not found */
185 } while ((mods = sep) != NULL((void *)0));
186 }
187
188 if (!raw && replace)
189 for (i = 0; (size_t)i < strlen(tmp); ++i)
190 if (strchr(MAILADDR_ESCAPE"!#$%&'*?`{|}~", tmp[i]))
191 tmp[i] = ':';
192
193 /* expanded string is empty */
194 i = strlen(string);
195 if (i == 0)
196 return 0;
197
198 /* begin offset beyond end of string */
199 if (begoff >= i)
200 return -1;
201
202 /* end offset beyond end of string, make it end of string */
203 if (endoff >= i)
204 endoff = i - 1;
205
206 /* negative begin offset, make it relative to end of string */
207 if (begoff < 0)
208 begoff += i;
209 /* negative end offset, make it relative to end of string,
210 * note that end offset is inclusive.
211 */
212 if (endoff < 0)
213 endoff += i - 1;
214
215 /* check that final offsets are valid */
216 if (begoff < 0 || endoff < 0 || endoff < begoff)
217 return -1;
218 endoff += 1; /* end offset is inclusive */
219
220 /* check that substring does not exceed destination buffer length */
221 i = endoff - begoff;
222 if ((size_t)i + 1 >= len)
223 return -1;
224
225 string += begoff;
226 for (; i; i--) {
227 *dest = *string;
228 dest++;
229 string++;
230 }
231
232 return endoff - begoff;
233}
234
235
236ssize_t
237mda_expand_format(char *buf, size_t len, const struct deliver *dlv,
238 const struct userinfo *ui, const char *mda_command)
239{
240 char tmpbuf[EXPAND_BUFFER1024], *ptmp, *pbuf, *ebuf;
241 char exptok[EXPAND_BUFFER1024];
242 ssize_t exptoklen;
243 char token[MAXTOKENLEN128];
244 size_t ret, tmpret;
245
246 if (len < sizeof tmpbuf) {
247 log_warnx("mda_expand_format: tmp buffer < rule buffer");
248 return -1;
249 }
250
251 memset(tmpbuf, 0, sizeof tmpbuf);
252 pbuf = buf;
253 ptmp = tmpbuf;
254 ret = tmpret = 0;
Although the value stored to 'tmpret' is used in the enclosing expression, the value is never actually read from 'tmpret'
255
256 /* special case: ~/ only allowed expanded at the beginning */
257 if (strncmp(pbuf, "~/", 2) == 0) {
258 tmpret = snprintf(ptmp, sizeof tmpbuf, "%s/", ui->directory);
259 if (tmpret >= sizeof tmpbuf) {
260 log_warnx("warn: user directory for %s too large",
261 ui->directory);
262 return 0;
263 }
264 ret += tmpret;
265 ptmp += tmpret;
266 pbuf += 2;
267 }
268
269
270 /* expansion loop */
271 for (; *pbuf && ret < sizeof tmpbuf; ret += tmpret) {
272 if (*pbuf == '%' && *(pbuf + 1) == '%') {
273 *ptmp++ = *pbuf++;
274 pbuf += 1;
275 tmpret = 1;
276 continue;
277 }
278
279 if (*pbuf != '%' || *(pbuf + 1) != '{') {
280 *ptmp++ = *pbuf++;
281 tmpret = 1;
282 continue;
283 }
284
285 /* %{...} otherwise fail */
286 if (*(pbuf+1) != '{' || (ebuf = strchr(pbuf+1, '}')) == NULL((void *)0))
287 return 0;
288
289 /* extract token from %{token} */
290 if ((size_t)(ebuf - pbuf) - 1 >= sizeof token)
291 return 0;
292
293 memcpy(token, pbuf+2, ebuf-pbuf-1);
294 if (strchr(token, '}') == NULL((void *)0))
295 return 0;
296 *strchr(token, '}') = '\0';
297
298 exptoklen = mda_expand_token(exptok, sizeof exptok, token, dlv,
299 ui, mda_command);
300 if (exptoklen == -1)
301 return -1;
302
303 /* writing expanded token at ptmp will overflow tmpbuf */
304 if (sizeof (tmpbuf) - (ptmp - tmpbuf) <= (size_t)exptoklen)
305 return -1;
306
307 memcpy(ptmp, exptok, exptoklen);
308 pbuf = ebuf + 1;
309 ptmp += exptoklen;
310 tmpret = exptoklen;
311 }
312 if (ret >= sizeof tmpbuf)
313 return -1;
314
315 if ((ret = strlcpy(buf, tmpbuf, len)) >= len)
316 return -1;
317
318 return ret;
319}
320
321static int
322mod_lowercase(char *buf, size_t len)
323{
324 char tmp[EXPAND_BUFFER1024];
325
326 if (!lowercase(tmp, buf, sizeof tmp))
327 return 0;
328 if (strlcpy(buf, tmp, len) >= len)
329 return 0;
330 return 1;
331}
332
333static int
334mod_uppercase(char *buf, size_t len)
335{
336 char tmp[EXPAND_BUFFER1024];
337
338 if (!uppercase(tmp, buf, sizeof tmp))
339 return 0;
340 if (strlcpy(buf, tmp, len) >= len)
341 return 0;
342 return 1;
343}
344
345static int
346mod_strip(char *buf, size_t len)
347{
348 char *tag, *at;
349 unsigned int i;
350
351 /* gilles+hackers -> gilles */
352 if ((tag = strchr(buf, *env->sc_subaddressing_delim)) != NULL((void *)0)) {
353 /* gilles+hackers@poolp.org -> gilles@poolp.org */
354 if ((at = strchr(tag, '@')) != NULL((void *)0)) {
355 for (i = 0; i <= strlen(at); ++i)
356 tag[i] = at[i];
357 } else
358 *tag = '\0';
359 }
360 return 1;
361}