1 /* $OpenBSD: application.c,v 1.35 2023/11/08 20:07:14 martijn Exp $ */ 2 3 /* 4 * Copyright (c) 2021 Martijn van Duren <martijn@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/queue.h> 20 #include <sys/tree.h> 21 22 #include <assert.h> 23 #include <errno.h> 24 #include <event.h> 25 #include <inttypes.h> 26 #include <stdlib.h> 27 #include <stdio.h> 28 #include <string.h> 29 30 #include "application.h" 31 #include "log.h" 32 #include "smi.h" 33 #include "snmp.h" 34 #include "snmpe.h" 35 #include "mib.h" 36 37 #define OID(...) (struct ber_oid){ { __VA_ARGS__ }, \ 38 (sizeof((uint32_t []) { __VA_ARGS__ }) / sizeof(uint32_t)) } 39 40 TAILQ_HEAD(, appl_context) contexts = TAILQ_HEAD_INITIALIZER(contexts); 41 42 struct appl_agentcap { 43 struct appl_backend *aa_backend; 44 struct appl_context *aa_context; 45 uint32_t aa_index; 46 struct ber_oid aa_oid; 47 char aa_descr[256]; 48 int aa_uptime; 49 50 TAILQ_ENTRY(appl_agentcap) aa_entry; 51 }; 52 53 struct appl_context { 54 char ac_name[APPL_CONTEXTNAME_MAX + 1]; 55 56 RB_HEAD(appl_regions, appl_region) ac_regions; 57 TAILQ_HEAD(, appl_agentcap) ac_agentcaps; 58 int ac_agentcap_lastid; 59 int ac_agentcap_lastchange; 60 61 TAILQ_ENTRY(appl_context) ac_entries; 62 }; 63 64 struct appl_region { 65 struct ber_oid ar_oid; 66 uint8_t ar_priority; 67 int32_t ar_timeout; 68 int ar_instance; 69 int ar_subtree; /* Claim entire subtree */ 70 struct appl_backend *ar_backend; 71 struct appl_region *ar_next; /* Sorted by priority */ 72 73 RB_ENTRY(appl_region) ar_entry; 74 }; 75 76 struct appl_request_upstream { 77 struct appl_context *aru_ctx; 78 struct snmp_message *aru_statereference; 79 enum snmp_pdutype aru_requesttype; 80 enum snmp_pdutype aru_responsetype; 81 int32_t aru_requestid; /* upstream requestid */ 82 int32_t aru_transactionid; /* RFC 2741 section 6.1 */ 83 uint16_t aru_nonrepeaters; 84 uint16_t aru_maxrepetitions; 85 struct appl_varbind_internal *aru_vblist; 86 size_t aru_varbindlen; 87 enum appl_error aru_error; 88 int16_t aru_index; 89 int aru_locked; /* Prevent recursion through appl_request_send */ 90 91 enum snmp_version aru_pduversion; 92 }; 93 94 struct appl_request_downstream { 95 struct appl_request_upstream *ard_request; 96 struct appl_backend *ard_backend; 97 enum snmp_pdutype ard_requesttype; 98 uint16_t ard_nonrepeaters; 99 uint16_t ard_maxrepetitions; 100 int32_t ard_requestid; 101 uint8_t ard_retries; 102 103 struct appl_varbind_internal *ard_vblist; 104 struct event ard_timer; 105 106 RB_ENTRY(appl_request_downstream) ard_entry; 107 }; 108 109 enum appl_varbind_state { 110 APPL_VBSTATE_MUSTFILL, 111 APPL_VBSTATE_NEW, 112 APPL_VBSTATE_PENDING, 113 APPL_VBSTATE_DONE 114 }; 115 116 struct appl_varbind_internal { 117 enum appl_varbind_state avi_state; 118 struct appl_varbind avi_varbind; 119 struct appl_region *avi_region; 120 struct ber_oid avi_origid; 121 int16_t avi_index; 122 struct appl_request_upstream *avi_request_upstream; 123 struct appl_request_downstream *avi_request_downstream; 124 struct appl_varbind_internal *avi_next; 125 struct appl_varbind_internal *avi_sub; 126 }; 127 128 /* SNMP-TARGET-MIB (RFC 3413) */ 129 struct snmp_target_mib { 130 uint32_t snmp_unavailablecontexts; 131 uint32_t snmp_unknowncontexts; 132 } snmp_target_mib; 133 134 void appl_agentcap_free(struct appl_agentcap *); 135 enum appl_error appl_region(struct appl_context *, uint32_t, uint8_t, 136 struct ber_oid *, int, int, struct appl_backend *); 137 void appl_region_free(struct appl_context *, struct appl_region *); 138 enum appl_error appl_region_unregister_match(struct appl_context *, uint8_t, 139 struct ber_oid *, char *, struct appl_backend *, int); 140 struct appl_region *appl_region_find(struct appl_context *, 141 const struct ber_oid *); 142 struct appl_region *appl_region_next(struct appl_context *, 143 struct ber_oid *, struct appl_region *); 144 void appl_request_upstream_free(struct appl_request_upstream *); 145 void appl_request_downstream_free(struct appl_request_downstream *); 146 void appl_request_upstream_resolve(struct appl_request_upstream *); 147 void appl_request_downstream_send(struct appl_request_downstream *); 148 void appl_request_downstream_timeout(int, short, void *); 149 void appl_request_upstream_reply(struct appl_request_upstream *); 150 int appl_varbind_valid(struct appl_varbind *, struct appl_varbind_internal *, 151 int, int, int, const char **); 152 int appl_error_valid(enum appl_error, enum snmp_pdutype); 153 int appl_varbind_backend(struct appl_varbind_internal *); 154 void appl_varbind_error(struct appl_varbind_internal *, enum appl_error); 155 void appl_pdu_log(struct appl_backend *, enum snmp_pdutype, int32_t, uint16_t, 156 uint16_t, struct appl_varbind *); 157 void ober_oid_nextsibling(struct ber_oid *); 158 159 int appl_region_cmp(struct appl_region *, struct appl_region *); 160 int appl_request_cmp(struct appl_request_downstream *, 161 struct appl_request_downstream *); 162 163 RB_PROTOTYPE_STATIC(appl_regions, appl_region, ar_entry, appl_region_cmp); 164 RB_PROTOTYPE_STATIC(appl_requests, appl_request_downstream, ard_entry, 165 appl_request_cmp); 166 167 #define APPL_CONTEXT_NAME(ctx) (ctx->ac_name[0] == '\0' ? NULL : ctx->ac_name) 168 169 void 170 appl(void) 171 { 172 appl_agentx(); 173 } 174 175 void 176 appl_init(void) 177 { 178 appl_blocklist_init(); 179 appl_internal_init(); 180 appl_legacy_init(); 181 appl_agentx_init(); 182 } 183 184 void 185 appl_shutdown(void) 186 { 187 struct appl_context *ctx, *tctx; 188 189 appl_blocklist_shutdown(); 190 appl_internal_shutdown(); 191 appl_legacy_shutdown(); 192 appl_agentx_shutdown(); 193 194 TAILQ_FOREACH_SAFE(ctx, &contexts, ac_entries, tctx) { 195 assert(RB_EMPTY(&(ctx->ac_regions))); 196 assert(TAILQ_EMPTY(&(ctx->ac_agentcaps))); 197 TAILQ_REMOVE(&contexts, ctx, ac_entries); 198 free(ctx); 199 } 200 } 201 202 struct appl_context * 203 appl_context(const char *name, int create) 204 { 205 struct appl_context *ctx; 206 207 if (name == NULL) 208 name = ""; 209 210 if (strlen(name) > APPL_CONTEXTNAME_MAX) { 211 errno = EINVAL; 212 return NULL; 213 } 214 215 TAILQ_FOREACH(ctx, &contexts, ac_entries) { 216 if (strcmp(name, ctx->ac_name) == 0) 217 return ctx; 218 } 219 220 /* Always allow the default namespace */ 221 if (!create && name[0] != '\0') { 222 errno = ENOENT; 223 return NULL; 224 } 225 226 if ((ctx = malloc(sizeof(*ctx))) == NULL) 227 return NULL; 228 229 strlcpy(ctx->ac_name, name, sizeof(ctx->ac_name)); 230 RB_INIT(&(ctx->ac_regions)); 231 TAILQ_INIT(&(ctx->ac_agentcaps)); 232 ctx->ac_agentcap_lastid = 0; 233 ctx->ac_agentcap_lastchange = 0; 234 235 TAILQ_INSERT_TAIL(&contexts, ctx, ac_entries); 236 return ctx; 237 } 238 239 /* Name from RFC 2741 section 6.2.14 */ 240 enum appl_error 241 appl_addagentcaps(const char *ctxname, struct ber_oid *oid, const char *descr, 242 struct appl_backend *backend) 243 { 244 struct appl_context *ctx; 245 struct appl_agentcap *cap; 246 char oidbuf[1024]; 247 248 if (ctxname == NULL) 249 ctxname = ""; 250 251 (void)smi_oid2string(oid, oidbuf, sizeof(oidbuf), 0); 252 log_info("%s: Adding agent capabilities %s context(%s)", 253 backend->ab_name, oidbuf, ctxname); 254 255 if ((ctx = appl_context(ctxname, 0)) == NULL) { 256 log_info("%s: Can't add agent capabilities %s: " 257 "Unsupported context \"%s\"", backend->ab_name, oidbuf, 258 ctxname); 259 return APPL_ERROR_UNSUPPORTEDCONTEXT; 260 } 261 262 if ((cap = malloc(sizeof(*ctx))) == NULL) { 263 log_warn("%s: Can't add agent capabilities %s", 264 backend->ab_name, oidbuf); 265 return APPL_ERROR_PROCESSINGERROR; 266 } 267 268 cap->aa_backend = backend; 269 cap->aa_context = ctx; 270 cap->aa_index = ++ctx->ac_agentcap_lastid; 271 cap->aa_oid = *oid; 272 cap->aa_uptime = smi_getticks(); 273 if (strlcpy(cap->aa_descr, descr, 274 sizeof(cap->aa_descr)) >= sizeof(cap->aa_descr)) { 275 log_info("%s: Can't add agent capabilities %s: " 276 "Invalid description", backend->ab_name, oidbuf); 277 free(cap); 278 return APPL_ERROR_PARSEERROR; 279 } 280 281 TAILQ_INSERT_TAIL(&(ctx->ac_agentcaps), cap, aa_entry); 282 ctx->ac_agentcap_lastchange = cap->aa_uptime; 283 284 return APPL_ERROR_NOERROR; 285 } 286 287 /* Name from RFC2741 section 6.2.15 */ 288 enum appl_error 289 appl_removeagentcaps(const char *ctxname, struct ber_oid *oid, 290 struct appl_backend *backend) 291 { 292 struct appl_context *ctx; 293 struct appl_agentcap *cap, *tmp; 294 char oidbuf[1024]; 295 int found = 0; 296 297 if (ctxname == NULL) 298 ctxname = ""; 299 300 (void)smi_oid2string(oid, oidbuf, sizeof(oidbuf), 0); 301 log_info("%s: Removing agent capabilities %s context(%s)", 302 backend->ab_name, oidbuf, ctxname); 303 304 if ((ctx = appl_context(ctxname, 0)) == NULL) { 305 log_info("%s: Can't remove agent capabilities %s: " 306 "Unsupported context \"%s\"", backend->ab_name, oidbuf, 307 ctxname); 308 return APPL_ERROR_UNSUPPORTEDCONTEXT; 309 } 310 311 TAILQ_FOREACH_SAFE(cap, &(ctx->ac_agentcaps), aa_entry, tmp) { 312 /* No duplicate oid check, just continue */ 313 if (cap->aa_backend != backend || 314 ober_oid_cmp(oid, &(cap->aa_oid)) != 0) 315 continue; 316 found = 1; 317 appl_agentcap_free(cap); 318 } 319 320 if (found) 321 return APPL_ERROR_NOERROR; 322 323 log_info("%s: Can't remove agent capabilities %s: not found", 324 backend->ab_name, oidbuf); 325 return APPL_ERROR_UNKNOWNAGENTCAPS; 326 } 327 328 void 329 appl_agentcap_free(struct appl_agentcap *cap) 330 { 331 TAILQ_REMOVE(&(cap->aa_context->ac_agentcaps), cap, aa_entry); 332 cap->aa_context->ac_agentcap_lastchange = smi_getticks(); 333 free(cap); 334 } 335 336 struct ber_element * 337 appl_sysorlastchange(struct ber_oid *oid) 338 { 339 struct appl_context *ctx; 340 struct ber_element *value; 341 342 ctx = appl_context(NULL, 0); 343 value = ober_add_integer(NULL, ctx->ac_agentcap_lastchange); 344 if (value != NULL) 345 ober_set_header(value, BER_CLASS_APPLICATION, SNMP_T_TIMETICKS); 346 else 347 log_warn("ober_add_integer"); 348 349 return value; 350 } 351 352 #define SYSORIDX_POS 10 353 struct ber_element * 354 appl_sysortable(struct ber_oid *oid) 355 { 356 struct appl_context *ctx; 357 struct appl_agentcap *cap; 358 struct ber_element *value = NULL; 359 360 if (oid->bo_n != SYSORIDX_POS + 1) 361 goto notfound; 362 363 ctx = appl_context(NULL, 0); 364 TAILQ_FOREACH(cap, &(ctx->ac_agentcaps), aa_entry) { 365 if (cap->aa_index == oid->bo_id[SYSORIDX_POS]) 366 break; 367 } 368 if (cap == NULL) 369 goto notfound; 370 371 if (ober_oid_cmp(&OID(MIB_sysORID), oid) == -2) 372 value = ober_add_oid(NULL, &(cap->aa_oid)); 373 else if (ober_oid_cmp(&OID(MIB_sysORDescr), oid) == -2) 374 value = ober_add_string(NULL, cap->aa_descr); 375 else if (ober_oid_cmp(&OID(MIB_sysORUpTime), oid) == -2) { 376 if ((value = ober_add_integer(NULL, cap->aa_uptime)) != NULL) 377 ober_set_header(value, 378 BER_CLASS_APPLICATION, SNMP_T_TIMETICKS); 379 } 380 if (value == NULL) 381 log_warn("ober_add_*"); 382 return value; 383 384 notfound: 385 if ((value = appl_exception(APPL_EXC_NOSUCHINSTANCE)) == NULL) 386 log_warn("appl_exception"); 387 return value; 388 } 389 390 struct ber_element * 391 appl_sysortable_getnext(int8_t include, struct ber_oid *oid) 392 { 393 struct appl_context *ctx; 394 struct appl_agentcap *cap; 395 struct ber_element *value = NULL; 396 397 if (oid->bo_n < SYSORIDX_POS + 1) { 398 include = 1; 399 oid->bo_id[SYSORIDX_POS] = 0; 400 } else if (oid->bo_n < SYSORIDX_POS + 1) 401 include = 0; 402 403 ctx = appl_context(NULL, 0); 404 TAILQ_FOREACH(cap, &(ctx->ac_agentcaps), aa_entry) { 405 if (cap->aa_index > oid->bo_id[SYSORIDX_POS]) 406 break; 407 if (cap->aa_index == oid->bo_id[SYSORIDX_POS] && include) 408 break; 409 } 410 if (cap == NULL) { 411 value = appl_exception(APPL_EXC_NOSUCHINSTANCE); 412 goto done; 413 } 414 415 oid->bo_id[SYSORIDX_POS] = cap->aa_index; 416 oid->bo_n = SYSORIDX_POS + 1; 417 418 if (ober_oid_cmp(&OID(MIB_sysORID), oid) == -2) 419 value = ober_add_oid(NULL, &(cap->aa_oid)); 420 else if (ober_oid_cmp(&OID(MIB_sysORDescr), oid) == -2) 421 value = ober_add_string(NULL, cap->aa_descr); 422 else if (ober_oid_cmp(&OID(MIB_sysORUpTime), oid) == -2) { 423 if ((value = ober_add_integer(NULL, cap->aa_uptime)) != NULL) 424 ober_set_header(value, 425 BER_CLASS_APPLICATION, SNMP_T_TIMETICKS); 426 } 427 done: 428 if (value == NULL) 429 log_warn("ober_add_*"); 430 return value; 431 } 432 433 struct ber_element * 434 appl_targetmib(struct ber_oid *oid) 435 { 436 struct ber_element *value = NULL; 437 438 if (ober_oid_cmp(oid, &OID(MIB_snmpUnavailableContexts, 0)) == 0) 439 value = ober_add_integer(NULL, 440 snmp_target_mib.snmp_unavailablecontexts); 441 else if (ober_oid_cmp(oid, &OID(MIB_snmpUnknownContexts, 0)) == 0) 442 value = ober_add_integer(NULL, 443 snmp_target_mib.snmp_unknowncontexts); 444 445 if (value != NULL) 446 ober_set_header(value, BER_CLASS_APPLICATION, SNMP_T_COUNTER32); 447 return value; 448 } 449 450 enum appl_error 451 appl_region(struct appl_context *ctx, uint32_t timeout, uint8_t priority, 452 struct ber_oid *oid, int instance, int subtree, 453 struct appl_backend *backend) 454 { 455 struct appl_region *region = NULL, *nregion; 456 char oidbuf[1024], regionbuf[1024], subidbuf[11]; 457 size_t i; 458 459 /* Don't use smi_oid2string, because appl_register can't use it */ 460 oidbuf[0] = '\0'; 461 for (i = 0; i < oid->bo_n; i++) { 462 if (i != 0) 463 strlcat(oidbuf, ".", sizeof(oidbuf)); 464 snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, 465 oid->bo_id[i]); 466 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 467 } 468 469 /* 470 * Don't allow overlap when subtree flag is set. 471 * This allows us to keep control of certain regions like system. 472 */ 473 region = appl_region_find(ctx, oid); 474 if (region != NULL && region->ar_subtree && 475 region->ar_backend != backend) 476 goto overlap; 477 478 if ((nregion = malloc(sizeof(*nregion))) == NULL) { 479 log_warn("%s: Can't register %s: Processing error", 480 backend->ab_name, oidbuf); 481 return APPL_ERROR_PROCESSINGERROR; 482 } 483 nregion->ar_oid = *oid; 484 nregion->ar_priority = priority; 485 nregion->ar_timeout = timeout; 486 nregion->ar_instance = instance; 487 nregion->ar_subtree = subtree; 488 nregion->ar_backend = backend; 489 nregion->ar_next = NULL; 490 491 region = RB_INSERT(appl_regions, &(ctx->ac_regions), nregion); 492 if (region == NULL) 493 return APPL_ERROR_NOERROR; 494 495 if (region->ar_priority == priority) 496 goto duplicate; 497 if (region->ar_priority > priority) { 498 RB_REMOVE(appl_regions, &(ctx->ac_regions), region); 499 RB_INSERT(appl_regions, &(ctx->ac_regions), nregion); 500 nregion->ar_next = region; 501 return APPL_ERROR_NOERROR; 502 } 503 504 while (region->ar_next != NULL && 505 region->ar_next->ar_priority < priority) 506 region = region->ar_next; 507 if (region->ar_next != NULL && region->ar_next->ar_priority == priority) 508 goto duplicate; 509 nregion->ar_next = region->ar_next; 510 region->ar_next = nregion; 511 512 return APPL_ERROR_NOERROR; 513 duplicate: 514 free(nregion); 515 log_info("%s: %s priority %"PRId8": Duplicate registration", 516 backend->ab_name, oidbuf, priority); 517 return APPL_ERROR_DUPLICATEREGISTRATION; 518 overlap: 519 regionbuf[0] = '\0'; 520 for (i = 0; i < region->ar_oid.bo_n; i++) { 521 if (i != 0) 522 strlcat(regionbuf, ".", sizeof(regionbuf)); 523 snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, 524 region->ar_oid.bo_id[i]); 525 strlcat(regionbuf, subidbuf, sizeof(regionbuf)); 526 } 527 log_info("%s: %s overlaps with %s: Request denied", 528 backend->ab_name, oidbuf, regionbuf); 529 return APPL_ERROR_REQUESTDENIED; 530 } 531 532 /* Name from RFC 2741 section 6.2.3 */ 533 enum appl_error 534 appl_register(const char *ctxname, uint32_t timeout, uint8_t priority, 535 struct ber_oid *oid, int instance, int subtree, uint8_t range_subid, 536 uint32_t upper_bound, struct appl_backend *backend) 537 { 538 struct appl_context *ctx; 539 struct appl_region *region, search; 540 char oidbuf[1024], subidbuf[11]; 541 enum appl_error error; 542 size_t i; 543 uint32_t lower_bound; 544 545 oidbuf[0] = '\0'; 546 /* smi_oid2string can't do ranges */ 547 for (i = 0; i < oid->bo_n; i++) { 548 snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, oid->bo_id[i]); 549 if (i != 0) 550 strlcat(oidbuf, ".", sizeof(oidbuf)); 551 if (range_subid == i + 1) { 552 strlcat(oidbuf, "[", sizeof(oidbuf)); 553 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 554 strlcat(oidbuf, "-", sizeof(oidbuf)); 555 snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, 556 upper_bound); 557 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 558 strlcat(oidbuf, "]", sizeof(oidbuf)); 559 } else 560 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 561 } 562 563 if (ctxname == NULL) 564 ctxname = ""; 565 log_info("%s: Registering %s%s context(%s) priority(%"PRIu8") " 566 "timeout(%"PRIu32".%02us)", backend->ab_name, oidbuf, 567 instance ? "(instance)" : "", ctxname, priority, 568 timeout/100, timeout % 100); 569 570 if ((ctx = appl_context(ctxname, 0)) == NULL) { 571 if (errno == ENOMEM) { 572 log_warn("%s: Can't register %s: Processing error", 573 backend->ab_name, oidbuf); 574 return APPL_ERROR_PROCESSINGERROR; 575 } 576 log_info("%s: Can't register %s: Unsupported context \"%s\"", 577 backend->ab_name, oidbuf, ctxname); 578 return APPL_ERROR_UNSUPPORTEDCONTEXT; 579 } 580 /* Default timeouts should be handled by backend */ 581 if (timeout == 0) 582 fatalx("%s: Timeout can't be 0", __func__); 583 if (priority == 0) { 584 log_warnx("%s: Can't register %s: priority can't be 0", 585 backend->ab_name, oidbuf); 586 return APPL_ERROR_PARSEERROR; 587 } 588 589 if (range_subid == 0) 590 return appl_region(ctx, timeout, priority, oid, instance, 591 subtree, backend); 592 593 range_subid--; 594 if (range_subid >= oid->bo_n) { 595 log_warnx("%s: Can't register %s: range_subid too large", 596 backend->ab_name, oidbuf); 597 return APPL_ERROR_PARSEERROR; 598 } 599 if (oid->bo_id[range_subid] > upper_bound) { 600 log_warnx("%s: Can't register %s: upper bound smaller than " 601 "range_subid", backend->ab_name, oidbuf); 602 return APPL_ERROR_PARSEERROR; 603 } 604 605 lower_bound = oid->bo_id[range_subid]; 606 do { 607 if ((error = appl_region(ctx, timeout, priority, oid, instance, 608 subtree, backend)) != APPL_ERROR_NOERROR) 609 goto fail; 610 } while (oid->bo_id[range_subid]++ != upper_bound); 611 if ((error = appl_region(ctx, timeout, priority, oid, instance, subtree, 612 backend)) != APPL_ERROR_NOERROR) 613 goto fail; 614 615 return APPL_ERROR_NOERROR; 616 fail: 617 search.ar_oid = *oid; 618 if (search.ar_oid.bo_id[range_subid] == lower_bound) 619 return error; 620 621 for (search.ar_oid.bo_id[range_subid]--; 622 search.ar_oid.bo_id[range_subid] != lower_bound; 623 search.ar_oid.bo_id[range_subid]--) { 624 region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); 625 while (region->ar_priority != priority) 626 region = region->ar_next; 627 appl_region_free(ctx, region); 628 } 629 region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); 630 while (region->ar_priority != priority) 631 region = region->ar_next; 632 appl_region_free(ctx, region); 633 return error; 634 } 635 636 /* Name from RFC 2741 section 6.2.4 */ 637 enum appl_error 638 appl_unregister(const char *ctxname, uint8_t priority, struct ber_oid *oid, 639 uint8_t range_subid, uint32_t upper_bound, struct appl_backend *backend) 640 { 641 struct appl_context *ctx; 642 char oidbuf[1024], subidbuf[11]; 643 enum appl_error error; 644 uint32_t lower_bound; 645 size_t i; 646 647 oidbuf[0] = '\0'; 648 for (i = 0; i < oid->bo_n; i++) { 649 snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, oid->bo_id[i]); 650 if (i != 0) 651 strlcat(oidbuf, ".", sizeof(oidbuf)); 652 if (range_subid == i + 1) { 653 strlcat(oidbuf, "[", sizeof(oidbuf)); 654 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 655 strlcat(oidbuf, "-", sizeof(oidbuf)); 656 snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, 657 upper_bound); 658 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 659 strlcat(oidbuf, "]", sizeof(oidbuf)); 660 } else 661 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 662 } 663 664 if (ctxname == NULL) 665 ctxname = ""; 666 log_info("%s: Unregistering %s context(%s) priority(%"PRIu8")", 667 backend->ab_name, oidbuf,ctxname, priority); 668 669 if ((ctx = appl_context(ctxname, 0)) == NULL) { 670 if (errno == ENOMEM) { 671 log_warn("%s: Can't unregister %s: Processing error", 672 backend->ab_name, oidbuf); 673 return APPL_ERROR_PROCESSINGERROR; 674 } 675 log_info("%s: Can't unregister %s: Unsupported context \"%s\"", 676 backend->ab_name, oidbuf, ctxname); 677 return APPL_ERROR_UNSUPPORTEDCONTEXT; 678 } 679 680 if (priority == 0) { 681 log_warnx("%s: Can't unregister %s: priority can't be 0", 682 backend->ab_name, oidbuf); 683 return APPL_ERROR_PARSEERROR; 684 } 685 686 if (range_subid == 0) 687 return appl_region_unregister_match(ctx, priority, oid, oidbuf, 688 backend, 1); 689 690 range_subid--; 691 if (range_subid >= oid->bo_n) { 692 log_warnx("%s: Can't unregiser %s: range_subid too large", 693 backend->ab_name, oidbuf); 694 return APPL_ERROR_PARSEERROR; 695 } 696 if (oid->bo_id[range_subid] > upper_bound) { 697 log_warnx("%s: Can't unregister %s: upper bound smaller than " 698 "range_subid", backend->ab_name, oidbuf); 699 return APPL_ERROR_PARSEERROR; 700 } 701 702 lower_bound = oid->bo_id[range_subid]; 703 do { 704 if ((error = appl_region_unregister_match(ctx, priority, oid, 705 oidbuf, backend, 0)) != APPL_ERROR_NOERROR) 706 return error; 707 } while (oid->bo_id[range_subid]++ != upper_bound); 708 709 oid->bo_id[range_subid] = lower_bound; 710 do { 711 (void)appl_region_unregister_match(ctx, priority, oid, oidbuf, 712 backend, 1); 713 } while (oid->bo_id[range_subid]++ != upper_bound); 714 715 return APPL_ERROR_NOERROR; 716 } 717 718 enum appl_error 719 appl_region_unregister_match(struct appl_context *ctx, uint8_t priority, 720 struct ber_oid *oid, char *oidbuf, struct appl_backend *backend, int dofree) 721 { 722 struct appl_region *region, search; 723 724 search.ar_oid = *oid; 725 region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); 726 while (region != NULL && region->ar_priority < priority) 727 region = region->ar_next; 728 if (region == NULL || region->ar_priority != priority) { 729 log_warnx("%s: Can't unregister %s: region not found", 730 backend->ab_name, oidbuf); 731 return APPL_ERROR_UNKNOWNREGISTRATION; 732 } 733 if (region->ar_backend != backend) { 734 log_warnx("%s: Can't unregister %s: region not owned " 735 "by backend", backend->ab_name, oidbuf); 736 return APPL_ERROR_UNKNOWNREGISTRATION; 737 } 738 if (dofree) 739 appl_region_free(ctx, region); 740 return APPL_ERROR_NOERROR; 741 } 742 743 void 744 appl_region_free(struct appl_context *ctx, struct appl_region *region) 745 { 746 struct appl_region *pregion; 747 748 pregion = RB_FIND(appl_regions, &(ctx->ac_regions), region); 749 750 if (pregion == region) { 751 RB_REMOVE(appl_regions, &(ctx->ac_regions), region); 752 if (region->ar_next != NULL) 753 RB_INSERT(appl_regions, &(ctx->ac_regions), 754 region->ar_next); 755 } else { 756 while (pregion->ar_next != region) 757 pregion = pregion->ar_next; 758 pregion->ar_next = region->ar_next; 759 } 760 761 free(region); 762 } 763 764 /* backend is owned by the sub-application, just release application.c stuff */ 765 void 766 appl_close(struct appl_backend *backend) 767 { 768 struct appl_context *ctx; 769 struct appl_agentcap *cap, *tcap; 770 struct appl_region *region, *tregion, *nregion; 771 struct appl_request_downstream *request, *trequest; 772 773 TAILQ_FOREACH(ctx, &contexts, ac_entries) { 774 TAILQ_FOREACH_SAFE(cap, &(ctx->ac_agentcaps), aa_entry, tcap) { 775 if (cap->aa_backend == backend) 776 appl_agentcap_free(cap); 777 } 778 RB_FOREACH_SAFE(region, appl_regions, 779 &(ctx->ac_regions), tregion) { 780 while (region != NULL) { 781 nregion = region->ar_next; 782 if (region->ar_backend == backend) 783 appl_region_free(ctx, region); 784 region = nregion; 785 } 786 } 787 } 788 789 RB_FOREACH_SAFE(request, appl_requests, 790 &(backend->ab_requests), trequest) 791 appl_request_downstream_free(request); 792 } 793 794 struct appl_region * 795 appl_region_find(struct appl_context *ctx, 796 const struct ber_oid *oid) 797 { 798 struct appl_region *region, search; 799 800 search.ar_oid = *oid; 801 while (search.ar_oid.bo_n > 0) { 802 region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); 803 if (region != NULL) 804 return region; 805 search.ar_oid.bo_n--; 806 } 807 return NULL; 808 } 809 810 struct appl_region * 811 appl_region_next(struct appl_context *ctx, struct ber_oid *oid, 812 struct appl_region *cregion) 813 { 814 struct appl_region search, *nregion, *pregion; 815 int cmp; 816 817 search.ar_oid = *oid; 818 nregion = RB_NFIND(appl_regions, &(ctx->ac_regions), &search); 819 820 if (cregion == nregion) 821 nregion = RB_NEXT(appl_regions, &(ctx->ac_regions), nregion); 822 /* Past last element in tree, we might still have a parent */ 823 if (nregion == NULL) { 824 search.ar_oid = cregion->ar_oid; 825 search.ar_oid.bo_n--; 826 return appl_region_find(ctx, &(search.ar_oid)); 827 } 828 cmp = appl_region_cmp(cregion, nregion); 829 if (cmp >= 0) 830 fatalx("%s: wrong OID order", __func__); 831 /* Direct descendant */ 832 if (cmp == -2) 833 return nregion; 834 835 /* cmp == -1 */ 836 search.ar_oid = cregion->ar_oid; 837 /* Find direct next sibling */ 838 ober_oid_nextsibling(&(search.ar_oid)); 839 if (ober_oid_cmp(&(nregion->ar_oid), &(search.ar_oid)) == 0) 840 return nregion; 841 /* Sibling gaps go to parent, or end end at border */ 842 search.ar_oid = cregion->ar_oid; 843 search.ar_oid.bo_n--; 844 pregion = appl_region_find(ctx, &(search.ar_oid)); 845 846 return pregion != NULL ? pregion : nregion; 847 } 848 849 /* Name from RFC 3413 section 3.2 */ 850 void 851 appl_processpdu(struct snmp_message *statereference, const char *ctxname, 852 enum snmp_version pduversion, struct ber_element *pdu) 853 { 854 struct appl_context *ctx; 855 struct appl_request_upstream *ureq; 856 struct ber_element *varbind, *varbindlist; 857 long long nonrepeaters, maxrepetitions; 858 static uint32_t transactionid; 859 int32_t requestid; 860 size_t i, varbindlen = 0, repeaterlen; 861 862 /* pdu must be ASN.1 validated in snmpe.c */ 863 (void) ober_scanf_elements(pdu, "{diie", &requestid, &nonrepeaters, 864 &maxrepetitions, &varbindlist); 865 866 /* RFC 3413, section 3.2, processPDU, item 5, final bullet */ 867 if ((ctx = appl_context(ctxname, 0)) == NULL) { 868 snmp_target_mib.snmp_unknowncontexts++; 869 appl_report(statereference, requestid, 870 &OID(MIB_snmpUnknownContexts, 0)); 871 return; 872 } 873 874 if ((ureq = malloc(sizeof(*ureq))) == NULL) 875 fatal("malloc"); 876 877 ureq->aru_ctx = ctx; 878 ureq->aru_statereference = statereference; 879 ureq->aru_transactionid = transactionid++; 880 ureq->aru_requesttype = pdu->be_type; 881 ureq->aru_responsetype = SNMP_C_RESPONSE; 882 ureq->aru_requestid = requestid; 883 ureq->aru_error = APPL_ERROR_NOERROR; 884 ureq->aru_index = 0; 885 ureq->aru_nonrepeaters = nonrepeaters; 886 ureq->aru_maxrepetitions = maxrepetitions; 887 ureq->aru_varbindlen = 0; 888 ureq->aru_locked = 0; 889 ureq->aru_pduversion = pduversion; 890 891 varbind = varbindlist->be_sub; 892 for (; varbind != NULL; varbind = varbind->be_next) 893 varbindlen++; 894 895 repeaterlen = varbindlen - nonrepeaters; 896 if (pdu->be_type == SNMP_C_GETBULKREQ) 897 ureq->aru_varbindlen = nonrepeaters + 898 (repeaterlen * maxrepetitions); 899 else 900 ureq->aru_varbindlen = varbindlen; 901 if ((ureq->aru_vblist = calloc(ureq->aru_varbindlen, 902 sizeof(*ureq->aru_vblist))) == NULL) 903 fatal("malloc"); 904 905 varbind = varbindlist->be_sub; 906 /* Use aru_varbindlen in case maxrepetitions == 0 */ 907 for (i = 0; i < ureq->aru_varbindlen; i++) { 908 ureq->aru_vblist[i].avi_request_upstream = ureq; 909 ureq->aru_vblist[i].avi_index = i + 1; 910 ureq->aru_vblist[i].avi_state = APPL_VBSTATE_NEW; 911 /* This can only happen with bulkreq */ 912 if (varbind == NULL) { 913 ureq->aru_vblist[i - repeaterlen].avi_sub = 914 &(ureq->aru_vblist[i]); 915 ureq->aru_vblist[i].avi_state = APPL_VBSTATE_MUSTFILL; 916 continue; 917 } 918 ober_get_oid(varbind->be_sub, 919 &(ureq->aru_vblist[i].avi_varbind.av_oid)); 920 ureq->aru_vblist[i].avi_origid = 921 ureq->aru_vblist[i].avi_varbind.av_oid; 922 if (i + 1 < ureq->aru_varbindlen) { 923 ureq->aru_vblist[i].avi_next = 924 &(ureq->aru_vblist[i + 1]); 925 ureq->aru_vblist[i].avi_varbind.av_next = 926 &(ureq->aru_vblist[i + 1].avi_varbind); 927 } else { 928 ureq->aru_vblist[i].avi_next = NULL; 929 ureq->aru_vblist[i].avi_varbind.av_next = NULL; 930 } 931 varbind = varbind->be_next; 932 } 933 934 appl_pdu_log(NULL, pdu->be_type, requestid, nonrepeaters, 935 maxrepetitions, &(ureq->aru_vblist[0].avi_varbind)); 936 937 appl_request_upstream_resolve(ureq); 938 } 939 940 void 941 appl_request_upstream_free(struct appl_request_upstream *ureq) 942 { 943 size_t i; 944 struct appl_varbind_internal *vb; 945 946 if (ureq == NULL) 947 return; 948 949 ureq->aru_locked = 1; 950 for (i = 0; i < ureq->aru_varbindlen && ureq->aru_vblist != NULL; i++) { 951 vb = &(ureq->aru_vblist[i]); 952 ober_free_elements(vb->avi_varbind.av_value); 953 appl_request_downstream_free(vb->avi_request_downstream); 954 } 955 free(ureq->aru_vblist); 956 957 assert(ureq->aru_statereference == NULL); 958 959 free(ureq); 960 } 961 962 void 963 appl_request_downstream_free(struct appl_request_downstream *dreq) 964 { 965 struct appl_varbind_internal *vb; 966 967 if (dreq == NULL) 968 return; 969 970 RB_REMOVE(appl_requests, &(dreq->ard_backend->ab_requests), dreq); 971 evtimer_del(&(dreq->ard_timer)); 972 973 for (vb = dreq->ard_vblist; vb != NULL; vb = vb->avi_next) { 974 vb->avi_request_downstream = NULL; 975 if (vb->avi_state == APPL_VBSTATE_PENDING) 976 vb->avi_state = APPL_VBSTATE_NEW; 977 } 978 979 appl_request_upstream_resolve(dreq->ard_request); 980 free(dreq); 981 } 982 983 void 984 appl_request_upstream_resolve(struct appl_request_upstream *ureq) 985 { 986 struct appl_varbind_internal *vb, *lvb, *tvb; 987 struct appl_request_downstream *dreq; 988 struct appl_region *region, *lregion; 989 struct timeval tv; 990 int done; 991 size_t i; 992 int32_t maxrepetitions; 993 int32_t timeout; 994 995 if (ureq->aru_locked) 996 return; 997 ureq->aru_locked = 1; 998 999 if (ureq->aru_requesttype == SNMP_C_SETREQ) { 1000 ureq->aru_error = APPL_ERROR_NOTWRITABLE; 1001 ureq->aru_index = 1; 1002 appl_request_upstream_reply(ureq); 1003 return; 1004 } 1005 1006 next: 1007 dreq = NULL; 1008 lvb = NULL; 1009 done = 1; 1010 timeout = 0; 1011 1012 if (ureq->aru_error != APPL_ERROR_NOERROR) { 1013 appl_request_upstream_reply(ureq); 1014 return; 1015 } 1016 for (i = 0; i < ureq->aru_varbindlen; i++) { 1017 vb = &(ureq->aru_vblist[i]); 1018 1019 switch (vb->avi_state) { 1020 case APPL_VBSTATE_MUSTFILL: 1021 case APPL_VBSTATE_PENDING: 1022 done = 0; 1023 continue; 1024 case APPL_VBSTATE_DONE: 1025 continue; 1026 case APPL_VBSTATE_NEW: 1027 break; 1028 } 1029 if (appl_varbind_backend(vb) == -1) 1030 fatal("appl_varbind_backend"); 1031 if (vb->avi_state != APPL_VBSTATE_DONE) 1032 done = 0; 1033 } 1034 1035 for (i = 0; i < ureq->aru_varbindlen; i++) { 1036 vb = &(ureq->aru_vblist[i]); 1037 1038 if (vb->avi_state != APPL_VBSTATE_NEW) 1039 continue; 1040 1041 vb = &(ureq->aru_vblist[i]); 1042 region = vb->avi_region; 1043 lregion = lvb != NULL ? lvb->avi_region : NULL; 1044 if (lvb != NULL && region->ar_backend != lregion->ar_backend) 1045 continue; 1046 1047 vb->avi_varbind.av_next = NULL; 1048 vb->avi_next = NULL; 1049 tvb = vb; 1050 for (maxrepetitions = 0; tvb != NULL; tvb = tvb->avi_sub) 1051 maxrepetitions++; 1052 if (dreq == NULL) { 1053 if ((dreq = malloc(sizeof(*dreq))) == NULL) 1054 fatal("malloc"); 1055 1056 dreq->ard_request = ureq; 1057 dreq->ard_vblist = vb; 1058 dreq->ard_backend = vb->avi_region->ar_backend; 1059 dreq->ard_retries = dreq->ard_backend->ab_retries; 1060 dreq->ard_requesttype = ureq->aru_requesttype; 1061 /* 1062 * We don't yet fully handle bulkrequest responses. 1063 * It's completely valid to map onto getrequest. 1064 * maxrepetitions calculated in preparation of support. 1065 */ 1066 if (dreq->ard_requesttype == SNMP_C_GETBULKREQ && 1067 dreq->ard_backend->ab_fn->ab_getbulk == NULL) 1068 dreq->ard_requesttype = SNMP_C_GETNEXTREQ; 1069 /* 1070 * If first varbind is nonrepeater, set maxrepetitions 1071 * to 0, so that the next varbind with 1072 * maxrepetitions > 1 determines length. 1073 */ 1074 if (maxrepetitions == 1) { 1075 dreq->ard_maxrepetitions = 0; 1076 dreq->ard_nonrepeaters = 1; 1077 } else { 1078 dreq->ard_maxrepetitions = maxrepetitions; 1079 dreq->ard_nonrepeaters = 0; 1080 } 1081 do { 1082 dreq->ard_requestid = arc4random(); 1083 } while (RB_INSERT(appl_requests, 1084 &(dreq->ard_backend->ab_requests), dreq) != NULL); 1085 lvb = vb; 1086 /* avi_sub isn't set on !bulkrequest, so we always enter here */ 1087 } else if (maxrepetitions == 1) { 1088 dreq->ard_nonrepeaters++; 1089 vb->avi_varbind.av_next = 1090 &(dreq->ard_vblist->avi_varbind); 1091 vb->avi_next = dreq->ard_vblist; 1092 dreq->ard_vblist = vb; 1093 } else { 1094 lvb->avi_varbind.av_next = &(vb->avi_varbind); 1095 lvb->avi_next = vb; 1096 /* RFC 2741 section 7.2.1.3: 1097 * The value of g.max_repetitions in the GetBulk-PDU may 1098 * be less than (but not greater than) the value in the 1099 * original request PDU. 1100 */ 1101 if (dreq->ard_maxrepetitions > maxrepetitions || 1102 dreq->ard_maxrepetitions == 0) 1103 dreq->ard_maxrepetitions = maxrepetitions; 1104 lvb = vb; 1105 } 1106 vb->avi_request_downstream = dreq; 1107 vb->avi_state = APPL_VBSTATE_PENDING; 1108 if (region->ar_timeout > timeout) 1109 timeout = region->ar_timeout; 1110 } 1111 1112 if (dreq == NULL) { 1113 ureq->aru_locked = 0; 1114 if (done) 1115 appl_request_upstream_reply(ureq); 1116 return; 1117 } 1118 1119 tv.tv_sec = timeout / 100; 1120 tv.tv_usec = (timeout % 100) * 10000; 1121 evtimer_set(&(dreq->ard_timer), appl_request_downstream_timeout, dreq); 1122 evtimer_add(&(dreq->ard_timer), &tv); 1123 1124 appl_request_downstream_send(dreq); 1125 goto next; 1126 } 1127 1128 void 1129 appl_request_downstream_send(struct appl_request_downstream *dreq) 1130 { 1131 1132 appl_pdu_log(dreq->ard_backend, dreq->ard_requesttype, 1133 dreq->ard_requestid, 0, 0, &(dreq->ard_vblist->avi_varbind)); 1134 1135 if (dreq->ard_requesttype == SNMP_C_GETREQ) { 1136 dreq->ard_backend->ab_fn->ab_get(dreq->ard_backend, 1137 dreq->ard_request->aru_transactionid, 1138 dreq->ard_requestid, 1139 APPL_CONTEXT_NAME(dreq->ard_request->aru_ctx), 1140 &(dreq->ard_vblist->avi_varbind)); 1141 } else if (dreq->ard_requesttype == SNMP_C_GETNEXTREQ) { 1142 dreq->ard_backend->ab_fn->ab_getnext(dreq->ard_backend, 1143 dreq->ard_request->aru_transactionid, 1144 dreq->ard_requestid, 1145 APPL_CONTEXT_NAME(dreq->ard_request->aru_ctx), 1146 &(dreq->ard_vblist->avi_varbind)); 1147 } 1148 } 1149 1150 void 1151 appl_request_downstream_timeout(__unused int fd, __unused short event, 1152 void *cookie) 1153 { 1154 struct appl_request_downstream *dreq = cookie; 1155 1156 log_info("%s: %"PRIu32" timed out%s", 1157 dreq->ard_backend->ab_name, dreq->ard_requestid, 1158 dreq->ard_retries > 0 ? ": retrying" : ""); 1159 if (dreq->ard_retries > 0) { 1160 dreq->ard_retries--; 1161 appl_request_downstream_send(dreq); 1162 } else 1163 appl_response(dreq->ard_backend, dreq->ard_requestid, 1164 APPL_ERROR_GENERR, 1, &(dreq->ard_vblist->avi_varbind)); 1165 } 1166 1167 void 1168 appl_request_upstream_reply(struct appl_request_upstream *ureq) 1169 { 1170 struct ber_element *varbindlist = NULL, *varbind = NULL, *value; 1171 struct appl_varbind_internal *vb; 1172 size_t i, repvarbinds, varbindlen; 1173 ssize_t match = -1; 1174 1175 varbindlen = ureq->aru_varbindlen; 1176 1177 if (ureq->aru_pduversion == SNMP_V1) { 1178 /* RFC 3584 section 4.2.2.2 Map exceptions */ 1179 for (i = 0; i < varbindlen; i++) { 1180 vb = &(ureq->aru_vblist[i]); 1181 value = vb->avi_varbind.av_value; 1182 if (value != NULL && 1183 value->be_class == BER_CLASS_CONTEXT) 1184 appl_varbind_error(vb, APPL_ERROR_NOSUCHNAME); 1185 } 1186 /* RFC 3584 section 4.4 Map errors */ 1187 switch (ureq->aru_error) { 1188 case APPL_ERROR_WRONGVALUE: 1189 case APPL_ERROR_WRONGENCODING: 1190 case APPL_ERROR_WRONGTYPE: 1191 case APPL_ERROR_WRONGLENGTH: 1192 case APPL_ERROR_INCONSISTENTVALUE: 1193 ureq->aru_error = APPL_ERROR_BADVALUE; 1194 break; 1195 case APPL_ERROR_NOACCESS: 1196 case APPL_ERROR_NOTWRITABLE: 1197 case APPL_ERROR_NOCREATION: 1198 case APPL_ERROR_INCONSISTENTNAME: 1199 case APPL_ERROR_AUTHORIZATIONERROR: 1200 ureq->aru_error = APPL_ERROR_NOSUCHNAME; 1201 break; 1202 case APPL_ERROR_RESOURCEUNAVAILABLE: 1203 case APPL_ERROR_COMMITFAILED: 1204 case APPL_ERROR_UNDOFAILED: 1205 ureq->aru_error = APPL_ERROR_GENERR; 1206 break; 1207 default: 1208 break; 1209 } 1210 } 1211 /* RFC 3416 section 4.2.{1,2,3} reset original varbinds */ 1212 if (ureq->aru_error != APPL_ERROR_NOERROR) { 1213 if (ureq->aru_requesttype == SNMP_C_GETBULKREQ) 1214 varbindlen = 1215 (ureq->aru_varbindlen - ureq->aru_nonrepeaters) / 1216 ureq->aru_maxrepetitions; 1217 for (i = 0; i < varbindlen; i++) { 1218 vb = &(ureq->aru_vblist[i]); 1219 vb->avi_varbind.av_oid = vb->avi_origid; 1220 ober_free_elements(vb->avi_varbind.av_value); 1221 vb->avi_varbind.av_value = ober_add_null(NULL); 1222 } 1223 /* RFC 3416 section 4.2.3: Strip excessive EOMV */ 1224 } else if (ureq->aru_requesttype == SNMP_C_GETBULKREQ) { 1225 repvarbinds = (ureq->aru_varbindlen - ureq->aru_nonrepeaters) / 1226 ureq->aru_maxrepetitions; 1227 for (i = ureq->aru_nonrepeaters; 1228 i < ureq->aru_varbindlen - repvarbinds; i++) { 1229 value = ureq->aru_vblist[i].avi_varbind.av_value; 1230 if ((i - ureq->aru_nonrepeaters) % repvarbinds == 0 && 1231 value->be_class == BER_CLASS_CONTEXT && 1232 value->be_type == APPL_EXC_ENDOFMIBVIEW) { 1233 if (match != -1) 1234 break; 1235 match = i; 1236 } 1237 if (value->be_class != BER_CLASS_CONTEXT || 1238 value->be_type != APPL_EXC_ENDOFMIBVIEW) 1239 match = -1; 1240 } 1241 if (match != -1) 1242 varbindlen = match + repvarbinds; 1243 } 1244 1245 for (i = 0; i < varbindlen; i++) { 1246 vb = &(ureq->aru_vblist[i]); 1247 vb->avi_varbind.av_next = 1248 &(ureq->aru_vblist[i + 1].avi_varbind); 1249 value = vb->avi_varbind.av_value; 1250 if (value->be_class == BER_CLASS_CONTEXT && 1251 value->be_type == APPL_EXC_ENDOFMIBVIEW) 1252 vb->avi_varbind.av_oid = vb->avi_origid; 1253 } 1254 1255 ureq->aru_vblist[i - 1].avi_varbind.av_next = NULL; 1256 appl_pdu_log(NULL, ureq->aru_responsetype, ureq->aru_requestid, 1257 ureq->aru_error, ureq->aru_index, 1258 &(ureq->aru_vblist[0].avi_varbind)); 1259 1260 for (i = 0; i < varbindlen; i++) { 1261 varbind = ober_printf_elements(varbind, "{Oe}", 1262 &(ureq->aru_vblist[i].avi_varbind.av_oid), 1263 ureq->aru_vblist[i].avi_varbind.av_value); 1264 ureq->aru_vblist[i].avi_varbind.av_value = NULL; 1265 if (varbind == NULL) 1266 fatal("ober_printf_elements"); 1267 if (varbindlist == NULL) 1268 varbindlist = varbind; 1269 } 1270 1271 snmpe_send(ureq->aru_statereference, ureq->aru_responsetype, 1272 ureq->aru_requestid, ureq->aru_error, ureq->aru_index, varbindlist); 1273 ureq->aru_statereference = NULL; 1274 appl_request_upstream_free(ureq); 1275 } 1276 1277 /* Name from RFC 2741 section 6.2.16 */ 1278 void 1279 appl_response(struct appl_backend *backend, int32_t requestid, 1280 enum appl_error error, int16_t index, struct appl_varbind *vblist) 1281 { 1282 struct appl_request_downstream *dreq, search; 1283 struct appl_request_upstream *ureq = NULL; 1284 const char *errstr; 1285 char oidbuf[1024]; 1286 struct appl_varbind *vb; 1287 struct appl_varbind_internal *origvb = NULL; 1288 int invalid = 0; 1289 int next = 0, eomv; 1290 int32_t i; 1291 1292 appl_pdu_log(backend, SNMP_C_RESPONSE, requestid, error, index, vblist); 1293 1294 search.ard_requestid = requestid; 1295 dreq = RB_FIND(appl_requests, &(backend->ab_requests), &search); 1296 if (dreq == NULL) { 1297 log_debug("%s: %"PRIu32" not outstanding", 1298 backend->ab_name, requestid); 1299 /* Continue to verify validity */ 1300 } else { 1301 ureq = dreq->ard_request; 1302 next = ureq->aru_requesttype == SNMP_C_GETNEXTREQ || 1303 ureq->aru_requesttype == SNMP_C_GETBULKREQ; 1304 origvb = dreq->ard_vblist; 1305 if (!appl_error_valid(error, dreq->ard_requesttype)) { 1306 log_warnx("%s: %"PRIu32" Invalid error", 1307 backend->ab_name, requestid); 1308 invalid = 1; 1309 } 1310 } 1311 1312 vb = vblist; 1313 for (i = 1; vb != NULL; vb = vb->av_next, i++) { 1314 if (!appl_varbind_valid(vb, origvb, next, 1315 error != APPL_ERROR_NOERROR, backend->ab_range, &errstr)) { 1316 smi_oid2string(&(vb->av_oid), oidbuf, 1317 sizeof(oidbuf), 0); 1318 log_warnx("%s: %"PRIu32" %s: %s", 1319 backend->ab_name, requestid, oidbuf, errstr); 1320 invalid = 1; 1321 } 1322 /* Transfer av_value */ 1323 if (origvb != NULL) { 1324 if (error != APPL_ERROR_NOERROR && i == index) 1325 appl_varbind_error(origvb, error); 1326 origvb->avi_state = APPL_VBSTATE_DONE; 1327 origvb->avi_varbind.av_oid = vb->av_oid; 1328 1329 eomv = vb->av_value != NULL && 1330 vb->av_value->be_class == BER_CLASS_CONTEXT && 1331 vb->av_value->be_type == APPL_EXC_ENDOFMIBVIEW; 1332 /* 1333 * Treat results past av_oid_end for backends that 1334 * don't support searchranges as EOMV 1335 */ 1336 eomv |= !backend->ab_range && next && 1337 ober_oid_cmp(&(vb->av_oid), 1338 &(origvb->avi_varbind.av_oid_end)) >= 0; 1339 /* RFC 3584 section 4.2.2.1 */ 1340 if (ureq->aru_pduversion == SNMP_V1 && 1341 vb->av_value != NULL && 1342 vb->av_value->be_class == BER_CLASS_APPLICATION && 1343 vb->av_value->be_type == SNMP_COUNTER64) { 1344 if (next) 1345 eomv = 1; 1346 else 1347 appl_varbind_error(origvb, 1348 APPL_ERROR_NOSUCHNAME); 1349 } 1350 1351 if (eomv) { 1352 ober_free_elements(vb->av_value); 1353 origvb->avi_varbind.av_oid = 1354 origvb->avi_varbind.av_oid_end; 1355 origvb->avi_varbind.av_include = 1; 1356 vb->av_value = NULL; 1357 origvb->avi_state = APPL_VBSTATE_NEW; 1358 } 1359 origvb->avi_varbind.av_value = vb->av_value; 1360 if (origvb->avi_varbind.av_next == NULL && 1361 vb->av_next != NULL) { 1362 log_warnx("%s: Request %"PRIu32" returned more " 1363 "varbinds then requested", 1364 backend->ab_name, requestid); 1365 invalid = 1; 1366 } 1367 if (origvb->avi_sub != NULL && 1368 origvb->avi_state == APPL_VBSTATE_DONE) { 1369 origvb->avi_sub->avi_varbind.av_oid = 1370 origvb->avi_varbind.av_oid; 1371 origvb->avi_sub->avi_state = APPL_VBSTATE_NEW; 1372 } 1373 origvb = origvb->avi_next; 1374 } else { 1375 ober_free_elements(vb->av_value); 1376 vb->av_value = NULL; 1377 } 1378 } 1379 if (error != APPL_ERROR_NOERROR && (index <= 0 || index >= i)) { 1380 log_warnx("Invalid error index"); 1381 invalid = 1; 1382 } 1383 /* amavisd-snmp-subagent sets index to 1, no reason to crash over it. */ 1384 #if PEDANTIC 1385 if (error == APPL_ERROR_NOERROR && index != 0) { 1386 log_warnx("error index with no error"); 1387 invalid = 1; 1388 } 1389 #endif 1390 if (vb == NULL && origvb != NULL) { 1391 log_warnx("%s: Request %"PRIu32" returned less varbinds then " 1392 "requested", backend->ab_name, requestid); 1393 invalid = 1; 1394 } 1395 1396 if (dreq != NULL) { 1397 if (invalid) 1398 appl_varbind_error(dreq->ard_vblist, APPL_ERROR_GENERR); 1399 appl_request_downstream_free(dreq); 1400 } 1401 1402 if (invalid && backend->ab_fn->ab_close != NULL) { 1403 log_warnx("%s: Closing: Too many parse errors", 1404 backend->ab_name); 1405 backend->ab_fn->ab_close(backend, APPL_CLOSE_REASONPARSEERROR); 1406 } 1407 } 1408 1409 int 1410 appl_varbind_valid(struct appl_varbind *varbind, 1411 struct appl_varbind_internal *request, int next, int null, int range, 1412 const char **errstr) 1413 { 1414 int cmp; 1415 int eomv = 0; 1416 1417 if (null) 1418 next = 0; 1419 1420 if (varbind->av_value == NULL) { 1421 if (!null) { 1422 *errstr = "missing value"; 1423 return 0; 1424 } 1425 return 1; 1426 } 1427 if (varbind->av_value->be_class == BER_CLASS_UNIVERSAL) { 1428 switch (varbind->av_value->be_type) { 1429 case BER_TYPE_NULL: 1430 if (null) 1431 break; 1432 *errstr = "not expecting null value"; 1433 return 0; 1434 case BER_TYPE_INTEGER: 1435 case BER_TYPE_OCTETSTRING: 1436 case BER_TYPE_OBJECT: 1437 if (!null) 1438 break; 1439 /* FALLTHROUGH */ 1440 default: 1441 *errstr = "invalid value"; 1442 return 0; 1443 } 1444 } else if (varbind->av_value->be_class == BER_CLASS_APPLICATION) { 1445 switch (varbind->av_value->be_type) { 1446 case SNMP_T_IPADDR: 1447 case SNMP_T_COUNTER32: 1448 case SNMP_T_GAUGE32: 1449 case SNMP_T_TIMETICKS: 1450 case SNMP_T_OPAQUE: 1451 case SNMP_T_COUNTER64: 1452 if (!null) 1453 break; 1454 /* FALLTHROUGH */ 1455 default: 1456 *errstr = "expecting null value"; 1457 return 0; 1458 } 1459 } else if (varbind->av_value->be_class == BER_CLASS_CONTEXT) { 1460 switch (varbind->av_value->be_type) { 1461 case APPL_EXC_NOSUCHOBJECT: 1462 if (next && request != NULL) { 1463 *errstr = "Unexpected noSuchObject"; 1464 return 0; 1465 } 1466 /* FALLTHROUGH */ 1467 case APPL_EXC_NOSUCHINSTANCE: 1468 if (null) { 1469 *errstr = "expecting null value"; 1470 return 0; 1471 } 1472 if (next && request != NULL) { 1473 *errstr = "Unexpected noSuchInstance"; 1474 return 0; 1475 } 1476 break; 1477 case APPL_EXC_ENDOFMIBVIEW: 1478 if (null) { 1479 *errstr = "expecting null value"; 1480 return 0; 1481 } 1482 if (!next && request != NULL) { 1483 *errstr = "Unexpected endOfMibView"; 1484 return 0; 1485 } 1486 eomv = 1; 1487 break; 1488 default: 1489 *errstr = "invalid exception"; 1490 return 0; 1491 } 1492 } else { 1493 *errstr = "invalid value"; 1494 return 0; 1495 } 1496 1497 if (request == NULL) 1498 return 1; 1499 1500 cmp = ober_oid_cmp(&(request->avi_varbind.av_oid), &(varbind->av_oid)); 1501 if (next) { 1502 if (request->avi_region->ar_instance && 1503 ober_oid_cmp(&(request->avi_region->ar_oid), 1504 &(varbind->av_oid)) != 0) { 1505 *errstr = "oid below instance"; 1506 return 0; 1507 } 1508 if (!eomv) { 1509 if (request->avi_varbind.av_include) { 1510 if (cmp > 0) { 1511 *errstr = "oid not incrementing"; 1512 return 0; 1513 } 1514 } else { 1515 if (cmp >= 0) { 1516 *errstr = "oid not incrementing"; 1517 return 0; 1518 } 1519 } 1520 if (range && ober_oid_cmp(&(varbind->av_oid), 1521 &(request->avi_varbind.av_oid_end)) >= 0) { 1522 *errstr = "end oid not honoured"; 1523 return 0; 1524 } 1525 } 1526 } else { 1527 if (cmp != 0) { 1528 *errstr = "oids not equal"; 1529 return 0; 1530 } 1531 } 1532 return 1; 1533 } 1534 1535 int 1536 appl_error_valid(enum appl_error error, enum snmp_pdutype type) 1537 { 1538 switch (error) { 1539 case APPL_ERROR_NOERROR: 1540 case APPL_ERROR_TOOBIG: 1541 case APPL_ERROR_NOSUCHNAME: 1542 case APPL_ERROR_GENERR: 1543 return 1; 1544 case APPL_ERROR_BADVALUE: 1545 case APPL_ERROR_READONLY: 1546 case APPL_ERROR_NOACCESS: 1547 case APPL_ERROR_WRONGTYPE: 1548 case APPL_ERROR_WRONGLENGTH: 1549 case APPL_ERROR_WRONGENCODING: 1550 case APPL_ERROR_WRONGVALUE: 1551 case APPL_ERROR_NOCREATION: 1552 case APPL_ERROR_INCONSISTENTVALUE: 1553 case APPL_ERROR_RESOURCEUNAVAILABLE: 1554 case APPL_ERROR_COMMITFAILED: 1555 case APPL_ERROR_UNDOFAILED: 1556 case APPL_ERROR_NOTWRITABLE: 1557 case APPL_ERROR_INCONSISTENTNAME: 1558 return type == SNMP_C_SETREQ; 1559 case APPL_ERROR_AUTHORIZATIONERROR: 1560 return type == SNMP_C_GETREQ || type == SNMP_C_SETREQ; 1561 default: 1562 return 0; 1563 } 1564 } 1565 1566 int 1567 appl_varbind_backend(struct appl_varbind_internal *ivb) 1568 { 1569 struct appl_request_upstream *ureq = ivb->avi_request_upstream; 1570 struct appl_region search, *region, *pregion; 1571 struct appl_varbind *vb = &(ivb->avi_varbind); 1572 struct ber_oid oid, nextsibling; 1573 int next, cmp; 1574 1575 next = ureq->aru_requesttype == SNMP_C_GETNEXTREQ || 1576 ureq->aru_requesttype == SNMP_C_GETBULKREQ; 1577 1578 region = appl_region_find(ureq->aru_ctx, &(vb->av_oid)); 1579 if (region == NULL) { 1580 if (!next) { 1581 vb->av_value = appl_exception(APPL_EXC_NOSUCHOBJECT); 1582 ivb->avi_state = APPL_VBSTATE_DONE; 1583 if (vb->av_value == NULL) 1584 return -1; 1585 return 0; 1586 } 1587 search.ar_oid = vb->av_oid; 1588 region = RB_NFIND(appl_regions, 1589 &(ureq->aru_ctx->ac_regions), &search); 1590 if (region == NULL) 1591 goto eomv; 1592 vb->av_oid = region->ar_oid; 1593 vb->av_include = 1; 1594 } 1595 cmp = ober_oid_cmp(&(region->ar_oid), &(vb->av_oid)); 1596 if (cmp == -2) { 1597 if (region->ar_instance) { 1598 if (!next) { 1599 vb->av_value = 1600 appl_exception(APPL_EXC_NOSUCHINSTANCE); 1601 ivb->avi_state = APPL_VBSTATE_DONE; 1602 if (vb->av_value == NULL) 1603 return -1; 1604 return 0; 1605 } 1606 vb->av_oid = region->ar_oid; 1607 ober_oid_nextsibling(&(vb->av_oid)); 1608 vb->av_include = 1; 1609 return appl_varbind_backend(ivb); 1610 } 1611 } else if (cmp == 0) { 1612 if (region->ar_instance && next && !vb->av_include) { 1613 vb->av_oid = region->ar_oid; 1614 ober_oid_nextsibling(&(vb->av_oid)); 1615 vb->av_include = 1; 1616 return appl_varbind_backend(ivb); 1617 } 1618 } 1619 ivb->avi_region = region; 1620 if (next) { 1621 oid = vb->av_oid; 1622 /* 1623 * For the searchrange end we only want contiguous regions. 1624 * This means directly connecting, or overlapping with the same 1625 * backend. 1626 */ 1627 do { 1628 pregion = region; 1629 region = appl_region_next(ureq->aru_ctx, &oid, pregion); 1630 if (region == NULL) { 1631 oid = pregion->ar_oid; 1632 ober_oid_nextsibling(&oid); 1633 break; 1634 } 1635 cmp = ober_oid_cmp(&(region->ar_oid), &oid); 1636 if (cmp == 2) 1637 oid = region->ar_oid; 1638 else if (cmp == 1) { 1639 /* Break out if we find a gap */ 1640 nextsibling = pregion->ar_oid; 1641 ober_oid_nextsibling(&nextsibling); 1642 if (ober_oid_cmp(&(region->ar_oid), 1643 &nextsibling) != 0) { 1644 oid = pregion->ar_oid; 1645 ober_oid_nextsibling(&oid); 1646 break; 1647 } 1648 oid = region->ar_oid; 1649 } else if (cmp == -2) { 1650 oid = pregion->ar_oid; 1651 ober_oid_nextsibling(&oid); 1652 } else 1653 fatalx("We can't stop/move back on getnext"); 1654 } while (region->ar_backend == pregion->ar_backend); 1655 vb->av_oid_end = oid; 1656 } 1657 return 0; 1658 1659 eomv: 1660 do { 1661 ivb->avi_varbind.av_value = 1662 appl_exception(APPL_EXC_ENDOFMIBVIEW); 1663 ivb->avi_state = APPL_VBSTATE_DONE; 1664 if (ivb->avi_varbind.av_value == NULL) 1665 return -1; 1666 if (ivb->avi_sub != NULL) 1667 ivb->avi_sub->avi_varbind.av_oid = 1668 ivb->avi_varbind.av_oid; 1669 ivb = ivb->avi_sub; 1670 } while (ivb != NULL); 1671 1672 return 0; 1673 } 1674 1675 void 1676 appl_varbind_error(struct appl_varbind_internal *avi, enum appl_error error) 1677 { 1678 struct appl_request_upstream *ureq = avi->avi_request_upstream; 1679 1680 if (ureq->aru_error == APPL_ERROR_GENERR) 1681 return; 1682 if (ureq->aru_error != APPL_ERROR_NOERROR && error != APPL_ERROR_GENERR) 1683 return; 1684 ureq->aru_error = error; 1685 ureq->aru_index = avi->avi_index; 1686 } 1687 1688 void 1689 appl_report(struct snmp_message *statereference, int32_t requestid, 1690 struct ber_oid *oid) 1691 { 1692 struct appl_request_upstream *ureq; 1693 1694 if ((ureq = calloc(1, sizeof(*ureq))) == NULL) 1695 fatal("malloc"); 1696 ureq->aru_ctx = appl_context(NULL, 0); 1697 ureq->aru_statereference = statereference; 1698 ureq->aru_requesttype = SNMP_C_GETREQ; 1699 ureq->aru_responsetype = SNMP_C_REPORT; 1700 ureq->aru_requestid = requestid; 1701 ureq->aru_transactionid = 0; 1702 ureq->aru_nonrepeaters = 0; 1703 ureq->aru_maxrepetitions = 0; 1704 if ((ureq->aru_vblist = calloc(1, sizeof(*ureq->aru_vblist))) == NULL) 1705 fatal("malloc"); 1706 ureq->aru_varbindlen = 1; 1707 ureq->aru_error = APPL_ERROR_NOERROR; 1708 ureq->aru_index = 0; 1709 ureq->aru_locked = 0; 1710 ureq->aru_pduversion = SNMP_V3; 1711 1712 ureq->aru_vblist[0].avi_state = APPL_VBSTATE_NEW; 1713 ureq->aru_vblist[0].avi_varbind.av_oid = *oid; 1714 ureq->aru_vblist[0].avi_varbind.av_value = NULL; 1715 ureq->aru_vblist[0].avi_varbind.av_next = NULL; 1716 ureq->aru_vblist[0].avi_origid = *oid; 1717 ureq->aru_vblist[0].avi_index = 1; 1718 ureq->aru_vblist[0].avi_request_upstream = ureq; 1719 ureq->aru_vblist[0].avi_request_downstream = NULL; 1720 ureq->aru_vblist[0].avi_next = NULL; 1721 ureq->aru_vblist[0].avi_sub = NULL; 1722 1723 appl_request_upstream_resolve(ureq); 1724 } 1725 1726 struct ber_element * 1727 appl_exception(enum appl_exception type) 1728 { 1729 struct ber_element *value; 1730 1731 if ((value = ober_add_null(NULL)) == NULL) { 1732 log_warn("malloc"); 1733 return NULL; 1734 } 1735 ober_set_header(value, BER_CLASS_CONTEXT, type); 1736 1737 return value; 1738 } 1739 1740 void 1741 appl_pdu_log(struct appl_backend *backend, enum snmp_pdutype pdutype, 1742 int32_t requestid, uint16_t error, uint16_t index, 1743 struct appl_varbind *vblist) 1744 { 1745 struct appl_varbind *vb; 1746 char buf[1024], oidbuf[1024], *str; 1747 int next; 1748 1749 if (log_getverbose() < 2) 1750 return; 1751 1752 next = (pdutype == SNMP_C_GETNEXTREQ || pdutype == SNMP_C_GETBULKREQ); 1753 1754 buf[0] = '\0'; 1755 for (vb = vblist; vb != NULL; vb = vb->av_next) { 1756 strlcat(buf, "{", sizeof(buf)); 1757 strlcat(buf, smi_oid2string(&(vb->av_oid), oidbuf, 1758 sizeof(oidbuf), 0), sizeof(buf)); 1759 if (next) { 1760 if (vb->av_include) 1761 strlcat(buf, "(incl)", sizeof(buf)); 1762 if (vb->av_oid_end.bo_n > 0) { 1763 strlcat(buf, "-", sizeof(buf)); 1764 strlcat(buf, smi_oid2string(&(vb->av_oid_end), 1765 oidbuf, sizeof(oidbuf), 0), sizeof(buf)); 1766 } 1767 } 1768 strlcat(buf, ":", sizeof(buf)); 1769 if (vb->av_value != NULL) { 1770 str = smi_print_element(vb->av_value); 1771 strlcat(buf, str == NULL ? "???" : str, sizeof(buf)); 1772 free(str); 1773 } else 1774 strlcat(buf, "null", sizeof(buf)); 1775 strlcat(buf, "}", sizeof(buf)); 1776 } 1777 log_debug("%s%s%s{%"PRId32", %"PRIu16", %"PRIu16", {%s}}", 1778 backend != NULL ? backend->ab_name : "", 1779 backend != NULL ? ": " : "", 1780 snmpe_pdutype2string(pdutype), requestid, error, index, buf); 1781 } 1782 1783 void 1784 ober_oid_nextsibling(struct ber_oid *oid) 1785 { 1786 while (oid->bo_n > 0) { 1787 oid->bo_id[oid->bo_n - 1]++; 1788 /* Overflow check */ 1789 if (oid->bo_id[oid->bo_n - 1] != 0) 1790 return; 1791 oid->bo_n--; 1792 } 1793 } 1794 1795 int 1796 appl_region_cmp(struct appl_region *r1, struct appl_region *r2) 1797 { 1798 return ober_oid_cmp(&(r1->ar_oid), &(r2->ar_oid)); 1799 } 1800 1801 int 1802 appl_request_cmp(struct appl_request_downstream *r1, 1803 struct appl_request_downstream *r2) 1804 { 1805 return r1->ard_requestid < r2->ard_requestid ? -1 : 1806 r1->ard_requestid > r2->ard_requestid; 1807 } 1808 1809 RB_GENERATE_STATIC(appl_regions, appl_region, ar_entry, appl_region_cmp); 1810 RB_GENERATE_STATIC(appl_requests, appl_request_downstream, ard_entry, 1811 appl_request_cmp); 1812