1 /* $OpenBSD: snmpd.c,v 1.23 2014/05/23 18:37:20 benno 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/param.h> 23 #include <sys/wait.h> 24 #include <sys/tree.h> 25 26 #include <net/if.h> 27 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 <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 pid = waitpid(WAIT_ANY, &status, WNOHANG); 76 if (pid <= 0) 77 continue; 78 79 fail = 0; 80 if (WIFSIGNALED(status)) { 81 fail = 1; 82 asprintf(&cause, "terminated; signal %d", 83 WTERMSIG(status)); 84 } else if (WIFEXITED(status)) { 85 if (WEXITSTATUS(status) != 0) { 86 fail = 1; 87 asprintf(&cause, "exited abnormally"); 88 } else 89 asprintf(&cause, "exited okay"); 90 } else 91 fatalx("unexpected cause of SIGCHLD"); 92 93 for (id = 0; id < PROC_MAX; id++) { 94 if (pid == ps->ps_pid[id] && 95 check_child(ps->ps_pid[id], 96 ps->ps_title[id])) { 97 die = 1; 98 if (fail) 99 log_warnx("lost child: %s %s", 100 ps->ps_title[id], cause); 101 break; 102 } 103 } 104 free(cause); 105 } while (pid > 0 || (pid == -1 && errno == EINTR)); 106 107 if (die) 108 snmpd_shutdown(env); 109 break; 110 case SIGHUP: 111 /* reconfigure */ 112 break; 113 default: 114 fatalx("unexpected signal"); 115 } 116 } 117 118 __dead void 119 usage(void) 120 { 121 extern char *__progname; 122 123 fprintf(stderr, "usage: %s [-dNnv] [-D macro=value] " 124 "[-f file]\n", __progname); 125 exit(1); 126 } 127 128 int 129 main(int argc, char *argv[]) 130 { 131 int c; 132 struct snmpd *env; 133 int debug = 0, verbose = 0; 134 u_int flags = 0; 135 int noaction = 0; 136 const char *conffile = CONF_FILE; 137 struct privsep *ps; 138 139 smi_init(); 140 log_init(1); /* log to stderr until daemonized */ 141 142 while ((c = getopt(argc, argv, "dD:nNf:v")) != -1) { 143 switch (c) { 144 case 'd': 145 debug++; 146 break; 147 case 'D': 148 if (cmdline_symset(optarg) < 0) 149 log_warnx("could not parse macro definition %s", 150 optarg); 151 break; 152 case 'n': 153 noaction++; 154 break; 155 case 'N': 156 flags |= SNMPD_F_NONAMES; 157 break; 158 case 'f': 159 conffile = optarg; 160 break; 161 case 'v': 162 verbose++; 163 flags |= SNMPD_F_VERBOSE; 164 break; 165 default: 166 usage(); 167 } 168 } 169 170 argc -= optind; 171 argv += optind; 172 if (argc > 0) 173 usage(); 174 175 if ((env = parse_config(conffile, flags)) == NULL) 176 exit(1); 177 178 ps = &env->sc_ps; 179 ps->ps_env = env; 180 snmpd_env = env; 181 182 if (noaction) { 183 fprintf(stderr, "configuration ok\n"); 184 exit(0); 185 } 186 187 if (geteuid()) 188 errx(1, "need root privileges"); 189 190 if ((ps->ps_pw = getpwnam(SNMPD_USER)) == NULL) 191 errx(1, "unknown user %s", SNMPD_USER); 192 193 log_init(debug); 194 log_verbose(verbose); 195 196 if (!debug && daemon(0, 0) == -1) 197 err(1, "failed to daemonize"); 198 199 gettimeofday(&env->sc_starttime, NULL); 200 env->sc_engine_boots = 0; 201 202 pf_init(); 203 snmpd_generate_engineid(env); 204 205 ps->ps_ninstances = 1; 206 proc_init(ps, procs, nitems(procs)); 207 208 setproctitle("parent"); 209 log_info("startup"); 210 211 event_init(); 212 213 signal_set(&ps->ps_evsigint, SIGINT, snmpd_sig_handler, ps); 214 signal_set(&ps->ps_evsigterm, SIGTERM, snmpd_sig_handler, ps); 215 signal_set(&ps->ps_evsigchld, SIGCHLD, snmpd_sig_handler, ps); 216 signal_set(&ps->ps_evsighup, SIGHUP, snmpd_sig_handler, ps); 217 signal_set(&ps->ps_evsigpipe, SIGPIPE, snmpd_sig_handler, ps); 218 219 signal_add(&ps->ps_evsigint, NULL); 220 signal_add(&ps->ps_evsigterm, NULL); 221 signal_add(&ps->ps_evsigchld, NULL); 222 signal_add(&ps->ps_evsighup, NULL); 223 signal_add(&ps->ps_evsigpipe, NULL); 224 225 proc_listen(ps, procs, nitems(procs)); 226 227 event_dispatch(); 228 229 log_debug("%d parent exiting", getpid()); 230 231 return (0); 232 } 233 234 void 235 snmpd_shutdown(struct snmpd *env) 236 { 237 proc_kill(&env->sc_ps); 238 239 free(env); 240 241 log_info("terminating"); 242 exit(0); 243 } 244 245 int 246 check_child(pid_t pid, const char *pname) 247 { 248 int status; 249 250 if (waitpid(pid, &status, WNOHANG) > 0) { 251 if (WIFEXITED(status)) { 252 log_warnx("check_child: lost child: %s exited", pname); 253 return (1); 254 } 255 if (WIFSIGNALED(status)) { 256 log_warnx("check_child: lost child: %s terminated; " 257 "signal %d", pname, WTERMSIG(status)); 258 return (1); 259 } 260 } 261 262 return (0); 263 } 264 265 int 266 snmpd_dispatch_snmpe(int fd, struct privsep_proc *p, struct imsg *imsg) 267 { 268 switch (imsg->hdr.type) { 269 case IMSG_CTL_RELOAD: 270 /* XXX notyet */ 271 default: 272 break; 273 } 274 275 return (-1); 276 } 277 278 int 279 snmpd_socket_af(struct sockaddr_storage *ss, in_port_t port) 280 { 281 int s; 282 283 switch (ss->ss_family) { 284 case AF_INET: 285 ((struct sockaddr_in *)ss)->sin_port = port; 286 ((struct sockaddr_in *)ss)->sin_len = 287 sizeof(struct sockaddr_in); 288 break; 289 case AF_INET6: 290 ((struct sockaddr_in6 *)ss)->sin6_port = port; 291 ((struct sockaddr_in6 *)ss)->sin6_len = 292 sizeof(struct sockaddr_in6); 293 break; 294 default: 295 return (-1); 296 } 297 298 s = socket(ss->ss_family, SOCK_DGRAM, IPPROTO_UDP); 299 return (s); 300 } 301 302 void 303 snmpd_generate_engineid(struct snmpd *env) 304 { 305 u_int32_t oid_enterprise, rnd, tim; 306 307 /* RFC 3411 */ 308 memset(env->sc_engineid, 0, sizeof(env->sc_engineid)); 309 oid_enterprise = htonl(OIDVAL_openBSD_eid); 310 memcpy(env->sc_engineid, &oid_enterprise, sizeof(oid_enterprise)); 311 env->sc_engineid[0] |= SNMP_ENGINEID_NEW; 312 env->sc_engineid_len = sizeof(oid_enterprise); 313 314 /* XXX alternatively configure engine id via snmpd.conf */ 315 env->sc_engineid[(env->sc_engineid_len)++] = SNMP_ENGINEID_FMT_EID; 316 rnd = arc4random(); 317 memcpy(&env->sc_engineid[env->sc_engineid_len], &rnd, sizeof(rnd)); 318 env->sc_engineid_len += sizeof(rnd); 319 320 tim = htonl(env->sc_starttime.tv_sec); 321 memcpy(&env->sc_engineid[env->sc_engineid_len], &tim, sizeof(tim)); 322 env->sc_engineid_len += sizeof(tim); 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 char * 344 tohexstr(u_int8_t *str, int len) 345 { 346 #define MAXHEXSTRLEN 256 347 static char hstr[2 * MAXHEXSTRLEN + 1]; 348 char *r = hstr; 349 350 if (len > MAXHEXSTRLEN) 351 len = MAXHEXSTRLEN; /* truncate */ 352 while (len-- > 0) 353 r += snprintf(r, len * 2, "%0*x", 2, *str++); 354 *r = '\0'; 355 return hstr; 356 } 357 358 void 359 socket_set_blockmode(int fd, enum blockmodes bm) 360 { 361 int flags; 362 363 if ((flags = fcntl(fd, F_GETFL, 0)) == -1) 364 fatal("fcntl F_GETFL"); 365 366 if (bm == BM_NONBLOCK) 367 flags |= O_NONBLOCK; 368 else 369 flags &= ~O_NONBLOCK; 370 371 if ((flags = fcntl(fd, F_SETFL, flags)) == -1) 372 fatal("fcntl F_SETFL"); 373 } 374