1 /* $OpenBSD: snmpd.c,v 1.44 2021/01/27 07:21:54 deraadt 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 }; 56 57 enum privsep_procid privsep_process; 58 59 void 60 snmpd_sig_handler(int sig, short event, void *arg) 61 { 62 struct privsep *ps = arg; 63 struct snmpd *env = ps->ps_env; 64 int die = 0, status, fail, id; 65 pid_t pid; 66 char *cause; 67 68 switch (sig) { 69 case SIGTERM: 70 case SIGINT: 71 die = 1; 72 /* FALLTHROUGH */ 73 case SIGCHLD: 74 do { 75 int len; 76 77 pid = waitpid(WAIT_ANY, &status, WNOHANG); 78 if (pid <= 0) 79 continue; 80 81 fail = 0; 82 if (WIFSIGNALED(status)) { 83 fail = 1; 84 len = asprintf(&cause, "terminated; signal %d", 85 WTERMSIG(status)); 86 } else if (WIFEXITED(status)) { 87 if (WEXITSTATUS(status) != 0) { 88 fail = 1; 89 len = asprintf(&cause, 90 "exited abnormally"); 91 } else 92 len = asprintf(&cause, "exited okay"); 93 } else 94 fatalx("unexpected cause of SIGCHLD"); 95 96 if (len == -1) 97 fatal("asprintf"); 98 99 for (id = 0; id < PROC_MAX; id++) { 100 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 free(cause); 111 } while (pid > 0 || (pid == -1 && errno == EINTR)); 112 113 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 } 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 const char *errp, *title = NULL; 151 152 smi_init(); 153 154 /* log to stderr until daemonized */ 155 log_init(1, LOG_DAEMON); 156 157 while ((c = getopt(argc, argv, "dD:nNf:I:P:v")) != -1) { 158 switch (c) { 159 case 'd': 160 debug++; 161 flags |= SNMPD_F_DEBUG; 162 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 conffile = optarg; 176 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 verbose++; 191 flags |= SNMPD_F_VERBOSE; 192 break; 193 default: 194 usage(); 195 } 196 } 197 198 argc -= optind; 199 argv += optind; 200 if (argc > 0) 201 usage(); 202 203 if ((env = parse_config(conffile, flags)) == NULL) 204 exit(1); 205 206 ps = &env->sc_ps; 207 ps->ps_env = env; 208 snmpd_env = env; 209 ps->ps_instance = proc_instance; 210 if (title) 211 ps->ps_title[proc_id] = title; 212 213 if (noaction) { 214 fprintf(stderr, "configuration ok\n"); 215 exit(0); 216 } 217 218 if (geteuid()) 219 errx(1, "need root privileges"); 220 221 if ((ps->ps_pw = getpwnam(SNMPD_USER)) == NULL) 222 errx(1, "unknown user %s", SNMPD_USER); 223 224 log_init(debug, LOG_DAEMON); 225 log_setverbose(verbose); 226 227 gettimeofday(&env->sc_starttime, NULL); 228 env->sc_engine_boots = 0; 229 230 pf_init(); 231 snmpd_generate_engineid(env); 232 233 proc_init(ps, procs, nitems(procs), debug, argc0, argv0, proc_id); 234 if (!debug && daemon(0, 0) == -1) 235 err(1, "failed to daemonize"); 236 237 log_procinit("parent"); 238 log_info("startup"); 239 240 event_init(); 241 242 signal_set(&ps->ps_evsigint, SIGINT, snmpd_sig_handler, ps); 243 signal_set(&ps->ps_evsigterm, SIGTERM, snmpd_sig_handler, ps); 244 signal_set(&ps->ps_evsigchld, SIGCHLD, snmpd_sig_handler, ps); 245 signal_set(&ps->ps_evsighup, SIGHUP, snmpd_sig_handler, ps); 246 signal_set(&ps->ps_evsigpipe, SIGPIPE, snmpd_sig_handler, ps); 247 signal_set(&ps->ps_evsigusr1, SIGUSR1, snmpd_sig_handler, ps); 248 249 signal_add(&ps->ps_evsigint, NULL); 250 signal_add(&ps->ps_evsigterm, NULL); 251 signal_add(&ps->ps_evsigchld, NULL); 252 signal_add(&ps->ps_evsighup, NULL); 253 signal_add(&ps->ps_evsigpipe, NULL); 254 signal_add(&ps->ps_evsigusr1, NULL); 255 256 proc_connect(ps); 257 258 if (pledge("stdio dns sendfd proc exec id", NULL) == -1) 259 fatal("pledge"); 260 261 event_dispatch(); 262 263 log_debug("%d parent exiting", getpid()); 264 265 return (0); 266 } 267 268 void 269 snmpd_shutdown(struct snmpd *env) 270 { 271 proc_kill(&env->sc_ps); 272 273 free(env); 274 275 log_info("terminating"); 276 exit(0); 277 } 278 279 int 280 check_child(pid_t pid, const char *pname) 281 { 282 int status; 283 284 if (waitpid(pid, &status, WNOHANG) > 0) { 285 if (WIFEXITED(status)) { 286 log_warnx("check_child: lost child: %s exited", pname); 287 return (1); 288 } 289 if (WIFSIGNALED(status)) { 290 log_warnx("check_child: lost child: %s terminated; " 291 "signal %d", pname, WTERMSIG(status)); 292 return (1); 293 } 294 } 295 296 return (0); 297 } 298 299 int 300 snmpd_dispatch_snmpe(int fd, struct privsep_proc *p, struct imsg *imsg) 301 { 302 switch (imsg->hdr.type) { 303 case IMSG_TRAP_EXEC: 304 return (traphandler_priv_recvmsg(p, imsg)); 305 case IMSG_CTL_RELOAD: 306 /* XXX notyet */ 307 default: 308 break; 309 } 310 311 return (-1); 312 } 313 314 int 315 snmpd_socket_af(struct sockaddr_storage *ss, int type) 316 { 317 return socket(ss->ss_family, (type == SOCK_STREAM ? 318 SOCK_STREAM | SOCK_NONBLOCK : SOCK_DGRAM) | SOCK_CLOEXEC, 0); 319 } 320 321 void 322 snmpd_generate_engineid(struct snmpd *env) 323 { 324 u_int32_t oid_enterprise, rnd, tim; 325 326 /* RFC 3411 */ 327 memset(env->sc_engineid, 0, sizeof(env->sc_engineid)); 328 oid_enterprise = htonl(OIDVAL_openBSD_eid); 329 memcpy(env->sc_engineid, &oid_enterprise, sizeof(oid_enterprise)); 330 env->sc_engineid[0] |= SNMP_ENGINEID_NEW; 331 env->sc_engineid_len = sizeof(oid_enterprise); 332 333 /* XXX alternatively configure engine id via snmpd.conf */ 334 env->sc_engineid[(env->sc_engineid_len)++] = SNMP_ENGINEID_FMT_EID; 335 rnd = arc4random(); 336 memcpy(&env->sc_engineid[env->sc_engineid_len], &rnd, sizeof(rnd)); 337 env->sc_engineid_len += sizeof(rnd); 338 339 tim = htonl(env->sc_starttime.tv_sec); 340 memcpy(&env->sc_engineid[env->sc_engineid_len], &tim, sizeof(tim)); 341 env->sc_engineid_len += sizeof(tim); 342 } 343 344 u_long 345 snmpd_engine_time(void) 346 { 347 struct timeval now; 348 349 /* 350 * snmpEngineBoots should be stored in a non-volatile storage. 351 * snmpEngineTime is the number of seconds since snmpEngineBoots 352 * was last incremented. We don't rely on non-volatile storage. 353 * snmpEngineBoots is set to zero and snmpEngineTime to the system 354 * clock. Hence, the tuple (snmpEngineBoots, snmpEngineTime) is 355 * still unique and protects us against replay attacks. It only 356 * 'expires' a little bit sooner than the RFC3414 method. 357 */ 358 gettimeofday(&now, NULL); 359 return now.tv_sec; 360 } 361 362 char * 363 tohexstr(u_int8_t *bstr, int len) 364 { 365 #define MAXHEXSTRLEN 256 366 static char hstr[2 * MAXHEXSTRLEN + 1]; 367 static const char hex[] = "0123456789abcdef"; 368 int i; 369 370 if (len > MAXHEXSTRLEN) 371 len = MAXHEXSTRLEN; /* truncate */ 372 for (i = 0; i < len; i++) { 373 hstr[i + i] = hex[bstr[i] >> 4]; 374 hstr[i + i + 1] = hex[bstr[i] & 0x0f]; 375 } 376 hstr[i + i] = '\0'; 377 return hstr; 378 } 379