Bug Summary

File:src/usr.sbin/smtpd/smtpd/../proxy.c
Warning:line 195, column 4
Value stored to 'buf' is never read

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 proxy.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/../proxy.c
1/*
2 * Copyright (c) 2017 Antoine Kaufmann <toni@famkaufmann.info>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <sys/un.h>
18
19#include <inttypes.h>
20#include <stdlib.h>
21#include <string.h>
22
23#include "smtpd.h"
24#include "log.h"
25
26/*
27 * The PROXYv2 protocol is described here:
28 * http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
29 */
30
31#define PROXY_CLOCAL0x0 0x0
32#define PROXY_CPROXY0x1 0x1
33#define PROXY_AF_UNSPEC0x0 0x0
34#define PROXY_AF_INET0x1 0x1
35#define PROXY_AF_INET60x2 0x2
36#define PROXY_AF_UNIX0x3 0x3
37#define PROXY_TF_UNSPEC0x0 0x0
38#define PROXY_TF_STREAM0x1 0x1
39#define PROXY_TF_DGRAM0x2 0x2
40
41#define PROXY_SESSION_TIMEOUT300 300
42
43static const uint8_t pv2_signature[] = {
44 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D,
45 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A
46};
47
48struct proxy_hdr_v2 {
49 uint8_t sig[12];
50 uint8_t ver_cmd;
51 uint8_t fam;
52 uint16_t len;
53} __attribute__((packed));
54
55
56struct proxy_addr_ipv4 {
57 uint32_t src_addr;
58 uint32_t dst_addr;
59 uint16_t src_port;
60 uint16_t dst_port;
61} __attribute__((packed));
62
63struct proxy_addr_ipv6 {
64 uint8_t src_addr[16];
65 uint8_t dst_addr[16];
66 uint16_t src_port;
67 uint16_t dst_port;
68} __attribute__((packed));
69
70struct proxy_addr_unix {
71 uint8_t src_addr[108];
72 uint8_t dst_addr[108];
73} __attribute__((packed));
74
75union proxy_addr {
76 struct proxy_addr_ipv4 ipv4;
77 struct proxy_addr_ipv6 ipv6;
78 struct proxy_addr_unix un;
79} __attribute__((packed));
80
81struct proxy_session {
82 struct listener *l;
83 struct io *io;
84
85 uint64_t id;
86 int fd;
87 uint16_t header_len;
88 uint16_t header_total;
89 uint16_t addr_len;
90 uint16_t addr_total;
91
92 struct sockaddr_storage ss;
93 struct proxy_hdr_v2 hdr;
94 union proxy_addr addr;
95
96 void (*cb_accepted)(struct listener *, int,
97 const struct sockaddr_storage *, struct io *);
98 void (*cb_dropped)(struct listener *, int,
99 const struct sockaddr_storage *);
100};
101
102static void proxy_io(struct io *, int, void *);
103static void proxy_error(struct proxy_session *, const char *, const char *);
104static int proxy_header_validate(struct proxy_session *);
105static int proxy_translate_ss(struct proxy_session *);
106
107int
108proxy_session(struct listener *listener, int sock,
109 const struct sockaddr_storage *ss,
110 void (*accepted)(struct listener *, int,
111 const struct sockaddr_storage *, struct io *),
112 void (*dropped)(struct listener *, int,
113 const struct sockaddr_storage *));
114
115int
116proxy_session(struct listener *listener, int sock,
117 const struct sockaddr_storage *ss,
118 void (*accepted)(struct listener *, int,
119 const struct sockaddr_storage *, struct io *),
120 void (*dropped)(struct listener *, int,
121 const struct sockaddr_storage *))
122{
123 struct proxy_session *s;
124
125 if ((s = calloc(1, sizeof(*s))) == NULL((void *)0))
126 return (-1);
127
128 if ((s->io = io_new()) == NULL((void *)0)) {
129 free(s);
130 return (-1);
131 }
132
133 s->id = generate_uid();
134 s->l = listener;
135 s->fd = sock;
136 s->header_len = 0;
137 s->addr_len = 0;
138 s->ss = *ss;
139 s->cb_accepted = accepted;
140 s->cb_dropped = dropped;
141
142 io_set_callback(s->io, proxy_io, s);
143 io_set_fd(s->io, sock);
144 io_set_timeout(s->io, PROXY_SESSION_TIMEOUT300 * 1000);
145 io_set_read(s->io);
146
147 log_info("%016"PRIx64"llx"" smtp event=proxy address=%s",
148 s->id, ss_to_text(&s->ss));
149
150 return 0;
151}
152
153static void
154proxy_io(struct io *io, int evt, void *arg)
155{
156 struct proxy_session *s = arg;
157 struct proxy_hdr_v2 *h = &s->hdr;
158 uint8_t *buf;
159 size_t len, off;
160
161 switch (evt) {
162
163 case IO_DATAIN:
164 buf = io_data(io);
165 len = io_datalen(io);
166
167 if (s->header_len < sizeof(s->hdr)) {
168 /* header is incomplete */
169 off = sizeof(s->hdr) - s->header_len;
170 off = (len < off ? len : off);
171 memcpy((uint8_t *) &s->hdr + s->header_len, buf, off);
172
173 s->header_len += off;
174 buf += off;
175 len -= off;
176 io_drop(s->io, off);
177
178 if (s->header_len < sizeof(s->hdr)) {
179 /* header is still not complete */
180 return;
181 }
182
183 if (proxy_header_validate(s) != 0)
184 return;
185 }
186
187 if (s->addr_len < s->addr_total) {
188 /* address is incomplete */
189 off = s->addr_total - s->addr_len;
190 off = (len < off ? len : off);
191 memcpy((uint8_t *) &s->addr + s->addr_len, buf, off);
192
193 s->header_len += off;
194 s->addr_len += off;
195 buf += off;
Value stored to 'buf' is never read
196 len -= off;
197 io_drop(s->io, off);
198
199 if (s->addr_len < s->addr_total) {
200 /* address is still not complete */
201 return;
202 }
203 }
204
205 if (s->header_len < s->header_total) {
206 /* additional parameters not complete */
207 /* these are ignored for now, but we still need to drop
208 * the bytes from the buffer */
209 off = s->header_total - s->header_len;
210 off = (len < off ? len : off);
211
212 s->header_len += off;
213 io_drop(s->io, off);
214
215 if (s->header_len < s->header_total)
216 /* not complete yet */
217 return;
218 }
219
220 switch(h->ver_cmd & 0xF) {
221 case PROXY_CLOCAL0x0:
222 /* local address, no need to modify ss */
223 break;
224
225 case PROXY_CPROXY0x1:
226 if (proxy_translate_ss(s) != 0)
227 return;
228 break;
229
230 default:
231 proxy_error(s, "protocol error", "unknown command");
232 return;
233 }
234
235 s->cb_accepted(s->l, s->fd, &s->ss, s->io);
236 /* we passed off s->io, so it does not need to be freed here */
237 free(s);
238 break;
239
240 case IO_TIMEOUT:
241 proxy_error(s, "timeout", NULL((void *)0));
242 break;
243
244 case IO_DISCONNECTED:
245 proxy_error(s, "disconnected", NULL((void *)0));
246 break;
247
248 case IO_ERROR:
249 proxy_error(s, "IO error", io_error(io));
250 break;
251
252 default:
253 fatalx("proxy_io()");
254 }
255
256}
257
258static void
259proxy_error(struct proxy_session *s, const char *reason, const char *extra)
260{
261 if (extra)
262 log_info("proxy %p event=closed address=%s reason=\"%s (%s)\"",
263 s, ss_to_text(&s->ss), reason, extra);
264 else
265 log_info("proxy %p event=closed address=%s reason=\"%s\"",
266 s, ss_to_text(&s->ss), reason);
267
268 s->cb_dropped(s->l, s->fd, &s->ss);
269 io_free(s->io);
270 free(s);
271}
272
273static int
274proxy_header_validate(struct proxy_session *s)
275{
276 struct proxy_hdr_v2 *h = &s->hdr;
277
278 if (memcmp(h->sig, pv2_signature,
279 sizeof(pv2_signature)) != 0) {
280 proxy_error(s, "protocol error", "invalid signature");
281 return (-1);
282 }
283
284 if ((h->ver_cmd >> 4) != 2) {
285 proxy_error(s, "protocol error", "invalid version");
286 return (-1);
287 }
288
289 switch (h->fam) {
290 case (PROXY_AF_UNSPEC0x0 << 4 | PROXY_TF_UNSPEC0x0):
291 s->addr_total = 0;
292 break;
293
294 case (PROXY_AF_INET0x1 << 4 | PROXY_TF_STREAM0x1):
295 s->addr_total = sizeof(s->addr.ipv4);
296 break;
297
298 case (PROXY_AF_INET60x2 << 4 | PROXY_TF_STREAM0x1):
299 s->addr_total = sizeof(s->addr.ipv6);
300 break;
301
302 case (PROXY_AF_UNIX0x3 << 4 | PROXY_TF_STREAM0x1):
303 s->addr_total = sizeof(s->addr.un);
304 break;
305
306 default:
307 proxy_error(s, "protocol error", "unsupported address family");
308 return (-1);
309 }
310
311 s->header_total = ntohs(h->len)(__uint16_t)(__builtin_constant_p(h->len) ? (__uint16_t)((
(__uint16_t)(h->len) & 0xffU) << 8 | ((__uint16_t
)(h->len) & 0xff00U) >> 8) : __swap16md(h->len
))
;
312 if (s->header_total > UINT16_MAX0xffff - sizeof(struct proxy_hdr_v2)) {
313 proxy_error(s, "protocol error", "header too long");
314 return (-1);
315 }
316 s->header_total += sizeof(struct proxy_hdr_v2);
317
318 if (s->header_total < sizeof(struct proxy_hdr_v2) + s->addr_total) {
319 proxy_error(s, "protocol error", "address info too short");
320 return (-1);
321 }
322
323 return 0;
324}
325
326static int
327proxy_translate_ss(struct proxy_session *s)
328{
329 struct sockaddr_in *sin = (struct sockaddr_in *) &s->ss;
330 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &s->ss;
331 struct sockaddr_un *sun = (struct sockaddr_un *) &s->ss;
332 size_t sun_len;
333
334 switch (s->hdr.fam) {
335 case (PROXY_AF_UNSPEC0x0 << 4 | PROXY_TF_UNSPEC0x0):
336 /* unspec: only supported for local */
337 proxy_error(s, "address translation", "UNSPEC family not "
338 "supported for PROXYing");
339 return (-1);
340
341 case (PROXY_AF_INET0x1 << 4 | PROXY_TF_STREAM0x1):
342 memset(&s->ss, 0, sizeof(s->ss));
343 sin->sin_family = AF_INET2;
344 sin->sin_port = s->addr.ipv4.src_port;
345 sin->sin_addr.s_addr = s->addr.ipv4.src_addr;
346 break;
347
348 case (PROXY_AF_INET60x2 << 4 | PROXY_TF_STREAM0x1):
349 memset(&s->ss, 0, sizeof(s->ss));
350 sin6->sin6_family = AF_INET624;
351 sin6->sin6_port = s->addr.ipv6.src_port;
352 memcpy(sin6->sin6_addr.s6_addr__u6_addr.__u6_addr8, s->addr.ipv6.src_addr,
353 sizeof(s->addr.ipv6.src_addr));
354 break;
355
356 case (PROXY_AF_UNIX0x3 << 4 | PROXY_TF_STREAM0x1):
357 memset(&s->ss, 0, sizeof(s->ss));
358 sun_len = strnlen(s->addr.un.src_addr,
359 sizeof(s->addr.un.src_addr));
360 if (sun_len > sizeof(sun->sun_path)) {
361 proxy_error(s, "address translation", "Unix socket path"
362 " longer than supported");
363 return (-1);
364 }
365 sun->sun_family = AF_UNIX1;
366 memcpy(sun->sun_path, s->addr.un.src_addr, sun_len);
367 break;
368
369 default:
370 fatalx("proxy_translate_ss()");
371 }
372
373 return 0;
374}