1 /* $OpenBSD: snmpd.c,v 1.52 2024/04/12 14:17:42 bluhm 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/wait.h>
21
22 #include <dirent.h>
23 #include <err.h>
24 #include <errno.h>
25 #include <pwd.h>
26 #include <signal.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <syslog.h>
30 #include <unistd.h>
31
32 #include "log.h"
33 #include "snmpd.h"
34
35 __dead void usage(void);
36
37 void snmpd_shutdown(struct snmpd *);
38 void snmpd_sig_handler(int, short, void *);
39 int snmpd_dispatch_snmpe(int, struct privsep_proc *, struct imsg *);
40 int check_child(pid_t, const char *);
41 void snmpd_backend(struct snmpd *);
42
43 struct snmpd *snmpd_env;
44
45 static struct privsep_proc procs[] = {
46 { "snmpe", PROC_SNMPE, snmpd_dispatch_snmpe, snmpe, snmpe_shutdown },
47 };
48
49 enum privsep_procid privsep_process;
50
51 void
snmpd_sig_handler(int sig,short event,void * arg)52 snmpd_sig_handler(int sig, short event, void *arg)
53 {
54 struct privsep *ps = arg;
55 struct snmpd *env = ps->ps_env;
56 int die = 0, status, fail, id;
57 pid_t pid;
58 char *cause;
59
60 switch (sig) {
61 case SIGTERM:
62 case SIGINT:
63 die = 1;
64 /* FALLTHROUGH */
65 case SIGCHLD:
66 do {
67 int len;
68
69 pid = waitpid(WAIT_ANY, &status, WNOHANG);
70 if (pid <= 0)
71 continue;
72
73 fail = 0;
74 if (WIFSIGNALED(status)) {
75 fail = 1;
76 len = asprintf(&cause, "terminated; signal %d",
77 WTERMSIG(status));
78 } else if (WIFEXITED(status)) {
79 if (WEXITSTATUS(status) != 0) {
80 fail = 1;
81 len = asprintf(&cause,
82 "exited abnormally");
83 } else
84 len = asprintf(&cause, "exited okay");
85 } else
86 fatalx("unexpected cause of SIGCHLD");
87
88 if (len == -1)
89 fatal("asprintf");
90
91 for (id = 0; id < PROC_MAX; id++) {
92 if (pid == ps->ps_pid[id] &&
93 check_child(ps->ps_pid[id],
94 ps->ps_title[id])) {
95 die = 1;
96 if (fail)
97 log_warnx("lost child: %s %s",
98 ps->ps_title[id], cause);
99 break;
100 }
101 }
102 free(cause);
103 } while (pid > 0 || (pid == -1 && errno == EINTR));
104
105 if (die)
106 snmpd_shutdown(env);
107 break;
108 case SIGHUP:
109 /* reconfigure */
110 break;
111 case SIGUSR1:
112 /* ignore */
113 break;
114 default:
115 fatalx("unexpected signal");
116 }
117 }
118
119 __dead void
usage(void)120 usage(void)
121 {
122 extern char *__progname;
123
124 fprintf(stderr, "usage: %s [-dNnv] [-D macro=value] "
125 "[-f file]\n", __progname);
126 exit(1);
127 }
128
129 int
main(int argc,char * argv[])130 main(int argc, char *argv[])
131 {
132 int c;
133 struct snmpd *env;
134 int debug = 0, verbose = 0;
135 u_int flags = 0;
136 int noaction = 0;
137 const char *conffile = CONF_FILE;
138 struct privsep *ps;
139 int proc_id = PROC_PARENT, proc_instance = 0;
140 int argc0 = argc;
141 char **argv0 = argv;
142 const char *errp, *title = NULL;
143
144 smi_init();
145
146 /* log to stderr until daemonized */
147 log_init(1, LOG_DAEMON);
148
149 while ((c = getopt(argc, argv, "dD:nNf:I:P:v")) != -1) {
150 switch (c) {
151 case 'd':
152 debug++;
153 flags |= SNMPD_F_DEBUG;
154 break;
155 case 'D':
156 if (cmdline_symset(optarg) < 0)
157 log_warnx("could not parse macro definition %s",
158 optarg);
159 break;
160 case 'n':
161 noaction = 1;
162 break;
163 case 'N':
164 flags |= SNMPD_F_NONAMES;
165 break;
166 case 'f':
167 conffile = optarg;
168 break;
169 case 'I':
170 proc_instance = strtonum(optarg, 0,
171 PROC_MAX_INSTANCES, &errp);
172 if (errp)
173 fatalx("invalid process instance");
174 break;
175 case 'P':
176 title = optarg;
177 proc_id = proc_getid(procs, nitems(procs), title);
178 if (proc_id == PROC_MAX)
179 fatalx("invalid process name");
180 break;
181 case 'v':
182 verbose++;
183 flags |= SNMPD_F_VERBOSE;
184 break;
185 default:
186 usage();
187 }
188 }
189
190 argc -= optind;
191 argv += optind;
192 if (argc > 0)
193 usage();
194
195 log_setverbose(verbose);
196
197 if ((env = parse_config(conffile, flags)) == NULL)
198 exit(1);
199
200 ps = &env->sc_ps;
201 ps->ps_env = env;
202 snmpd_env = env;
203 ps->ps_instance = proc_instance;
204 if (title)
205 ps->ps_title[proc_id] = title;
206
207 if (noaction) {
208 fprintf(stderr, "configuration ok\n");
209 exit(0);
210 }
211
212 if (geteuid())
213 errx(1, "need root privileges");
214
215 if ((ps->ps_pw = getpwnam(SNMPD_USER)) == NULL)
216 errx(1, "unknown user %s", SNMPD_USER);
217
218 log_init(debug, LOG_DAEMON);
219 log_setverbose(verbose);
220
221 gettimeofday(&env->sc_starttime, NULL);
222 env->sc_engine_boots = 0;
223
224 proc_init(ps, procs, nitems(procs), debug, argc0, argv0, proc_id);
225
226 log_procinit("parent");
227 log_info("startup");
228
229 event_init();
230
231 signal_set(&ps->ps_evsigint, SIGINT, snmpd_sig_handler, ps);
232 signal_set(&ps->ps_evsigterm, SIGTERM, snmpd_sig_handler, ps);
233 signal_set(&ps->ps_evsigchld, SIGCHLD, snmpd_sig_handler, ps);
234 signal_set(&ps->ps_evsighup, SIGHUP, snmpd_sig_handler, ps);
235 signal_set(&ps->ps_evsigpipe, SIGPIPE, snmpd_sig_handler, ps);
236 signal_set(&ps->ps_evsigusr1, SIGUSR1, snmpd_sig_handler, ps);
237
238 signal_add(&ps->ps_evsigint, NULL);
239 signal_add(&ps->ps_evsigterm, NULL);
240 signal_add(&ps->ps_evsigchld, NULL);
241 signal_add(&ps->ps_evsighup, NULL);
242 signal_add(&ps->ps_evsigpipe, NULL);
243 signal_add(&ps->ps_evsigusr1, NULL);
244
245 proc_connect(ps);
246 snmpd_backend(env);
247
248 if (pledge("stdio dns sendfd proc exec id", NULL) == -1)
249 fatal("pledge");
250
251 event_dispatch();
252
253 log_debug("%d parent exiting", getpid());
254
255 return (0);
256 }
257
258 void
snmpd_shutdown(struct snmpd * env)259 snmpd_shutdown(struct snmpd *env)
260 {
261 proc_kill(&env->sc_ps);
262
263 free(env);
264
265 log_info("terminating");
266 exit(0);
267 }
268
269 int
check_child(pid_t pid,const char * pname)270 check_child(pid_t pid, const char *pname)
271 {
272 int status;
273
274 if (waitpid(pid, &status, WNOHANG) > 0) {
275 if (WIFEXITED(status)) {
276 log_warnx("check_child: lost child: %s exited", pname);
277 return (1);
278 }
279 if (WIFSIGNALED(status)) {
280 log_warnx("check_child: lost child: %s terminated; "
281 "signal %d", pname, WTERMSIG(status));
282 return (1);
283 }
284 }
285
286 return (0);
287 }
288
289 int
snmpd_dispatch_snmpe(int fd,struct privsep_proc * p,struct imsg * imsg)290 snmpd_dispatch_snmpe(int fd, struct privsep_proc *p, struct imsg *imsg)
291 {
292 switch (imsg->hdr.type) {
293 case IMSG_TRAP_EXEC:
294 return (traphandler_priv_recvmsg(p, imsg));
295 default:
296 break;
297 }
298
299 return (-1);
300 }
301
302 int
snmpd_socket_af(struct sockaddr_storage * ss,int type)303 snmpd_socket_af(struct sockaddr_storage *ss, int type)
304 {
305 int fd, serrno;
306 const int enable = 1;
307
308 fd = socket(ss->ss_family, (type == SOCK_STREAM ?
309 SOCK_STREAM | SOCK_NONBLOCK : SOCK_DGRAM) | SOCK_CLOEXEC, 0);
310 if (fd == -1)
311 return -1;
312
313 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable,
314 sizeof(enable)) == -1) {
315 serrno = errno;
316 close(fd);
317 errno = serrno;
318 return -1;
319 }
320 return fd;
321 }
322
323 u_long
snmpd_engine_time(void)324 snmpd_engine_time(void)
325 {
326 struct timeval now;
327
328 /*
329 * snmpEngineBoots should be stored in a non-volatile storage.
330 * snmpEngineTime is the number of seconds since snmpEngineBoots
331 * was last incremented. We don't rely on non-volatile storage.
332 * snmpEngineBoots is set to zero and snmpEngineTime to the system
333 * clock. Hence, the tuple (snmpEngineBoots, snmpEngineTime) is
334 * still unique and protects us against replay attacks. It only
335 * 'expires' a little bit sooner than the RFC3414 method.
336 */
337 gettimeofday(&now, NULL);
338 return now.tv_sec;
339 }
340
341 void
snmpd_backend(struct snmpd * env)342 snmpd_backend(struct snmpd *env)
343 {
344 DIR *dir;
345 struct dirent *file;
346 int pair[2];
347 char *argv[8];
348 char execpath[PATH_MAX];
349 size_t i = 0;
350
351 if ((dir = opendir(SNMPD_BACKEND)) == NULL)
352 fatal("opendir \"%s\"", SNMPD_BACKEND);
353
354 argv[i++] = execpath;
355 if (env->sc_rtfilter) {
356 argv[i++] = "-C";
357 argv[i++] = "filter-routes";
358 }
359 if (env->sc_flags & SNMPD_F_VERBOSE)
360 argv[i++] = "-vv";
361 if (env->sc_flags & SNMPD_F_DEBUG)
362 argv[i++] = "-d";
363 argv[i++] = "-x";
364 argv[i++] = "3";
365 argv[i] = NULL;
366 while ((file = readdir(dir)) != NULL) {
367 if (file->d_name[0] == '.')
368 continue;
369 if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1)
370 fatal("socketpair");
371 switch (fork()) {
372 case -1:
373 fatal("fork");
374 case 0:
375 close(pair[1]);
376 if (dup2(pair[0], 3) == -1)
377 fatal("dup2");
378 if (closefrom(4) == -1)
379 fatal("closefrom");
380 (void)snprintf(execpath, sizeof(execpath), "%s/%s",
381 SNMPD_BACKEND, file->d_name);
382 execv(argv[0], argv);
383 fatal("execv");
384 default:
385 close(pair[0]);
386 if (proc_compose_imsg(&env->sc_ps, PROC_SNMPE, -1,
387 IMSG_AX_FD, -1, pair[1], NULL, 0) == -1)
388 fatal("proc_compose_imsg");
389 continue;
390 }
391 }
392 }
393