Bug Summary

File:src/usr.sbin/smtpd/smtpd/../mda_variables.c
Warning:line 256, 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.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name mda_variables.c -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 -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -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/llvm16/lib/clang/16 -I /usr/src/usr.sbin/smtpd/smtpd/.. -D IO_TLS -D QUEUE_PROFILING -internal-isystem /usr/local/llvm16/lib/clang/16/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 -fcf-protection=branch -fno-jump-tables -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/scan/2024-01-11-140451-98009-1 -x c /usr/src/usr.sbin/smtpd/smtpd/../mda_variables.c
1/* $OpenBSD: mda_variables.c,v 1.9 2023/03/19 16:43:44 millert 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 = NULL((void *)0);
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 (string == NULL((void *)0))
163 return -1;
164 if (strlcpy(tmp, string, sizeof tmp) >= sizeof tmp)
165 return -1;
166 string = tmp;
167 }
168
169 /* apply modifiers */
170 if (mods != NULL((void *)0)) {
171 do {
172 if ((sep = strchr(mods, '|')) != NULL((void *)0))
173 *sep++ = '\0';
174 for (i = 0; (size_t)i < nitems(token_modifiers)(sizeof((token_modifiers)) / sizeof((token_modifiers)[0])); ++i) {
175 if (!strcasecmp(token_modifiers[i].name, mods)) {
176 if (token_modifiers[i].f == NULL((void *)0)) {
177 raw = 1;
178 break;
179 }
180 if (!token_modifiers[i].f(tmp, sizeof tmp))
181 return -1; /* modifier error */
182 break;
183 }
184 }
185 if ((size_t)i == nitems(token_modifiers)(sizeof((token_modifiers)) / sizeof((token_modifiers)[0])))
186 return -1; /* modifier not found */
187 } while ((mods = sep) != NULL((void *)0));
188 }
189
190 if (!raw && replace)
191 for (i = 0; (size_t)i < strlen(tmp); ++i)
192 if (strchr(MAILADDR_ESCAPE"!#$%&'*?`{|}~", tmp[i]))
193 tmp[i] = ':';
194
195 /* expanded string is empty */
196 i = strlen(string);
197 if (i == 0)
198 return 0;
199
200 /* begin offset beyond end of string */
201 if (begoff >= i)
202 return -1;
203
204 /* end offset beyond end of string, make it end of string */
205 if (endoff >= i)
206 endoff = i - 1;
207
208 /* negative begin offset, make it relative to end of string */
209 if (begoff < 0)
210 begoff += i;
211 /* negative end offset, make it relative to end of string,
212 * note that end offset is inclusive.
213 */
214 if (endoff < 0)
215 endoff += i - 1;
216
217 /* check that final offsets are valid */
218 if (begoff < 0 || endoff < 0 || endoff < begoff)
219 return -1;
220 endoff += 1; /* end offset is inclusive */
221
222 /* check that substring does not exceed destination buffer length */
223 i = endoff - begoff;
224 if ((size_t)i + 1 >= len)
225 return -1;
226
227 string += begoff;
228 for (; i; i--) {
229 *dest = *string;
230 dest++;
231 string++;
232 }
233
234 return endoff - begoff;
235}
236
237
238ssize_t
239mda_expand_format(char *buf, size_t len, const struct deliver *dlv,
240 const struct userinfo *ui, const char *mda_command)
241{
242 char tmpbuf[EXPAND_BUFFER1024], *ptmp, *pbuf, *ebuf;
243 char exptok[EXPAND_BUFFER1024];
244 ssize_t exptoklen;
245 char token[MAXTOKENLEN128];
246 size_t ret, tmpret, toklen;
247
248 if (len < sizeof tmpbuf) {
249 log_warnx("mda_expand_format: tmp buffer < rule buffer");
250 return -1;
251 }
252
253 memset(tmpbuf, 0, sizeof tmpbuf);
254 pbuf = buf;
255 ptmp = tmpbuf;
256 ret = tmpret = 0;
Although the value stored to 'tmpret' is used in the enclosing expression, the value is never actually read from 'tmpret'
257
258 /* special case: ~/ only allowed expanded at the beginning */
259 if (strncmp(pbuf, "~/", 2) == 0) {
260 tmpret = snprintf(ptmp, sizeof tmpbuf, "%s/", ui->directory);
261 if (tmpret >= sizeof tmpbuf) {
262 log_warnx("warn: user directory for %s too large",
263 ui->directory);
264 return 0;
265 }
266 ret += tmpret;
267 ptmp += tmpret;
268 pbuf += 2;
269 }
270
271 /* expansion loop */
272 for (; *pbuf && ret < sizeof tmpbuf; ret += tmpret) {
273 if (*pbuf == '%' && *(pbuf + 1) == '%') {
274 *ptmp++ = *pbuf++;
275 pbuf += 1;
276 tmpret = 1;
277 continue;
278 }
279
280 if (*pbuf != '%' || *(pbuf + 1) != '{') {
281 *ptmp++ = *pbuf++;
282 tmpret = 1;
283 continue;
284 }
285
286 /* %{...} otherwise fail */
287 if ((ebuf = strchr(pbuf+2, '}')) == NULL((void *)0))
288 return 0;
289
290 /* extract token from %{token} */
291 toklen = ebuf - (pbuf+2);
292 if (toklen >= sizeof token)
293 return 0;
294
295 memcpy(token, pbuf+2, toklen);
296 token[toklen] = '\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}