main.c revision 312058
1/* 2 * Copyright (c) 2001-2003 3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4 * All rights reserved. 5 * 6 * Author: Harti Brandt <harti@freebsd.org> 7 * 8 * Copyright (c) 2010 The FreeBSD Foundation 9 * All rights reserved. 10 * 11 * Portions of this software were developed by Shteryana Sotirova Shopova 12 * under sponsorship from the FreeBSD Foundation. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 23 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * $Begemot: bsnmp/snmpd/main.c,v 1.100 2006/02/14 09:04:20 brandt_h Exp $ 36 * 37 * SNMPd main stuff. 38 */ 39 40#include <sys/queue.h> 41#include <sys/param.h> 42#include <sys/un.h> 43#include <sys/ucred.h> 44#include <sys/uio.h> 45#include <stdio.h> 46#include <stdlib.h> 47#include <stddef.h> 48#include <string.h> 49#include <stdarg.h> 50#include <ctype.h> 51#include <errno.h> 52#include <syslog.h> 53#include <unistd.h> 54#include <signal.h> 55#include <dlfcn.h> 56 57#ifdef USE_TCPWRAPPERS 58#include <arpa/inet.h> 59#include <tcpd.h> 60#endif 61 62#include "support.h" 63#include "snmpmod.h" 64#include "snmpd.h" 65#include "tree.h" 66#include "oid.h" 67 68#define PATH_PID "/var/run/%s.pid" 69#define PATH_CONFIG "/etc/%s.config" 70#define PATH_ENGINE "/var/%s.engine" 71 72uint64_t this_tick; /* start of processing of current packet (absolute) */ 73uint64_t start_tick; /* start of processing */ 74 75struct systemg systemg = { 76 NULL, 77 { 8, { 1, 3, 6, 1, 4, 1, 1115, 7352 }}, 78 NULL, NULL, NULL, 79 64 + 8 + 4, 80 0 81}; 82struct debug debug = { 83 0, /* dump_pdus */ 84 LOG_DEBUG, /* log_pri */ 85 0, /* evdebug */ 86}; 87 88struct snmpd snmpd = { 89 2048, /* txbuf */ 90 2048, /* rxbuf */ 91 0, /* comm_dis */ 92 0, /* auth_traps */ 93 {0, 0, 0, 0}, /* trap1addr */ 94 VERS_ENABLE_ALL,/* version_enable */ 95}; 96struct snmpd_stats snmpd_stats; 97 98struct snmpd_usmstat snmpd_usmstats; 99 100/* snmpEngine */ 101struct snmp_engine snmpd_engine; 102 103/* snmpSerialNo */ 104int32_t snmp_serial_no; 105 106struct snmpd_target_stats snmpd_target_stats; 107 108/* search path for config files */ 109const char *syspath = PATH_SYSCONFIG; 110 111/* list of all loaded modules */ 112struct lmodules lmodules = TAILQ_HEAD_INITIALIZER(lmodules); 113 114/* list of loaded modules during start-up in the order they were loaded */ 115static struct lmodules modules_start = TAILQ_HEAD_INITIALIZER(modules_start); 116 117/* list of all known communities */ 118struct community_list community_list = TAILQ_HEAD_INITIALIZER(community_list); 119 120/* list of all known USM users */ 121static struct usm_userlist usm_userlist = SLIST_HEAD_INITIALIZER(usm_userlist); 122 123/* A list of all VACM users configured, including v1, v2c and v3 */ 124static struct vacm_userlist vacm_userlist = 125 SLIST_HEAD_INITIALIZER(vacm_userlist); 126 127/* A list of all VACM groups */ 128static struct vacm_grouplist vacm_grouplist = 129 SLIST_HEAD_INITIALIZER(vacm_grouplist); 130 131static struct vacm_group vacm_default_group = { 132 .groupname = "", 133}; 134 135/* The list of configured access entries */ 136static struct vacm_accesslist vacm_accesslist = 137 TAILQ_HEAD_INITIALIZER(vacm_accesslist); 138 139/* The list of configured views */ 140static struct vacm_viewlist vacm_viewlist = 141 SLIST_HEAD_INITIALIZER(vacm_viewlist); 142 143/* The list of configured contexts */ 144static struct vacm_contextlist vacm_contextlist = 145 SLIST_HEAD_INITIALIZER(vacm_contextlist); 146 147/* list of all installed object resources */ 148struct objres_list objres_list = TAILQ_HEAD_INITIALIZER(objres_list); 149 150/* community value generator */ 151static u_int next_community_index = 1; 152 153/* list of all known ranges */ 154struct idrange_list idrange_list = TAILQ_HEAD_INITIALIZER(idrange_list); 155 156/* identifier generator */ 157u_int next_idrange = 1; 158 159/* list of all current timers */ 160struct timer_list timer_list = LIST_HEAD_INITIALIZER(timer_list); 161 162/* list of file descriptors */ 163struct fdesc_list fdesc_list = LIST_HEAD_INITIALIZER(fdesc_list); 164 165/* program arguments */ 166static char **progargs; 167static int nprogargs; 168 169/* current community */ 170u_int community; 171static struct community *comm; 172 173/* current USM user */ 174struct usm_user *usm_user; 175 176/* file names */ 177static char config_file[MAXPATHLEN + 1]; 178static char pid_file[MAXPATHLEN + 1]; 179char engine_file[MAXPATHLEN + 1]; 180 181#ifndef USE_LIBBEGEMOT 182/* event context */ 183static evContext evctx; 184#endif 185 186/* signal mask */ 187static sigset_t blocked_sigs; 188 189/* signal handling */ 190static int work; 191#define WORK_DOINFO 0x0001 192#define WORK_RECONFIG 0x0002 193 194/* oids */ 195static const struct asn_oid 196 oid_snmpMIB = OIDX_snmpMIB, 197 oid_begemotSnmpd = OIDX_begemotSnmpd, 198 oid_coldStart = OIDX_coldStart, 199 oid_authenticationFailure = OIDX_authenticationFailure; 200 201const struct asn_oid oid_zeroDotZero = { 2, { 0, 0 }}; 202 203const struct asn_oid oid_usmUnknownEngineIDs = 204 { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 4, 0}}; 205 206const struct asn_oid oid_usmNotInTimeWindows = 207 { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 2, 0}}; 208 209/* request id generator for traps */ 210u_int trap_reqid; 211 212/* help text */ 213static const char usgtxt[] = "\ 214Begemot simple SNMP daemon. Copyright (c) 2001-2002 Fraunhofer Institute for\n\ 215Open Communication Systems (FhG Fokus). All rights reserved.\n\ 216Copyright (c) 2010 The FreeBSD Foundation. All rights reserved.\n\ 217usage: snmpd [-dh] [-c file] [-D options] [-e file] [-I path]\n\ 218 [-l prefix] [-m variable=value] [-p file]\n\ 219options:\n\ 220 -d don't daemonize\n\ 221 -h print this info\n\ 222 -c file specify configuration file\n\ 223 -D options debugging options\n\ 224 -e file specify engine id file\n\ 225 -I path system include path\n\ 226 -l prefix default basename for pid and config file\n\ 227 -m var=val define variable\n\ 228 -p file specify pid file\n\ 229"; 230 231/* hosts_access(3) request */ 232#ifdef USE_TCPWRAPPERS 233static struct request_info req; 234#endif 235 236/* transports */ 237extern const struct transport_def udp_trans; 238extern const struct transport_def lsock_trans; 239 240struct transport_list transport_list = TAILQ_HEAD_INITIALIZER(transport_list); 241 242/* forward declarations */ 243static void snmp_printf_func(const char *fmt, ...); 244static void snmp_error_func(const char *err, ...); 245static void snmp_debug_func(const char *err, ...); 246static void asn_error_func(const struct asn_buf *b, const char *err, ...); 247 248/* 249 * Allocate rx/tx buffer. We allocate one byte more for rx. 250 */ 251void * 252buf_alloc(int tx) 253{ 254 void *buf; 255 256 if ((buf = malloc(tx ? snmpd.txbuf : snmpd.rxbuf)) == NULL) { 257 syslog(LOG_CRIT, "cannot allocate buffer"); 258 if (tx) 259 snmpd_stats.noTxbuf++; 260 else 261 snmpd_stats.noRxbuf++; 262 return (NULL); 263 } 264 return (buf); 265} 266 267/* 268 * Return the buffer size. 269 */ 270size_t 271buf_size(int tx) 272{ 273 return (tx ? snmpd.txbuf : snmpd.rxbuf); 274} 275 276/* 277 * Prepare a PDU for output 278 */ 279void 280snmp_output(struct snmp_pdu *pdu, u_char *sndbuf, size_t *sndlen, 281 const char *dest) 282{ 283 struct asn_buf resp_b; 284 enum snmp_code code; 285 286 resp_b.asn_ptr = sndbuf; 287 resp_b.asn_len = snmpd.txbuf; 288 289 if ((code = snmp_pdu_encode(pdu, &resp_b)) != SNMP_CODE_OK) { 290 syslog(LOG_ERR, "cannot encode message (code=%d)", code); 291 abort(); 292 } 293 if (debug.dump_pdus) { 294 snmp_printf("%s <- ", dest); 295 snmp_pdu_dump(pdu); 296 } 297 *sndlen = (size_t)(resp_b.asn_ptr - sndbuf); 298} 299 300/* 301 * Check USM PDU header credentials against local SNMP Engine & users. 302 */ 303static enum snmp_code 304snmp_pdu_auth_user(struct snmp_pdu *pdu) 305{ 306 usm_user = NULL; 307 308 /* un-authenticated snmpEngineId discovery */ 309 if (pdu->engine.engine_len == 0 && strlen(pdu->user.sec_name) == 0) { 310 pdu->engine.engine_len = snmpd_engine.engine_len; 311 memcpy(pdu->engine.engine_id, snmpd_engine.engine_id, 312 snmpd_engine.engine_len); 313 update_snmpd_engine_time(); 314 pdu->engine.engine_boots = snmpd_engine.engine_boots; 315 pdu->engine.engine_time = snmpd_engine.engine_time; 316 pdu->flags |= SNMP_MSG_AUTODISCOVER; 317 return (SNMP_CODE_OK); 318 } 319 320 if ((usm_user = usm_find_user(pdu->engine.engine_id, 321 pdu->engine.engine_len, pdu->user.sec_name)) == NULL || 322 usm_user->status != 1 /* active */) 323 return (SNMP_CODE_BADUSER); 324 325 if (usm_user->user_engine_len != snmpd_engine.engine_len || 326 memcmp(usm_user->user_engine_id, snmpd_engine.engine_id, 327 snmpd_engine.engine_len) != 0) 328 return (SNMP_CODE_BADENGINE); 329 330 pdu->user.priv_proto = usm_user->suser.priv_proto; 331 memcpy(pdu->user.priv_key, usm_user->suser.priv_key, 332 sizeof(pdu->user.priv_key)); 333 334 /* authenticated snmpEngineId discovery */ 335 if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) { 336 update_snmpd_engine_time(); 337 pdu->user.auth_proto = usm_user->suser.auth_proto; 338 memcpy(pdu->user.auth_key, usm_user->suser.auth_key, 339 sizeof(pdu->user.auth_key)); 340 341 if (pdu->engine.engine_boots == 0 && 342 pdu->engine.engine_time == 0) { 343 update_snmpd_engine_time(); 344 pdu->flags |= SNMP_MSG_AUTODISCOVER; 345 return (SNMP_CODE_OK); 346 } 347 348 if (pdu->engine.engine_boots != snmpd_engine.engine_boots || 349 abs(pdu->engine.engine_time - snmpd_engine.engine_time) > 350 SNMP_TIME_WINDOW) 351 return (SNMP_CODE_NOTINTIME); 352 } 353 354 if (((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0 && 355 (pdu->flags & SNMP_MSG_AUTH_FLAG) == 0) || 356 ((pdu->flags & SNMP_MSG_AUTH_FLAG) == 0 && 357 usm_user->suser.auth_proto != SNMP_AUTH_NOAUTH) || 358 ((pdu->flags & SNMP_MSG_PRIV_FLAG) == 0 && 359 usm_user->suser.priv_proto != SNMP_PRIV_NOPRIV)) 360 return (SNMP_CODE_BADSECLEVEL); 361 362 return (SNMP_CODE_OK); 363} 364 365/* 366 * Check whether access to each of var bindings in the PDU is allowed based 367 * on the user credentials against the configured User groups & VACM views. 368 */ 369enum snmp_code 370snmp_pdu_auth_access(struct snmp_pdu *pdu, int32_t *ip) 371{ 372 const char *uname; 373 int32_t suboid, smodel; 374 uint32_t i; 375 struct vacm_user *vuser; 376 struct vacm_access *acl; 377 struct vacm_context *vacmctx; 378 struct vacm_view *view; 379 380 /* 381 * At least a default context exists if the snmpd_vacm(3) module is 382 * running. 383 */ 384 if (SLIST_EMPTY(&vacm_contextlist) || 385 (pdu->flags & SNMP_MSG_AUTODISCOVER) != 0) 386 return (SNMP_CODE_OK); 387 388 switch (pdu->version) { 389 case SNMP_V1: 390 if ((uname = comm_string(community)) == NULL) 391 return (SNMP_CODE_FAILED); 392 smodel = SNMP_SECMODEL_SNMPv1; 393 break; 394 395 case SNMP_V2c: 396 if ((uname = comm_string(community)) == NULL) 397 return (SNMP_CODE_FAILED); 398 smodel = SNMP_SECMODEL_SNMPv2c; 399 break; 400 401 case SNMP_V3: 402 uname = pdu->user.sec_name; 403 if ((smodel = pdu->security_model) != SNMP_SECMODEL_USM) 404 return (SNMP_CODE_FAILED); 405 /* Compare the PDU context engine id against the agent's */ 406 if (pdu->context_engine_len != snmpd_engine.engine_len || 407 memcmp(pdu->context_engine, snmpd_engine.engine_id, 408 snmpd_engine.engine_len) != 0) 409 return (SNMP_CODE_FAILED); 410 break; 411 412 default: 413 abort(); 414 } 415 416 SLIST_FOREACH(vuser, &vacm_userlist, vvu) 417 if (strcmp(uname, vuser->secname) == 0 && 418 vuser->sec_model == smodel) 419 break; 420 421 if (vuser == NULL || vuser->group == NULL) 422 return (SNMP_CODE_FAILED); 423 424 /* XXX: shteryana - recheck */ 425 TAILQ_FOREACH_REVERSE(acl, &vacm_accesslist, vacm_accesslist, vva) { 426 if (acl->group != vuser->group) 427 continue; 428 SLIST_FOREACH(vacmctx, &vacm_contextlist, vcl) 429 if (memcmp(vacmctx->ctxname, acl->ctx_prefix, 430 acl->ctx_match) == 0) 431 goto match; 432 } 433 434 return (SNMP_CODE_FAILED); 435 436match: 437 438 switch (pdu->type) { 439 case SNMP_PDU_GET: 440 case SNMP_PDU_GETNEXT: 441 case SNMP_PDU_GETBULK: 442 if ((view = acl->read_view) == NULL) 443 return (SNMP_CODE_FAILED); 444 break; 445 446 case SNMP_PDU_SET: 447 if ((view = acl->write_view) == NULL) 448 return (SNMP_CODE_FAILED); 449 break; 450 451 case SNMP_PDU_TRAP: 452 case SNMP_PDU_INFORM: 453 case SNMP_PDU_TRAP2: 454 case SNMP_PDU_REPORT: 455 if ((view = acl->notify_view) == NULL) 456 return (SNMP_CODE_FAILED); 457 break; 458 case SNMP_PDU_RESPONSE: 459 /* NOTREACHED */ 460 return (SNMP_CODE_FAILED); 461 default: 462 abort(); 463 } 464 465 for (i = 0; i < pdu->nbindings; i++) { 466 /* XXX - view->mask*/ 467 suboid = asn_is_suboid(&view->subtree, &pdu->bindings[i].var); 468 if ((!suboid && !view->exclude) || (suboid && view->exclude)) { 469 *ip = i + 1; 470 return (SNMP_CODE_FAILED); 471 } 472 } 473 474 return (SNMP_CODE_OK); 475} 476 477/* 478 * SNMP input. Start: decode the PDU, find the user or community. 479 */ 480enum snmpd_input_err 481snmp_input_start(const u_char *buf, size_t len, const char *source, 482 struct snmp_pdu *pdu, int32_t *ip, size_t *pdulen) 483{ 484 struct asn_buf b; 485 enum snmp_code code; 486 enum snmpd_input_err ret; 487 int sret; 488 489 /* update uptime */ 490 this_tick = get_ticks(); 491 492 b.asn_cptr = buf; 493 b.asn_len = len; 494 495 ret = SNMPD_INPUT_OK; 496 497 /* look whether we have enough bytes for the entire PDU. */ 498 switch (sret = snmp_pdu_snoop(&b)) { 499 500 case 0: 501 return (SNMPD_INPUT_TRUNC); 502 503 case -1: 504 snmpd_stats.inASNParseErrs++; 505 return (SNMPD_INPUT_FAILED); 506 } 507 b.asn_len = *pdulen = (size_t)sret; 508 509 memset(pdu, 0, sizeof(*pdu)); 510 if ((code = snmp_pdu_decode_header(&b, pdu)) != SNMP_CODE_OK) 511 goto decoded; 512 513 if (pdu->version == SNMP_V3) { 514 if (pdu->security_model != SNMP_SECMODEL_USM) { 515 code = SNMP_CODE_FAILED; 516 goto decoded; 517 } 518 if ((code = snmp_pdu_auth_user(pdu)) != SNMP_CODE_OK) 519 goto decoded; 520 if ((code = snmp_pdu_decode_secmode(&b, pdu)) != SNMP_CODE_OK) 521 goto decoded; 522 } 523 code = snmp_pdu_decode_scoped(&b, pdu, ip); 524 525decoded: 526 snmpd_stats.inPkts++; 527 528 switch (code) { 529 530 case SNMP_CODE_FAILED: 531 snmpd_stats.inASNParseErrs++; 532 return (SNMPD_INPUT_FAILED); 533 534 case SNMP_CODE_BADVERS: 535 bad_vers: 536 snmpd_stats.inBadVersions++; 537 return (SNMPD_INPUT_FAILED); 538 539 case SNMP_CODE_BADLEN: 540 if (pdu->type == SNMP_OP_SET) 541 ret = SNMPD_INPUT_VALBADLEN; 542 break; 543 544 case SNMP_CODE_OORANGE: 545 if (pdu->type == SNMP_OP_SET) 546 ret = SNMPD_INPUT_VALRANGE; 547 break; 548 549 case SNMP_CODE_BADENC: 550 if (pdu->type == SNMP_OP_SET) 551 ret = SNMPD_INPUT_VALBADENC; 552 break; 553 554 case SNMP_CODE_BADSECLEVEL: 555 snmpd_usmstats.unsupported_seclevels++; 556 return (SNMPD_INPUT_FAILED); 557 558 case SNMP_CODE_NOTINTIME: 559 snmpd_usmstats.not_in_time_windows++; 560 return (SNMPD_INPUT_FAILED); 561 562 case SNMP_CODE_BADUSER: 563 snmpd_usmstats.unknown_users++; 564 return (SNMPD_INPUT_FAILED); 565 566 case SNMP_CODE_BADENGINE: 567 snmpd_usmstats.unknown_engine_ids++; 568 return (SNMPD_INPUT_FAILED); 569 570 case SNMP_CODE_BADDIGEST: 571 snmpd_usmstats.wrong_digests++; 572 return (SNMPD_INPUT_FAILED); 573 574 case SNMP_CODE_EDECRYPT: 575 snmpd_usmstats.decrypt_errors++; 576 return (SNMPD_INPUT_FAILED); 577 578 case SNMP_CODE_OK: 579 switch (pdu->version) { 580 581 case SNMP_V1: 582 if (!(snmpd.version_enable & VERS_ENABLE_V1)) 583 goto bad_vers; 584 break; 585 586 case SNMP_V2c: 587 if (!(snmpd.version_enable & VERS_ENABLE_V2C)) 588 goto bad_vers; 589 break; 590 591 case SNMP_V3: 592 if (!(snmpd.version_enable & VERS_ENABLE_V3)) 593 goto bad_vers; 594 break; 595 596 case SNMP_Verr: 597 goto bad_vers; 598 } 599 break; 600 } 601 602 if (debug.dump_pdus) { 603 snmp_printf("%s -> ", source); 604 snmp_pdu_dump(pdu); 605 } 606 607 /* 608 * Look, whether we know the community or user 609 */ 610 611 if (pdu->version != SNMP_V3) { 612 TAILQ_FOREACH(comm, &community_list, link) 613 if (comm->string != NULL && 614 strcmp(comm->string, pdu->community) == 0) 615 break; 616 617 if (comm == NULL) { 618 snmpd_stats.inBadCommunityNames++; 619 snmp_pdu_free(pdu); 620 if (snmpd.auth_traps) 621 snmp_send_trap(&oid_authenticationFailure, 622 (struct snmp_value *)NULL); 623 ret = SNMPD_INPUT_BAD_COMM; 624 } else 625 community = comm->value; 626 } else if (pdu->nbindings == 0) { 627 /* RFC 3414 - snmpEngineID Discovery */ 628 if (strlen(pdu->user.sec_name) == 0) { 629 asn_append_oid(&(pdu->bindings[pdu->nbindings++].var), 630 &oid_usmUnknownEngineIDs); 631 pdu->context_engine_len = snmpd_engine.engine_len; 632 memcpy(pdu->context_engine, snmpd_engine.engine_id, 633 snmpd_engine.engine_len); 634 } else if (pdu->engine.engine_boots == 0 && 635 pdu->engine.engine_time == 0) { 636 asn_append_oid(&(pdu->bindings[pdu->nbindings++].var), 637 &oid_usmNotInTimeWindows); 638 update_snmpd_engine_time(); 639 pdu->engine.engine_boots = snmpd_engine.engine_boots; 640 pdu->engine.engine_time = snmpd_engine.engine_time; 641 } 642 } else if (usm_user->suser.auth_proto != SNMP_AUTH_NOAUTH && 643 (pdu->engine.engine_boots == 0 || pdu->engine.engine_time == 0)) { 644 snmpd_usmstats.not_in_time_windows++; 645 ret = SNMPD_INPUT_FAILED; 646 } 647 648 if ((code = snmp_pdu_auth_access(pdu, ip)) != SNMP_CODE_OK) 649 ret = SNMPD_INPUT_FAILED; 650 651 return (ret); 652} 653 654/* 655 * Will return only _OK or _FAILED 656 */ 657enum snmpd_input_err 658snmp_input_finish(struct snmp_pdu *pdu, const u_char *rcvbuf, size_t rcvlen, 659 u_char *sndbuf, size_t *sndlen, const char *source, 660 enum snmpd_input_err ierr, int32_t ivar, void *data) 661{ 662 struct snmp_pdu resp; 663 struct asn_buf resp_b, pdu_b; 664 enum snmp_ret ret; 665 666 resp_b.asn_ptr = sndbuf; 667 resp_b.asn_len = snmpd.txbuf; 668 669 pdu_b.asn_cptr = rcvbuf; 670 pdu_b.asn_len = rcvlen; 671 672 if (ierr != SNMPD_INPUT_OK) { 673 /* error decoding the input of a SET */ 674 if (pdu->version == SNMP_V1) 675 pdu->error_status = SNMP_ERR_BADVALUE; 676 else if (ierr == SNMPD_INPUT_VALBADLEN) 677 pdu->error_status = SNMP_ERR_WRONG_LENGTH; 678 else if (ierr == SNMPD_INPUT_VALRANGE) 679 pdu->error_status = SNMP_ERR_WRONG_VALUE; 680 else 681 pdu->error_status = SNMP_ERR_WRONG_ENCODING; 682 683 pdu->error_index = ivar; 684 685 if (snmp_make_errresp(pdu, &pdu_b, &resp_b) == SNMP_RET_IGN) { 686 syslog(LOG_WARNING, "could not encode error response"); 687 snmpd_stats.silentDrops++; 688 return (SNMPD_INPUT_FAILED); 689 } 690 691 if (debug.dump_pdus) { 692 snmp_printf("%s <- ", source); 693 snmp_pdu_dump(pdu); 694 } 695 *sndlen = (size_t)(resp_b.asn_ptr - sndbuf); 696 return (SNMPD_INPUT_OK); 697 } 698 699 switch (pdu->type) { 700 701 case SNMP_PDU_GET: 702 ret = snmp_get(pdu, &resp_b, &resp, data); 703 break; 704 705 case SNMP_PDU_GETNEXT: 706 ret = snmp_getnext(pdu, &resp_b, &resp, data); 707 break; 708 709 case SNMP_PDU_SET: 710 ret = snmp_set(pdu, &resp_b, &resp, data); 711 break; 712 713 case SNMP_PDU_GETBULK: 714 ret = snmp_getbulk(pdu, &resp_b, &resp, data); 715 break; 716 717 default: 718 ret = SNMP_RET_IGN; 719 break; 720 } 721 722 switch (ret) { 723 724 case SNMP_RET_OK: 725 /* normal return - send a response */ 726 if (debug.dump_pdus) { 727 snmp_printf("%s <- ", source); 728 snmp_pdu_dump(&resp); 729 } 730 *sndlen = (size_t)(resp_b.asn_ptr - sndbuf); 731 snmp_pdu_free(&resp); 732 return (SNMPD_INPUT_OK); 733 734 case SNMP_RET_IGN: 735 /* error - send nothing */ 736 snmpd_stats.silentDrops++; 737 return (SNMPD_INPUT_FAILED); 738 739 case SNMP_RET_ERR: 740 /* error - send error response. The snmp routine has 741 * changed the error fields in the original message. */ 742 resp_b.asn_ptr = sndbuf; 743 resp_b.asn_len = snmpd.txbuf; 744 if (snmp_make_errresp(pdu, &pdu_b, &resp_b) == SNMP_RET_IGN) { 745 syslog(LOG_WARNING, "could not encode error response"); 746 snmpd_stats.silentDrops++; 747 return (SNMPD_INPUT_FAILED); 748 } else { 749 if (debug.dump_pdus) { 750 snmp_printf("%s <- ", source); 751 snmp_pdu_dump(pdu); 752 } 753 *sndlen = (size_t)(resp_b.asn_ptr - sndbuf); 754 return (SNMPD_INPUT_OK); 755 } 756 } 757 abort(); 758} 759 760/* 761 * Insert a port into the right place in the transport's table of ports 762 */ 763void 764trans_insert_port(struct transport *t, struct tport *port) 765{ 766 struct tport *p; 767 768 TAILQ_FOREACH(p, &t->table, link) { 769 if (asn_compare_oid(&p->index, &port->index) > 0) { 770 TAILQ_INSERT_BEFORE(p, port, link); 771 return; 772 } 773 } 774 port->transport = t; 775 TAILQ_INSERT_TAIL(&t->table, port, link); 776} 777 778/* 779 * Remove a port from a transport's list 780 */ 781void 782trans_remove_port(struct tport *port) 783{ 784 785 TAILQ_REMOVE(&port->transport->table, port, link); 786} 787 788/* 789 * Find a port on a transport's list 790 */ 791struct tport * 792trans_find_port(struct transport *t, const struct asn_oid *idx, u_int sub) 793{ 794 795 return (FIND_OBJECT_OID(&t->table, idx, sub)); 796} 797 798/* 799 * Find next port on a transport's list 800 */ 801struct tport * 802trans_next_port(struct transport *t, const struct asn_oid *idx, u_int sub) 803{ 804 805 return (NEXT_OBJECT_OID(&t->table, idx, sub)); 806} 807 808/* 809 * Return first port 810 */ 811struct tport * 812trans_first_port(struct transport *t) 813{ 814 815 return (TAILQ_FIRST(&t->table)); 816} 817 818/* 819 * Iterate through all ports until a function returns a 0. 820 */ 821struct tport * 822trans_iter_port(struct transport *t, int (*func)(struct tport *, intptr_t), 823 intptr_t arg) 824{ 825 struct tport *p; 826 827 TAILQ_FOREACH(p, &t->table, link) 828 if (func(p, arg) == 0) 829 return (p); 830 return (NULL); 831} 832 833/* 834 * Register a transport 835 */ 836int 837trans_register(const struct transport_def *def, struct transport **pp) 838{ 839 u_int i; 840 char or_descr[256]; 841 842 if ((*pp = malloc(sizeof(**pp))) == NULL) 843 return (SNMP_ERR_GENERR); 844 845 /* construct index */ 846 (*pp)->index.len = strlen(def->name) + 1; 847 (*pp)->index.subs[0] = strlen(def->name); 848 for (i = 0; i < (*pp)->index.subs[0]; i++) 849 (*pp)->index.subs[i + 1] = def->name[i]; 850 851 (*pp)->vtab = def; 852 853 if (FIND_OBJECT_OID(&transport_list, &(*pp)->index, 0) != NULL) { 854 free(*pp); 855 return (SNMP_ERR_INCONS_VALUE); 856 } 857 858 /* register module */ 859 snprintf(or_descr, sizeof(or_descr), "%s transport mapping", def->name); 860 if (((*pp)->or_index = or_register(&def->id, or_descr, NULL)) == 0) { 861 free(*pp); 862 return (SNMP_ERR_GENERR); 863 } 864 865 INSERT_OBJECT_OID((*pp), &transport_list); 866 867 TAILQ_INIT(&(*pp)->table); 868 869 return (SNMP_ERR_NOERROR); 870} 871 872/* 873 * Unregister transport 874 */ 875int 876trans_unregister(struct transport *t) 877{ 878 if (!TAILQ_EMPTY(&t->table)) 879 return (SNMP_ERR_INCONS_VALUE); 880 881 or_unregister(t->or_index); 882 TAILQ_REMOVE(&transport_list, t, link); 883 884 return (SNMP_ERR_NOERROR); 885} 886 887/* 888 * File descriptor support 889 */ 890#ifdef USE_LIBBEGEMOT 891static void 892input(int fd, int mask __unused, void *uap) 893#else 894static void 895input(evContext ctx __unused, void *uap, int fd, int mask __unused) 896#endif 897{ 898 struct fdesc *f = uap; 899 900 (*f->func)(fd, f->udata); 901} 902 903void 904fd_suspend(void *p) 905{ 906 struct fdesc *f = p; 907 908#ifdef USE_LIBBEGEMOT 909 if (f->id >= 0) { 910 poll_unregister(f->id); 911 f->id = -1; 912 } 913#else 914 if (evTestID(f->id)) { 915 (void)evDeselectFD(evctx, f->id); 916 evInitID(&f->id); 917 } 918#endif 919} 920 921int 922fd_resume(void *p) 923{ 924 struct fdesc *f = p; 925 int err; 926 927#ifdef USE_LIBBEGEMOT 928 if (f->id >= 0) 929 return (0); 930 if ((f->id = poll_register(f->fd, input, f, POLL_IN)) < 0) { 931 err = errno; 932 syslog(LOG_ERR, "select fd %d: %m", f->fd); 933 errno = err; 934 return (-1); 935 } 936#else 937 if (evTestID(f->id)) 938 return (0); 939 if (evSelectFD(evctx, f->fd, EV_READ, input, f, &f->id)) { 940 err = errno; 941 syslog(LOG_ERR, "select fd %d: %m", f->fd); 942 errno = err; 943 return (-1); 944 } 945#endif 946 return (0); 947} 948 949void * 950fd_select(int fd, void (*func)(int, void *), void *udata, struct lmodule *mod) 951{ 952 struct fdesc *f; 953 int err; 954 955 if ((f = malloc(sizeof(struct fdesc))) == NULL) { 956 err = errno; 957 syslog(LOG_ERR, "fd_select: %m"); 958 errno = err; 959 return (NULL); 960 } 961 f->fd = fd; 962 f->func = func; 963 f->udata = udata; 964 f->owner = mod; 965#ifdef USE_LIBBEGEMOT 966 f->id = -1; 967#else 968 evInitID(&f->id); 969#endif 970 971 if (fd_resume(f)) { 972 err = errno; 973 free(f); 974 errno = err; 975 return (NULL); 976 } 977 978 LIST_INSERT_HEAD(&fdesc_list, f, link); 979 980 return (f); 981} 982 983void 984fd_deselect(void *p) 985{ 986 struct fdesc *f = p; 987 988 LIST_REMOVE(f, link); 989 fd_suspend(f); 990 free(f); 991} 992 993static void 994fd_flush(struct lmodule *mod) 995{ 996 struct fdesc *t, *t1; 997 998 t = LIST_FIRST(&fdesc_list); 999 while (t != NULL) { 1000 t1 = LIST_NEXT(t, link); 1001 if (t->owner == mod) 1002 fd_deselect(t); 1003 t = t1; 1004 } 1005} 1006 1007/* 1008 * Consume a message from the input buffer 1009 */ 1010static void 1011snmp_input_consume(struct port_input *pi) 1012{ 1013 if (!pi->stream) { 1014 /* always consume everything */ 1015 pi->length = 0; 1016 return; 1017 } 1018 if (pi->consumed >= pi->length) { 1019 /* all bytes consumed */ 1020 pi->length = 0; 1021 return; 1022 } 1023 memmove(pi->buf, pi->buf + pi->consumed, pi->length - pi->consumed); 1024 pi->length -= pi->consumed; 1025} 1026 1027/* 1028 * Input from a socket 1029 */ 1030int 1031snmpd_input(struct port_input *pi, struct tport *tport) 1032{ 1033 u_char *sndbuf; 1034 size_t sndlen; 1035 struct snmp_pdu pdu; 1036 enum snmpd_input_err ierr, ferr; 1037 enum snmpd_proxy_err perr; 1038 ssize_t ret, slen; 1039 int32_t vi; 1040#ifdef USE_TCPWRAPPERS 1041 char client[16]; 1042#endif 1043 struct msghdr msg; 1044 struct iovec iov[1]; 1045 1046 ret = tport->transport->vtab->recv(pi); 1047 if (ret == -1) 1048 return (-1); 1049 1050#ifdef USE_TCPWRAPPERS 1051 /* 1052 * In case of AF_INET{6} peer, do hosts_access(5) check. 1053 */ 1054 if (pi->peer->sa_family != AF_LOCAL && 1055 inet_ntop(pi->peer->sa_family, 1056 &((const struct sockaddr_in *)(const void *)pi->peer)->sin_addr, 1057 client, sizeof(client)) != NULL) { 1058 request_set(&req, RQ_CLIENT_ADDR, client, 0); 1059 if (hosts_access(&req) == 0) { 1060 syslog(LOG_ERR, "refused connection from %.500s", 1061 eval_client(&req)); 1062 return (-1); 1063 } 1064 } else if (pi->peer->sa_family != AF_LOCAL) 1065 syslog(LOG_ERR, "inet_ntop(): %m"); 1066#endif 1067 1068 /* 1069 * Handle input 1070 */ 1071 ierr = snmp_input_start(pi->buf, pi->length, "SNMP", &pdu, &vi, 1072 &pi->consumed); 1073 if (ierr == SNMPD_INPUT_TRUNC) { 1074 /* need more bytes. This is ok only for streaming transports. 1075 * but only if we have not reached bufsiz yet. */ 1076 if (pi->stream) { 1077 if (pi->length == buf_size(0)) { 1078 snmpd_stats.silentDrops++; 1079 return (-1); 1080 } 1081 return (0); 1082 } 1083 snmpd_stats.silentDrops++; 1084 return (-1); 1085 } 1086 1087 /* can't check for bad SET pdus here, because a proxy may have to 1088 * check the access first. We don't want to return an error response 1089 * to a proxy PDU with a wrong community */ 1090 if (ierr == SNMPD_INPUT_FAILED) { 1091 /* for streaming transports this is fatal */ 1092 if (pi->stream) 1093 return (-1); 1094 snmp_input_consume(pi); 1095 return (0); 1096 } 1097 if (ierr == SNMPD_INPUT_BAD_COMM) { 1098 snmp_input_consume(pi); 1099 return (0); 1100 } 1101 1102 /* 1103 * If that is a module community and the module has a proxy function, 1104 * the hand it over to the module. 1105 */ 1106 if (comm != NULL && comm->owner != NULL && 1107 comm->owner->config->proxy != NULL) { 1108 perr = (*comm->owner->config->proxy)(&pdu, tport->transport, 1109 &tport->index, pi->peer, pi->peerlen, ierr, vi, 1110 !pi->cred || pi->priv); 1111 1112 switch (perr) { 1113 1114 case SNMPD_PROXY_OK: 1115 snmp_input_consume(pi); 1116 return (0); 1117 1118 case SNMPD_PROXY_REJ: 1119 break; 1120 1121 case SNMPD_PROXY_DROP: 1122 snmp_input_consume(pi); 1123 snmp_pdu_free(&pdu); 1124 snmpd_stats.proxyDrops++; 1125 return (0); 1126 1127 case SNMPD_PROXY_BADCOMM: 1128 snmp_input_consume(pi); 1129 snmp_pdu_free(&pdu); 1130 snmpd_stats.inBadCommunityNames++; 1131 if (snmpd.auth_traps) 1132 snmp_send_trap(&oid_authenticationFailure, 1133 (struct snmp_value *)NULL); 1134 return (0); 1135 1136 case SNMPD_PROXY_BADCOMMUSE: 1137 snmp_input_consume(pi); 1138 snmp_pdu_free(&pdu); 1139 snmpd_stats.inBadCommunityUses++; 1140 if (snmpd.auth_traps) 1141 snmp_send_trap(&oid_authenticationFailure, 1142 (struct snmp_value *)NULL); 1143 return (0); 1144 } 1145 } 1146 1147 /* 1148 * Check type 1149 */ 1150 if (pdu.type == SNMP_PDU_RESPONSE || 1151 pdu.type == SNMP_PDU_TRAP || 1152 pdu.type == SNMP_PDU_TRAP2) { 1153 snmpd_stats.silentDrops++; 1154 snmpd_stats.inBadPduTypes++; 1155 snmp_pdu_free(&pdu); 1156 snmp_input_consume(pi); 1157 return (0); 1158 } 1159 1160 /* 1161 * Check community 1162 */ 1163 if (pdu.version < SNMP_V3 && 1164 ((pi->cred && !pi->priv && pdu.type == SNMP_PDU_SET) || 1165 (community != COMM_WRITE && 1166 (pdu.type == SNMP_PDU_SET || community != COMM_READ)))) { 1167 snmpd_stats.inBadCommunityUses++; 1168 snmp_pdu_free(&pdu); 1169 snmp_input_consume(pi); 1170 if (snmpd.auth_traps) 1171 snmp_send_trap(&oid_authenticationFailure, 1172 (struct snmp_value *)NULL); 1173 return (0); 1174 } 1175 1176 /* 1177 * Execute it. 1178 */ 1179 if ((sndbuf = buf_alloc(1)) == NULL) { 1180 snmpd_stats.silentDrops++; 1181 snmp_pdu_free(&pdu); 1182 snmp_input_consume(pi); 1183 return (0); 1184 } 1185 ferr = snmp_input_finish(&pdu, pi->buf, pi->length, 1186 sndbuf, &sndlen, "SNMP", ierr, vi, NULL); 1187 1188 if (ferr == SNMPD_INPUT_OK) { 1189 msg.msg_name = pi->peer; 1190 msg.msg_namelen = pi->peerlen; 1191 msg.msg_iov = iov; 1192 msg.msg_iovlen = 1; 1193 msg.msg_flags = 0; 1194 iov[0].iov_base = sndbuf; 1195 iov[0].iov_len = sndlen; 1196 1197 slen = sendmsg(pi->fd, &msg, 0); 1198 if (slen == -1) 1199 syslog(LOG_ERR, "sendmsg: %m"); 1200 else if ((size_t)slen != sndlen) 1201 syslog(LOG_ERR, "sendmsg: short write %zu/%zu", 1202 sndlen, (size_t)slen); 1203 } 1204 snmp_pdu_free(&pdu); 1205 free(sndbuf); 1206 snmp_input_consume(pi); 1207 1208 return (0); 1209} 1210 1211/* 1212 * Send a PDU to a given port 1213 */ 1214void 1215snmp_send_port(void *targ, const struct asn_oid *port, struct snmp_pdu *pdu, 1216 const struct sockaddr *addr, socklen_t addrlen) 1217{ 1218 struct transport *trans = targ; 1219 struct tport *tp; 1220 u_char *sndbuf; 1221 size_t sndlen; 1222 ssize_t len; 1223 1224 TAILQ_FOREACH(tp, &trans->table, link) 1225 if (asn_compare_oid(port, &tp->index) == 0) 1226 break; 1227 if (tp == 0) 1228 return; 1229 1230 if ((sndbuf = buf_alloc(1)) == NULL) 1231 return; 1232 1233 snmp_output(pdu, sndbuf, &sndlen, "SNMP PROXY"); 1234 1235 len = trans->vtab->send(tp, sndbuf, sndlen, addr, addrlen); 1236 1237 if (len == -1) 1238 syslog(LOG_ERR, "sendto: %m"); 1239 else if ((size_t)len != sndlen) 1240 syslog(LOG_ERR, "sendto: short write %zu/%zu", 1241 sndlen, (size_t)len); 1242 1243 free(sndbuf); 1244} 1245 1246 1247/* 1248 * Close an input source 1249 */ 1250void 1251snmpd_input_close(struct port_input *pi) 1252{ 1253 if (pi->id != NULL) 1254 fd_deselect(pi->id); 1255 if (pi->fd >= 0) 1256 (void)close(pi->fd); 1257 if (pi->buf != NULL) 1258 free(pi->buf); 1259} 1260 1261/* 1262 * Dump internal state. 1263 */ 1264#ifdef USE_LIBBEGEMOT 1265static void 1266info_func(void) 1267#else 1268static void 1269info_func(evContext ctx __unused, void *uap __unused, const void *tag __unused) 1270#endif 1271{ 1272 struct lmodule *m; 1273 u_int i; 1274 char buf[10000]; 1275 1276 syslog(LOG_DEBUG, "Dump of SNMPd %lu\n", (u_long)getpid()); 1277 for (i = 0; i < tree_size; i++) { 1278 switch (tree[i].type) { 1279 1280 case SNMP_NODE_LEAF: 1281 sprintf(buf, "LEAF: %s %s", tree[i].name, 1282 asn_oid2str(&tree[i].oid)); 1283 break; 1284 1285 case SNMP_NODE_COLUMN: 1286 sprintf(buf, "COL: %s %s", tree[i].name, 1287 asn_oid2str(&tree[i].oid)); 1288 break; 1289 } 1290 syslog(LOG_DEBUG, "%s", buf); 1291 } 1292 1293 TAILQ_FOREACH(m, &lmodules, link) 1294 if (m->config->dump) 1295 (*m->config->dump)(); 1296} 1297 1298/* 1299 * Re-read configuration 1300 */ 1301#ifdef USE_LIBBEGEMOT 1302static void 1303config_func(void) 1304#else 1305static void 1306config_func(evContext ctx __unused, void *uap __unused, 1307 const void *tag __unused) 1308#endif 1309{ 1310 struct lmodule *m; 1311 1312 if (read_config(config_file, NULL)) { 1313 syslog(LOG_ERR, "error reading config file '%s'", config_file); 1314 return; 1315 } 1316 TAILQ_FOREACH(m, &lmodules, link) 1317 if (m->config->config) 1318 (*m->config->config)(); 1319} 1320 1321/* 1322 * On USR1 dump actual configuration. 1323 */ 1324static void 1325onusr1(int s __unused) 1326{ 1327 1328 work |= WORK_DOINFO; 1329} 1330static void 1331onhup(int s __unused) 1332{ 1333 1334 work |= WORK_RECONFIG; 1335} 1336 1337static void 1338onterm(int s __unused) 1339{ 1340 1341 /* allow clean-up */ 1342 exit(0); 1343} 1344 1345static void 1346init_sigs(void) 1347{ 1348 struct sigaction sa; 1349 1350 sa.sa_handler = onusr1; 1351 sa.sa_flags = SA_RESTART; 1352 sigemptyset(&sa.sa_mask); 1353 if (sigaction(SIGUSR1, &sa, NULL)) { 1354 syslog(LOG_ERR, "sigaction: %m"); 1355 exit(1); 1356 } 1357 1358 sa.sa_handler = onhup; 1359 if (sigaction(SIGHUP, &sa, NULL)) { 1360 syslog(LOG_ERR, "sigaction: %m"); 1361 exit(1); 1362 } 1363 1364 sa.sa_handler = onterm; 1365 sa.sa_flags = 0; 1366 sigemptyset(&sa.sa_mask); 1367 if (sigaction(SIGTERM, &sa, NULL)) { 1368 syslog(LOG_ERR, "sigaction: %m"); 1369 exit(1); 1370 } 1371 if (sigaction(SIGINT, &sa, NULL)) { 1372 syslog(LOG_ERR, "sigaction: %m"); 1373 exit(1); 1374 } 1375} 1376 1377static void 1378block_sigs(void) 1379{ 1380 sigset_t set; 1381 1382 sigfillset(&set); 1383 if (sigprocmask(SIG_BLOCK, &set, &blocked_sigs) == -1) { 1384 syslog(LOG_ERR, "SIG_BLOCK: %m"); 1385 exit(1); 1386 } 1387} 1388static void 1389unblock_sigs(void) 1390{ 1391 if (sigprocmask(SIG_SETMASK, &blocked_sigs, NULL) == -1) { 1392 syslog(LOG_ERR, "SIG_SETMASK: %m"); 1393 exit(1); 1394 } 1395} 1396 1397/* 1398 * Shut down 1399 */ 1400static void 1401term(void) 1402{ 1403 (void)unlink(pid_file); 1404} 1405 1406static void 1407trans_stop(void) 1408{ 1409 struct transport *t; 1410 1411 TAILQ_FOREACH(t, &transport_list, link) 1412 (void)t->vtab->stop(1); 1413} 1414 1415/* 1416 * Define a macro from the command line 1417 */ 1418static void 1419do_macro(char *arg) 1420{ 1421 char *eq; 1422 int err; 1423 1424 if ((eq = strchr(arg, '=')) == NULL) 1425 err = define_macro(arg, ""); 1426 else { 1427 *eq++ = '\0'; 1428 err = define_macro(arg, eq); 1429 } 1430 if (err == -1) { 1431 syslog(LOG_ERR, "cannot save macro: %m"); 1432 exit(1); 1433 } 1434} 1435 1436/* 1437 * Re-implement getsubopt from scratch, because the second argument is broken 1438 * and will not compile with WARNS=5. 1439 */ 1440static int 1441getsubopt1(char **arg, const char *const *options, char **valp, char **optp) 1442{ 1443 static const char *const delim = ",\t "; 1444 u_int i; 1445 char *ptr; 1446 1447 *optp = NULL; 1448 1449 /* skip leading junk */ 1450 for (ptr = *arg; *ptr != '\0'; ptr++) 1451 if (strchr(delim, *ptr) == NULL) 1452 break; 1453 if (*ptr == '\0') { 1454 *arg = ptr; 1455 return (-1); 1456 } 1457 *optp = ptr; 1458 1459 /* find the end of the option */ 1460 while (*++ptr != '\0') 1461 if (strchr(delim, *ptr) != NULL || *ptr == '=') 1462 break; 1463 1464 if (*ptr != '\0') { 1465 if (*ptr == '=') { 1466 *ptr++ = '\0'; 1467 *valp = ptr; 1468 while (*ptr != '\0' && strchr(delim, *ptr) == NULL) 1469 ptr++; 1470 if (*ptr != '\0') 1471 *ptr++ = '\0'; 1472 } else 1473 *ptr++ = '\0'; 1474 } 1475 1476 *arg = ptr; 1477 1478 for (i = 0; *options != NULL; options++, i++) 1479 if (strcmp(*optp, *options) == 0) 1480 return (i); 1481 return (-1); 1482} 1483 1484int 1485main(int argc, char *argv[]) 1486{ 1487 int opt; 1488 FILE *fp; 1489 int background = 1; 1490 struct tport *p; 1491 const char *prefix = "snmpd"; 1492 struct lmodule *m; 1493 char *value = NULL, *option; /* XXX */ 1494 struct transport *t; 1495 1496#define DBG_DUMP 0 1497#define DBG_EVENTS 1 1498#define DBG_TRACE 2 1499 static const char *const debug_opts[] = { 1500 "dump", 1501 "events", 1502 "trace", 1503 NULL 1504 }; 1505 1506 snmp_printf = snmp_printf_func; 1507 snmp_error = snmp_error_func; 1508 snmp_debug = snmp_debug_func; 1509 asn_error = asn_error_func; 1510 1511 while ((opt = getopt(argc, argv, "c:dD:e:hI:l:m:p:")) != EOF) 1512 switch (opt) { 1513 1514 case 'c': 1515 strlcpy(config_file, optarg, sizeof(config_file)); 1516 break; 1517 1518 case 'd': 1519 background = 0; 1520 break; 1521 1522 case 'D': 1523 while (*optarg) { 1524 switch (getsubopt1(&optarg, debug_opts, 1525 &value, &option)) { 1526 1527 case DBG_DUMP: 1528 debug.dump_pdus = 1; 1529 break; 1530 1531 case DBG_EVENTS: 1532 debug.evdebug++; 1533 break; 1534 1535 case DBG_TRACE: 1536 if (value == NULL) 1537 syslog(LOG_ERR, 1538 "no value for 'trace'"); 1539 else 1540 snmp_trace = strtoul(value, 1541 NULL, 0); 1542 break; 1543 1544 case -1: 1545 if (suboptarg) 1546 syslog(LOG_ERR, 1547 "unknown debug flag '%s'", 1548 option); 1549 else 1550 syslog(LOG_ERR, 1551 "missing debug flag"); 1552 break; 1553 } 1554 } 1555 break; 1556 1557 case 'e': 1558 strlcpy(engine_file, optarg, sizeof(engine_file)); 1559 break; 1560 case 'h': 1561 fprintf(stderr, "%s", usgtxt); 1562 exit(0); 1563 1564 case 'I': 1565 syspath = optarg; 1566 break; 1567 1568 case 'l': 1569 prefix = optarg; 1570 break; 1571 1572 case 'm': 1573 do_macro(optarg); 1574 break; 1575 1576 case 'p': 1577 strlcpy(pid_file, optarg, sizeof(pid_file)); 1578 break; 1579 } 1580 1581 openlog(prefix, LOG_PID | (background ? 0 : LOG_PERROR), LOG_USER); 1582 setlogmask(LOG_UPTO(debug.logpri - 1)); 1583 1584 if (background && daemon(0, 0) < 0) { 1585 syslog(LOG_ERR, "daemon: %m"); 1586 exit(1); 1587 } 1588 1589 argc -= optind; 1590 argv += optind; 1591 1592 progargs = argv; 1593 nprogargs = argc; 1594 1595 srandomdev(); 1596 1597 snmp_serial_no = random(); 1598 1599#ifdef USE_TCPWRAPPERS 1600 /* 1601 * Initialize hosts_access(3) handler. 1602 */ 1603 request_init(&req, RQ_DAEMON, "snmpd", 0); 1604 sock_methods(&req); 1605#endif 1606 1607 /* 1608 * Initialize the tree. 1609 */ 1610 if ((tree = malloc(sizeof(struct snmp_node) * CTREE_SIZE)) == NULL) { 1611 syslog(LOG_ERR, "%m"); 1612 exit(1); 1613 } 1614 memcpy(tree, ctree, sizeof(struct snmp_node) * CTREE_SIZE); 1615 tree_size = CTREE_SIZE; 1616 1617 /* 1618 * Get standard communities 1619 */ 1620 (void)comm_define(1, "SNMP read", NULL, NULL); 1621 (void)comm_define(2, "SNMP write", NULL, NULL); 1622 community = COMM_INITIALIZE; 1623 1624 trap_reqid = reqid_allocate(512, NULL); 1625 1626 if (config_file[0] == '\0') 1627 snprintf(config_file, sizeof(config_file), PATH_CONFIG, prefix); 1628 1629 init_actvals(); 1630 init_snmpd_engine(); 1631 1632 this_tick = get_ticks(); 1633 start_tick = this_tick; 1634 1635 /* start transports */ 1636 if (atexit(trans_stop) == -1) { 1637 syslog(LOG_ERR, "atexit failed: %m"); 1638 exit(1); 1639 } 1640 if (udp_trans.start() != SNMP_ERR_NOERROR) 1641 syslog(LOG_WARNING, "cannot start UDP transport"); 1642 if (lsock_trans.start() != SNMP_ERR_NOERROR) 1643 syslog(LOG_WARNING, "cannot start LSOCK transport"); 1644 1645#ifdef USE_LIBBEGEMOT 1646 if (debug.evdebug > 0) 1647 rpoll_trace = 1; 1648#else 1649 if (evCreate(&evctx)) { 1650 syslog(LOG_ERR, "evCreate: %m"); 1651 exit(1); 1652 } 1653 if (debug.evdebug > 0) 1654 evSetDebug(evctx, 10, stderr); 1655#endif 1656 1657 if (engine_file[0] == '\0') 1658 snprintf(engine_file, sizeof(engine_file), PATH_ENGINE, prefix); 1659 1660 if (read_config(config_file, NULL)) { 1661 syslog(LOG_ERR, "error in config file"); 1662 exit(1); 1663 } 1664 1665 TAILQ_FOREACH(t, &transport_list, link) 1666 TAILQ_FOREACH(p, &t->table, link) 1667 t->vtab->init_port(p); 1668 1669 init_sigs(); 1670 1671 if (pid_file[0] == '\0') 1672 snprintf(pid_file, sizeof(pid_file), PATH_PID, prefix); 1673 1674 if ((fp = fopen(pid_file, "w")) != NULL) { 1675 fprintf(fp, "%u", getpid()); 1676 fclose(fp); 1677 if (atexit(term) == -1) { 1678 syslog(LOG_ERR, "atexit failed: %m"); 1679 (void)remove(pid_file); 1680 exit(0); 1681 } 1682 } 1683 1684 if (or_register(&oid_snmpMIB, "The MIB module for SNMPv2 entities.", 1685 NULL) == 0) { 1686 syslog(LOG_ERR, "cannot register SNMPv2 MIB"); 1687 exit(1); 1688 } 1689 if (or_register(&oid_begemotSnmpd, "The MIB module for the Begemot SNMPd.", 1690 NULL) == 0) { 1691 syslog(LOG_ERR, "cannot register begemotSnmpd MIB"); 1692 exit(1); 1693 } 1694 1695 while ((m = TAILQ_FIRST(&modules_start)) != NULL) { 1696 m->flags &= ~LM_ONSTARTLIST; 1697 TAILQ_REMOVE(&modules_start, m, start); 1698 lm_start(m); 1699 } 1700 1701 snmp_send_trap(&oid_coldStart, (struct snmp_value *)NULL); 1702 1703 for (;;) { 1704#ifndef USE_LIBBEGEMOT 1705 evEvent event; 1706#endif 1707 struct lmodule *mod; 1708 1709 TAILQ_FOREACH(mod, &lmodules, link) 1710 if (mod->config->idle != NULL) 1711 (*mod->config->idle)(); 1712 1713#ifndef USE_LIBBEGEMOT 1714 if (evGetNext(evctx, &event, EV_WAIT) == 0) { 1715 if (evDispatch(evctx, event)) 1716 syslog(LOG_ERR, "evDispatch: %m"); 1717 } else if (errno != EINTR) { 1718 syslog(LOG_ERR, "evGetNext: %m"); 1719 exit(1); 1720 } 1721#else 1722 poll_dispatch(1); 1723#endif 1724 1725 if (work != 0) { 1726 block_sigs(); 1727 if (work & WORK_DOINFO) { 1728#ifdef USE_LIBBEGEMOT 1729 info_func(); 1730#else 1731 if (evWaitFor(evctx, &work, info_func, 1732 NULL, NULL) == -1) { 1733 syslog(LOG_ERR, "evWaitFor: %m"); 1734 exit(1); 1735 } 1736#endif 1737 } 1738 if (work & WORK_RECONFIG) { 1739#ifdef USE_LIBBEGEMOT 1740 config_func(); 1741#else 1742 if (evWaitFor(evctx, &work, config_func, 1743 NULL, NULL) == -1) { 1744 syslog(LOG_ERR, "evWaitFor: %m"); 1745 exit(1); 1746 } 1747#endif 1748 } 1749 work = 0; 1750 unblock_sigs(); 1751#ifndef USE_LIBBEGEMOT 1752 if (evDo(evctx, &work) == -1) { 1753 syslog(LOG_ERR, "evDo: %m"); 1754 exit(1); 1755 } 1756#endif 1757 } 1758 } 1759 1760 return (0); 1761} 1762 1763uint64_t 1764get_ticks(void) 1765{ 1766 struct timeval tv; 1767 uint64_t ret; 1768 1769 if (gettimeofday(&tv, NULL)) 1770 abort(); 1771 ret = tv.tv_sec * 100ULL + tv.tv_usec / 10000ULL; 1772 return (ret); 1773} 1774 1775/* 1776 * Timer support 1777 */ 1778 1779/* 1780 * Trampoline for the non-repeatable timers. 1781 */ 1782#ifdef USE_LIBBEGEMOT 1783static void 1784tfunc(int tid __unused, void *uap) 1785#else 1786static void 1787tfunc(evContext ctx __unused, void *uap, struct timespec due __unused, 1788 struct timespec inter __unused) 1789#endif 1790{ 1791 struct timer *tp = uap; 1792 1793 LIST_REMOVE(tp, link); 1794 tp->func(tp->udata); 1795 free(tp); 1796} 1797 1798/* 1799 * Trampoline for the repeatable timers. 1800 */ 1801#ifdef USE_LIBBEGEMOT 1802static void 1803trfunc(int tid __unused, void *uap) 1804#else 1805static void 1806trfunc(evContext ctx __unused, void *uap, struct timespec due __unused, 1807 struct timespec inter __unused) 1808#endif 1809{ 1810 struct timer *tp = uap; 1811 1812 tp->func(tp->udata); 1813} 1814 1815/* 1816 * Start a one-shot timer 1817 */ 1818void * 1819timer_start(u_int ticks, void (*func)(void *), void *udata, struct lmodule *mod) 1820{ 1821 struct timer *tp; 1822#ifndef USE_LIBBEGEMOT 1823 struct timespec due; 1824#endif 1825 1826 if ((tp = malloc(sizeof(struct timer))) == NULL) { 1827 syslog(LOG_CRIT, "out of memory for timer"); 1828 exit(1); 1829 } 1830 1831#ifndef USE_LIBBEGEMOT 1832 due = evAddTime(evNowTime(), 1833 evConsTime(ticks / 100, (ticks % 100) * 10000)); 1834#endif 1835 1836 tp->udata = udata; 1837 tp->owner = mod; 1838 tp->func = func; 1839 1840 LIST_INSERT_HEAD(&timer_list, tp, link); 1841 1842#ifdef USE_LIBBEGEMOT 1843 if ((tp->id = poll_start_timer(ticks * 10, 0, tfunc, tp)) < 0) { 1844 syslog(LOG_ERR, "cannot set timer: %m"); 1845 exit(1); 1846 } 1847#else 1848 if (evSetTimer(evctx, tfunc, tp, due, evConsTime(0, 0), &tp->id) 1849 == -1) { 1850 syslog(LOG_ERR, "cannot set timer: %m"); 1851 exit(1); 1852 } 1853#endif 1854 return (tp); 1855} 1856 1857/* 1858 * Start a repeatable timer. When used with USE_LIBBEGEMOT the first argument 1859 * is currently ignored and the initial number of ticks is set to the 1860 * repeat number of ticks. 1861 */ 1862void * 1863timer_start_repeat(u_int ticks __unused, u_int repeat_ticks, 1864 void (*func)(void *), void *udata, struct lmodule *mod) 1865{ 1866 struct timer *tp; 1867#ifndef USE_LIBBEGEMOT 1868 struct timespec due; 1869 struct timespec inter; 1870#endif 1871 1872 if ((tp = malloc(sizeof(struct timer))) == NULL) { 1873 syslog(LOG_CRIT, "out of memory for timer"); 1874 exit(1); 1875 } 1876 1877#ifndef USE_LIBBEGEMOT 1878 due = evAddTime(evNowTime(), 1879 evConsTime(ticks / 100, (ticks % 100) * 10000)); 1880 inter = evConsTime(repeat_ticks / 100, (repeat_ticks % 100) * 10000); 1881#endif 1882 1883 tp->udata = udata; 1884 tp->owner = mod; 1885 tp->func = func; 1886 1887 LIST_INSERT_HEAD(&timer_list, tp, link); 1888 1889#ifdef USE_LIBBEGEMOT 1890 if ((tp->id = poll_start_timer(repeat_ticks * 10, 1, trfunc, tp)) < 0) { 1891 syslog(LOG_ERR, "cannot set timer: %m"); 1892 exit(1); 1893 } 1894#else 1895 if (evSetTimer(evctx, trfunc, tp, due, inter, &tp->id) == -1) { 1896 syslog(LOG_ERR, "cannot set timer: %m"); 1897 exit(1); 1898 } 1899#endif 1900 return (tp); 1901} 1902 1903/* 1904 * Stop a timer. 1905 */ 1906void 1907timer_stop(void *p) 1908{ 1909 struct timer *tp = p; 1910 1911 LIST_REMOVE(tp, link); 1912#ifdef USE_LIBBEGEMOT 1913 poll_stop_timer(tp->id); 1914#else 1915 if (evClearTimer(evctx, tp->id) == -1) { 1916 syslog(LOG_ERR, "cannot stop timer: %m"); 1917 exit(1); 1918 } 1919#endif 1920 free(p); 1921} 1922 1923static void 1924timer_flush(struct lmodule *mod) 1925{ 1926 struct timer *t, *t1; 1927 1928 t = LIST_FIRST(&timer_list); 1929 while (t != NULL) { 1930 t1 = LIST_NEXT(t, link); 1931 if (t->owner == mod) 1932 timer_stop(t); 1933 t = t1; 1934 } 1935} 1936 1937static void 1938snmp_printf_func(const char *fmt, ...) 1939{ 1940 va_list ap; 1941 static char *pend = NULL; 1942 char *ret, *new; 1943 1944 va_start(ap, fmt); 1945 vasprintf(&ret, fmt, ap); 1946 va_end(ap); 1947 1948 if (ret == NULL) 1949 return; 1950 if (pend != NULL) { 1951 if ((new = realloc(pend, strlen(pend) + strlen(ret) + 1)) 1952 == NULL) { 1953 free(ret); 1954 return; 1955 } 1956 pend = new; 1957 strcat(pend, ret); 1958 free(ret); 1959 } else 1960 pend = ret; 1961 1962 while ((ret = strchr(pend, '\n')) != NULL) { 1963 *ret = '\0'; 1964 syslog(LOG_DEBUG, "%s", pend); 1965 if (strlen(ret + 1) == 0) { 1966 free(pend); 1967 pend = NULL; 1968 break; 1969 } 1970 strcpy(pend, ret + 1); 1971 } 1972} 1973 1974static void 1975snmp_error_func(const char *err, ...) 1976{ 1977 char errbuf[1000]; 1978 va_list ap; 1979 1980 if (!(snmp_trace & LOG_SNMP_ERRORS)) 1981 return; 1982 1983 va_start(ap, err); 1984 snprintf(errbuf, sizeof(errbuf), "SNMP: "); 1985 vsnprintf(errbuf + strlen(errbuf), 1986 sizeof(errbuf) - strlen(errbuf), err, ap); 1987 va_end(ap); 1988 1989 syslog(LOG_ERR, "%s", errbuf); 1990} 1991 1992static void 1993snmp_debug_func(const char *err, ...) 1994{ 1995 char errbuf[1000]; 1996 va_list ap; 1997 1998 va_start(ap, err); 1999 snprintf(errbuf, sizeof(errbuf), "SNMP: "); 2000 vsnprintf(errbuf+strlen(errbuf), sizeof(errbuf)-strlen(errbuf), 2001 err, ap); 2002 va_end(ap); 2003 2004 syslog(LOG_DEBUG, "%s", errbuf); 2005} 2006 2007static void 2008asn_error_func(const struct asn_buf *b, const char *err, ...) 2009{ 2010 char errbuf[1000]; 2011 va_list ap; 2012 u_int i; 2013 2014 if (!(snmp_trace & LOG_ASN1_ERRORS)) 2015 return; 2016 2017 va_start(ap, err); 2018 snprintf(errbuf, sizeof(errbuf), "ASN.1: "); 2019 vsnprintf(errbuf + strlen(errbuf), 2020 sizeof(errbuf) - strlen(errbuf), err, ap); 2021 va_end(ap); 2022 2023 if (b != NULL) { 2024 snprintf(errbuf + strlen(errbuf), 2025 sizeof(errbuf) - strlen(errbuf), " at"); 2026 for (i = 0; b->asn_len > i; i++) 2027 snprintf(errbuf + strlen(errbuf), 2028 sizeof(errbuf) - strlen(errbuf), 2029 " %02x", b->asn_cptr[i]); 2030 } 2031 2032 syslog(LOG_ERR, "%s", errbuf); 2033} 2034 2035/* 2036 * Create a new community 2037 */ 2038u_int 2039comm_define(u_int priv, const char *descr, struct lmodule *owner, 2040 const char *str) 2041{ 2042 struct community *c, *p; 2043 u_int ncomm; 2044 2045 /* generate an identifier */ 2046 do { 2047 if ((ncomm = next_community_index++) == UINT_MAX) 2048 next_community_index = 1; 2049 TAILQ_FOREACH(c, &community_list, link) 2050 if (c->value == ncomm) 2051 break; 2052 } while (c != NULL); 2053 2054 if ((c = malloc(sizeof(struct community))) == NULL) { 2055 syslog(LOG_ERR, "comm_define: %m"); 2056 return (0); 2057 } 2058 c->owner = owner; 2059 c->value = ncomm; 2060 c->descr = descr; 2061 c->string = NULL; 2062 c->private = priv; 2063 2064 if (str != NULL) { 2065 if((c->string = malloc(strlen(str)+1)) == NULL) { 2066 free(c); 2067 return (0); 2068 } 2069 strcpy(c->string, str); 2070 } 2071 2072 /* make index */ 2073 if (c->owner == NULL) { 2074 c->index.len = 1; 2075 c->index.subs[0] = 0; 2076 } else { 2077 c->index = c->owner->index; 2078 } 2079 c->index.subs[c->index.len++] = c->private; 2080 2081 /* 2082 * Insert ordered 2083 */ 2084 TAILQ_FOREACH(p, &community_list, link) { 2085 if (asn_compare_oid(&p->index, &c->index) > 0) { 2086 TAILQ_INSERT_BEFORE(p, c, link); 2087 break; 2088 } 2089 } 2090 if (p == NULL) 2091 TAILQ_INSERT_TAIL(&community_list, c, link); 2092 return (c->value); 2093} 2094 2095const char * 2096comm_string(u_int ncomm) 2097{ 2098 struct community *p; 2099 2100 TAILQ_FOREACH(p, &community_list, link) 2101 if (p->value == ncomm) 2102 return (p->string); 2103 return (NULL); 2104} 2105 2106/* 2107 * Delete all communities allocated by a module 2108 */ 2109static void 2110comm_flush(struct lmodule *mod) 2111{ 2112 struct community *p, *p1; 2113 2114 p = TAILQ_FIRST(&community_list); 2115 while (p != NULL) { 2116 p1 = TAILQ_NEXT(p, link); 2117 if (p->owner == mod) { 2118 free(p->string); 2119 TAILQ_REMOVE(&community_list, p, link); 2120 free(p); 2121 } 2122 p = p1; 2123 } 2124} 2125 2126/* 2127 * Request ID handling. 2128 * 2129 * Allocate a new range of request ids. Use a first fit algorithm. 2130 */ 2131u_int 2132reqid_allocate(int size, struct lmodule *mod) 2133{ 2134 u_int type; 2135 struct idrange *r, *r1; 2136 2137 if (size <= 0 || size > INT32_MAX) { 2138 syslog(LOG_CRIT, "%s: size out of range: %d", __func__, size); 2139 return (0); 2140 } 2141 /* allocate a type id */ 2142 do { 2143 if ((type = next_idrange++) == UINT_MAX) 2144 next_idrange = 1; 2145 TAILQ_FOREACH(r, &idrange_list, link) 2146 if (r->type == type) 2147 break; 2148 } while(r != NULL); 2149 2150 /* find a range */ 2151 if (TAILQ_EMPTY(&idrange_list)) 2152 r = NULL; 2153 else { 2154 r = TAILQ_FIRST(&idrange_list); 2155 if (r->base < size) { 2156 while((r1 = TAILQ_NEXT(r, link)) != NULL) { 2157 if (r1->base - (r->base + r->size) >= size) 2158 break; 2159 r = r1; 2160 } 2161 r = r1; 2162 } 2163 if (r == NULL) { 2164 r1 = TAILQ_LAST(&idrange_list, idrange_list); 2165 if (INT32_MAX - size + 1 < r1->base + r1->size) { 2166 syslog(LOG_ERR, "out of id ranges (%u)", size); 2167 return (0); 2168 } 2169 } 2170 } 2171 2172 /* allocate structure */ 2173 if ((r1 = malloc(sizeof(struct idrange))) == NULL) { 2174 syslog(LOG_ERR, "%s: %m", __FUNCTION__); 2175 return (0); 2176 } 2177 2178 r1->type = type; 2179 r1->size = size; 2180 r1->owner = mod; 2181 if (TAILQ_EMPTY(&idrange_list) || r == TAILQ_FIRST(&idrange_list)) { 2182 r1->base = 0; 2183 TAILQ_INSERT_HEAD(&idrange_list, r1, link); 2184 } else if (r == NULL) { 2185 r = TAILQ_LAST(&idrange_list, idrange_list); 2186 r1->base = r->base + r->size; 2187 TAILQ_INSERT_TAIL(&idrange_list, r1, link); 2188 } else { 2189 r = TAILQ_PREV(r, idrange_list, link); 2190 r1->base = r->base + r->size; 2191 TAILQ_INSERT_AFTER(&idrange_list, r, r1, link); 2192 } 2193 r1->next = r1->base; 2194 2195 return (type); 2196} 2197 2198int32_t 2199reqid_next(u_int type) 2200{ 2201 struct idrange *r; 2202 int32_t id; 2203 2204 TAILQ_FOREACH(r, &idrange_list, link) 2205 if (r->type == type) 2206 break; 2207 if (r == NULL) { 2208 syslog(LOG_CRIT, "wrong idrange type"); 2209 abort(); 2210 } 2211 if ((id = r->next++) == r->base + (r->size - 1)) 2212 r->next = r->base; 2213 return (id); 2214} 2215 2216int32_t 2217reqid_base(u_int type) 2218{ 2219 struct idrange *r; 2220 2221 TAILQ_FOREACH(r, &idrange_list, link) 2222 if (r->type == type) 2223 return (r->base); 2224 syslog(LOG_CRIT, "wrong idrange type"); 2225 abort(); 2226} 2227 2228u_int 2229reqid_type(int32_t reqid) 2230{ 2231 struct idrange *r; 2232 2233 TAILQ_FOREACH(r, &idrange_list, link) 2234 if (reqid >= r->base && reqid <= r->base + (r->size - 1)) 2235 return (r->type); 2236 return (0); 2237} 2238 2239int 2240reqid_istype(int32_t reqid, u_int type) 2241{ 2242 return (reqid_type(reqid) == type); 2243} 2244 2245/* 2246 * Delete all communities allocated by a module 2247 */ 2248static void 2249reqid_flush(struct lmodule *mod) 2250{ 2251 struct idrange *p, *p1; 2252 2253 p = TAILQ_FIRST(&idrange_list); 2254 while (p != NULL) { 2255 p1 = TAILQ_NEXT(p, link); 2256 if (p->owner == mod) { 2257 TAILQ_REMOVE(&idrange_list, p, link); 2258 free(p); 2259 } 2260 p = p1; 2261 } 2262} 2263 2264/* 2265 * Merge the given tree for the given module into the main tree. 2266 */ 2267static int 2268compare_node(const void *v1, const void *v2) 2269{ 2270 const struct snmp_node *n1 = v1; 2271 const struct snmp_node *n2 = v2; 2272 2273 return (asn_compare_oid(&n1->oid, &n2->oid)); 2274} 2275static int 2276tree_merge(const struct snmp_node *ntree, u_int nsize, struct lmodule *mod) 2277{ 2278 struct snmp_node *xtree; 2279 u_int i; 2280 2281 xtree = realloc(tree, sizeof(*tree) * (tree_size + nsize)); 2282 if (xtree == NULL) { 2283 syslog(LOG_ERR, "tree_merge: %m"); 2284 return (-1); 2285 } 2286 tree = xtree; 2287 memcpy(&tree[tree_size], ntree, sizeof(*tree) * nsize); 2288 2289 for (i = 0; i < nsize; i++) 2290 tree[tree_size + i].tree_data = mod; 2291 2292 tree_size += nsize; 2293 2294 qsort(tree, tree_size, sizeof(tree[0]), compare_node); 2295 2296 return (0); 2297} 2298 2299/* 2300 * Remove all nodes belonging to the loadable module 2301 */ 2302static void 2303tree_unmerge(struct lmodule *mod) 2304{ 2305 u_int s, d; 2306 2307 for(s = d = 0; s < tree_size; s++) 2308 if (tree[s].tree_data != mod) { 2309 if (s != d) 2310 tree[d] = tree[s]; 2311 d++; 2312 } 2313 tree_size = d; 2314} 2315 2316/* 2317 * Loadable modules 2318 */ 2319struct lmodule * 2320lm_load(const char *path, const char *section) 2321{ 2322 struct lmodule *m; 2323 int err; 2324 int i; 2325 char *av[MAX_MOD_ARGS + 1]; 2326 int ac; 2327 u_int u; 2328 2329 if ((m = malloc(sizeof(*m))) == NULL) { 2330 syslog(LOG_ERR, "lm_load: %m"); 2331 return (NULL); 2332 } 2333 m->handle = NULL; 2334 m->flags = 0; 2335 strlcpy(m->section, section, sizeof(m->section)); 2336 2337 if ((m->path = strdup(path)) == NULL) { 2338 syslog(LOG_ERR, "lm_load: %m"); 2339 goto err; 2340 } 2341 2342 /* 2343 * Make index 2344 */ 2345 m->index.subs[0] = strlen(section); 2346 m->index.len = m->index.subs[0] + 1; 2347 for (u = 0; u < m->index.subs[0]; u++) 2348 m->index.subs[u + 1] = section[u]; 2349 2350 /* 2351 * Load the object file and locate the config structure 2352 */ 2353 if ((m->handle = dlopen(m->path, RTLD_NOW|RTLD_GLOBAL)) == NULL) { 2354 syslog(LOG_ERR, "lm_load: open %s", dlerror()); 2355 goto err; 2356 } 2357 2358 if ((m->config = dlsym(m->handle, "config")) == NULL) { 2359 syslog(LOG_ERR, "lm_load: no 'config' symbol %s", dlerror()); 2360 goto err; 2361 } 2362 2363 /* 2364 * Insert it into the right place 2365 */ 2366 INSERT_OBJECT_OID(m, &lmodules); 2367 2368 /* preserve order */ 2369 if (community == COMM_INITIALIZE) { 2370 m->flags |= LM_ONSTARTLIST; 2371 TAILQ_INSERT_TAIL(&modules_start, m, start); 2372 } 2373 2374 /* 2375 * make the argument vector. 2376 */ 2377 ac = 0; 2378 for (i = 0; i < nprogargs; i++) { 2379 if (strlen(progargs[i]) >= strlen(section) + 1 && 2380 strncmp(progargs[i], section, strlen(section)) == 0 && 2381 progargs[i][strlen(section)] == ':') { 2382 if (ac == MAX_MOD_ARGS) { 2383 syslog(LOG_WARNING, "too many arguments for " 2384 "module '%s", section); 2385 break; 2386 } 2387 av[ac++] = &progargs[i][strlen(section)+1]; 2388 } 2389 } 2390 av[ac] = NULL; 2391 2392 /* 2393 * Run the initialization function 2394 */ 2395 if ((err = (*m->config->init)(m, ac, av)) != 0) { 2396 syslog(LOG_ERR, "lm_load: init failed: %d", err); 2397 TAILQ_REMOVE(&lmodules, m, link); 2398 goto err; 2399 } 2400 2401 return (m); 2402 2403 err: 2404 if ((m->flags & LM_ONSTARTLIST) != 0) 2405 TAILQ_REMOVE(&modules_start, m, start); 2406 if (m->handle) 2407 dlclose(m->handle); 2408 free(m->path); 2409 free(m); 2410 return (NULL); 2411} 2412 2413/* 2414 * Start a module 2415 */ 2416void 2417lm_start(struct lmodule *mod) 2418{ 2419 const struct lmodule *m; 2420 2421 /* 2422 * Merge tree. If this fails, unload the module. 2423 */ 2424 if (tree_merge(mod->config->tree, mod->config->tree_size, mod)) { 2425 lm_unload(mod); 2426 return; 2427 } 2428 2429 /* 2430 * Read configuration 2431 */ 2432 if (read_config(config_file, mod)) { 2433 syslog(LOG_ERR, "error in config file"); 2434 lm_unload(mod); 2435 return; 2436 } 2437 if (mod->config->start) 2438 (*mod->config->start)(); 2439 2440 mod->flags |= LM_STARTED; 2441 2442 /* 2443 * Inform other modules 2444 */ 2445 TAILQ_FOREACH(m, &lmodules, link) 2446 if (m->config->loading) 2447 (*m->config->loading)(mod, 1); 2448} 2449 2450 2451/* 2452 * Unload a module. 2453 */ 2454void 2455lm_unload(struct lmodule *m) 2456{ 2457 int err; 2458 const struct lmodule *mod; 2459 2460 TAILQ_REMOVE(&lmodules, m, link); 2461 if (m->flags & LM_ONSTARTLIST) 2462 TAILQ_REMOVE(&modules_start, m, start); 2463 tree_unmerge(m); 2464 2465 if ((m->flags & LM_STARTED) && m->config->fini && 2466 (err = (*m->config->fini)()) != 0) 2467 syslog(LOG_WARNING, "lm_unload(%s): fini %d", m->section, err); 2468 2469 comm_flush(m); 2470 reqid_flush(m); 2471 timer_flush(m); 2472 fd_flush(m); 2473 2474 dlclose(m->handle); 2475 free(m->path); 2476 2477 /* 2478 * Inform other modules 2479 */ 2480 TAILQ_FOREACH(mod, &lmodules, link) 2481 if (mod->config->loading) 2482 (*mod->config->loading)(m, 0); 2483 2484 free(m); 2485} 2486 2487/* 2488 * Register an object resource and return the index (or 0 on failures) 2489 */ 2490u_int 2491or_register(const struct asn_oid *or, const char *descr, struct lmodule *mod) 2492{ 2493 struct objres *objres, *or1; 2494 u_int idx; 2495 2496 /* find a free index */ 2497 idx = 1; 2498 for (objres = TAILQ_FIRST(&objres_list); 2499 objres != NULL; 2500 objres = TAILQ_NEXT(objres, link)) { 2501 if ((or1 = TAILQ_NEXT(objres, link)) == NULL || 2502 or1->index > objres->index + 1) { 2503 idx = objres->index + 1; 2504 break; 2505 } 2506 } 2507 2508 if ((objres = malloc(sizeof(*objres))) == NULL) 2509 return (0); 2510 2511 objres->index = idx; 2512 objres->oid = *or; 2513 strlcpy(objres->descr, descr, sizeof(objres->descr)); 2514 objres->uptime = (uint32_t)(get_ticks() - start_tick); 2515 objres->module = mod; 2516 2517 INSERT_OBJECT_INT(objres, &objres_list); 2518 2519 systemg.or_last_change = objres->uptime; 2520 2521 return (idx); 2522} 2523 2524void 2525or_unregister(u_int idx) 2526{ 2527 struct objres *objres; 2528 2529 TAILQ_FOREACH(objres, &objres_list, link) 2530 if (objres->index == idx) { 2531 TAILQ_REMOVE(&objres_list, objres, link); 2532 free(objres); 2533 return; 2534 } 2535} 2536 2537/* 2538 * RFC 3414 User-based Security Model support 2539 */ 2540 2541struct snmpd_usmstat * 2542bsnmpd_get_usm_stats(void) 2543{ 2544 return (&snmpd_usmstats); 2545} 2546 2547void 2548bsnmpd_reset_usm_stats(void) 2549{ 2550 memset(&snmpd_usmstats, 0, sizeof(snmpd_usmstats)); 2551} 2552 2553struct usm_user * 2554usm_first_user(void) 2555{ 2556 return (SLIST_FIRST(&usm_userlist)); 2557} 2558 2559struct usm_user * 2560usm_next_user(struct usm_user *uuser) 2561{ 2562 if (uuser == NULL) 2563 return (NULL); 2564 2565 return (SLIST_NEXT(uuser, up)); 2566} 2567 2568struct usm_user * 2569usm_find_user(uint8_t *engine, uint32_t elen, char *uname) 2570{ 2571 struct usm_user *uuser; 2572 2573 SLIST_FOREACH(uuser, &usm_userlist, up) 2574 if (uuser->user_engine_len == elen && 2575 memcmp(uuser->user_engine_id, engine, elen) == 0 && 2576 strlen(uuser->suser.sec_name) == strlen(uname) && 2577 strcmp(uuser->suser.sec_name, uname) == 0) 2578 break; 2579 2580 return (uuser); 2581} 2582 2583static int 2584usm_compare_user(struct usm_user *u1, struct usm_user *u2) 2585{ 2586 uint32_t i; 2587 2588 if (u1->user_engine_len < u2->user_engine_len) 2589 return (-1); 2590 if (u1->user_engine_len > u2->user_engine_len) 2591 return (1); 2592 2593 for (i = 0; i < u1->user_engine_len; i++) { 2594 if (u1->user_engine_id[i] < u2->user_engine_id[i]) 2595 return (-1); 2596 if (u1->user_engine_id[i] > u2->user_engine_id[i]) 2597 return (1); 2598 } 2599 2600 if (strlen(u1->suser.sec_name) < strlen(u2->suser.sec_name)) 2601 return (-1); 2602 if (strlen(u1->suser.sec_name) > strlen(u2->suser.sec_name)) 2603 return (1); 2604 2605 for (i = 0; i < strlen(u1->suser.sec_name); i++) { 2606 if (u1->suser.sec_name[i] < u2->suser.sec_name[i]) 2607 return (-1); 2608 if (u1->suser.sec_name[i] > u2->suser.sec_name[i]) 2609 return (1); 2610 } 2611 2612 return (0); 2613} 2614 2615struct usm_user * 2616usm_new_user(uint8_t *eid, uint32_t elen, char *uname) 2617{ 2618 int cmp; 2619 struct usm_user *uuser, *temp, *prev; 2620 2621 for (uuser = usm_first_user(); uuser != NULL; 2622 (uuser = usm_next_user(uuser))) { 2623 if (uuser->user_engine_len == elen && 2624 strlen(uname) == strlen(uuser->suser.sec_name) && 2625 strcmp(uname, uuser->suser.sec_name) == 0 && 2626 memcmp(eid, uuser->user_engine_id, elen) == 0) 2627 return (NULL); 2628 } 2629 2630 if ((uuser = (struct usm_user *)malloc(sizeof(*uuser))) == NULL) 2631 return (NULL); 2632 2633 memset(uuser, 0, sizeof(*uuser)); 2634 strlcpy(uuser->suser.sec_name, uname, SNMP_ADM_STR32_SIZ); 2635 memcpy(uuser->user_engine_id, eid, elen); 2636 uuser->user_engine_len = elen; 2637 2638 if ((prev = SLIST_FIRST(&usm_userlist)) == NULL || 2639 usm_compare_user(uuser, prev) < 0) { 2640 SLIST_INSERT_HEAD(&usm_userlist, uuser, up); 2641 return (uuser); 2642 } 2643 2644 SLIST_FOREACH(temp, &usm_userlist, up) { 2645 if ((cmp = usm_compare_user(uuser, temp)) <= 0) 2646 break; 2647 prev = temp; 2648 } 2649 2650 if (temp == NULL || cmp < 0) 2651 SLIST_INSERT_AFTER(prev, uuser, up); 2652 else if (cmp > 0) 2653 SLIST_INSERT_AFTER(temp, uuser, up); 2654 else { 2655 syslog(LOG_ERR, "User %s exists", uuser->suser.sec_name); 2656 free(uuser); 2657 return (NULL); 2658 } 2659 2660 return (uuser); 2661} 2662 2663void 2664usm_delete_user(struct usm_user *uuser) 2665{ 2666 SLIST_REMOVE(&usm_userlist, uuser, usm_user, up); 2667 free(uuser); 2668} 2669 2670void 2671usm_flush_users(void) 2672{ 2673 struct usm_user *uuser; 2674 2675 while ((uuser = SLIST_FIRST(&usm_userlist)) != NULL) { 2676 SLIST_REMOVE_HEAD(&usm_userlist, up); 2677 free(uuser); 2678 } 2679 2680 SLIST_INIT(&usm_userlist); 2681} 2682 2683/* 2684 * RFC 3415 View-based Access Control Model support 2685 */ 2686struct vacm_user * 2687vacm_first_user(void) 2688{ 2689 return (SLIST_FIRST(&vacm_userlist)); 2690} 2691 2692struct vacm_user * 2693vacm_next_user(struct vacm_user *vuser) 2694{ 2695 if (vuser == NULL) 2696 return (NULL); 2697 2698 return (SLIST_NEXT(vuser, vvu)); 2699} 2700 2701static int 2702vacm_compare_user(struct vacm_user *v1, struct vacm_user *v2) 2703{ 2704 uint32_t i; 2705 2706 if (v1->sec_model < v2->sec_model) 2707 return (-1); 2708 if (v1->sec_model > v2->sec_model) 2709 return (1); 2710 2711 if (strlen(v1->secname) < strlen(v2->secname)) 2712 return (-1); 2713 if (strlen(v1->secname) > strlen(v2->secname)) 2714 return (1); 2715 2716 for (i = 0; i < strlen(v1->secname); i++) { 2717 if (v1->secname[i] < v2->secname[i]) 2718 return (-1); 2719 if (v1->secname[i] > v2->secname[i]) 2720 return (1); 2721 } 2722 2723 return (0); 2724} 2725 2726struct vacm_user * 2727vacm_new_user(int32_t smodel, char *uname) 2728{ 2729 int cmp; 2730 struct vacm_user *user, *temp, *prev; 2731 2732 SLIST_FOREACH(user, &vacm_userlist, vvu) 2733 if (strcmp(uname, user->secname) == 0 && 2734 smodel == user->sec_model) 2735 return (NULL); 2736 2737 if ((user = (struct vacm_user *)malloc(sizeof(*user))) == NULL) 2738 return (NULL); 2739 2740 memset(user, 0, sizeof(*user)); 2741 user->group = &vacm_default_group; 2742 SLIST_INSERT_HEAD(&vacm_default_group.group_users, user, vvg); 2743 user->sec_model = smodel; 2744 strlcpy(user->secname, uname, sizeof(user->secname)); 2745 2746 if ((prev = SLIST_FIRST(&vacm_userlist)) == NULL || 2747 vacm_compare_user(user, prev) < 0) { 2748 SLIST_INSERT_HEAD(&vacm_userlist, user, vvu); 2749 return (user); 2750 } 2751 2752 SLIST_FOREACH(temp, &vacm_userlist, vvu) { 2753 if ((cmp = vacm_compare_user(user, temp)) <= 0) 2754 break; 2755 prev = temp; 2756 } 2757 2758 if (temp == NULL || cmp < 0) 2759 SLIST_INSERT_AFTER(prev, user, vvu); 2760 else if (cmp > 0) 2761 SLIST_INSERT_AFTER(temp, user, vvu); 2762 else { 2763 syslog(LOG_ERR, "User %s exists", user->secname); 2764 free(user); 2765 return (NULL); 2766 } 2767 2768 return (user); 2769} 2770 2771int 2772vacm_delete_user(struct vacm_user *user) 2773{ 2774 if (user->group != NULL && user->group != &vacm_default_group) { 2775 SLIST_REMOVE(&user->group->group_users, user, vacm_user, vvg); 2776 if (SLIST_EMPTY(&user->group->group_users)) { 2777 SLIST_REMOVE(&vacm_grouplist, user->group, 2778 vacm_group, vge); 2779 free(user->group); 2780 } 2781 } 2782 2783 SLIST_REMOVE(&vacm_userlist, user, vacm_user, vvu); 2784 free(user); 2785 2786 return (0); 2787} 2788 2789int 2790vacm_user_set_group(struct vacm_user *user, u_char *octets, u_int len) 2791{ 2792 struct vacm_group *group; 2793 2794 if (len >= SNMP_ADM_STR32_SIZ) 2795 return (-1); 2796 2797 SLIST_FOREACH(group, &vacm_grouplist, vge) 2798 if (strlen(group->groupname) == len && 2799 memcmp(octets, group->groupname, len) == 0) 2800 break; 2801 2802 if (group == NULL) { 2803 if ((group = (struct vacm_group *)malloc(sizeof(*group))) == NULL) 2804 return (-1); 2805 memset(group, 0, sizeof(*group)); 2806 memcpy(group->groupname, octets, len); 2807 group->groupname[len] = '\0'; 2808 SLIST_INSERT_HEAD(&vacm_grouplist, group, vge); 2809 } 2810 2811 SLIST_REMOVE(&user->group->group_users, user, vacm_user, vvg); 2812 SLIST_INSERT_HEAD(&group->group_users, user, vvg); 2813 user->group = group; 2814 2815 return (0); 2816} 2817 2818void 2819vacm_groups_init(void) 2820{ 2821 SLIST_INSERT_HEAD(&vacm_grouplist, &vacm_default_group, vge); 2822} 2823 2824struct vacm_access * 2825vacm_first_access_rule(void) 2826{ 2827 return (TAILQ_FIRST(&vacm_accesslist)); 2828} 2829 2830struct vacm_access * 2831vacm_next_access_rule(struct vacm_access *acl) 2832{ 2833 if (acl == NULL) 2834 return (NULL); 2835 2836 return (TAILQ_NEXT(acl, vva)); 2837} 2838 2839static int 2840vacm_compare_access_rule(struct vacm_access *v1, struct vacm_access *v2) 2841{ 2842 uint32_t i; 2843 2844 if (strlen(v1->group->groupname) < strlen(v2->group->groupname)) 2845 return (-1); 2846 if (strlen(v1->group->groupname) > strlen(v2->group->groupname)) 2847 return (1); 2848 2849 for (i = 0; i < strlen(v1->group->groupname); i++) { 2850 if (v1->group->groupname[i] < v2->group->groupname[i]) 2851 return (-1); 2852 if (v1->group->groupname[i] > v2->group->groupname[i]) 2853 return (1); 2854 } 2855 2856 if (strlen(v1->ctx_prefix) < strlen(v2->ctx_prefix)) 2857 return (-1); 2858 if (strlen(v1->ctx_prefix) > strlen(v2->ctx_prefix)) 2859 return (1); 2860 2861 for (i = 0; i < strlen(v1->ctx_prefix); i++) { 2862 if (v1->ctx_prefix[i] < v2->ctx_prefix[i]) 2863 return (-1); 2864 if (v1->ctx_prefix[i] > v2->ctx_prefix[i]) 2865 return (1); 2866 } 2867 2868 if (v1->sec_model < v2->sec_model) 2869 return (-1); 2870 if (v1->sec_model > v2->sec_model) 2871 return (1); 2872 2873 if (v1->sec_level < v2->sec_level) 2874 return (-1); 2875 if (v1->sec_level > v2->sec_level) 2876 return (1); 2877 2878 return (0); 2879} 2880 2881struct vacm_access * 2882vacm_new_access_rule(char *gname, char *cprefix, int32_t smodel, int32_t slevel) 2883{ 2884 struct vacm_group *group; 2885 struct vacm_access *acl, *temp; 2886 2887 TAILQ_FOREACH(acl, &vacm_accesslist, vva) { 2888 if (acl->group == NULL) 2889 continue; 2890 if (strcmp(gname, acl->group->groupname) == 0 && 2891 strcmp(cprefix, acl->ctx_prefix) == 0 && 2892 acl->sec_model == smodel && acl->sec_level == slevel) 2893 return (NULL); 2894 } 2895 2896 /* Make sure the group exists */ 2897 SLIST_FOREACH(group, &vacm_grouplist, vge) 2898 if (strcmp(gname, group->groupname) == 0) 2899 break; 2900 2901 if (group == NULL) 2902 return (NULL); 2903 2904 if ((acl = (struct vacm_access *)malloc(sizeof(*acl))) == NULL) 2905 return (NULL); 2906 2907 memset(acl, 0, sizeof(*acl)); 2908 acl->group = group; 2909 strlcpy(acl->ctx_prefix, cprefix, sizeof(acl->ctx_prefix)); 2910 acl->sec_model = smodel; 2911 acl->sec_level = slevel; 2912 2913 if ((temp = TAILQ_FIRST(&vacm_accesslist)) == NULL || 2914 vacm_compare_access_rule(acl, temp) < 0) { 2915 TAILQ_INSERT_HEAD(&vacm_accesslist, acl, vva); 2916 return (acl); 2917 } 2918 2919 TAILQ_FOREACH(temp, &vacm_accesslist, vva) 2920 if (vacm_compare_access_rule(acl, temp) < 0) { 2921 TAILQ_INSERT_BEFORE(temp, acl, vva); 2922 return (acl); 2923 } 2924 2925 TAILQ_INSERT_TAIL(&vacm_accesslist, acl, vva); 2926 2927 return (acl); 2928} 2929 2930int 2931vacm_delete_access_rule(struct vacm_access *acl) 2932{ 2933 TAILQ_REMOVE(&vacm_accesslist, acl, vva); 2934 free(acl); 2935 2936 return (0); 2937} 2938 2939struct vacm_view * 2940vacm_first_view(void) 2941{ 2942 return (SLIST_FIRST(&vacm_viewlist)); 2943} 2944 2945struct vacm_view * 2946vacm_next_view(struct vacm_view *view) 2947{ 2948 if (view == NULL) 2949 return (NULL); 2950 2951 return (SLIST_NEXT(view, vvl)); 2952} 2953 2954static int 2955vacm_compare_view(struct vacm_view *v1, struct vacm_view *v2) 2956{ 2957 uint32_t i; 2958 2959 if (strlen(v1->viewname) < strlen(v2->viewname)) 2960 return (-1); 2961 if (strlen(v1->viewname) > strlen(v2->viewname)) 2962 return (1); 2963 2964 for (i = 0; i < strlen(v1->viewname); i++) { 2965 if (v1->viewname[i] < v2->viewname[i]) 2966 return (-1); 2967 if (v1->viewname[i] > v2->viewname[i]) 2968 return (1); 2969 } 2970 2971 return (asn_compare_oid(&v1->subtree, &v2->subtree)); 2972} 2973 2974struct vacm_view * 2975vacm_new_view(char *vname, struct asn_oid *oid) 2976{ 2977 int cmp; 2978 struct vacm_view *view, *temp, *prev; 2979 2980 SLIST_FOREACH(view, &vacm_viewlist, vvl) 2981 if (strcmp(vname, view->viewname) == 0) 2982 return (NULL); 2983 2984 if ((view = (struct vacm_view *)malloc(sizeof(*view))) == NULL) 2985 return (NULL); 2986 2987 memset(view, 0, sizeof(*view)); 2988 strlcpy(view->viewname, vname, sizeof(view->viewname)); 2989 asn_append_oid(&view->subtree, oid); 2990 2991 if ((prev = SLIST_FIRST(&vacm_viewlist)) == NULL || 2992 vacm_compare_view(view, prev) < 0) { 2993 SLIST_INSERT_HEAD(&vacm_viewlist, view, vvl); 2994 return (view); 2995 } 2996 2997 SLIST_FOREACH(temp, &vacm_viewlist, vvl) { 2998 if ((cmp = vacm_compare_view(view, temp)) <= 0) 2999 break; 3000 prev = temp; 3001 } 3002 3003 if (temp == NULL || cmp < 0) 3004 SLIST_INSERT_AFTER(prev, view, vvl); 3005 else if (cmp > 0) 3006 SLIST_INSERT_AFTER(temp, view, vvl); 3007 else { 3008 syslog(LOG_ERR, "View %s exists", view->viewname); 3009 free(view); 3010 return (NULL); 3011 } 3012 3013 return (view); 3014} 3015 3016int 3017vacm_delete_view(struct vacm_view *view) 3018{ 3019 SLIST_REMOVE(&vacm_viewlist, view, vacm_view, vvl); 3020 free(view); 3021 3022 return (0); 3023} 3024 3025struct vacm_context * 3026vacm_first_context(void) 3027{ 3028 return (SLIST_FIRST(&vacm_contextlist)); 3029} 3030 3031struct vacm_context * 3032vacm_next_context(struct vacm_context *vacmctx) 3033{ 3034 if (vacmctx == NULL) 3035 return (NULL); 3036 3037 return (SLIST_NEXT(vacmctx, vcl)); 3038} 3039 3040struct vacm_context * 3041vacm_add_context(char *ctxname, int regid) 3042{ 3043 int cmp; 3044 struct vacm_context *ctx, *temp, *prev; 3045 3046 SLIST_FOREACH(ctx, &vacm_contextlist, vcl) 3047 if (strcmp(ctxname, ctx->ctxname) == 0) { 3048 syslog(LOG_ERR, "Context %s exists", ctx->ctxname); 3049 return (NULL); 3050 } 3051 3052 if ((ctx = (struct vacm_context *)malloc(sizeof(*ctx))) == NULL) 3053 return (NULL); 3054 3055 memset(ctx, 0, sizeof(*ctx)); 3056 strlcpy(ctx->ctxname, ctxname, sizeof(ctx->ctxname)); 3057 ctx->regid = regid; 3058 3059 if ((prev = SLIST_FIRST(&vacm_contextlist)) == NULL || 3060 strlen(ctx->ctxname) < strlen(prev->ctxname) || 3061 strcmp(ctx->ctxname, prev->ctxname) < 0) { 3062 SLIST_INSERT_HEAD(&vacm_contextlist, ctx, vcl); 3063 return (ctx); 3064 } 3065 3066 SLIST_FOREACH(temp, &vacm_contextlist, vcl) { 3067 if (strlen(ctx->ctxname) < strlen(temp->ctxname) || 3068 strcmp(ctx->ctxname, temp->ctxname) < 0) { 3069 cmp = -1; 3070 break; 3071 } 3072 prev = temp; 3073 } 3074 3075 if (temp == NULL || cmp < 0) 3076 SLIST_INSERT_AFTER(prev, ctx, vcl); 3077 else if (cmp > 0) 3078 SLIST_INSERT_AFTER(temp, ctx, vcl); 3079 else { 3080 syslog(LOG_ERR, "Context %s exists", ctx->ctxname); 3081 free(ctx); 3082 return (NULL); 3083 } 3084 3085 return (ctx); 3086} 3087 3088void 3089vacm_flush_contexts(int regid) 3090{ 3091 struct vacm_context *ctx, *temp; 3092 3093 SLIST_FOREACH_SAFE(ctx, &vacm_contextlist, vcl, temp) 3094 if (ctx->regid == regid) { 3095 SLIST_REMOVE(&vacm_contextlist, ctx, vacm_context, vcl); 3096 free(ctx); 3097 } 3098} 3099