Bug Summary

File:src/usr.sbin/smtpd/smtpctl/../makemap.c
Warning:line 126, column 8
Null pointer passed as 1st argument to string comparison function

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 makemap.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/smtpctl/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.sbin/smtpd/smtpctl/.. -D NO_IO -D CONFIG_MINIMUM -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/smtpd/smtpctl/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/smtpctl/../makemap.c
1/* $OpenBSD: makemap.c,v 1.75 2021/06/14 17:58:15 eric Exp $ */
2
3/*
4 * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
5 * Copyright (c) 2008-2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
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 <sys/stat.h>
21
22#include <ctype.h>
23#include <db.h>
24#include <err.h>
25#include <errno(*__errno()).h>
26#include <fcntl.h>
27#include <stdlib.h>
28#include <string.h>
29#include <syslog.h>
30#include <unistd.h>
31#include <util.h>
32
33#include "smtpd.h"
34#include "log.h"
35
36#define PATH_ALIASES"/etc/mail/aliases" "/etc/mail/aliases"
37
38static void usage(void);
39static int parse_map(DB *, int *, char *);
40static int parse_entry(DB *, int *, char *, size_t, size_t);
41static int parse_mapentry(DB *, int *, char *, size_t, size_t);
42static int parse_setentry(DB *, int *, char *, size_t, size_t);
43static int make_plain(DBT *, char *);
44static int make_aliases(DBT *, char *);
45static char *conf_aliases(char *);
46static int dump_db(const char *, DBTYPE);
47
48char *source;
49static int mode;
50
51enum output_type {
52 T_PLAIN,
53 T_ALIASES,
54 T_SET
55} type;
56
57/*
58 * Stub functions so that makemap compiles using minimum object files.
59 */
60int
61fork_proc_backend(const char *backend, const char *conf, const char *procname)
62{
63 return (-1);
64}
65
66int
67makemap(int prog_mode, int argc, char *argv[])
68{
69 struct stat sb;
70 char dbname[PATH_MAX1024];
71 DB *db;
72 const char *opts;
73 char *conf, *oflag = NULL((void *)0);
74 int ch, dbputs = 0, Uflag = 0;
75 DBTYPE dbtype = DB_HASH;
76 char *p;
77 gid_t gid;
78 int fd = -1;
79
80 gid = getgid();
81 if (setresgid(gid, gid, gid) == -1)
1
Assuming the condition is false
2
Taking false branch
82 err(1, "setresgid");
83
84 if ((env = config_default()) == NULL((void *)0))
3
Assuming the condition is false
4
Taking false branch
85 err(1, NULL((void *)0));
86
87 log_init(1, LOG_MAIL(2<<3));
88
89 mode = prog_mode;
90 conf = CONF_FILE"/etc/mail/smtpd.conf";
91 type = T_PLAIN;
92 opts = "b:C:d:ho:O:t:U";
93 if (mode == P_NEWALIASES1)
5
Assuming 'mode' is not equal to P_NEWALIASES
6
Taking false branch
94 opts = "f:h";
95
96 while ((ch = getopt(argc, argv, opts)) != -1) {
7
Assuming the condition is true
8
Loop condition is true. Entering loop body
12
Assuming the condition is true
13
Loop condition is true. Entering loop body
97 switch (ch) {
9
Control jumps to 'case 98:' at line 98
14
Control jumps to 'case 116:' at line 125
98 case 'b':
99 if (optarg && strcmp(optarg, "i") == 0)
10
Assuming 'optarg' is null
100 mode = P_NEWALIASES1;
101 break;
11
Execution continues on line 96
102 case 'C':
103 break; /* for compatibility */
104 case 'd':
105 if (strcmp(optarg, "hash") == 0)
106 dbtype = DB_HASH;
107 else if (strcmp(optarg, "btree") == 0)
108 dbtype = DB_BTREE;
109 else
110 errx(1, "unsupported DB type '%s'", optarg);
111 break;
112 case 'f':
113 conf = optarg;
114 break;
115 case 'o':
116 oflag = optarg;
117 break;
118 case 'O':
119 if (strncmp(optarg, "AliasFile=", 10) != 0)
120 break;
121 type = T_ALIASES;
122 p = strchr(optarg, '=');
123 source = ++p;
124 break;
125 case 't':
126 if (strcmp(optarg, "aliases") == 0)
15
Null pointer passed as 1st argument to string comparison function
127 type = T_ALIASES;
128 else if (strcmp(optarg, "set") == 0)
129 type = T_SET;
130 else
131 errx(1, "unsupported type '%s'", optarg);
132 break;
133 case 'U':
134 Uflag = 1;
135 break;
136 default:
137 usage();
138 }
139 }
140 argc -= optind;
141 argv += optind;
142
143 /* sendmail-compat makemap ... re-execute using proper interface */
144 if (argc == 2) {
145 if (oflag)
146 usage();
147
148 p = strstr(argv[1], ".db");
149 if (p == NULL((void *)0) || strcmp(p, ".db") != 0) {
150 if (!bsnprintf(dbname, sizeof dbname, "%s.db",
151 argv[1]))
152 errx(1, "database name too long");
153 }
154 else {
155 if (strlcpy(dbname, argv[1], sizeof dbname)
156 >= sizeof dbname)
157 errx(1, "database name too long");
158 }
159
160 execl(PATH_MAKEMAP"/usr/sbin/makemap", "makemap", "-d", argv[0], "-o", dbname,
161 "-", (char *)NULL((void *)0));
162 err(1, "execl");
163 }
164
165 if (mode == P_NEWALIASES1) {
166 if (geteuid())
167 errx(1, "need root privileges");
168 if (argc != 0)
169 usage();
170 type = T_ALIASES;
171 if (source == NULL((void *)0))
172 source = conf_aliases(conf);
173 } else {
174 if (argc != 1)
175 usage();
176 source = argv[0];
177 }
178
179 if (Uflag)
180 return dump_db(source, dbtype);
181
182 if (oflag == NULL((void *)0) && asprintf(&oflag, "%s.db", source) == -1)
183 err(1, "asprintf");
184
185 if (strcmp(source, "-") != 0)
186 if (stat(source, &sb) == -1)
187 err(1, "stat: %s", source);
188
189 if (!bsnprintf(dbname, sizeof(dbname), "%s.XXXXXXXXXXX", oflag))
190 errx(1, "path too long");
191 if ((fd = mkstemp(dbname)) == -1)
192 err(1, "mkstemp");
193
194 db = dbopen(dbname, O_TRUNC0x0400|O_RDWR0x0002, 0644, dbtype, NULL((void *)0));
195 if (db == NULL((void *)0)) {
196 warn("dbopen: %s", dbname);
197 goto bad;
198 }
199
200 if (strcmp(source, "-") != 0)
201 if (fchmod(db->fd(db), sb.st_mode) == -1 ||
202 fchown(db->fd(db), sb.st_uid, sb.st_gid) == -1) {
203 warn("couldn't carry ownership and perms to %s",
204 dbname);
205 goto bad;
206 }
207
208 if (!parse_map(db, &dbputs, source))
209 goto bad;
210
211 if (db->close(db) == -1) {
212 warn("dbclose: %s", dbname);
213 goto bad;
214 }
215
216 /* force to disk before renaming over an existing file */
217 if (fsync(fd) == -1) {
218 warn("fsync: %s", dbname);
219 goto bad;
220 }
221 if (close(fd) == -1) {
222 fd = -1;
223 warn("close: %s", dbname);
224 goto bad;
225 }
226 fd = -1;
227
228 if (rename(dbname, oflag) == -1) {
229 warn("rename");
230 goto bad;
231 }
232
233 if (mode == P_NEWALIASES1)
234 printf("%s: %d aliases\n", source, dbputs);
235 else if (dbputs == 0)
236 warnx("warning: empty map created: %s", oflag);
237
238 return 0;
239bad:
240 if (fd != -1)
241 close(fd);
242 unlink(dbname);
243 return 1;
244}
245
246static int
247parse_map(DB *db, int *dbputs, char *filename)
248{
249 FILE *fp;
250 char *line;
251 size_t len;
252 size_t lineno = 0;
253
254 if (strcmp(filename, "-") == 0)
255 fp = fdopen(0, "r");
256 else
257 fp = fopen(filename, "r");
258 if (fp == NULL((void *)0)) {
259 warn("%s", filename);
260 return 0;
261 }
262
263 if (!isatty(fileno(fp)(!__isthreaded ? ((fp)->_file) : (fileno)(fp))) && flock(fileno(fp)(!__isthreaded ? ((fp)->_file) : (fileno)(fp)), LOCK_SH0x01|LOCK_NB0x04) == -1) {
264 if (errno(*__errno()) == EWOULDBLOCK35)
265 warnx("%s is locked", filename);
266 else
267 warn("%s: flock", filename);
268 fclose(fp);
269 return 0;
270 }
271
272 while ((line = fparseln(fp, &len, &lineno,
273 NULL((void *)0), FPARSELN_UNESCCOMM0x04)) != NULL((void *)0)) {
274 if (!parse_entry(db, dbputs, line, len, lineno)) {
275 free(line);
276 fclose(fp);
277 return 0;
278 }
279 free(line);
280 }
281
282 fclose(fp);
283 return 1;
284}
285
286static int
287parse_entry(DB *db, int *dbputs, char *line, size_t len, size_t lineno)
288{
289 switch (type) {
290 case T_PLAIN:
291 case T_ALIASES:
292 return parse_mapentry(db, dbputs, line, len, lineno);
293 case T_SET:
294 return parse_setentry(db, dbputs, line, len, lineno);
295 }
296 return 0;
297}
298
299static int
300parse_mapentry(DB *db, int *dbputs, char *line, size_t len, size_t lineno)
301{
302 DBT key;
303 DBT val;
304 char *keyp;
305 char *valp;
306
307 keyp = line;
308 while (isspace((unsigned char)*keyp))
309 keyp++;
310 if (*keyp == '\0')
311 return 1;
312
313 valp = keyp;
314 strsep(&valp, " \t:");
315 if (valp == NULL((void *)0) || valp == keyp)
316 goto bad;
317 while (*valp == ':' || isspace((unsigned char)*valp))
318 valp++;
319 if (*valp == '\0')
320 goto bad;
321
322 /* Check for dups. */
323 key.data = keyp;
324 key.size = strlen(keyp) + 1;
325
326 xlowercase(key.data, key.data, strlen(key.data) + 1);
327 if (db->get(db, &key, &val, 0) == 0) {
328 warnx("%s:%zd: duplicate entry for %s", source, lineno, keyp);
329 return 0;
330 }
331
332 if (type == T_PLAIN) {
333 if (!make_plain(&val, valp))
334 goto bad;
335 }
336 else if (type == T_ALIASES) {
337 if (!make_aliases(&val, valp))
338 goto bad;
339 }
340
341 if (db->put(db, &key, &val, 0) == -1) {
342 warn("dbput");
343 return 0;
344 }
345
346 (*dbputs)++;
347
348 free(val.data);
349
350 return 1;
351
352bad:
353 warnx("%s:%zd: invalid entry", source, lineno);
354 return 0;
355}
356
357static int
358parse_setentry(DB *db, int *dbputs, char *line, size_t len, size_t lineno)
359{
360 DBT key;
361 DBT val;
362 char *keyp;
363
364 keyp = line;
365 while (isspace((unsigned char)*keyp))
366 keyp++;
367 if (*keyp == '\0')
368 return 1;
369
370 val.data = "<set>";
371 val.size = strlen(val.data) + 1;
372
373 /* Check for dups. */
374 key.data = keyp;
375 key.size = strlen(keyp) + 1;
376 xlowercase(key.data, key.data, strlen(key.data) + 1);
377 if (db->get(db, &key, &val, 0) == 0) {
378 warnx("%s:%zd: duplicate entry for %s", source, lineno, keyp);
379 return 0;
380 }
381
382 if (db->put(db, &key, &val, 0) == -1) {
383 warn("dbput");
384 return 0;
385 }
386
387 (*dbputs)++;
388
389 return 1;
390}
391
392static int
393make_plain(DBT *val, char *text)
394{
395 val->data = xstrdup(text);
396 val->size = strlen(text) + 1;
397
398 return (val->size);
399}
400
401static int
402make_aliases(DBT *val, char *text)
403{
404 struct expandnode xn;
405 char *subrcpt;
406 char *origtext;
407
408 val->data = NULL((void *)0);
409 val->size = 0;
410
411 origtext = xstrdup(text);
412
413 while ((subrcpt = strsep(&text, ",")) != NULL((void *)0)) {
414 /* subrcpt: strip initial and trailing whitespace. */
415 subrcpt = strip(subrcpt);
416 if (*subrcpt == '\0')
417 goto error;
418
419 if (!text_to_expandnode(&xn, subrcpt))
420 goto error;
421 }
422
423 val->data = origtext;
424 val->size = strlen(origtext) + 1;
425 return (val->size);
426
427error:
428 free(origtext);
429
430 return 0;
431}
432
433static char *
434conf_aliases(char *cfgpath)
435{
436 struct table *table;
437 char *path;
438 char *p;
439
440 if (parse_config(env, cfgpath, 0))
441 exit(1);
442
443 table = table_find(env, "aliases");
444 if (table == NULL((void *)0))
445 return (PATH_ALIASES"/etc/mail/aliases");
446
447 path = xstrdup(table->t_config);
448 p = strstr(path, ".db");
449 if (p == NULL((void *)0) || strcmp(p, ".db") != 0) {
450 return (path);
451 }
452 *p = '\0';
453 return (path);
454}
455
456static int
457dump_db(const char *dbname, DBTYPE dbtype)
458{
459 DB *db;
460 DBT key, val;
461 char *keystr, *valstr;
462 int r;
463
464 db = dbopen(dbname, O_RDONLY0x0000, 0644, dbtype, NULL((void *)0));
465 if (db == NULL((void *)0))
466 err(1, "dbopen: %s", dbname);
467
468 for (r = db->seq(db, &key, &val, R_FIRST3); r == 0;
469 r = db->seq(db, &key, &val, R_NEXT7)) {
470 keystr = key.data;
471 valstr = val.data;
472 if (keystr[key.size - 1] == '\0')
473 key.size--;
474 if (valstr[val.size - 1] == '\0')
475 val.size--;
476 printf("%.*s\t%.*s\n", (int)key.size, keystr,
477 (int)val.size, valstr);
478 }
479 if (r == -1)
480 err(1, "db->seq: %s", dbname);
481
482 if (db->close(db) == -1)
483 err(1, "dbclose: %s", dbname);
484
485 return 0;
486}
487
488static void
489usage(void)
490{
491 if (mode == P_NEWALIASES1)
492 fprintf(stderr(&__sF[2]), "usage: newaliases [-f file]\n");
493 else
494 fprintf(stderr(&__sF[2]), "usage: makemap [-U] [-d dbtype] [-o dbfile] "
495 "[-t type] file\n");
496 exit(1);
497}