Bug Summary

File:src/usr.sbin/lpr/lpd/allowedhost.c
Warning:line 222, column 10
Access to field 'ai_canonname' results in a dereference of a null pointer (loaded from variable 'res')

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 allowedhost.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/lpr/lpd/obj -resource-dir /usr/local/llvm16/lib/clang/16 -I /usr/src/usr.sbin/lpr/lpd/../common_source -internal-isystem /usr/local/llvm16/lib/clang/16/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/lpr/lpd/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/lpr/lpd/allowedhost.c
1/*
2 * Copyright (c) 1995, 1996, 1998 Theo de Raadt. All rights reserved.
3 * Copyright (c) 1983, 1993, 1994
4 * The Regents of the University of California. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the University nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <sys/socket.h>
32#include <sys/types.h>
33
34#include <netinet/in.h>
35#include <arpa/inet.h>
36
37#include <ctype.h>
38#include <limits.h>
39#include <netdb.h>
40#include <netgroup.h>
41#include <signal.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <syslog.h>
46#include <unistd.h>
47
48static int checkhost(struct sockaddr *, socklen_t, const char *);
49static char *gethostloop(struct sockaddr *, socklen_t);
50
51/*
52 * Check whether the specified addr is listed in hostf.
53 * Returns 0 if allowed, else -1.
54 */
55int
56allowedhost(FILE *hostf, struct sockaddr *raddr, socklen_t salen)
57{
58 char *cp, *ep, *line = NULL((void *)0);
59 char *ahost, *rhost = (char *)-1;
60 char domain[HOST_NAME_MAX255+1];
61 size_t linesize = 0;
62 ssize_t linelen;
63 int hostok = 0;
64
65 getdomainname(domain, sizeof(domain));
66
67 while ((linelen = getline(&line, &linesize, hostf)) != -1) {
1
Assuming the condition is true
2
Loop condition is true. Entering loop body
68 cp = line;
69 ep = line + linelen;
70 if (*cp == '#')
3
Assuming the condition is false
71 continue;
72 while (cp < ep && !isspace((unsigned char)*cp)) {
4
Assuming 'cp' is < 'ep'
5
Assuming the character is not a whitespace character
6
Loop condition is true. Entering loop body
11
Assuming 'cp' is < 'ep'
12
Assuming the character is not a whitespace character
13
Loop condition is true. Entering loop body
18
Assuming 'cp' is >= 'ep'
73 if (!isprint((unsigned char)*cp))
7
Assuming the character is printable
14
Assuming the character is printable
74 goto bail;
75 *cp = isupper((unsigned char)*cp) ?
8
Taking false branch
9
Assuming the character is not an uppercase letter
10
'?' condition is false
15
Taking false branch
16
Assuming the character is an uppercase letter
17
'?' condition is true
76 tolower((unsigned char)*cp) : *cp;
77 cp++;
78 }
79 if (cp
18.1
'cp' is not equal to 'line'
== line)
19
Taking false branch
80 continue;
81 *cp = '\0';
82
83 ahost = line;
84 if (strlen(ahost) > HOST_NAME_MAX255)
20
Assuming the condition is false
21
Taking false branch
85 continue;
86
87 /*
88 * innetgr() must lookup a hostname (we do not attempt
89 * to change the semantics so that netgroups may have
90 * #.#.#.# addresses in the list.)
91 */
92 switch (ahost[0]) {
22
Control jumps to 'case 45:' at line 94
93 case '+':
94 case '-':
95 switch (ahost[1]) {
23
Control jumps to 'case 64:' at line 99
96 case '\0':
97 hostok = 1;
98 break;
99 case '@':
100 if (rhost == (char *)-1)
24
Taking true branch
101 rhost = gethostloop(raddr, salen);
25
Calling 'gethostloop'
102 hostok = 0;
103 if (rhost)
104 hostok = innetgr(&ahost[2], rhost,
105 NULL((void *)0), domain);
106 break;
107 default:
108 hostok = checkhost(raddr, salen, &ahost[1]);
109 break;
110 }
111 if (ahost[0] == '-')
112 hostok = -hostok;
113 break;
114 default:
115 hostok = checkhost(raddr, salen, ahost);
116 break;
117 }
118
119 /* Check if we got a match (positive or negative). */
120 if (hostok != 0)
121 break;
122 }
123bail:
124 free(line);
125 return (hostok > 0 ? 0 : -1);
126}
127
128/*
129 * Returns 1 if match, 0 if no match. If we do not find any
130 * semblance of an A->PTR->A loop, allow a simple #.#.#.# match to work.
131 */
132static int
133checkhost(struct sockaddr *raddr, socklen_t salen, const char *lhost)
134{
135 struct addrinfo hints, *res, *r;
136 char h1[NI_MAXHOST256], h2[NI_MAXHOST256];
137 int error;
138 const int niflags = NI_NUMERICHOST1;
139
140 h1[0] = '\0';
141 if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL((void *)0), 0, niflags) != 0)
142 return (0);
143
144 /* Resolve laddr into sockaddr */
145 memset(&hints, 0, sizeof(hints));
146 hints.ai_family = raddr->sa_family;
147 hints.ai_socktype = SOCK_DGRAM2; /*dummy*/
148 res = NULL((void *)0);
149 error = getaddrinfo(lhost, "0", &hints, &res);
150 if (error)
151 return (0);
152
153 /*
154 * Try string comparisons between raddr and laddr.
155 */
156 for (r = res; r; r = r->ai_next) {
157 h2[0] = '\0';
158 if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2),
159 NULL((void *)0), 0, niflags) != 0)
160 continue;
161 if (strcasecmp(h1, h2) == 0) {
162 freeaddrinfo(res);
163 return (1);
164 }
165 }
166
167 /* No match. */
168 freeaddrinfo(res);
169 return (0);
170}
171
172/*
173 * Return the hostname associated with the supplied address.
174 * Do a reverse lookup as well for security. If a loop cannot
175 * be found, pack the result of inet_ntoa() into the string.
176 */
177static char *
178gethostloop(struct sockaddr *raddr, socklen_t salen)
179{
180 static char remotehost[NI_MAXHOST256];
181 char h1[NI_MAXHOST256], h2[NI_MAXHOST256];
182 struct addrinfo hints, *res, *r;
183 int error;
184 const int niflags = NI_NUMERICHOST1;
185
186 h1[0] = remotehost[0] = '\0';
187 if (getnameinfo(raddr, salen, remotehost, sizeof(remotehost),
26
Assuming the condition is false
27
Taking false branch
188 NULL((void *)0), 0, NI_NAMEREQD8) != 0)
189 return (NULL((void *)0));
190 if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL((void *)0), 0, niflags) != 0)
28
Assuming the condition is false
29
Taking false branch
191 return (NULL((void *)0));
192
193 /*
194 * Look up the name and check that the supplied
195 * address is in the list
196 */
197 memset(&hints, 0, sizeof(hints));
198 hints.ai_family = raddr->sa_family;
199 hints.ai_socktype = SOCK_DGRAM2; /*dummy*/
200 hints.ai_flags = AI_CANONNAME2;
201 res = NULL((void *)0);
202 error = getaddrinfo(remotehost, "0", &hints, &res);
30
Value assigned to 'res'
203 if (error)
31
Assuming 'error' is 0
32
Taking false branch
204 return (NULL((void *)0));
205
206 for (r = res; r; r = r->ai_next) {
33
Assuming pointer value is null
34
Loop condition is false. Execution continues on line 221
207 h2[0] = '\0';
208 if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2),
209 NULL((void *)0), 0, niflags) != 0)
210 continue;
211 if (strcasecmp(h1, h2) == 0) {
212 freeaddrinfo(res);
213 return (remotehost);
214 }
215 }
216
217 /*
218 * Either the DNS administrator has made a configuration
219 * mistake, or someone has attempted to spoof us.
220 */
221 syslog(LOG_NOTICE5, "lpd: address %s not listed for host %s",
222 h1, res->ai_canonname ? res->ai_canonname : remotehost);
35
Access to field 'ai_canonname' results in a dereference of a null pointer (loaded from variable 'res')
223 freeaddrinfo(res);
224 return (NULL((void *)0));
225}
226
227#ifdef DEBUG
228int
229main(int argc, char *argv[])
230{
231 struct addrinfo hints;
232 int i;
233
234 memset(&hints, 0, sizeof(hints));
235 hints.ai_family = PF_UNSPEC0;
236 hints.ai_socktype = SOCK_STREAM1;
237
238 for (i = 1; i < argc; i++) {
239 struct addrinfo *res0;
240 int error = getaddrinfo(argv[i], NULL((void *)0), &hints, &res0);
241 if (error) {
242 printf("%s: %s\n", argv[i], gai_strerror(error));
243 continue;
244 }
245 error = allowedhost(stdin(&__sF[0]), res0->ai_addr, res0->ai_addrlen);
246 printf("%s: %s\n", argv[i], error ? "denied" : "allowed");
247 }
248 exit(0);
249}
250#endif /* DEBUG */