1 /* $OpenBSD: snmpd.c,v 1.46 2021/08/10 06:52:03 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 <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 int check_child(pid_t, const char *); 49 50 struct snmpd *snmpd_env; 51 52 static struct privsep_proc procs[] = { 53 { "snmpe", PROC_SNMPE, snmpd_dispatch_snmpe, snmpe, snmpe_shutdown }, 54 }; 55 56 enum privsep_procid privsep_process; 57 58 void 59 snmpd_sig_handler(int sig, short event, void *arg) 60 { 61 struct privsep *ps = arg; 62 struct snmpd *env = ps->ps_env; 63 int die = 0, status, fail, id; 64 pid_t pid; 65 char *cause; 66 67 switch (sig) { 68 case SIGTERM: 69 case SIGINT: 70 die = 1; 71 /* FALLTHROUGH */ 72 case SIGCHLD: 73 do { 74 int len; 75 76 pid = waitpid(WAIT_ANY, &status, WNOHANG); 77 if (pid <= 0) 78 continue; 79 80 fail = 0; 81 if (WIFSIGNALED(status)) { 82 fail = 1; 83 len = asprintf(&cause, "terminated; signal %d", 84 WTERMSIG(status)); 85 } else if (WIFEXITED(status)) { 86 if (WEXITSTATUS(status) != 0) { 87 fail = 1; 88 len = asprintf(&cause, 89 "exited abnormally"); 90 } else 91 len = asprintf(&cause, "exited okay"); 92 } else 93 fatalx("unexpected cause of SIGCHLD"); 94 95 if (len == -1) 96 fatal("asprintf"); 97 98 for (id = 0; id < PROC_MAX; id++) { 99 if (pid == ps->ps_pid[id] && 100 check_child(ps->ps_pid[id], 101 ps->ps_title[id])) { 102 die = 1; 103 if (fail) 104 log_warnx("lost child: %s %s", 105 ps->ps_title[id], cause); 106 break; 107 } 108 } 109 free(cause); 110 } while (pid > 0 || (pid == -1 && errno == EINTR)); 111 112 if (die) 113 snmpd_shutdown(env); 114 break; 115 case SIGHUP: 116 /* reconfigure */ 117 break; 118 case SIGUSR1: 119 /* ignore */ 120 break; 121 default: 122 fatalx("unexpected signal"); 123 } 124 } 125 126 __dead void 127 usage(void) 128 { 129 extern char *__progname; 130 131 fprintf(stderr, "usage: %s [-dNnv] [-D macro=value] " 132 "[-f file]\n", __progname); 133 exit(1); 134 } 135 136 int 137 main(int argc, char *argv[]) 138 { 139 int c; 140 struct snmpd *env; 141 int debug = 0, verbose = 0; 142 u_int flags = 0; 143 int noaction = 0; 144 const char *conffile = CONF_FILE; 145 struct privsep *ps; 146 int proc_id = PROC_PARENT, proc_instance = 0; 147 int argc0 = argc; 148 char **argv0 = argv; 149 const char *errp, *title = NULL; 150 151 smi_init(); 152 153 /* log to stderr until daemonized */ 154 log_init(1, LOG_DAEMON); 155 156 while ((c = getopt(argc, argv, "dD:nNf:I:P:v")) != -1) { 157 switch (c) { 158 case 'd': 159 debug++; 160 flags |= SNMPD_F_DEBUG; 161 break; 162 case 'D': 163 if (cmdline_symset(optarg) < 0) 164 log_warnx("could not parse macro definition %s", 165 optarg); 166 break; 167 case 'n': 168 noaction = 1; 169 break; 170 case 'N': 171 flags |= SNMPD_F_NONAMES; 172 break; 173 case 'f': 174 conffile = optarg; 175 break; 176 case 'I': 177 proc_instance = strtonum(optarg, 0, 178 PROC_MAX_INSTANCES, &errp); 179 if (errp) 180 fatalx("invalid process instance"); 181 break; 182 case 'P': 183 title = optarg; 184 proc_id = proc_getid(procs, nitems(procs), title); 185 if (proc_id == PROC_MAX) 186 fatalx("invalid process name"); 187 break; 188 case 'v': 189 verbose++; 190 flags |= SNMPD_F_VERBOSE; 191 break; 192 default: 193 usage(); 194 } 195 } 196 197 argc -= optind; 198 argv += optind; 199 if (argc > 0) 200 usage(); 201 202 if ((env = parse_config(conffile, flags)) == NULL) 203 exit(1); 204 205 ps = &env->sc_ps; 206 ps->ps_env = env; 207 snmpd_env = env; 208 ps->ps_instance = proc_instance; 209 if (title) 210 ps->ps_title[proc_id] = title; 211 212 if (noaction) { 213 fprintf(stderr, "configuration ok\n"); 214 exit(0); 215 } 216 217 if (geteuid()) 218 errx(1, "need root privileges"); 219 220 if ((ps->ps_pw = getpwnam(SNMPD_USER)) == NULL) 221 errx(1, "unknown user %s", SNMPD_USER); 222 223 log_init(debug, LOG_DAEMON); 224 log_setverbose(verbose); 225 226 gettimeofday(&env->sc_starttime, NULL); 227 env->sc_engine_boots = 0; 228 229 pf_init(); 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 256 if (pledge("stdio dns sendfd proc exec id", NULL) == -1) 257 fatal("pledge"); 258 259 event_dispatch(); 260 261 log_debug("%d parent exiting", getpid()); 262 263 return (0); 264 } 265 266 void 267 snmpd_shutdown(struct snmpd *env) 268 { 269 proc_kill(&env->sc_ps); 270 271 free(env); 272 273 log_info("terminating"); 274 exit(0); 275 } 276 277 int 278 check_child(pid_t pid, const char *pname) 279 { 280 int status; 281 282 if (waitpid(pid, &status, WNOHANG) > 0) { 283 if (WIFEXITED(status)) { 284 log_warnx("check_child: lost child: %s exited", pname); 285 return (1); 286 } 287 if (WIFSIGNALED(status)) { 288 log_warnx("check_child: lost child: %s terminated; " 289 "signal %d", pname, WTERMSIG(status)); 290 return (1); 291 } 292 } 293 294 return (0); 295 } 296 297 int 298 snmpd_dispatch_snmpe(int fd, struct privsep_proc *p, struct imsg *imsg) 299 { 300 switch (imsg->hdr.type) { 301 case IMSG_TRAP_EXEC: 302 return (traphandler_priv_recvmsg(p, imsg)); 303 case IMSG_CTL_RELOAD: 304 /* XXX notyet */ 305 default: 306 break; 307 } 308 309 return (-1); 310 } 311 312 int 313 snmpd_socket_af(struct sockaddr_storage *ss, int type) 314 { 315 int fd, serrno; 316 const int enable = 1; 317 318 fd = socket(ss->ss_family, (type == SOCK_STREAM ? 319 SOCK_STREAM | SOCK_NONBLOCK : SOCK_DGRAM) | SOCK_CLOEXEC, 0); 320 if (fd == -1) 321 return -1; 322 323 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, 324 sizeof(enable)) == -1) { 325 serrno = errno; 326 close(fd); 327 errno = serrno; 328 return -1; 329 } 330 return fd; 331 } 332 333 u_long 334 snmpd_engine_time(void) 335 { 336 struct timeval now; 337 338 /* 339 * snmpEngineBoots should be stored in a non-volatile storage. 340 * snmpEngineTime is the number of seconds since snmpEngineBoots 341 * was last incremented. We don't rely on non-volatile storage. 342 * snmpEngineBoots is set to zero and snmpEngineTime to the system 343 * clock. Hence, the tuple (snmpEngineBoots, snmpEngineTime) is 344 * still unique and protects us against replay attacks. It only 345 * 'expires' a little bit sooner than the RFC3414 method. 346 */ 347 gettimeofday(&now, NULL); 348 return now.tv_sec; 349 } 350