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