1 /* $OpenBSD: application.c,v 1.41 2023/12/21 12:43:30 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/time.h> 21 #include <sys/tree.h> 22 #include <sys/types.h> 23 24 #include <assert.h> 25 #include <errno.h> 26 #include <event.h> 27 #include <inttypes.h> 28 #include <stdlib.h> 29 #include <stdio.h> 30 #include <string.h> 31 32 #include "application.h" 33 #include "log.h" 34 #include "smi.h" 35 #include "snmp.h" 36 #include "snmpe.h" 37 #include "mib.h" 38 39 #define OID(...) (struct ber_oid){ { __VA_ARGS__ }, \ 40 (sizeof((uint32_t []) { __VA_ARGS__ }) / sizeof(uint32_t)) } 41 42 TAILQ_HEAD(, appl_context) contexts = TAILQ_HEAD_INITIALIZER(contexts); 43 44 struct appl_agentcap { 45 struct appl_backend *aa_backend; 46 struct appl_context *aa_context; 47 uint32_t aa_index; 48 struct ber_oid aa_oid; 49 char aa_descr[256]; 50 int aa_uptime; 51 52 TAILQ_ENTRY(appl_agentcap) aa_entry; 53 }; 54 55 struct appl_context { 56 char ac_name[APPL_CONTEXTNAME_MAX + 1]; 57 58 RB_HEAD(appl_regions, appl_region) ac_regions; 59 TAILQ_HEAD(, appl_agentcap) ac_agentcaps; 60 int ac_agentcap_lastid; 61 int ac_agentcap_lastchange; 62 63 TAILQ_ENTRY(appl_context) ac_entries; 64 }; 65 66 struct appl_region { 67 struct ber_oid ar_oid; 68 uint8_t ar_priority; 69 int32_t ar_timeout; 70 int ar_instance; 71 int ar_subtree; /* Claim entire subtree */ 72 struct appl_backend *ar_backend; 73 struct appl_region *ar_next; /* Sorted by priority */ 74 75 RB_ENTRY(appl_region) ar_entry; 76 }; 77 78 struct appl_request_upstream { 79 struct appl_context *aru_ctx; 80 struct snmp_message *aru_statereference; 81 enum snmp_pdutype aru_requesttype; 82 enum snmp_pdutype aru_responsetype; 83 int32_t aru_requestid; /* upstream requestid */ 84 int32_t aru_transactionid; /* RFC 2741 section 6.1 */ 85 uint16_t aru_nonrepeaters; 86 uint16_t aru_maxrepetitions; 87 struct appl_varbind_internal *aru_vblist; 88 size_t aru_varbindlen; 89 enum appl_error aru_error; 90 int16_t aru_index; 91 int aru_locked; /* Prevent recursion through appl_request_send */ 92 93 enum snmp_version aru_pduversion; 94 }; 95 96 struct appl_request_downstream { 97 struct appl_request_upstream *ard_request; 98 struct appl_backend *ard_backend; 99 enum snmp_pdutype ard_requesttype; 100 uint16_t ard_nonrepeaters; 101 uint16_t ard_maxrepetitions; 102 int32_t ard_requestid; 103 uint8_t ard_retries; 104 105 struct appl_varbind_internal *ard_vblist; 106 struct event ard_timer; 107 108 RB_ENTRY(appl_request_downstream) ard_entry; 109 }; 110 111 enum appl_varbind_state { 112 APPL_VBSTATE_MUSTFILL, 113 APPL_VBSTATE_NEW, 114 APPL_VBSTATE_PENDING, 115 APPL_VBSTATE_DONE 116 }; 117 118 struct appl_varbind_internal { 119 enum appl_varbind_state avi_state; 120 struct appl_varbind avi_varbind; 121 struct appl_region *avi_region; 122 struct ber_oid avi_origid; 123 int16_t avi_index; 124 struct appl_request_upstream *avi_request_upstream; 125 struct appl_request_downstream *avi_request_downstream; 126 struct appl_varbind_internal *avi_next; 127 struct appl_varbind_internal *avi_sub; 128 }; 129 130 /* SNMP-TARGET-MIB (RFC 3413) */ 131 struct snmp_target_mib { 132 uint32_t snmp_unavailablecontexts; 133 uint32_t snmp_unknowncontexts; 134 } snmp_target_mib; 135 136 void appl_agentcap_free(struct appl_agentcap *); 137 enum appl_error appl_region(struct appl_context *, uint32_t, uint8_t, 138 struct ber_oid *, int, int, struct appl_backend *); 139 void appl_region_free(struct appl_context *, struct appl_region *); 140 enum appl_error appl_region_unregister_match(struct appl_context *, uint8_t, 141 struct ber_oid *, char *, struct appl_backend *, int); 142 struct appl_region *appl_region_find(struct appl_context *, 143 const struct ber_oid *); 144 struct appl_region *appl_region_next(struct appl_context *, 145 struct ber_oid *, struct appl_region *); 146 void appl_request_upstream_free(struct appl_request_upstream *); 147 void appl_request_downstream_free(struct appl_request_downstream *); 148 void appl_request_upstream_resolve(struct appl_request_upstream *); 149 void appl_request_downstream_send(struct appl_request_downstream *); 150 void appl_request_downstream_timeout(int, short, void *); 151 void appl_request_upstream_reply(struct appl_request_upstream *); 152 int appl_varbind_valid(struct appl_varbind *, struct appl_varbind_internal *, 153 int, int, int, const char **); 154 int appl_error_valid(enum appl_error, enum snmp_pdutype); 155 int appl_varbind_backend(struct appl_varbind_internal *); 156 void appl_varbind_error(struct appl_varbind_internal *, enum appl_error); 157 void appl_pdu_log(struct appl_backend *, enum snmp_pdutype, int32_t, uint16_t, 158 uint16_t, struct appl_varbind *); 159 void ober_oid_nextsibling(struct ber_oid *); 160 161 int appl_region_cmp(struct appl_region *, struct appl_region *); 162 int appl_request_cmp(struct appl_request_downstream *, 163 struct appl_request_downstream *); 164 165 RB_PROTOTYPE_STATIC(appl_regions, appl_region, ar_entry, appl_region_cmp); 166 RB_PROTOTYPE_STATIC(appl_requests, appl_request_downstream, ard_entry, 167 appl_request_cmp); 168 169 #define APPL_CONTEXT_NAME(ctx) (ctx->ac_name[0] == '\0' ? NULL : ctx->ac_name) 170 171 void 172 appl(void) 173 { 174 appl_agentx(); 175 } 176 177 void 178 appl_init(void) 179 { 180 appl_blocklist_init(); 181 appl_internal_init(); 182 appl_agentx_init(); 183 } 184 185 void 186 appl_shutdown(void) 187 { 188 struct appl_context *ctx, *tctx; 189 190 appl_blocklist_shutdown(); 191 appl_internal_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(*cap))) == 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 ureq->aru_vblist[i].avi_index = 917 ureq->aru_vblist[i - repeaterlen].avi_index; 918 continue; 919 } 920 ober_get_oid(varbind->be_sub, 921 &(ureq->aru_vblist[i].avi_varbind.av_oid)); 922 ureq->aru_vblist[i].avi_origid = 923 ureq->aru_vblist[i].avi_varbind.av_oid; 924 if (i + 1 < varbindlen) 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_varbind.av_next = NULL; 929 varbind = varbind->be_next; 930 } 931 932 appl_pdu_log(NULL, pdu->be_type, requestid, nonrepeaters, 933 maxrepetitions, &(ureq->aru_vblist[0].avi_varbind)); 934 935 appl_request_upstream_resolve(ureq); 936 } 937 938 void 939 appl_request_upstream_free(struct appl_request_upstream *ureq) 940 { 941 size_t i; 942 struct appl_varbind_internal *vb; 943 944 if (ureq == NULL) 945 return; 946 947 ureq->aru_locked = 1; 948 for (i = 0; i < ureq->aru_varbindlen && ureq->aru_vblist != NULL; i++) { 949 vb = &(ureq->aru_vblist[i]); 950 ober_free_elements(vb->avi_varbind.av_value); 951 appl_request_downstream_free(vb->avi_request_downstream); 952 } 953 free(ureq->aru_vblist); 954 955 assert(ureq->aru_statereference == NULL); 956 957 free(ureq); 958 } 959 960 void 961 appl_request_downstream_free(struct appl_request_downstream *dreq) 962 { 963 struct appl_varbind_internal *vb; 964 965 if (dreq == NULL) 966 return; 967 968 RB_REMOVE(appl_requests, &(dreq->ard_backend->ab_requests), dreq); 969 evtimer_del(&(dreq->ard_timer)); 970 971 for (vb = dreq->ard_vblist; vb != NULL; vb = vb->avi_next) { 972 vb->avi_request_downstream = NULL; 973 if (vb->avi_state == APPL_VBSTATE_PENDING) 974 vb->avi_state = APPL_VBSTATE_NEW; 975 } 976 977 appl_request_upstream_resolve(dreq->ard_request); 978 free(dreq); 979 } 980 981 void 982 appl_request_upstream_resolve(struct appl_request_upstream *ureq) 983 { 984 struct appl_varbind_internal *vb, *lvb, *tvb; 985 struct appl_request_downstream *dreq; 986 struct appl_region *region, *lregion; 987 struct timeval tv; 988 int done; 989 size_t i; 990 int32_t maxrepetitions; 991 int32_t timeout; 992 993 if (ureq->aru_locked) 994 return; 995 ureq->aru_locked = 1; 996 997 if (ureq->aru_requesttype == SNMP_C_SETREQ) { 998 ureq->aru_error = APPL_ERROR_NOTWRITABLE; 999 ureq->aru_index = 1; 1000 appl_request_upstream_reply(ureq); 1001 return; 1002 } 1003 1004 next: 1005 dreq = NULL; 1006 lvb = NULL; 1007 done = 1; 1008 timeout = 0; 1009 1010 if (ureq->aru_error != APPL_ERROR_NOERROR) { 1011 appl_request_upstream_reply(ureq); 1012 return; 1013 } 1014 for (i = 0; i < ureq->aru_varbindlen; i++) { 1015 vb = &(ureq->aru_vblist[i]); 1016 1017 switch (vb->avi_state) { 1018 case APPL_VBSTATE_MUSTFILL: 1019 case APPL_VBSTATE_PENDING: 1020 done = 0; 1021 continue; 1022 case APPL_VBSTATE_DONE: 1023 continue; 1024 case APPL_VBSTATE_NEW: 1025 break; 1026 } 1027 if (appl_varbind_backend(vb) == -1) 1028 fatal("appl_varbind_backend"); 1029 if (vb->avi_state != APPL_VBSTATE_DONE) 1030 done = 0; 1031 } 1032 1033 for (i = 0; i < ureq->aru_varbindlen; i++) { 1034 vb = &(ureq->aru_vblist[i]); 1035 1036 if (vb->avi_state != APPL_VBSTATE_NEW) 1037 continue; 1038 1039 vb = &(ureq->aru_vblist[i]); 1040 region = vb->avi_region; 1041 lregion = lvb != NULL ? lvb->avi_region : NULL; 1042 if (lvb != NULL && region->ar_backend != lregion->ar_backend) 1043 continue; 1044 1045 vb->avi_varbind.av_next = NULL; 1046 vb->avi_next = NULL; 1047 tvb = vb; 1048 for (maxrepetitions = 0; tvb != NULL; tvb = tvb->avi_sub) 1049 maxrepetitions++; 1050 if (dreq == NULL) { 1051 if ((dreq = malloc(sizeof(*dreq))) == NULL) 1052 fatal("malloc"); 1053 1054 dreq->ard_request = ureq; 1055 dreq->ard_vblist = vb; 1056 dreq->ard_backend = vb->avi_region->ar_backend; 1057 dreq->ard_retries = dreq->ard_backend->ab_retries; 1058 dreq->ard_requesttype = ureq->aru_requesttype; 1059 /* 1060 * We don't yet fully handle bulkrequest responses. 1061 * It's completely valid to map onto getrequest. 1062 * maxrepetitions calculated in preparation of support. 1063 */ 1064 if (dreq->ard_requesttype == SNMP_C_GETBULKREQ && 1065 dreq->ard_backend->ab_fn->ab_getbulk == NULL) 1066 dreq->ard_requesttype = SNMP_C_GETNEXTREQ; 1067 /* 1068 * If first varbind is nonrepeater, set maxrepetitions 1069 * to 0, so that the next varbind with 1070 * maxrepetitions > 1 determines length. 1071 */ 1072 if (maxrepetitions == 1) { 1073 dreq->ard_maxrepetitions = 0; 1074 dreq->ard_nonrepeaters = 1; 1075 } else { 1076 dreq->ard_maxrepetitions = maxrepetitions; 1077 dreq->ard_nonrepeaters = 0; 1078 } 1079 do { 1080 dreq->ard_requestid = arc4random(); 1081 } while (RB_INSERT(appl_requests, 1082 &(dreq->ard_backend->ab_requests), dreq) != NULL); 1083 lvb = vb; 1084 /* avi_sub isn't set on !bulkrequest, so we always enter here */ 1085 } else if (maxrepetitions == 1) { 1086 dreq->ard_nonrepeaters++; 1087 vb->avi_varbind.av_next = 1088 &(dreq->ard_vblist->avi_varbind); 1089 vb->avi_next = dreq->ard_vblist; 1090 dreq->ard_vblist = vb; 1091 } else { 1092 lvb->avi_varbind.av_next = &(vb->avi_varbind); 1093 lvb->avi_next = vb; 1094 /* RFC 2741 section 7.2.1.3: 1095 * The value of g.max_repetitions in the GetBulk-PDU may 1096 * be less than (but not greater than) the value in the 1097 * original request PDU. 1098 */ 1099 if (dreq->ard_maxrepetitions > maxrepetitions || 1100 dreq->ard_maxrepetitions == 0) 1101 dreq->ard_maxrepetitions = maxrepetitions; 1102 lvb = vb; 1103 } 1104 vb->avi_request_downstream = dreq; 1105 vb->avi_state = APPL_VBSTATE_PENDING; 1106 if (region->ar_timeout > timeout) 1107 timeout = region->ar_timeout; 1108 } 1109 1110 if (dreq == NULL) { 1111 ureq->aru_locked = 0; 1112 if (done) 1113 appl_request_upstream_reply(ureq); 1114 return; 1115 } 1116 1117 tv.tv_sec = timeout / 100; 1118 tv.tv_usec = (timeout % 100) * 10000; 1119 evtimer_set(&(dreq->ard_timer), appl_request_downstream_timeout, dreq); 1120 evtimer_add(&(dreq->ard_timer), &tv); 1121 1122 appl_request_downstream_send(dreq); 1123 goto next; 1124 } 1125 1126 void 1127 appl_request_downstream_send(struct appl_request_downstream *dreq) 1128 { 1129 1130 appl_pdu_log(dreq->ard_backend, dreq->ard_requesttype, 1131 dreq->ard_requestid, 0, 0, &(dreq->ard_vblist->avi_varbind)); 1132 1133 if (dreq->ard_requesttype == SNMP_C_GETREQ) { 1134 dreq->ard_backend->ab_fn->ab_get(dreq->ard_backend, 1135 dreq->ard_request->aru_transactionid, 1136 dreq->ard_requestid, 1137 APPL_CONTEXT_NAME(dreq->ard_request->aru_ctx), 1138 &(dreq->ard_vblist->avi_varbind)); 1139 } else if (dreq->ard_requesttype == SNMP_C_GETNEXTREQ) { 1140 dreq->ard_backend->ab_fn->ab_getnext(dreq->ard_backend, 1141 dreq->ard_request->aru_transactionid, 1142 dreq->ard_requestid, 1143 APPL_CONTEXT_NAME(dreq->ard_request->aru_ctx), 1144 &(dreq->ard_vblist->avi_varbind)); 1145 } 1146 } 1147 1148 void 1149 appl_request_downstream_timeout(__unused int fd, __unused short event, 1150 void *cookie) 1151 { 1152 struct appl_request_downstream *dreq = cookie; 1153 1154 log_info("%s: %"PRIu32" timed out%s", 1155 dreq->ard_backend->ab_name, dreq->ard_requestid, 1156 dreq->ard_retries > 0 ? ": retrying" : ""); 1157 if (dreq->ard_retries > 0) { 1158 dreq->ard_retries--; 1159 appl_request_downstream_send(dreq); 1160 } else 1161 appl_response(dreq->ard_backend, dreq->ard_requestid, 1162 APPL_ERROR_GENERR, 1, &(dreq->ard_vblist->avi_varbind)); 1163 } 1164 1165 void 1166 appl_request_upstream_reply(struct appl_request_upstream *ureq) 1167 { 1168 struct ber_element *varbindlist = NULL, *varbind = NULL, *value; 1169 struct appl_varbind_internal *vb; 1170 size_t i, repvarbinds, varbindlen; 1171 ssize_t match = -1; 1172 1173 varbindlen = ureq->aru_varbindlen; 1174 1175 if (ureq->aru_pduversion == SNMP_V1) { 1176 /* RFC 3584 section 4.2.2.2 Map exceptions */ 1177 for (i = 0; i < varbindlen; i++) { 1178 vb = &(ureq->aru_vblist[i]); 1179 value = vb->avi_varbind.av_value; 1180 if (value != NULL && 1181 value->be_class == BER_CLASS_CONTEXT) 1182 appl_varbind_error(vb, APPL_ERROR_NOSUCHNAME); 1183 } 1184 /* RFC 3584 section 4.4 Map errors */ 1185 switch (ureq->aru_error) { 1186 case APPL_ERROR_WRONGVALUE: 1187 case APPL_ERROR_WRONGENCODING: 1188 case APPL_ERROR_WRONGTYPE: 1189 case APPL_ERROR_WRONGLENGTH: 1190 case APPL_ERROR_INCONSISTENTVALUE: 1191 ureq->aru_error = APPL_ERROR_BADVALUE; 1192 break; 1193 case APPL_ERROR_NOACCESS: 1194 case APPL_ERROR_NOTWRITABLE: 1195 case APPL_ERROR_NOCREATION: 1196 case APPL_ERROR_INCONSISTENTNAME: 1197 case APPL_ERROR_AUTHORIZATIONERROR: 1198 ureq->aru_error = APPL_ERROR_NOSUCHNAME; 1199 break; 1200 case APPL_ERROR_RESOURCEUNAVAILABLE: 1201 case APPL_ERROR_COMMITFAILED: 1202 case APPL_ERROR_UNDOFAILED: 1203 ureq->aru_error = APPL_ERROR_GENERR; 1204 break; 1205 default: 1206 break; 1207 } 1208 } 1209 /* RFC 3416 section 4.2.{1,2,3} reset original varbinds */ 1210 if (ureq->aru_error != APPL_ERROR_NOERROR) { 1211 if (ureq->aru_requesttype == SNMP_C_GETBULKREQ) 1212 varbindlen = 1213 (ureq->aru_varbindlen - ureq->aru_nonrepeaters) / 1214 ureq->aru_maxrepetitions; 1215 for (i = 0; i < varbindlen; i++) { 1216 vb = &(ureq->aru_vblist[i]); 1217 vb->avi_varbind.av_oid = vb->avi_origid; 1218 ober_free_elements(vb->avi_varbind.av_value); 1219 vb->avi_varbind.av_value = ober_add_null(NULL); 1220 } 1221 /* RFC 3416 section 4.2.3: Strip excessive EOMV */ 1222 } else if (ureq->aru_requesttype == SNMP_C_GETBULKREQ) { 1223 repvarbinds = (ureq->aru_varbindlen - ureq->aru_nonrepeaters) / 1224 ureq->aru_maxrepetitions; 1225 for (i = ureq->aru_nonrepeaters; 1226 i < ureq->aru_varbindlen - repvarbinds; i++) { 1227 value = ureq->aru_vblist[i].avi_varbind.av_value; 1228 if ((i - ureq->aru_nonrepeaters) % repvarbinds == 0 && 1229 value->be_class == BER_CLASS_CONTEXT && 1230 value->be_type == APPL_EXC_ENDOFMIBVIEW) { 1231 if (match != -1) 1232 break; 1233 match = i; 1234 } 1235 if (value->be_class != BER_CLASS_CONTEXT || 1236 value->be_type != APPL_EXC_ENDOFMIBVIEW) 1237 match = -1; 1238 } 1239 if (match != -1) 1240 varbindlen = match + repvarbinds; 1241 } 1242 1243 for (i = 0; i < varbindlen; i++) { 1244 vb = &(ureq->aru_vblist[i]); 1245 vb->avi_varbind.av_next = 1246 &(ureq->aru_vblist[i + 1].avi_varbind); 1247 value = vb->avi_varbind.av_value; 1248 if (value->be_class == BER_CLASS_CONTEXT && 1249 value->be_type == APPL_EXC_ENDOFMIBVIEW) 1250 vb->avi_varbind.av_oid = vb->avi_origid; 1251 } 1252 1253 ureq->aru_vblist[i - 1].avi_varbind.av_next = NULL; 1254 appl_pdu_log(NULL, ureq->aru_responsetype, ureq->aru_requestid, 1255 ureq->aru_error, ureq->aru_index, 1256 &(ureq->aru_vblist[0].avi_varbind)); 1257 1258 for (i = 0; i < varbindlen; i++) { 1259 varbind = ober_printf_elements(varbind, "{Oe}", 1260 &(ureq->aru_vblist[i].avi_varbind.av_oid), 1261 ureq->aru_vblist[i].avi_varbind.av_value); 1262 ureq->aru_vblist[i].avi_varbind.av_value = NULL; 1263 if (varbind == NULL) 1264 fatal("ober_printf_elements"); 1265 if (varbindlist == NULL) 1266 varbindlist = varbind; 1267 } 1268 1269 snmpe_send(ureq->aru_statereference, ureq->aru_responsetype, 1270 ureq->aru_requestid, ureq->aru_error, ureq->aru_index, varbindlist); 1271 ureq->aru_statereference = NULL; 1272 appl_request_upstream_free(ureq); 1273 } 1274 1275 /* Name from RFC 2741 section 6.2.16 */ 1276 void 1277 appl_response(struct appl_backend *backend, int32_t requestid, 1278 enum appl_error error, int16_t index, struct appl_varbind *vblist) 1279 { 1280 struct appl_request_downstream *dreq, search; 1281 struct appl_request_upstream *ureq = NULL; 1282 const char *errstr; 1283 char oidbuf[1024]; 1284 struct appl_varbind *vb; 1285 struct appl_varbind_internal *origvb = NULL; 1286 int invalid = 0; 1287 int next = 0, eomv; 1288 int32_t i; 1289 1290 appl_pdu_log(backend, SNMP_C_RESPONSE, requestid, error, index, vblist); 1291 1292 search.ard_requestid = requestid; 1293 dreq = RB_FIND(appl_requests, &(backend->ab_requests), &search); 1294 if (dreq == NULL) { 1295 log_debug("%s: %"PRIu32" not outstanding", 1296 backend->ab_name, requestid); 1297 /* Continue to verify validity */ 1298 } else { 1299 ureq = dreq->ard_request; 1300 next = ureq->aru_requesttype == SNMP_C_GETNEXTREQ || 1301 ureq->aru_requesttype == SNMP_C_GETBULKREQ; 1302 origvb = dreq->ard_vblist; 1303 if (!appl_error_valid(error, dreq->ard_requesttype)) { 1304 log_warnx("%s: %"PRIu32" Invalid error", 1305 backend->ab_name, requestid); 1306 invalid = 1; 1307 } 1308 } 1309 1310 vb = vblist; 1311 for (i = 1; vb != NULL; vb = vb->av_next, i++) { 1312 if (!appl_varbind_valid(vb, origvb, next, 1313 error != APPL_ERROR_NOERROR, backend->ab_range, &errstr)) { 1314 smi_oid2string(&(vb->av_oid), oidbuf, 1315 sizeof(oidbuf), 0); 1316 log_warnx("%s: %"PRIu32" %s: %s", 1317 backend->ab_name, requestid, oidbuf, errstr); 1318 invalid = 1; 1319 } 1320 /* Transfer av_value */ 1321 if (origvb != NULL) { 1322 if (error != APPL_ERROR_NOERROR && i == index) 1323 appl_varbind_error(origvb, error); 1324 origvb->avi_state = APPL_VBSTATE_DONE; 1325 origvb->avi_varbind.av_oid = vb->av_oid; 1326 1327 eomv = vb->av_value != NULL && 1328 vb->av_value->be_class == BER_CLASS_CONTEXT && 1329 vb->av_value->be_type == APPL_EXC_ENDOFMIBVIEW; 1330 /* 1331 * Treat results past av_oid_end for backends that 1332 * don't support searchranges as EOMV 1333 */ 1334 eomv |= !backend->ab_range && next && 1335 ober_oid_cmp(&(vb->av_oid), 1336 &(origvb->avi_varbind.av_oid_end)) >= 0; 1337 /* RFC 3584 section 4.2.2.1 */ 1338 if (ureq->aru_pduversion == SNMP_V1 && 1339 vb->av_value != NULL && 1340 vb->av_value->be_class == BER_CLASS_APPLICATION && 1341 vb->av_value->be_type == SNMP_COUNTER64) { 1342 if (next) 1343 eomv = 1; 1344 else 1345 appl_varbind_error(origvb, 1346 APPL_ERROR_NOSUCHNAME); 1347 } 1348 1349 if (eomv) { 1350 ober_free_elements(vb->av_value); 1351 origvb->avi_varbind.av_oid = 1352 origvb->avi_varbind.av_oid_end; 1353 origvb->avi_varbind.av_include = 1; 1354 vb->av_value = NULL; 1355 origvb->avi_state = APPL_VBSTATE_NEW; 1356 } 1357 origvb->avi_varbind.av_value = vb->av_value; 1358 if (origvb->avi_varbind.av_next == NULL && 1359 vb->av_next != NULL) { 1360 log_warnx("%s: Request %"PRIu32" returned more " 1361 "varbinds then requested", 1362 backend->ab_name, requestid); 1363 invalid = 1; 1364 } 1365 if (origvb->avi_sub != NULL && 1366 origvb->avi_state == APPL_VBSTATE_DONE) { 1367 origvb->avi_sub->avi_varbind.av_oid = 1368 origvb->avi_varbind.av_oid; 1369 origvb->avi_sub->avi_origid = 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->avi_sub->avi_origid = ivb->avi_origid; 1670 } 1671 ivb = ivb->avi_sub; 1672 } while (ivb != NULL); 1673 1674 return 0; 1675 } 1676 1677 void 1678 appl_varbind_error(struct appl_varbind_internal *avi, enum appl_error error) 1679 { 1680 struct appl_request_upstream *ureq = avi->avi_request_upstream; 1681 1682 if (ureq->aru_error == APPL_ERROR_GENERR) 1683 return; 1684 if (ureq->aru_error != APPL_ERROR_NOERROR && error != APPL_ERROR_GENERR) 1685 return; 1686 ureq->aru_error = error; 1687 ureq->aru_index = avi->avi_index; 1688 } 1689 1690 void 1691 appl_report(struct snmp_message *statereference, int32_t requestid, 1692 struct ber_oid *oid) 1693 { 1694 struct appl_request_upstream *ureq; 1695 1696 if ((ureq = calloc(1, sizeof(*ureq))) == NULL) 1697 fatal("malloc"); 1698 ureq->aru_ctx = appl_context(NULL, 0); 1699 ureq->aru_statereference = statereference; 1700 ureq->aru_requesttype = SNMP_C_GETREQ; 1701 ureq->aru_responsetype = SNMP_C_REPORT; 1702 ureq->aru_requestid = requestid; 1703 ureq->aru_transactionid = 0; 1704 ureq->aru_nonrepeaters = 0; 1705 ureq->aru_maxrepetitions = 0; 1706 if ((ureq->aru_vblist = calloc(1, sizeof(*ureq->aru_vblist))) == NULL) 1707 fatal("malloc"); 1708 ureq->aru_varbindlen = 1; 1709 ureq->aru_error = APPL_ERROR_NOERROR; 1710 ureq->aru_index = 0; 1711 ureq->aru_locked = 0; 1712 ureq->aru_pduversion = SNMP_V3; 1713 1714 ureq->aru_vblist[0].avi_state = APPL_VBSTATE_NEW; 1715 ureq->aru_vblist[0].avi_varbind.av_oid = *oid; 1716 ureq->aru_vblist[0].avi_varbind.av_value = NULL; 1717 ureq->aru_vblist[0].avi_varbind.av_next = NULL; 1718 ureq->aru_vblist[0].avi_origid = *oid; 1719 ureq->aru_vblist[0].avi_index = 1; 1720 ureq->aru_vblist[0].avi_request_upstream = ureq; 1721 ureq->aru_vblist[0].avi_request_downstream = NULL; 1722 ureq->aru_vblist[0].avi_next = NULL; 1723 ureq->aru_vblist[0].avi_sub = NULL; 1724 1725 appl_request_upstream_resolve(ureq); 1726 } 1727 1728 struct ber_element * 1729 appl_exception(enum appl_exception type) 1730 { 1731 struct ber_element *value; 1732 1733 if ((value = ober_add_null(NULL)) == NULL) { 1734 log_warn("malloc"); 1735 return NULL; 1736 } 1737 ober_set_header(value, BER_CLASS_CONTEXT, type); 1738 1739 return value; 1740 } 1741 1742 void 1743 appl_pdu_log(struct appl_backend *backend, enum snmp_pdutype pdutype, 1744 int32_t requestid, uint16_t error, uint16_t index, 1745 struct appl_varbind *vblist) 1746 { 1747 struct appl_varbind *vb; 1748 char buf[1024], oidbuf[1024], *str; 1749 int next; 1750 1751 if (log_getverbose() < 2) 1752 return; 1753 1754 next = (pdutype == SNMP_C_GETNEXTREQ || pdutype == SNMP_C_GETBULKREQ); 1755 1756 buf[0] = '\0'; 1757 for (vb = vblist; vb != NULL; vb = vb->av_next) { 1758 strlcat(buf, "{", sizeof(buf)); 1759 strlcat(buf, smi_oid2string(&(vb->av_oid), oidbuf, 1760 sizeof(oidbuf), 0), sizeof(buf)); 1761 if (next) { 1762 if (vb->av_include) 1763 strlcat(buf, "(incl)", sizeof(buf)); 1764 if (vb->av_oid_end.bo_n > 0) { 1765 strlcat(buf, "-", sizeof(buf)); 1766 strlcat(buf, smi_oid2string(&(vb->av_oid_end), 1767 oidbuf, sizeof(oidbuf), 0), sizeof(buf)); 1768 } 1769 } 1770 strlcat(buf, ":", sizeof(buf)); 1771 if (vb->av_value != NULL) { 1772 str = smi_print_element(vb->av_value); 1773 strlcat(buf, str == NULL ? "???" : str, sizeof(buf)); 1774 free(str); 1775 } else 1776 strlcat(buf, "null", sizeof(buf)); 1777 strlcat(buf, "}", sizeof(buf)); 1778 } 1779 log_debug("%s%s%s{%"PRId32", %"PRIu16", %"PRIu16", {%s}}", 1780 backend != NULL ? backend->ab_name : "", 1781 backend != NULL ? ": " : "", 1782 snmpe_pdutype2string(pdutype), requestid, error, index, buf); 1783 } 1784 1785 void 1786 ober_oid_nextsibling(struct ber_oid *oid) 1787 { 1788 while (oid->bo_n > 0) { 1789 oid->bo_id[oid->bo_n - 1]++; 1790 /* Overflow check */ 1791 if (oid->bo_id[oid->bo_n - 1] != 0) 1792 return; 1793 oid->bo_n--; 1794 } 1795 } 1796 1797 int 1798 appl_region_cmp(struct appl_region *r1, struct appl_region *r2) 1799 { 1800 return ober_oid_cmp(&(r1->ar_oid), &(r2->ar_oid)); 1801 } 1802 1803 int 1804 appl_request_cmp(struct appl_request_downstream *r1, 1805 struct appl_request_downstream *r2) 1806 { 1807 return r1->ard_requestid < r2->ard_requestid ? -1 : 1808 r1->ard_requestid > r2->ard_requestid; 1809 } 1810 1811 RB_GENERATE_STATIC(appl_regions, appl_region, ar_entry, appl_region_cmp); 1812 RB_GENERATE_STATIC(appl_requests, appl_request_downstream, ard_entry, 1813 appl_request_cmp); 1814