1 /* $OpenBSD: snmpd.c,v 1.33 2016/08/16 18:41:57 tedu 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 { "traphandler", PROC_TRAP, snmpd_dispatch_traphandler, traphandler, 56 traphandler_shutdown } 57 }; 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 148 smi_init(); 149 150 /* log to stderr until daemonized */ 151 log_init(1, LOG_DAEMON); 152 153 while ((c = getopt(argc, argv, "dD:nNf:v")) != -1) { 154 switch (c) { 155 case 'd': 156 debug++; 157 break; 158 case 'D': 159 if (cmdline_symset(optarg) < 0) 160 log_warnx("could not parse macro definition %s", 161 optarg); 162 break; 163 case 'n': 164 noaction = 1; 165 break; 166 case 'N': 167 flags |= SNMPD_F_NONAMES; 168 break; 169 case 'f': 170 conffile = optarg; 171 break; 172 case 'v': 173 verbose++; 174 flags |= SNMPD_F_VERBOSE; 175 break; 176 default: 177 usage(); 178 } 179 } 180 181 argc -= optind; 182 argv += optind; 183 if (argc > 0) 184 usage(); 185 186 if ((env = parse_config(conffile, flags)) == NULL) 187 exit(1); 188 189 ps = &env->sc_ps; 190 ps->ps_env = env; 191 snmpd_env = env; 192 193 if (noaction) { 194 fprintf(stderr, "configuration ok\n"); 195 exit(0); 196 } 197 198 if (geteuid()) 199 errx(1, "need root privileges"); 200 201 if ((ps->ps_pw = getpwnam(SNMPD_USER)) == NULL) 202 errx(1, "unknown user %s", SNMPD_USER); 203 204 log_init(debug, LOG_DAEMON); 205 log_verbose(verbose); 206 207 if (!debug && daemon(0, 0) == -1) 208 err(1, "failed to daemonize"); 209 210 gettimeofday(&env->sc_starttime, NULL); 211 env->sc_engine_boots = 0; 212 213 pf_init(); 214 snmpd_generate_engineid(env); 215 216 ps->ps_ninstances = 1; 217 proc_init(ps, procs, nitems(procs)); 218 219 log_procinit("parent"); 220 log_info("startup"); 221 222 event_init(); 223 224 signal_set(&ps->ps_evsigint, SIGINT, snmpd_sig_handler, ps); 225 signal_set(&ps->ps_evsigterm, SIGTERM, snmpd_sig_handler, ps); 226 signal_set(&ps->ps_evsigchld, SIGCHLD, snmpd_sig_handler, ps); 227 signal_set(&ps->ps_evsighup, SIGHUP, snmpd_sig_handler, ps); 228 signal_set(&ps->ps_evsigpipe, SIGPIPE, snmpd_sig_handler, ps); 229 signal_set(&ps->ps_evsigusr1, SIGUSR1, snmpd_sig_handler, ps); 230 231 signal_add(&ps->ps_evsigint, NULL); 232 signal_add(&ps->ps_evsigterm, NULL); 233 signal_add(&ps->ps_evsigchld, NULL); 234 signal_add(&ps->ps_evsighup, NULL); 235 signal_add(&ps->ps_evsigpipe, NULL); 236 signal_add(&ps->ps_evsigusr1, NULL); 237 238 proc_listen(ps, procs, nitems(procs)); 239 240 event_dispatch(); 241 242 log_debug("%d parent exiting", getpid()); 243 244 return (0); 245 } 246 247 void 248 snmpd_shutdown(struct snmpd *env) 249 { 250 proc_kill(&env->sc_ps); 251 252 if (env->sc_ps.ps_csock.cs_name != NULL) 253 (void)unlink(env->sc_ps.ps_csock.cs_name); 254 255 free(env); 256 257 log_info("terminating"); 258 exit(0); 259 } 260 261 int 262 check_child(pid_t pid, const char *pname) 263 { 264 int status; 265 266 if (waitpid(pid, &status, WNOHANG) > 0) { 267 if (WIFEXITED(status)) { 268 log_warnx("check_child: lost child: %s exited", pname); 269 return (1); 270 } 271 if (WIFSIGNALED(status)) { 272 log_warnx("check_child: lost child: %s terminated; " 273 "signal %d", pname, WTERMSIG(status)); 274 return (1); 275 } 276 } 277 278 return (0); 279 } 280 281 int 282 snmpd_dispatch_snmpe(int fd, struct privsep_proc *p, struct imsg *imsg) 283 { 284 switch (imsg->hdr.type) { 285 case IMSG_CTL_RELOAD: 286 /* XXX notyet */ 287 default: 288 break; 289 } 290 291 return (-1); 292 } 293 294 int 295 snmpd_socket_af(struct sockaddr_storage *ss, in_port_t port) 296 { 297 int s; 298 299 switch (ss->ss_family) { 300 case AF_INET: 301 ((struct sockaddr_in *)ss)->sin_port = port; 302 ((struct sockaddr_in *)ss)->sin_len = 303 sizeof(struct sockaddr_in); 304 break; 305 case AF_INET6: 306 ((struct sockaddr_in6 *)ss)->sin6_port = port; 307 ((struct sockaddr_in6 *)ss)->sin6_len = 308 sizeof(struct sockaddr_in6); 309 break; 310 default: 311 return (-1); 312 } 313 314 s = socket(ss->ss_family, SOCK_DGRAM, IPPROTO_UDP); 315 return (s); 316 } 317 318 void 319 snmpd_generate_engineid(struct snmpd *env) 320 { 321 u_int32_t oid_enterprise, rnd, tim; 322 323 /* RFC 3411 */ 324 memset(env->sc_engineid, 0, sizeof(env->sc_engineid)); 325 oid_enterprise = htonl(OIDVAL_openBSD_eid); 326 memcpy(env->sc_engineid, &oid_enterprise, sizeof(oid_enterprise)); 327 env->sc_engineid[0] |= SNMP_ENGINEID_NEW; 328 env->sc_engineid_len = sizeof(oid_enterprise); 329 330 /* XXX alternatively configure engine id via snmpd.conf */ 331 env->sc_engineid[(env->sc_engineid_len)++] = SNMP_ENGINEID_FMT_EID; 332 rnd = arc4random(); 333 memcpy(&env->sc_engineid[env->sc_engineid_len], &rnd, sizeof(rnd)); 334 env->sc_engineid_len += sizeof(rnd); 335 336 tim = htonl(env->sc_starttime.tv_sec); 337 memcpy(&env->sc_engineid[env->sc_engineid_len], &tim, sizeof(tim)); 338 env->sc_engineid_len += sizeof(tim); 339 } 340 341 u_long 342 snmpd_engine_time(void) 343 { 344 struct timeval now; 345 346 /* 347 * snmpEngineBoots should be stored in a non-volatile storage. 348 * snmpEngineTime is the number of seconds since snmpEngineBoots 349 * was last incremented. We don't rely on non-volatile storage. 350 * snmpEngineBoots is set to zero and snmpEngineTime to the system 351 * clock. Hence, the tuple (snmpEngineBoots, snmpEngineTime) is 352 * still unique and protects us against replay attacks. It only 353 * 'expires' a little bit sooner than the RFC3414 method. 354 */ 355 gettimeofday(&now, NULL); 356 return now.tv_sec; 357 } 358 359 char * 360 tohexstr(u_int8_t *str, int len) 361 { 362 #define MAXHEXSTRLEN 256 363 static char hstr[2 * MAXHEXSTRLEN + 1]; 364 char *r = hstr; 365 366 if (len > MAXHEXSTRLEN) 367 len = MAXHEXSTRLEN; /* truncate */ 368 while (len-- > 0) 369 r += snprintf(r, len * 2, "%0*x", 2, *str++); 370 *r = '\0'; 371 return hstr; 372 } 373