GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: snmpd.c,v 1.37 2017/08/12 04:29:57 rob Exp $ */ |
||
2 |
|||
3 |
/* |
||
4 |
* Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org> |
||
5 |
* |
||
6 |
* Permission to use, copy, modify, and distribute this software for any |
||
7 |
* purpose with or without fee is hereby granted, provided that the above |
||
8 |
* copyright notice and this permission notice appear in all copies. |
||
9 |
* |
||
10 |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||
11 |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||
12 |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
||
13 |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
||
14 |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
||
15 |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
||
16 |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||
17 |
*/ |
||
18 |
|||
19 |
#include <sys/types.h> |
||
20 |
#include <sys/queue.h> |
||
21 |
#include <sys/socket.h> |
||
22 |
#include <sys/wait.h> |
||
23 |
#include <sys/tree.h> |
||
24 |
|||
25 |
#include <net/if.h> |
||
26 |
|||
27 |
#include <string.h> |
||
28 |
#include <stdio.h> |
||
29 |
#include <stdlib.h> |
||
30 |
#include <getopt.h> |
||
31 |
#include <err.h> |
||
32 |
#include <errno.h> |
||
33 |
#include <event.h> |
||
34 |
#include <signal.h> |
||
35 |
#include <syslog.h> |
||
36 |
#include <unistd.h> |
||
37 |
#include <fcntl.h> |
||
38 |
#include <pwd.h> |
||
39 |
|||
40 |
#include "snmpd.h" |
||
41 |
#include "mib.h" |
||
42 |
|||
43 |
__dead void usage(void); |
||
44 |
|||
45 |
void snmpd_shutdown(struct snmpd *); |
||
46 |
void snmpd_sig_handler(int, short, void *); |
||
47 |
int snmpd_dispatch_snmpe(int, struct privsep_proc *, struct imsg *); |
||
48 |
void snmpd_generate_engineid(struct snmpd *); |
||
49 |
int check_child(pid_t, const char *); |
||
50 |
|||
51 |
struct snmpd *snmpd_env; |
||
52 |
|||
53 |
static struct privsep_proc procs[] = { |
||
54 |
{ "snmpe", PROC_SNMPE, snmpd_dispatch_snmpe, snmpe, snmpe_shutdown }, |
||
55 |
{ "traphandler", PROC_TRAP, snmpd_dispatch_traphandler, traphandler, |
||
56 |
traphandler_shutdown } |
||
57 |
}; |
||
58 |
|||
59 |
void |
||
60 |
snmpd_sig_handler(int sig, short event, void *arg) |
||
61 |
{ |
||
62 |
80 |
struct privsep *ps = arg; |
|
63 |
60 |
struct snmpd *env = ps->ps_env; |
|
64 |
60 |
int die = 0, status, fail, id; |
|
65 |
pid_t pid; |
||
66 |
60 |
char *cause; |
|
67 |
|||
68 |
✗✗✓✗ ✗✓ |
60 |
switch (sig) { |
69 |
case SIGTERM: |
||
70 |
case SIGINT: |
||
71 |
die = 1; |
||
72 |
/* FALLTHROUGH */ |
||
73 |
case SIGCHLD: |
||
74 |
do { |
||
75 |
int len; |
||
76 |
|||
77 |
40 |
pid = waitpid(WAIT_ANY, &status, WNOHANG); |
|
78 |
✓✓ | 40 |
if (pid <= 0) |
79 |
20 |
continue; |
|
80 |
|||
81 |
fail = 0; |
||
82 |
✓✗✗✓ |
40 |
if (WIFSIGNALED(status)) { |
83 |
fail = 1; |
||
84 |
len = asprintf(&cause, "terminated; signal %d", |
||
85 |
WTERMSIG(status)); |
||
86 |
✓✗ | 20 |
} else if (WIFEXITED(status)) { |
87 |
✗✓ | 20 |
if (WEXITSTATUS(status) != 0) { |
88 |
fail = 1; |
||
89 |
len = asprintf(&cause, |
||
90 |
"exited abnormally"); |
||
91 |
} else |
||
92 |
20 |
len = asprintf(&cause, "exited okay"); |
|
93 |
} else |
||
94 |
fatalx("unexpected cause of SIGCHLD"); |
||
95 |
|||
96 |
✗✓ | 20 |
if (len == -1) |
97 |
fatal("asprintf"); |
||
98 |
|||
99 |
✓✓ | 160 |
for (id = 0; id < PROC_MAX; id++) { |
100 |
✗✓✗✗ |
60 |
if (pid == ps->ps_pid[id] && |
101 |
check_child(ps->ps_pid[id], |
||
102 |
ps->ps_title[id])) { |
||
103 |
die = 1; |
||
104 |
if (fail) |
||
105 |
log_warnx("lost child: %s %s", |
||
106 |
ps->ps_title[id], cause); |
||
107 |
break; |
||
108 |
} |
||
109 |
} |
||
110 |
20 |
free(cause); |
|
111 |
✓✓✗✓ ✗✗ |
80 |
} while (pid > 0 || (pid == -1 && errno == EINTR)); |
112 |
|||
113 |
✗✓ | 20 |
if (die) |
114 |
snmpd_shutdown(env); |
||
115 |
break; |
||
116 |
case SIGHUP: |
||
117 |
/* reconfigure */ |
||
118 |
break; |
||
119 |
case SIGUSR1: |
||
120 |
/* ignore */ |
||
121 |
break; |
||
122 |
default: |
||
123 |
fatalx("unexpected signal"); |
||
124 |
} |
||
125 |
20 |
} |
|
126 |
|||
127 |
__dead void |
||
128 |
usage(void) |
||
129 |
{ |
||
130 |
extern char *__progname; |
||
131 |
|||
132 |
fprintf(stderr, "usage: %s [-dNnv] [-D macro=value] " |
||
133 |
"[-f file]\n", __progname); |
||
134 |
exit(1); |
||
135 |
} |
||
136 |
|||
137 |
int |
||
138 |
main(int argc, char *argv[]) |
||
139 |
{ |
||
140 |
int c; |
||
141 |
struct snmpd *env; |
||
142 |
int debug = 0, verbose = 0; |
||
143 |
u_int flags = 0; |
||
144 |
int noaction = 0; |
||
145 |
const char *conffile = CONF_FILE; |
||
146 |
struct privsep *ps; |
||
147 |
int proc_id = PROC_PARENT, proc_instance = 0; |
||
148 |
int argc0 = argc; |
||
149 |
char **argv0 = argv; |
||
150 |
32 |
const char *errp, *title = NULL; |
|
151 |
|||
152 |
16 |
smi_init(); |
|
153 |
|||
154 |
/* log to stderr until daemonized */ |
||
155 |
16 |
log_init(1, LOG_DAEMON); |
|
156 |
|||
157 |
✓✓ | 80 |
while ((c = getopt(argc, argv, "dD:nNf:I:P:v")) != -1) { |
158 |
✓✗✗✗ ✓✗✗✓ ✗ |
48 |
switch (c) { |
159 |
case 'd': |
||
160 |
16 |
debug++; |
|
161 |
16 |
flags |= SNMPD_F_DEBUG; |
|
162 |
16 |
break; |
|
163 |
case 'D': |
||
164 |
if (cmdline_symset(optarg) < 0) |
||
165 |
log_warnx("could not parse macro definition %s", |
||
166 |
optarg); |
||
167 |
break; |
||
168 |
case 'n': |
||
169 |
noaction = 1; |
||
170 |
break; |
||
171 |
case 'N': |
||
172 |
flags |= SNMPD_F_NONAMES; |
||
173 |
break; |
||
174 |
case 'f': |
||
175 |
16 |
conffile = optarg; |
|
176 |
16 |
break; |
|
177 |
case 'I': |
||
178 |
proc_instance = strtonum(optarg, 0, |
||
179 |
PROC_MAX_INSTANCES, &errp); |
||
180 |
if (errp) |
||
181 |
fatalx("invalid process instance"); |
||
182 |
break; |
||
183 |
case 'P': |
||
184 |
title = optarg; |
||
185 |
proc_id = proc_getid(procs, nitems(procs), title); |
||
186 |
if (proc_id == PROC_MAX) |
||
187 |
fatalx("invalid process name"); |
||
188 |
break; |
||
189 |
case 'v': |
||
190 |
16 |
verbose++; |
|
191 |
16 |
flags |= SNMPD_F_VERBOSE; |
|
192 |
16 |
break; |
|
193 |
default: |
||
194 |
usage(); |
||
195 |
} |
||
196 |
} |
||
197 |
|||
198 |
16 |
argc -= optind; |
|
199 |
16 |
argv += optind; |
|
200 |
✗✓ | 16 |
if (argc > 0) |
201 |
usage(); |
||
202 |
|||
203 |
✗✓ | 16 |
if ((env = parse_config(conffile, flags)) == NULL) |
204 |
exit(1); |
||
205 |
|||
206 |
16 |
ps = &env->sc_ps; |
|
207 |
16 |
ps->ps_env = env; |
|
208 |
16 |
snmpd_env = env; |
|
209 |
16 |
ps->ps_instance = proc_instance; |
|
210 |
✗✓ | 16 |
if (title) |
211 |
ps->ps_title[proc_id] = title; |
||
212 |
|||
213 |
✗✓ | 16 |
if (noaction) { |
214 |
fprintf(stderr, "configuration ok\n"); |
||
215 |
exit(0); |
||
216 |
} |
||
217 |
|||
218 |
✗✓ | 16 |
if (geteuid()) |
219 |
errx(1, "need root privileges"); |
||
220 |
|||
221 |
✗✓ | 16 |
if ((ps->ps_pw = getpwnam(SNMPD_USER)) == NULL) |
222 |
errx(1, "unknown user %s", SNMPD_USER); |
||
223 |
|||
224 |
16 |
log_init(debug, LOG_DAEMON); |
|
225 |
16 |
log_setverbose(verbose); |
|
226 |
|||
227 |
16 |
gettimeofday(&env->sc_starttime, NULL); |
|
228 |
16 |
env->sc_engine_boots = 0; |
|
229 |
|||
230 |
16 |
pf_init(); |
|
231 |
16 |
snmpd_generate_engineid(env); |
|
232 |
|||
233 |
16 |
proc_init(ps, procs, nitems(procs), argc0, argv0, proc_id); |
|
234 |
✗✓✗✗ |
16 |
if (!debug && daemon(0, 0) == -1) |
235 |
err(1, "failed to daemonize"); |
||
236 |
|||
237 |
16 |
log_procinit("parent"); |
|
238 |
16 |
log_info("startup"); |
|
239 |
|||
240 |
16 |
event_init(); |
|
241 |
|||
242 |
16 |
signal_set(&ps->ps_evsigint, SIGINT, snmpd_sig_handler, ps); |
|
243 |
16 |
signal_set(&ps->ps_evsigterm, SIGTERM, snmpd_sig_handler, ps); |
|
244 |
16 |
signal_set(&ps->ps_evsigchld, SIGCHLD, snmpd_sig_handler, ps); |
|
245 |
16 |
signal_set(&ps->ps_evsighup, SIGHUP, snmpd_sig_handler, ps); |
|
246 |
16 |
signal_set(&ps->ps_evsigpipe, SIGPIPE, snmpd_sig_handler, ps); |
|
247 |
16 |
signal_set(&ps->ps_evsigusr1, SIGUSR1, snmpd_sig_handler, ps); |
|
248 |
|||
249 |
16 |
signal_add(&ps->ps_evsigint, NULL); |
|
250 |
16 |
signal_add(&ps->ps_evsigterm, NULL); |
|
251 |
16 |
signal_add(&ps->ps_evsigchld, NULL); |
|
252 |
16 |
signal_add(&ps->ps_evsighup, NULL); |
|
253 |
16 |
signal_add(&ps->ps_evsigpipe, NULL); |
|
254 |
16 |
signal_add(&ps->ps_evsigusr1, NULL); |
|
255 |
|||
256 |
16 |
proc_connect(ps); |
|
257 |
|||
258 |
✗✓ | 16 |
if (pledge("stdio rpath cpath dns id proc sendfd exec flock wpath", NULL) == -1) |
259 |
fatal("pledge"); |
||
260 |
|||
261 |
16 |
event_dispatch(); |
|
262 |
|||
263 |
16 |
log_debug("%d parent exiting", getpid()); |
|
264 |
|||
265 |
16 |
return (0); |
|
266 |
16 |
} |
|
267 |
|||
268 |
void |
||
269 |
snmpd_shutdown(struct snmpd *env) |
||
270 |
{ |
||
271 |
proc_kill(&env->sc_ps); |
||
272 |
|||
273 |
if (env->sc_ps.ps_csock.cs_name != NULL) |
||
274 |
(void)unlink(env->sc_ps.ps_csock.cs_name); |
||
275 |
|||
276 |
free(env); |
||
277 |
|||
278 |
log_info("terminating"); |
||
279 |
exit(0); |
||
280 |
} |
||
281 |
|||
282 |
int |
||
283 |
check_child(pid_t pid, const char *pname) |
||
284 |
{ |
||
285 |
int status; |
||
286 |
|||
287 |
if (waitpid(pid, &status, WNOHANG) > 0) { |
||
288 |
if (WIFEXITED(status)) { |
||
289 |
log_warnx("check_child: lost child: %s exited", pname); |
||
290 |
return (1); |
||
291 |
} |
||
292 |
if (WIFSIGNALED(status)) { |
||
293 |
log_warnx("check_child: lost child: %s terminated; " |
||
294 |
"signal %d", pname, WTERMSIG(status)); |
||
295 |
return (1); |
||
296 |
} |
||
297 |
} |
||
298 |
|||
299 |
return (0); |
||
300 |
} |
||
301 |
|||
302 |
int |
||
303 |
snmpd_dispatch_snmpe(int fd, struct privsep_proc *p, struct imsg *imsg) |
||
304 |
{ |
||
305 |
switch (imsg->hdr.type) { |
||
306 |
case IMSG_CTL_RELOAD: |
||
307 |
/* XXX notyet */ |
||
308 |
default: |
||
309 |
break; |
||
310 |
} |
||
311 |
|||
312 |
return (-1); |
||
313 |
} |
||
314 |
|||
315 |
int |
||
316 |
snmpd_socket_af(struct sockaddr_storage *ss, in_port_t port) |
||
317 |
{ |
||
318 |
int s; |
||
319 |
|||
320 |
switch (ss->ss_family) { |
||
321 |
case AF_INET: |
||
322 |
((struct sockaddr_in *)ss)->sin_port = port; |
||
323 |
((struct sockaddr_in *)ss)->sin_len = |
||
324 |
sizeof(struct sockaddr_in); |
||
325 |
break; |
||
326 |
case AF_INET6: |
||
327 |
((struct sockaddr_in6 *)ss)->sin6_port = port; |
||
328 |
((struct sockaddr_in6 *)ss)->sin6_len = |
||
329 |
sizeof(struct sockaddr_in6); |
||
330 |
break; |
||
331 |
default: |
||
332 |
return (-1); |
||
333 |
} |
||
334 |
|||
335 |
s = socket(ss->ss_family, SOCK_DGRAM, IPPROTO_UDP); |
||
336 |
return (s); |
||
337 |
} |
||
338 |
|||
339 |
void |
||
340 |
snmpd_generate_engineid(struct snmpd *env) |
||
341 |
{ |
||
342 |
u_int32_t oid_enterprise, rnd, tim; |
||
343 |
|||
344 |
/* RFC 3411 */ |
||
345 |
32 |
memset(env->sc_engineid, 0, sizeof(env->sc_engineid)); |
|
346 |
oid_enterprise = htonl(OIDVAL_openBSD_eid); |
||
347 |
16 |
memcpy(env->sc_engineid, &oid_enterprise, sizeof(oid_enterprise)); |
|
348 |
16 |
env->sc_engineid[0] |= SNMP_ENGINEID_NEW; |
|
349 |
16 |
env->sc_engineid_len = sizeof(oid_enterprise); |
|
350 |
|||
351 |
/* XXX alternatively configure engine id via snmpd.conf */ |
||
352 |
16 |
env->sc_engineid[(env->sc_engineid_len)++] = SNMP_ENGINEID_FMT_EID; |
|
353 |
16 |
rnd = arc4random(); |
|
354 |
16 |
memcpy(&env->sc_engineid[env->sc_engineid_len], &rnd, sizeof(rnd)); |
|
355 |
16 |
env->sc_engineid_len += sizeof(rnd); |
|
356 |
|||
357 |
16 |
tim = htonl(env->sc_starttime.tv_sec); |
|
358 |
16 |
memcpy(&env->sc_engineid[env->sc_engineid_len], &tim, sizeof(tim)); |
|
359 |
16 |
env->sc_engineid_len += sizeof(tim); |
|
360 |
16 |
} |
|
361 |
|||
362 |
u_long |
||
363 |
snmpd_engine_time(void) |
||
364 |
{ |
||
365 |
struct timeval now; |
||
366 |
|||
367 |
/* |
||
368 |
* snmpEngineBoots should be stored in a non-volatile storage. |
||
369 |
* snmpEngineTime is the number of seconds since snmpEngineBoots |
||
370 |
* was last incremented. We don't rely on non-volatile storage. |
||
371 |
* snmpEngineBoots is set to zero and snmpEngineTime to the system |
||
372 |
* clock. Hence, the tuple (snmpEngineBoots, snmpEngineTime) is |
||
373 |
* still unique and protects us against replay attacks. It only |
||
374 |
* 'expires' a little bit sooner than the RFC3414 method. |
||
375 |
*/ |
||
376 |
gettimeofday(&now, NULL); |
||
377 |
return now.tv_sec; |
||
378 |
} |
||
379 |
|||
380 |
char * |
||
381 |
tohexstr(u_int8_t *bstr, int len) |
||
382 |
{ |
||
383 |
#define MAXHEXSTRLEN 256 |
||
384 |
static char hstr[2 * MAXHEXSTRLEN + 1]; |
||
385 |
static const char hex[] = "0123456789abcdef"; |
||
386 |
int i; |
||
387 |
|||
388 |
if (len > MAXHEXSTRLEN) |
||
389 |
len = MAXHEXSTRLEN; /* truncate */ |
||
390 |
for (i = 0; i < len; i++) { |
||
391 |
hstr[i + i] = hex[bstr[i] >> 4]; |
||
392 |
hstr[i + i + 1] = hex[bstr[i] & 0x0f]; |
||
393 |
} |
||
394 |
hstr[i + i] = '\0'; |
||
395 |
return hstr; |
||
396 |
} |
Generated by: GCOVR (Version 3.3) |