1 /* $OpenBSD: snmpd.c,v 1.50 2023/12/22 13:04:30 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/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 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 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 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 if (!debug && daemon(0, 0) == -1) 226 err(1, "failed to daemonize"); 227 228 log_procinit("parent"); 229 log_info("startup"); 230 231 event_init(); 232 233 signal_set(&ps->ps_evsigint, SIGINT, snmpd_sig_handler, ps); 234 signal_set(&ps->ps_evsigterm, SIGTERM, snmpd_sig_handler, ps); 235 signal_set(&ps->ps_evsigchld, SIGCHLD, snmpd_sig_handler, ps); 236 signal_set(&ps->ps_evsighup, SIGHUP, snmpd_sig_handler, ps); 237 signal_set(&ps->ps_evsigpipe, SIGPIPE, snmpd_sig_handler, ps); 238 signal_set(&ps->ps_evsigusr1, SIGUSR1, snmpd_sig_handler, ps); 239 240 signal_add(&ps->ps_evsigint, NULL); 241 signal_add(&ps->ps_evsigterm, NULL); 242 signal_add(&ps->ps_evsigchld, NULL); 243 signal_add(&ps->ps_evsighup, NULL); 244 signal_add(&ps->ps_evsigpipe, NULL); 245 signal_add(&ps->ps_evsigusr1, NULL); 246 247 proc_connect(ps); 248 snmpd_backend(env); 249 250 if (pledge("stdio dns sendfd proc exec id", NULL) == -1) 251 fatal("pledge"); 252 253 event_dispatch(); 254 255 log_debug("%d parent exiting", getpid()); 256 257 return (0); 258 } 259 260 void 261 snmpd_shutdown(struct snmpd *env) 262 { 263 proc_kill(&env->sc_ps); 264 265 free(env); 266 267 log_info("terminating"); 268 exit(0); 269 } 270 271 int 272 check_child(pid_t pid, const char *pname) 273 { 274 int status; 275 276 if (waitpid(pid, &status, WNOHANG) > 0) { 277 if (WIFEXITED(status)) { 278 log_warnx("check_child: lost child: %s exited", pname); 279 return (1); 280 } 281 if (WIFSIGNALED(status)) { 282 log_warnx("check_child: lost child: %s terminated; " 283 "signal %d", pname, WTERMSIG(status)); 284 return (1); 285 } 286 } 287 288 return (0); 289 } 290 291 int 292 snmpd_dispatch_snmpe(int fd, struct privsep_proc *p, struct imsg *imsg) 293 { 294 switch (imsg->hdr.type) { 295 case IMSG_TRAP_EXEC: 296 return (traphandler_priv_recvmsg(p, imsg)); 297 default: 298 break; 299 } 300 301 return (-1); 302 } 303 304 int 305 snmpd_socket_af(struct sockaddr_storage *ss, int type) 306 { 307 int fd, serrno; 308 const int enable = 1; 309 310 fd = socket(ss->ss_family, (type == SOCK_STREAM ? 311 SOCK_STREAM | SOCK_NONBLOCK : SOCK_DGRAM) | SOCK_CLOEXEC, 0); 312 if (fd == -1) 313 return -1; 314 315 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, 316 sizeof(enable)) == -1) { 317 serrno = errno; 318 close(fd); 319 errno = serrno; 320 return -1; 321 } 322 return fd; 323 } 324 325 u_long 326 snmpd_engine_time(void) 327 { 328 struct timeval now; 329 330 /* 331 * snmpEngineBoots should be stored in a non-volatile storage. 332 * snmpEngineTime is the number of seconds since snmpEngineBoots 333 * was last incremented. We don't rely on non-volatile storage. 334 * snmpEngineBoots is set to zero and snmpEngineTime to the system 335 * clock. Hence, the tuple (snmpEngineBoots, snmpEngineTime) is 336 * still unique and protects us against replay attacks. It only 337 * 'expires' a little bit sooner than the RFC3414 method. 338 */ 339 gettimeofday(&now, NULL); 340 return now.tv_sec; 341 } 342 343 void 344 snmpd_backend(struct snmpd *env) 345 { 346 DIR *dir; 347 struct dirent *file; 348 int pair[2]; 349 char *argv[8]; 350 char execpath[PATH_MAX]; 351 size_t i = 0; 352 353 if ((dir = opendir(SNMPD_BACKEND)) == NULL) 354 fatal("opendir \"%s\"", SNMPD_BACKEND); 355 356 argv[i++] = execpath; 357 if (env->sc_rtfilter) { 358 argv[i++] = "-C"; 359 argv[i++] = "filter-routes"; 360 } 361 if (env->sc_flags & SNMPD_F_VERBOSE) 362 argv[i++] = "-vv"; 363 if (env->sc_flags & SNMPD_F_DEBUG) { 364 argv[i++] = "-d"; 365 argv[i++] = "-x"; 366 argv[i++] = "3"; 367 } else { 368 argv[i++] = "-x"; 369 argv[i++] = "0"; 370 } 371 argv[i] = NULL; 372 while ((file = readdir(dir)) != NULL) { 373 if (file->d_name[0] == '.') 374 continue; 375 if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) 376 fatal("socketpair"); 377 switch (fork()) { 378 case -1: 379 fatal("fork"); 380 case 0: 381 close(pair[1]); 382 if (dup2(pair[0], 383 env->sc_flags & SNMPD_F_DEBUG ? 3 : 0) == -1) 384 fatal("dup2"); 385 if (closefrom(env->sc_flags & SNMPD_F_DEBUG ? 4 : 1) == -1) 386 fatal("closefrom"); 387 (void)snprintf(execpath, sizeof(execpath), "%s/%s", 388 SNMPD_BACKEND, file->d_name); 389 execv(argv[0], argv); 390 fatal("execv"); 391 default: 392 close(pair[0]); 393 if (proc_compose_imsg(&env->sc_ps, PROC_SNMPE, -1, 394 IMSG_AX_FD, -1, pair[1], NULL, 0) == -1) 395 fatal("proc_compose_imsg"); 396 continue; 397 } 398 } 399 } 400