Bug Summary

File:src/usr.sbin/npppd/npppd/radius_req.c
Warning:line 295, column 2
Potential leak of memory pointed to by 'lap'

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 radius_req.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/npppd/npppd/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.sbin/npppd/npppd/../common -I /usr/src/usr.sbin/npppd/npppd -I /usr/src/usr.sbin/npppd/npppd/../pptp -I /usr/src/usr.sbin/npppd/npppd/../l2tp -I /usr/src/usr.sbin/npppd/npppd/../pppoe -D USE_NPPPD_PPTP -D USE_NPPPD_L2TP -D USE_NPPPD_PPPOE -D __COPYRIGHT(x)= -D __RCSID(x)= -D NPPPD_MAX_IFACE=8 -D NPPPD_MAX_POOL=8 -D USE_NPPPD_MPPE -D USE_NPPPD_PIPEX -D USE_NPPPD_RADIUS -D USE_SA_COOKIE -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/npppd/npppd/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/npppd/npppd/radius_req.c
1/* $OpenBSD: radius_req.c,v 1.11 2015/12/05 18:43:36 mmcc Exp $ */
2
3/*-
4 * Copyright (c) 2009 Internet Initiative Japan Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28/**@file
29 * This file provides functions for RADIUS request using radius(3) and event(3).
30 * @author Yasuoka Masahiko
31 * $Id: radius_req.c,v 1.11 2015/12/05 18:43:36 mmcc Exp $
32 */
33#include <sys/types.h>
34#include <sys/time.h>
35#include <sys/socket.h>
36#include <netinet/in.h>
37#include <unistd.h>
38#include <stdio.h>
39#include <syslog.h>
40#include <stdlib.h>
41#include <debugutil.h>
42#include <time.h>
43#include <event.h>
44#include <string.h>
45#include <errno(*__errno()).h>
46
47#include "radius_req.h"
48#include <radius.h>
49
50#ifndef nitems
51#define nitems(_a)(sizeof((_a)) / sizeof((_a)[0])) (sizeof((_a)) / sizeof((_a)[0]))
52#endif
53
54struct overlapped {
55 struct event ev_sock;
56 int socket;
57 int ntry;
58 int max_tries;
59 int failovers;
60 int acct_delay_time;
61 int response_fn_calling;
62 struct sockaddr_storage ss;
63 struct timespec req_time;
64 void *context;
65 radius_response *response_fn;
66 char secret[MAX_RADIUS_SECRET128];
67 RADIUS_PACKET *pkt;
68 radius_req_setting *setting;
69};
70
71static int radius_request0 (struct overlapped *, int);
72static int radius_prepare_socket(struct overlapped *);
73static void radius_request_io_event (int, short, void *);
74static void radius_on_response(RADIUS_REQUEST_CTX, RADIUS_PACKET *, int, int);
75static int select_srcaddr(struct sockaddr const *, struct sockaddr *, socklen_t *);
76static void radius_req_setting_ref(radius_req_setting *);
77static void radius_req_setting_unref(radius_req_setting *);
78
79#ifdef RADIUS_REQ_DEBUG
80#define RADIUS_REQ_DBG(x) log_printf x
81#define RADIUS_REQ_ASSERT(cond) \
82 if (!(cond)) { \
83 fprintf(stderr(&__sF[2]), \
84 "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\
85 , __func__, __FILE__"/usr/src/usr.sbin/npppd/npppd/radius_req.c", __LINE__85); \
86 abort(); \
87 }
88#else
89#define RADIUS_REQ_ASSERT(cond)
90#define RADIUS_REQ_DBG(x)
91#endif
92
93/**
94 * Send RADIUS request message. The pkt(RADIUS packet) will be released
95 * by this implementation.
96 */
97void
98radius_request(RADIUS_REQUEST_CTX ctx, RADIUS_PACKET *pkt)
99{
100 uint32_t ival;
101 struct overlapped *lap;
102
103 RADIUS_REQ_ASSERT(pkt != NULL);
104 RADIUS_REQ_ASSERT(ctx != NULL);
105 lap = ctx;
106 lap->pkt = pkt;
107 if (radius_get_uint32_attr(pkt, RADIUS_TYPE_ACCT_DELAY_TIME41, &ival)
108 == 0)
109 lap->acct_delay_time = 1;
110 radius_request0(lap, 0);
111}
112
113/**
114 * Prepare NAS-IP-Address or NAS-IPv6-Address. If
115 * setting->server[setting->curr_server].sock is not initialized, address
116 * will be selected automatically.
117 */
118int
119radius_prepare_nas_address(radius_req_setting *setting,
120 RADIUS_PACKET *pkt)
121{
122 int af;
123 struct sockaddr_in *sin4;
124 struct sockaddr_in6 *sin6;
125 socklen_t socklen;
126
127 /* See RFC 2765, 3162 */
128 RADIUS_REQ_ASSERT(setting != NULL);
129
130 af = setting->server[setting->curr_server].peer.sin6.sin6_family;
131 RADIUS_REQ_ASSERT(af == AF_INET6 || af == AF_INET);
132
133 sin4 = &setting->server[setting->curr_server].sock.sin4;
134 sin6 = &setting->server[setting->curr_server].sock.sin6;
135
136 switch (af) {
137 case AF_INET2:
138 socklen = sizeof(*sin4);
139 if (sin4->sin_addr.s_addr == INADDR_ANY((u_int32_t)(0x00000000))) {
140 if (select_srcaddr((struct sockaddr const *)
141 &setting->server[setting->curr_server].peer,
142 (struct sockaddr *)sin4, &socklen) != 0) {
143 RADIUS_REQ_ASSERT("NOTREACHED" == NULL);
144 goto fail;
145 }
146 }
147 if (radius_put_ipv4_attr(pkt, RADIUS_TYPE_NAS_IP_ADDRESS4,
148 sin4->sin_addr) != 0)
149 goto fail;
150 break;
151 case AF_INET624:
152 socklen = sizeof(*sin6);
153 if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)((*(const u_int32_t *)(const void *)(&(&sin6->sin6_addr
)->__u6_addr.__u6_addr8[0]) == 0) && (*(const u_int32_t
*)(const void *)(&(&sin6->sin6_addr)->__u6_addr
.__u6_addr8[4]) == 0) && (*(const u_int32_t *)(const void
*)(&(&sin6->sin6_addr)->__u6_addr.__u6_addr8[8
]) == 0) && (*(const u_int32_t *)(const void *)(&
(&sin6->sin6_addr)->__u6_addr.__u6_addr8[12]) == 0)
)
) {
154 if (select_srcaddr((struct sockaddr const *)
155 &setting->server[setting->curr_server].peer,
156 (struct sockaddr *)sin4, &socklen) != 0) {
157 RADIUS_REQ_ASSERT("NOTREACHED" == NULL);
158 goto fail;
159 }
160 }
161 if (radius_put_raw_attr(pkt, RADIUS_TYPE_NAS_IPV6_ADDRESS95,
162 sin6->sin6_addr.s6_addr__u6_addr.__u6_addr8, sizeof(sin6->sin6_addr.s6_addr__u6_addr.__u6_addr8))
163 != 0)
164 goto fail;
165 break;
166 }
167
168 return 0;
169fail:
170 return 1;
171}
172
173
174/** Checks whether the request can fail over to another server */
175int
176radius_request_can_failover(RADIUS_REQUEST_CTX ctx)
177{
178 struct overlapped *lap;
179 radius_req_setting *setting;
180
181 lap = ctx;
182 setting = lap->setting;
183
184 if (lap->failovers >= setting->max_failovers)
185 return 0;
186 if (memcmp(&lap->ss, &setting->server[setting->curr_server].peer,
187 setting->server[setting->curr_server].peer.sin6.sin6_len) == 0)
188 /* flagged server doesn't differ from the last server. */
189 return 0;
190
191 return 1;
192}
193
194/** Send RADIUS request failing over to another server. */
195int
196radius_request_failover(RADIUS_REQUEST_CTX ctx)
197{
198 struct overlapped *lap;
199
200 lap = ctx;
201 RADIUS_REQ_ASSERT(lap != NULL);
202 RADIUS_REQ_ASSERT(lap->socket >= 0)
203
204 if (!radius_request_can_failover(lap))
205 return -1;
206
207 if (radius_prepare_socket(lap) != 0)
208 return -1;
209
210 if (radius_request0(lap, 1) != 0)
211 return -1;
212
213 lap->failovers++;
214
215 return 0;
216}
217
218static int
219radius_prepare_socket(struct overlapped *lap)
220{
221 int sock;
222 radius_req_setting *setting;
223 struct sockaddr *sa;
224
225 setting = lap->setting;
226 if (lap->socket >= 0)
227 close(lap->socket);
228 lap->socket = -1;
229
230 sa = (struct sockaddr *)&setting->server[setting->curr_server].peer;
231
232 if ((sock = socket(sa->sa_family, SOCK_DGRAM2, IPPROTO_UDP17)) < 0) {
233 log_printf(LOG_ERR3, "socket() failed in %s: %m", __func__);
234 return -1;
235 }
236 if (connect(sock, sa, sa->sa_len) != 0) {
237 log_printf(LOG_ERR3, "connect() failed in %s: %m", __func__);
238 close(sock);
239 return -1;
240 }
241 memcpy(&lap->ss, sa, sa->sa_len);
242 lap->socket = sock;
243 memcpy(lap->secret, setting->server[setting->curr_server].secret,
244 sizeof(lap->secret));
245 lap->ntry = lap->max_tries;
246
247 return 0;
248}
249
250/**
251 * Prepare sending RADIUS request. This implementation will call back to
252 * notice that it receives the response or it fails for timeouts to the
253 * The context that is set as 'pctx' and response packet that is given
254 * by the callback function will be released by this implementation internally.
255 * @param setting Setting for RADIUS server or request.
256 * @param context Context for the caller.
257 * @param pctx Pointer to the space for context of RADIUS request
258 * (RADIUS_REQUEST_CTX). This will be used for canceling.
259 * NULL can be specified when you don't need.
260 * @param response_fn Specify callback function as a pointer. The function
261 * will be called when it receives a response or when
262 * request fails for timeouts.
263 * @param timeout response timeout in second.
264 */
265int
266radius_prepare(radius_req_setting *setting, void *context,
267 RADIUS_REQUEST_CTX *pctx, radius_response response_fn)
268{
269 struct overlapped *lap;
270
271 RADIUS_REQ_ASSERT(setting != NULL);
272 lap = NULL((void *)0);
273
274 if (setting->server[setting->curr_server].enabled == 0)
1
Assuming field 'enabled' is not equal to 0
2
Taking false branch
275 return 1;
276 if ((lap = calloc(1, sizeof(struct overlapped))) == NULL((void *)0)) {
3
Memory is allocated
4
Assuming the condition is false
5
Taking false branch
277 log_printf(LOG_ERR3, "calloc() failed in %s: %m", __func__);
278 goto fail;
279 }
280 lap->context = context;
281 lap->response_fn = response_fn;
282 lap->socket = -1;
283 lap->setting = setting;
284
285 lap->max_tries = setting->max_tries;
286 if (lap->max_tries <= 0)
6
Assuming field 'max_tries' is > 0
7
Taking false branch
287 lap->max_tries = 3; /* default max tries */
288
289 if (radius_prepare_socket(lap) != 0)
8
Taking false branch
290 goto fail;
291
292 if (pctx != NULL((void *)0))
9
Assuming 'pctx' is equal to NULL
10
Taking false branch
293 *pctx = lap;
294
295 radius_req_setting_ref(setting);
11
Potential leak of memory pointed to by 'lap'
296
297 return 0;
298fail:
299 free(lap);
300
301 return 1;
302}
303
304/**
305 * Cancel the RADIUS request.
306 * @param The context received by {@link radius_request()}
307 */
308void
309radius_cancel_request(RADIUS_REQUEST_CTX ctx)
310{
311 struct overlapped *lap = ctx;
312
313 /*
314 * Don't call this function from the callback function.
315 * The context will be freed after the callback function is called.
316 */
317 RADIUS_REQ_ASSERT(lap->response_fn_calling == 0);
318 if (lap->response_fn_calling != 0)
319 return;
320
321 if (lap->socket >= 0) {
322 event_del(&lap->ev_sock);
323 close(lap->socket);
324 lap->socket = -1;
325 }
326 if (lap->pkt != NULL((void *)0)) {
327 radius_delete_packet(lap->pkt);
328 lap->pkt = NULL((void *)0);
329 }
330 radius_req_setting_unref(lap->setting);
331
332 memset(lap->secret, 0x41, sizeof(lap->secret));
333
334 free(lap);
335}
336
337/** Return the shared secret for RADIUS server that is used by this context. */
338const char *
339radius_get_server_secret(RADIUS_REQUEST_CTX ctx)
340{
341 struct overlapped *lap;
342
343 lap = ctx;
344 RADIUS_REQ_ASSERT(lap != NULL);
345
346 return lap->secret;
347}
348
349/** Return the address of RADIUS server that is used by this context. */
350struct sockaddr *
351radius_get_server_address(RADIUS_REQUEST_CTX ctx)
352{
353 struct overlapped *lap;
354
355 lap = ctx;
356 RADIUS_REQ_ASSERT(lap != NULL);
357
358 return (struct sockaddr *)&lap->ss;
359}
360
361static int
362radius_request0(struct overlapped *lap, int new_message)
363{
364 struct timeval tv0;
365
366 RADIUS_REQ_ASSERT(lap->ntry > 0);
367
368 if (lap->acct_delay_time != 0) {
369 struct timespec curr, delta;
370
371 if (clock_gettime(CLOCK_MONOTONIC3, &curr) != 0) {
372 log_printf(LOG_CRIT2,
373 "clock_gettime(CLOCK_MONOTONIC,) failed: %m");
374 RADIUS_REQ_ASSERT(0);
375 }
376 if (!timespecisset(&lap->req_time)((&lap->req_time)->tv_sec || (&lap->req_time
)->tv_nsec)
)
377 lap->req_time = curr;
378 else {
379 timespecsub(&curr, &lap->req_time, &delta)do { (&delta)->tv_sec = (&curr)->tv_sec - (&
lap->req_time)->tv_sec; (&delta)->tv_nsec = (&
curr)->tv_nsec - (&lap->req_time)->tv_nsec; if (
(&delta)->tv_nsec < 0) { (&delta)->tv_sec--;
(&delta)->tv_nsec += 1000000000L; } } while (0)
;
380 if (radius_set_uint32_attr(lap->pkt,
381 RADIUS_TYPE_ACCT_DELAY_TIME41, delta.tv_sec) == 0) {
382 radius_update_id(lap->pkt);
383 new_message = 1;
384 }
385 }
386 }
387 if (new_message) {
388 radius_set_accounting_request_authenticator(lap->pkt,
389 radius_get_server_secret(lap));
390 }
391
392 lap->ntry--;
393 if (radius_send(lap->socket, lap->pkt, 0) != 0) {
394 log_printf(LOG_ERR3, "sendto() failed in %s: %m",
395 __func__);
396 radius_on_response(lap, NULL((void *)0), RADIUS_REQUEST_ERROR0x0001, 1);
397 return 1;
398 }
399 tv0.tv_usec = 0;
400 tv0.tv_sec = lap->setting->timeout;
401
402 event_set(&lap->ev_sock, lap->socket, EV_READ0x02 | EV_PERSIST0x10,
403 radius_request_io_event, lap);
404 event_add(&lap->ev_sock, &tv0);
405
406 return 0;
407}
408
409static void
410radius_request_io_event(int fd, short evmask, void *context)
411{
412 struct overlapped *lap;
413 struct sockaddr_storage ss;
414 int flags;
415 socklen_t len;
416 RADIUS_PACKET *respkt;
417
418 RADIUS_REQ_ASSERT(context != NULL);
419
420 lap = context;
421 respkt = NULL((void *)0);
422 flags = 0;
423 if ((evmask & EV_READ0x02) != 0) {
424 RADIUS_REQ_ASSERT(lap->socket >= 0);
425 if (lap->socket < 0)
426 return;
427 RADIUS_REQ_ASSERT(lap->pkt != NULL);
428 memset(&ss, 0, sizeof(ss));
429 len = sizeof(ss);
430 if ((respkt = radius_recv(lap->socket, 0)) == NULL((void *)0)) {
431 RADIUS_REQ_DBG((LOG_DEBUG,
432 "radius_recv() on %s(): %m", __func__));
433 /*
434 * Ignore error by icmp. Wait a response from the
435 * server anyway, it may eventually become ready.
436 */
437 switch (errno(*__errno())) {
438 case EHOSTDOWN64: case EHOSTUNREACH65: case ECONNREFUSED61:
439 return; /* sleep the rest of timeout time */
440 }
441 flags |= RADIUS_REQUEST_ERROR0x0001;
442 } else if (lap->secret[0] == '\0') {
443 flags |= RADIUS_REQUEST_CHECK_AUTHENTICATOR_NO_CHECK0x0020;
444 } else {
445 radius_set_request_packet(respkt, lap->pkt);
446 if (!radius_check_response_authenticator(respkt,
447 lap->secret))
448 flags |= RADIUS_REQUEST_CHECK_AUTHENTICATOR_OK0x0010;
449 }
450 radius_on_response(lap, respkt, flags, 0);
451 radius_delete_packet(respkt);
452 } else if ((evmask & EV_TIMEOUT0x01) != 0) {
453 if (lap->ntry > 0) {
454 RADIUS_REQ_DBG((LOG_DEBUG,
455 "%s() timed out retry", __func__));
456 radius_request0(lap, 0);
457 return;
458 }
459 RADIUS_REQ_DBG((LOG_DEBUG, "%s() timed out", __func__));
460 flags |= RADIUS_REQUEST_TIMEOUT0x0002;
461 radius_on_response(lap, NULL((void *)0), flags, 1);
462 }
463}
464
465static void
466radius_on_response(RADIUS_REQUEST_CTX ctx, RADIUS_PACKET *pkt, int flags,
467 int server_failure)
468{
469 struct overlapped *lap;
470 int failovers;
471
472 lap = ctx;
473 if (server_failure) {
474 int i, n;
475 struct sockaddr *sa_curr;
476
477 sa_curr = (struct sockaddr *)&lap->setting->server[
478 lap->setting->curr_server].peer;
479 if (sa_curr->sa_len == lap->ss.ss_len &&
480 memcmp(sa_curr, &lap->ss, sa_curr->sa_len) == 0) {
481 /*
482 * The server on failure is flagged as the current.
483 * change the current
484 */
485 for (i = 1; i < nitems(lap->setting->server)(sizeof((lap->setting->server)) / sizeof((lap->setting
->server)[0]))
; i++) {
486 n = (lap->setting->curr_server + i) %
487 nitems(lap->setting->server)(sizeof((lap->setting->server)) / sizeof((lap->setting
->server)[0]))
;
488 if (lap->setting->server[n].enabled) {
489 lap->setting->curr_server = n;
490 break;
491 }
492 }
493 }
494 }
495
496 failovers = lap->failovers;
497 if (lap->response_fn != NULL((void *)0)) {
498 lap->response_fn_calling++;
499 lap->response_fn(lap->context, pkt, flags, ctx);
500 lap->response_fn_calling--;
501 }
502 if (failovers == lap->failovers)
503 radius_cancel_request(lap);
504}
505
506static int
507select_srcaddr(struct sockaddr const *dst, struct sockaddr *src,
508 socklen_t *srclen)
509{
510 int sock;
511
512 sock = -1;
513 if ((sock = socket(dst->sa_family, SOCK_DGRAM2, IPPROTO_UDP17)) < 0)
514 goto fail;
515 if (connect(sock, dst, dst->sa_len) != 0)
516 goto fail;
517 if (getsockname(sock, src, srclen) != 0)
518 goto fail;
519
520 close(sock);
521
522 return 0;
523fail:
524 if (sock >= 0)
525 close(sock);
526
527 return 1;
528}
529
530radius_req_setting *
531radius_req_setting_create(void)
532{
533 return calloc(1, sizeof(radius_req_setting));
534}
535
536int
537radius_req_setting_has_server(radius_req_setting *setting)
538{
539 return setting->server[setting->curr_server].enabled;
540}
541
542void
543radius_req_setting_destroy(radius_req_setting *setting)
544{
545 setting->destroyed = 1;
546
547 if (setting->refcnt == 0)
548 free(setting);
549}
550
551static void
552radius_req_setting_ref(radius_req_setting *setting)
553{
554 setting->refcnt++;
555}
556
557static void
558radius_req_setting_unref(radius_req_setting *setting)
559{
560 setting->refcnt--;
561 if (setting->destroyed)
562 radius_req_setting_destroy(setting);
563}