1 /* 2 * Copyright (c) 2001-2003 3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4 * All rights reserved. 5 * 6 * Author: Harti Brandt <harti@freebsd.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $Begemot: bsnmp/lib/snmpagent.c,v 1.20 2005/10/04 11:21:33 brandt_h Exp $ 30 * 31 * SNMP Agent functions 32 */ 33 #include <sys/types.h> 34 #include <sys/queue.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <stddef.h> 38 #include <stdarg.h> 39 #ifdef HAVE_STDINT_H 40 #include <stdint.h> 41 #elif defined(HAVE_INTTYPES_H) 42 #include <inttypes.h> 43 #endif 44 #include <string.h> 45 46 #include "asn1.h" 47 #include "snmp.h" 48 #include "snmppriv.h" 49 #include "snmpagent.h" 50 51 static void snmp_debug_func(const char *fmt, ...); 52 53 void (*snmp_debug)(const char *fmt, ...) = snmp_debug_func; 54 55 struct snmp_node *tree; 56 u_int tree_size; 57 58 /* 59 * Structure to hold dependencies during SET processing 60 * The last two members of this structure must be the 61 * dependency visible by the user and the user data. 62 */ 63 struct depend { 64 TAILQ_ENTRY(depend) link; 65 size_t len; /* size of data part */ 66 snmp_depop_t func; 67 struct snmp_dependency dep; 68 #if defined(__GNUC__) && __GNUC__ < 3 69 u_char data[0]; 70 #else 71 u_char data[]; 72 #endif 73 }; 74 TAILQ_HEAD(depend_list, depend); 75 76 /* 77 * Set context 78 */ 79 struct context { 80 struct snmp_context ctx; 81 struct depend_list dlist; 82 const struct snmp_node *node[SNMP_MAX_BINDINGS]; 83 struct snmp_scratch scratch[SNMP_MAX_BINDINGS]; 84 struct depend *depend; 85 }; 86 87 #define TR(W) (snmp_trace & SNMP_TRACE_##W) 88 u_int snmp_trace = 0; 89 90 static char oidbuf[ASN_OIDSTRLEN]; 91 92 /* 93 * Allocate a context 94 */ 95 struct snmp_context * 96 snmp_init_context(void) 97 { 98 struct context *context; 99 100 if ((context = malloc(sizeof(*context))) == NULL) 101 return (NULL); 102 103 memset(context, 0, sizeof(*context)); 104 TAILQ_INIT(&context->dlist); 105 106 return (&context->ctx); 107 } 108 109 /* 110 * Find a variable for SET/GET and the first GETBULK pass. 111 * Return the node pointer. If the search fails, set the errp to 112 * the correct SNMPv2 GET exception code. 113 */ 114 static struct snmp_node * 115 find_node(const struct snmp_value *value, enum snmp_syntax *errp) 116 { 117 struct snmp_node *tp; 118 119 if (TR(FIND)) 120 snmp_debug("find: searching %s", 121 asn_oid2str_r(&value->var, oidbuf)); 122 123 /* 124 * If we have an exact match (the entry in the table is a 125 * sub-oid from the variable) we have found what we are for. 126 * If the table oid is higher than the variable, there is no match. 127 */ 128 for (tp = tree; tp < tree + tree_size; tp++) { 129 if (asn_is_suboid(&tp->oid, &value->var)) 130 goto found; 131 if (asn_compare_oid(&tp->oid, &value->var) >= 0) 132 break; 133 } 134 135 if (TR(FIND)) 136 snmp_debug("find: no match"); 137 *errp = SNMP_SYNTAX_NOSUCHOBJECT; 138 return (NULL); 139 140 found: 141 /* leafs must have a 0 instance identifier */ 142 if (tp->type == SNMP_NODE_LEAF && 143 (value->var.len != tp->oid.len + 1 || 144 value->var.subs[tp->oid.len] != 0)) { 145 if (TR(FIND)) 146 snmp_debug("find: bad leaf index"); 147 *errp = SNMP_SYNTAX_NOSUCHINSTANCE; 148 return (NULL); 149 } 150 if (TR(FIND)) 151 snmp_debug("find: found %s", 152 asn_oid2str_r(&value->var, oidbuf)); 153 return (tp); 154 } 155 156 static struct snmp_node * 157 find_subnode(const struct snmp_value *value) 158 { 159 struct snmp_node *tp; 160 161 for (tp = tree; tp < tree + tree_size; tp++) { 162 if (asn_is_suboid(&value->var, &tp->oid)) 163 return (tp); 164 } 165 return (NULL); 166 } 167 168 static void 169 snmp_pdu_create_response(const struct snmp_pdu *pdu, struct snmp_pdu *resp) 170 { 171 memset(resp, 0, sizeof(*resp)); 172 strcpy(resp->community, pdu->community); 173 resp->version = pdu->version; 174 if (pdu->flags & SNMP_MSG_AUTODISCOVER) 175 resp->type = SNMP_PDU_REPORT; /* RFC 3414.4 */ 176 else 177 resp->type = SNMP_PDU_RESPONSE; 178 resp->request_id = pdu->request_id; 179 resp->version = pdu->version; 180 181 if (resp->version != SNMP_V3) 182 return; 183 184 memcpy(&resp->engine, &pdu->engine, sizeof(pdu->engine)); 185 memcpy(&resp->user, &pdu->user, sizeof(pdu->user)); 186 snmp_pdu_init_secparams(resp); 187 resp->identifier = pdu->identifier; 188 resp->security_model = pdu->security_model; 189 resp->context_engine_len = pdu->context_engine_len; 190 memcpy(resp->context_engine, pdu->context_engine, 191 resp->context_engine_len); 192 strlcpy(resp->context_name, pdu->context_name, 193 sizeof(resp->context_name)); 194 } 195 196 /* 197 * Execute a GET operation. The tree is rooted at the global 'root'. 198 * Build the response PDU on the fly. If the return code is SNMP_RET_ERR 199 * the pdu error status and index will be set. 200 */ 201 enum snmp_ret 202 snmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b, 203 struct snmp_pdu *resp, void *data) 204 { 205 int ret; 206 u_int i; 207 struct snmp_node *tp; 208 enum snmp_syntax except; 209 struct context context; 210 enum asn_err err; 211 212 memset(&context, 0, sizeof(context)); 213 context.ctx.data = data; 214 215 snmp_pdu_create_response(pdu, resp); 216 217 if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK) 218 /* cannot even encode header - very bad */ 219 return (SNMP_RET_IGN); 220 221 for (i = 0; i < pdu->nbindings; i++) { 222 resp->bindings[i].var = pdu->bindings[i].var; 223 if ((tp = find_node(&pdu->bindings[i], &except)) == NULL) { 224 if (pdu->version == SNMP_V1) { 225 if (TR(GET)) 226 snmp_debug("get: nosuchname"); 227 pdu->error_status = SNMP_ERR_NOSUCHNAME; 228 pdu->error_index = i + 1; 229 snmp_pdu_free(resp); 230 return (SNMP_RET_ERR); 231 } 232 if (TR(GET)) 233 snmp_debug("get: exception %u", except); 234 resp->bindings[i].syntax = except; 235 236 } else { 237 /* call the action to fetch the value. */ 238 resp->bindings[i].syntax = tp->syntax; 239 ret = (*tp->op)(&context.ctx, &resp->bindings[i], 240 tp->oid.len, tp->index, SNMP_OP_GET); 241 if (TR(GET)) 242 snmp_debug("get: action returns %d", ret); 243 244 if (ret == SNMP_ERR_NOSUCHNAME) { 245 if (pdu->version == SNMP_V1) { 246 pdu->error_status = SNMP_ERR_NOSUCHNAME; 247 pdu->error_index = i + 1; 248 snmp_pdu_free(resp); 249 return (SNMP_RET_ERR); 250 } 251 if (TR(GET)) 252 snmp_debug("get: exception noSuchInstance"); 253 resp->bindings[i].syntax = SNMP_SYNTAX_NOSUCHINSTANCE; 254 255 } else if (ret != SNMP_ERR_NOERROR) { 256 pdu->error_status = SNMP_ERR_GENERR; 257 pdu->error_index = i + 1; 258 snmp_pdu_free(resp); 259 return (SNMP_RET_ERR); 260 } 261 } 262 resp->nbindings++; 263 264 err = snmp_binding_encode(resp_b, &resp->bindings[i]); 265 266 if (err == ASN_ERR_EOBUF) { 267 pdu->error_status = SNMP_ERR_TOOBIG; 268 pdu->error_index = 0; 269 snmp_pdu_free(resp); 270 return (SNMP_RET_ERR); 271 } 272 if (err != ASN_ERR_OK) { 273 if (TR(GET)) 274 snmp_debug("get: binding encoding: %u", err); 275 pdu->error_status = SNMP_ERR_GENERR; 276 pdu->error_index = i + 1; 277 snmp_pdu_free(resp); 278 return (SNMP_RET_ERR); 279 } 280 } 281 282 if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { 283 snmp_debug("get: failed to encode PDU"); 284 return (SNMP_RET_ERR); 285 } 286 287 return (SNMP_RET_OK); 288 } 289 290 static struct snmp_node * 291 next_node(const struct snmp_value *value, int *pnext) 292 { 293 struct snmp_node *tp; 294 295 if (TR(FIND)) 296 snmp_debug("next: searching %s", 297 asn_oid2str_r(&value->var, oidbuf)); 298 299 *pnext = 0; 300 for (tp = tree; tp < tree + tree_size; tp++) { 301 if (asn_is_suboid(&tp->oid, &value->var)) { 302 /* the tree OID is a sub-oid of the requested OID. */ 303 if (tp->type == SNMP_NODE_LEAF) { 304 if (tp->oid.len == value->var.len) { 305 /* request for scalar type */ 306 if (TR(FIND)) 307 snmp_debug("next: found scalar %s", 308 asn_oid2str_r(&tp->oid, oidbuf)); 309 return (tp); 310 } 311 /* try next */ 312 } else { 313 if (TR(FIND)) 314 snmp_debug("next: found column %s", 315 asn_oid2str_r(&tp->oid, oidbuf)); 316 return (tp); 317 } 318 } else if (asn_is_suboid(&value->var, &tp->oid) || 319 asn_compare_oid(&tp->oid, &value->var) >= 0) { 320 if (TR(FIND)) 321 snmp_debug("next: found %s", 322 asn_oid2str_r(&tp->oid, oidbuf)); 323 *pnext = 1; 324 return (tp); 325 } 326 } 327 328 if (TR(FIND)) 329 snmp_debug("next: failed"); 330 331 return (NULL); 332 } 333 334 static enum snmp_ret 335 do_getnext(struct context *context, const struct snmp_value *inb, 336 struct snmp_value *outb, struct snmp_pdu *pdu) 337 { 338 const struct snmp_node *tp; 339 int ret, next; 340 341 if ((tp = next_node(inb, &next)) == NULL) 342 goto eofMib; 343 344 /* retain old variable if we are doing a GETNEXT on an exact 345 * matched leaf only */ 346 if (tp->type == SNMP_NODE_LEAF || next) 347 outb->var = tp->oid; 348 else 349 outb->var = inb->var; 350 351 for (;;) { 352 outb->syntax = tp->syntax; 353 if (tp->type == SNMP_NODE_LEAF) { 354 /* make a GET operation */ 355 outb->var.subs[outb->var.len++] = 0; 356 ret = (*tp->op)(&context->ctx, outb, tp->oid.len, 357 tp->index, SNMP_OP_GET); 358 } else { 359 /* make a GETNEXT */ 360 ret = (*tp->op)(&context->ctx, outb, tp->oid.len, 361 tp->index, SNMP_OP_GETNEXT); 362 } 363 if (ret != SNMP_ERR_NOSUCHNAME) { 364 /* got something */ 365 if (ret != SNMP_ERR_NOERROR && TR(GETNEXT)) 366 snmp_debug("getnext: %s returns %u", 367 asn_oid2str(&outb->var), ret); 368 break; 369 } 370 371 /* object has no data - try next */ 372 if (++tp == tree + tree_size) 373 break; 374 375 if (TR(GETNEXT)) 376 snmp_debug("getnext: no data - avancing to %s", 377 asn_oid2str(&tp->oid)); 378 379 outb->var = tp->oid; 380 } 381 382 if (ret == SNMP_ERR_NOSUCHNAME) { 383 eofMib: 384 outb->var = inb->var; 385 if (pdu->version == SNMP_V1) { 386 pdu->error_status = SNMP_ERR_NOSUCHNAME; 387 return (SNMP_RET_ERR); 388 } 389 outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW; 390 391 } else if (ret != SNMP_ERR_NOERROR) { 392 pdu->error_status = SNMP_ERR_GENERR; 393 return (SNMP_RET_ERR); 394 } 395 return (SNMP_RET_OK); 396 } 397 398 399 /* 400 * Execute a GETNEXT operation. The tree is rooted at the global 'root'. 401 * Build the response PDU on the fly. The return is: 402 */ 403 enum snmp_ret 404 snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b, 405 struct snmp_pdu *resp, void *data) 406 { 407 struct context context; 408 u_int i; 409 enum asn_err err; 410 enum snmp_ret result; 411 412 memset(&context, 0, sizeof(context)); 413 context.ctx.data = data; 414 415 snmp_pdu_create_response(pdu, resp); 416 417 if (snmp_pdu_encode_header(resp_b, resp)) 418 return (SNMP_RET_IGN); 419 420 for (i = 0; i < pdu->nbindings; i++) { 421 result = do_getnext(&context, &pdu->bindings[i], 422 &resp->bindings[i], pdu); 423 424 if (result != SNMP_RET_OK) { 425 pdu->error_index = i + 1; 426 snmp_pdu_free(resp); 427 return (result); 428 } 429 430 resp->nbindings++; 431 432 err = snmp_binding_encode(resp_b, &resp->bindings[i]); 433 434 if (err == ASN_ERR_EOBUF) { 435 pdu->error_status = SNMP_ERR_TOOBIG; 436 pdu->error_index = 0; 437 snmp_pdu_free(resp); 438 return (SNMP_RET_ERR); 439 } 440 if (err != ASN_ERR_OK) { 441 if (TR(GET)) 442 snmp_debug("getnext: binding encoding: %u", err); 443 pdu->error_status = SNMP_ERR_GENERR; 444 pdu->error_index = i + 1; 445 snmp_pdu_free(resp); 446 return (SNMP_RET_ERR); 447 } 448 } 449 450 if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { 451 snmp_debug("getnext: failed to encode PDU"); 452 return (SNMP_RET_ERR); 453 } 454 455 return (SNMP_RET_OK); 456 } 457 458 enum snmp_ret 459 snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b, 460 struct snmp_pdu *resp, void *data) 461 { 462 struct context context; 463 u_int i; 464 int cnt; 465 u_int non_rep; 466 int eomib; 467 enum snmp_ret result; 468 enum asn_err err; 469 470 memset(&context, 0, sizeof(context)); 471 context.ctx.data = data; 472 473 snmp_pdu_create_response(pdu, resp); 474 475 if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK) 476 /* cannot even encode header - very bad */ 477 return (SNMP_RET_IGN); 478 479 if ((non_rep = pdu->error_status) > pdu->nbindings) 480 non_rep = pdu->nbindings; 481 482 /* non-repeaters */ 483 for (i = 0; i < non_rep; i++) { 484 result = do_getnext(&context, &pdu->bindings[i], 485 &resp->bindings[resp->nbindings], pdu); 486 487 if (result != SNMP_RET_OK) { 488 pdu->error_index = i + 1; 489 snmp_pdu_free(resp); 490 return (result); 491 } 492 493 err = snmp_binding_encode(resp_b, 494 &resp->bindings[resp->nbindings++]); 495 496 if (err == ASN_ERR_EOBUF) 497 goto done; 498 499 if (err != ASN_ERR_OK) { 500 if (TR(GET)) 501 snmp_debug("getnext: binding encoding: %u", err); 502 pdu->error_status = SNMP_ERR_GENERR; 503 pdu->error_index = i + 1; 504 snmp_pdu_free(resp); 505 return (SNMP_RET_ERR); 506 } 507 } 508 509 if (non_rep == pdu->nbindings) 510 goto done; 511 512 /* repeates */ 513 for (cnt = 0; cnt < pdu->error_index; cnt++) { 514 eomib = 1; 515 for (i = non_rep; i < pdu->nbindings; i++) { 516 517 if (resp->nbindings == SNMP_MAX_BINDINGS) 518 /* PDU is full */ 519 goto done; 520 521 if (cnt == 0) 522 result = do_getnext(&context, &pdu->bindings[i], 523 &resp->bindings[resp->nbindings], pdu); 524 else 525 result = do_getnext(&context, 526 &resp->bindings[resp->nbindings - 527 (pdu->nbindings - non_rep)], 528 &resp->bindings[resp->nbindings], pdu); 529 530 if (result != SNMP_RET_OK) { 531 pdu->error_index = i + 1; 532 snmp_pdu_free(resp); 533 return (result); 534 } 535 if (resp->bindings[resp->nbindings].syntax != 536 SNMP_SYNTAX_ENDOFMIBVIEW) 537 eomib = 0; 538 539 err = snmp_binding_encode(resp_b, 540 &resp->bindings[resp->nbindings++]); 541 542 if (err == ASN_ERR_EOBUF) 543 goto done; 544 545 if (err != ASN_ERR_OK) { 546 if (TR(GET)) 547 snmp_debug("getnext: binding encoding: %u", err); 548 pdu->error_status = SNMP_ERR_GENERR; 549 pdu->error_index = i + 1; 550 snmp_pdu_free(resp); 551 return (SNMP_RET_ERR); 552 } 553 } 554 if (eomib) 555 break; 556 } 557 558 done: 559 if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { 560 snmp_debug("getnext: failed to encode PDU"); 561 return (SNMP_RET_ERR); 562 } 563 564 return (SNMP_RET_OK); 565 } 566 567 /* 568 * Rollback a SET operation. Failed index is 'i'. 569 */ 570 static void 571 rollback(struct context *context, struct snmp_pdu *pdu, u_int i) 572 { 573 struct snmp_value *b; 574 const struct snmp_node *np; 575 int ret; 576 577 while (i-- > 0) { 578 b = &pdu->bindings[i]; 579 np = context->node[i]; 580 581 context->ctx.scratch = &context->scratch[i]; 582 583 ret = (*np->op)(&context->ctx, b, np->oid.len, np->index, 584 SNMP_OP_ROLLBACK); 585 586 if (ret != SNMP_ERR_NOERROR) { 587 snmp_error("set: rollback failed (%d) on variable %s " 588 "index %u", ret, asn_oid2str(&b->var), i); 589 if (pdu->version != SNMP_V1) { 590 pdu->error_status = SNMP_ERR_UNDO_FAILED; 591 pdu->error_index = 0; 592 } 593 } 594 } 595 } 596 597 /* 598 * Commit dependencies. 599 */ 600 int 601 snmp_dep_commit(struct snmp_context *ctx) 602 { 603 struct context *context = (struct context *)ctx; 604 int ret; 605 606 TAILQ_FOREACH(context->depend, &context->dlist, link) { 607 ctx->dep = &context->depend->dep; 608 609 if (TR(SET)) 610 snmp_debug("set: dependency commit %s", 611 asn_oid2str(&ctx->dep->obj)); 612 613 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT); 614 615 if (ret != SNMP_ERR_NOERROR) { 616 if (TR(SET)) 617 snmp_debug("set: dependency failed %d", ret); 618 return (ret); 619 } 620 } 621 return (SNMP_ERR_NOERROR); 622 } 623 624 /* 625 * Rollback dependencies 626 */ 627 int 628 snmp_dep_rollback(struct snmp_context *ctx) 629 { 630 struct context *context = (struct context *)ctx; 631 int ret, ret1; 632 char objbuf[ASN_OIDSTRLEN]; 633 char idxbuf[ASN_OIDSTRLEN]; 634 635 ret1 = SNMP_ERR_NOERROR; 636 while ((context->depend = 637 TAILQ_PREV(context->depend, depend_list, link)) != NULL) { 638 ctx->dep = &context->depend->dep; 639 640 if (TR(SET)) 641 snmp_debug("set: dependency rollback %s", 642 asn_oid2str(&ctx->dep->obj)); 643 644 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK); 645 646 if (ret != SNMP_ERR_NOERROR) { 647 snmp_debug("set: dep rollback returns %u: %s %s", ret, 648 asn_oid2str_r(&ctx->dep->obj, objbuf), 649 asn_oid2str_r(&ctx->dep->idx, idxbuf)); 650 if (ret1 == SNMP_ERR_NOERROR) 651 ret1 = ret; 652 } 653 } 654 return (ret1); 655 } 656 657 void 658 snmp_dep_finish(struct snmp_context *ctx) 659 { 660 struct context *context = (struct context *)ctx; 661 struct depend *d; 662 663 while ((d = TAILQ_FIRST(&context->dlist)) != NULL) { 664 ctx->dep = &d->dep; 665 (void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH); 666 TAILQ_REMOVE(&context->dlist, d, link); 667 free(d); 668 } 669 } 670 671 /* 672 * Do a SET operation. 673 */ 674 enum snmp_ret 675 snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b, 676 struct snmp_pdu *resp, void *data) 677 { 678 int ret; 679 u_int i; 680 enum asn_err asnerr; 681 struct context context; 682 const struct snmp_node *np; 683 struct snmp_value *b; 684 enum snmp_syntax except; 685 686 memset(&context, 0, sizeof(context)); 687 TAILQ_INIT(&context.dlist); 688 context.ctx.data = data; 689 690 snmp_pdu_create_response(pdu, resp); 691 692 if (snmp_pdu_encode_header(resp_b, resp)) 693 return (SNMP_RET_IGN); 694 695 /* 696 * 1. Find all nodes, check that they are writeable and 697 * that the syntax is ok, copy over the binding to the response. 698 */ 699 for (i = 0; i < pdu->nbindings; i++) { 700 b = &pdu->bindings[i]; 701 702 if ((np = context.node[i] = find_node(b, &except)) == NULL) { 703 /* not found altogether or LEAF with wrong index */ 704 if (TR(SET)) 705 snmp_debug("set: node not found %s", 706 asn_oid2str_r(&b->var, oidbuf)); 707 if (pdu->version == SNMP_V1) { 708 pdu->error_index = i + 1; 709 pdu->error_status = SNMP_ERR_NOSUCHNAME; 710 } else if ((np = find_subnode(b)) != NULL) { 711 /* 2. intermediate object */ 712 pdu->error_index = i + 1; 713 pdu->error_status = SNMP_ERR_NOT_WRITEABLE; 714 } else if (except == SNMP_SYNTAX_NOSUCHOBJECT) { 715 pdu->error_index = i + 1; 716 pdu->error_status = SNMP_ERR_NO_ACCESS; 717 } else { 718 pdu->error_index = i + 1; 719 pdu->error_status = SNMP_ERR_NO_CREATION; 720 } 721 snmp_pdu_free(resp); 722 return (SNMP_RET_ERR); 723 } 724 /* 725 * 2. write/createable? 726 * Can check this for leafs only, because in v2 we have 727 * to differentiate between NOT_WRITEABLE and NO_CREATION 728 * and only the action routine for COLUMNS knows, whether 729 * a column exists. 730 */ 731 if (np->type == SNMP_NODE_LEAF && 732 !(np->flags & SNMP_NODE_CANSET)) { 733 if (pdu->version == SNMP_V1) { 734 pdu->error_index = i + 1; 735 pdu->error_status = SNMP_ERR_NOSUCHNAME; 736 } else { 737 pdu->error_index = i + 1; 738 pdu->error_status = SNMP_ERR_NOT_WRITEABLE; 739 } 740 snmp_pdu_free(resp); 741 return (SNMP_RET_ERR); 742 } 743 /* 744 * 3. Ensure the right syntax 745 */ 746 if (np->syntax != b->syntax) { 747 if (pdu->version == SNMP_V1) { 748 pdu->error_index = i + 1; 749 pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */ 750 } else { 751 pdu->error_index = i + 1; 752 pdu->error_status = SNMP_ERR_WRONG_TYPE; 753 } 754 snmp_pdu_free(resp); 755 return (SNMP_RET_ERR); 756 } 757 /* 758 * 4. Copy binding 759 */ 760 if (snmp_value_copy(&resp->bindings[i], b)) { 761 pdu->error_index = i + 1; 762 pdu->error_status = SNMP_ERR_GENERR; 763 snmp_pdu_free(resp); 764 return (SNMP_RET_ERR); 765 } 766 asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]); 767 if (asnerr == ASN_ERR_EOBUF) { 768 pdu->error_index = i + 1; 769 pdu->error_status = SNMP_ERR_TOOBIG; 770 snmp_pdu_free(resp); 771 return (SNMP_RET_ERR); 772 } else if (asnerr != ASN_ERR_OK) { 773 pdu->error_index = i + 1; 774 pdu->error_status = SNMP_ERR_GENERR; 775 snmp_pdu_free(resp); 776 return (SNMP_RET_ERR); 777 } 778 resp->nbindings++; 779 } 780 781 context.ctx.code = SNMP_RET_OK; 782 783 /* 784 * 2. Call the SET method for each node. If a SET fails, rollback 785 * everything. Map error codes depending on the version. 786 */ 787 for (i = 0; i < pdu->nbindings; i++) { 788 b = &pdu->bindings[i]; 789 np = context.node[i]; 790 791 context.ctx.var_index = i + 1; 792 context.ctx.scratch = &context.scratch[i]; 793 794 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index, 795 SNMP_OP_SET); 796 797 if (TR(SET)) 798 snmp_debug("set: action %s returns %d", np->name, ret); 799 800 if (pdu->version == SNMP_V1) { 801 switch (ret) { 802 case SNMP_ERR_NO_ACCESS: 803 ret = SNMP_ERR_NOSUCHNAME; 804 break; 805 case SNMP_ERR_WRONG_TYPE: 806 /* should no happen */ 807 ret = SNMP_ERR_BADVALUE; 808 break; 809 case SNMP_ERR_WRONG_LENGTH: 810 ret = SNMP_ERR_BADVALUE; 811 break; 812 case SNMP_ERR_WRONG_ENCODING: 813 /* should not happen */ 814 ret = SNMP_ERR_BADVALUE; 815 break; 816 case SNMP_ERR_WRONG_VALUE: 817 ret = SNMP_ERR_BADVALUE; 818 break; 819 case SNMP_ERR_NO_CREATION: 820 ret = SNMP_ERR_NOSUCHNAME; 821 break; 822 case SNMP_ERR_INCONS_VALUE: 823 ret = SNMP_ERR_BADVALUE; 824 break; 825 case SNMP_ERR_RES_UNAVAIL: 826 ret = SNMP_ERR_GENERR; 827 break; 828 case SNMP_ERR_COMMIT_FAILED: 829 ret = SNMP_ERR_GENERR; 830 break; 831 case SNMP_ERR_UNDO_FAILED: 832 ret = SNMP_ERR_GENERR; 833 break; 834 case SNMP_ERR_AUTH_ERR: 835 /* should not happen */ 836 ret = SNMP_ERR_GENERR; 837 break; 838 case SNMP_ERR_NOT_WRITEABLE: 839 ret = SNMP_ERR_NOSUCHNAME; 840 break; 841 case SNMP_ERR_INCONS_NAME: 842 ret = SNMP_ERR_BADVALUE; 843 break; 844 } 845 } 846 if (ret != SNMP_ERR_NOERROR) { 847 pdu->error_index = i + 1; 848 pdu->error_status = ret; 849 850 rollback(&context, pdu, i); 851 snmp_pdu_free(resp); 852 853 context.ctx.code = SNMP_RET_ERR; 854 855 goto errout; 856 } 857 } 858 859 /* 860 * 3. Call dependencies 861 */ 862 if (TR(SET)) 863 snmp_debug("set: set operations ok"); 864 865 if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) { 866 pdu->error_status = ret; 867 pdu->error_index = context.ctx.var_index; 868 869 if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) { 870 if (pdu->version != SNMP_V1) { 871 pdu->error_status = SNMP_ERR_UNDO_FAILED; 872 pdu->error_index = 0; 873 } 874 } 875 rollback(&context, pdu, i); 876 snmp_pdu_free(resp); 877 878 context.ctx.code = SNMP_RET_ERR; 879 880 goto errout; 881 } 882 883 /* 884 * 4. Commit and copy values from the original packet to the response. 885 * This is not the commit operation from RFC 1905 but rather an 886 * 'FREE RESOURCES' operation. It shouldn't fail. 887 */ 888 if (TR(SET)) 889 snmp_debug("set: commiting"); 890 891 for (i = 0; i < pdu->nbindings; i++) { 892 b = &resp->bindings[i]; 893 np = context.node[i]; 894 895 context.ctx.var_index = i + 1; 896 context.ctx.scratch = &context.scratch[i]; 897 898 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index, 899 SNMP_OP_COMMIT); 900 901 if (ret != SNMP_ERR_NOERROR) 902 snmp_error("set: commit failed (%d) on" 903 " variable %s index %u", ret, 904 asn_oid2str_r(&b->var, oidbuf), i); 905 } 906 907 if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { 908 snmp_error("set: fix_encoding failed"); 909 snmp_pdu_free(resp); 910 context.ctx.code = SNMP_RET_IGN; 911 } 912 913 /* 914 * Done 915 */ 916 errout: 917 snmp_dep_finish(&context.ctx); 918 919 if (TR(SET)) 920 snmp_debug("set: returning %d", context.ctx.code); 921 922 return (context.ctx.code); 923 } 924 /* 925 * Lookup a dependency. If it doesn't exist, create one 926 */ 927 struct snmp_dependency * 928 snmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj, 929 const struct asn_oid *idx, size_t len, snmp_depop_t func) 930 { 931 struct context *context; 932 struct depend *d; 933 934 context = (struct context *)(void *) 935 ((char *)ctx - offsetof(struct context, ctx)); 936 if (TR(DEPEND)) { 937 snmp_debug("depend: looking for %s", asn_oid2str(obj)); 938 if (idx) 939 snmp_debug("depend: index is %s", asn_oid2str(idx)); 940 } 941 TAILQ_FOREACH(d, &context->dlist, link) 942 if (asn_compare_oid(obj, &d->dep.obj) == 0 && 943 ((idx == NULL && d->dep.idx.len == 0) || 944 (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) { 945 if(TR(DEPEND)) 946 snmp_debug("depend: found"); 947 return (&d->dep); 948 } 949 950 if(TR(DEPEND)) 951 snmp_debug("depend: creating"); 952 953 if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL) 954 return (NULL); 955 memset(&d->dep, 0, len); 956 957 d->dep.obj = *obj; 958 if (idx == NULL) 959 d->dep.idx.len = 0; 960 else 961 d->dep.idx = *idx; 962 d->len = len; 963 d->func = func; 964 965 TAILQ_INSERT_TAIL(&context->dlist, d, link); 966 967 return (&d->dep); 968 } 969 970 /* 971 * Make an error response from a PDU. We do this without decoding the 972 * variable bindings. This means we can sent the junk back to a caller 973 * that has sent us junk in the first place. 974 */ 975 enum snmp_ret 976 snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b, 977 struct asn_buf *resp_b) 978 { 979 u_char type; 980 asn_len_t len; 981 struct snmp_pdu resp; 982 enum asn_err err; 983 enum snmp_code code; 984 985 snmp_pdu_create_response(pdu, &resp); 986 987 if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK) 988 return (SNMP_RET_IGN); 989 990 if (pdu->version == SNMP_V3) { 991 if (resp.user.priv_proto != SNMP_PRIV_NOPRIV && 992 (asn_get_header(pdu_b, &type, &resp.scoped_len) != ASN_ERR_OK 993 || type != ASN_TYPE_OCTETSTRING)) { 994 snmp_error("cannot decode encrypted pdu"); 995 return (SNMP_RET_IGN); 996 } 997 998 if (asn_get_sequence(pdu_b, &len) != ASN_ERR_OK) { 999 snmp_error("cannot decode scoped pdu header"); 1000 return (SNMP_RET_IGN); 1001 } 1002 1003 len = SNMP_ENGINE_ID_SIZ; 1004 if (asn_get_octetstring(pdu_b, (u_char *)resp.context_engine, 1005 &len) != ASN_ERR_OK) { 1006 snmp_error("cannot decode msg context engine"); 1007 return (SNMP_RET_IGN); 1008 } 1009 resp.context_engine_len = len; 1010 len = SNMP_CONTEXT_NAME_SIZ; 1011 if (asn_get_octetstring(pdu_b, (u_char *)resp.context_name, 1012 &len) != ASN_ERR_OK) { 1013 snmp_error("cannot decode msg context name"); 1014 return (SNMP_RET_IGN); 1015 } 1016 resp.context_name[len] = '\0'; 1017 } 1018 1019 1020 if (asn_get_header(pdu_b, &type, &len) != ASN_ERR_OK) { 1021 snmp_error("cannot get pdu header"); 1022 return (SNMP_RET_IGN); 1023 } 1024 1025 if ((type & ~ASN_TYPE_MASK) != 1026 (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT)) { 1027 snmp_error("bad pdu header tag"); 1028 return (SNMP_RET_IGN); 1029 } 1030 1031 err = snmp_parse_pdus_hdr(pdu_b, &resp, &len); 1032 if (ASN_ERR_STOPPED(err)) 1033 return (SNMP_RET_IGN); 1034 if (pdu_b->asn_len < len) 1035 return (SNMP_RET_IGN); 1036 pdu_b->asn_len = len; 1037 1038 /* now we have the bindings left - construct new message */ 1039 resp.error_status = pdu->error_status; 1040 resp.error_index = pdu->error_index; 1041 resp.type = SNMP_PDU_RESPONSE; 1042 1043 code = snmp_pdu_encode_header(resp_b, &resp); 1044 if (code != SNMP_CODE_OK) 1045 return (SNMP_RET_IGN); 1046 1047 if (pdu_b->asn_len > resp_b->asn_len) 1048 /* too short */ 1049 return (SNMP_RET_IGN); 1050 (void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len); 1051 resp_b->asn_len -= pdu_b->asn_len; 1052 resp_b->asn_ptr += pdu_b->asn_len; 1053 1054 code = snmp_fix_encoding(resp_b, &resp); 1055 if (code != SNMP_CODE_OK) 1056 return (SNMP_RET_IGN); 1057 1058 return (SNMP_RET_OK); 1059 } 1060 1061 static void 1062 snmp_debug_func(const char *fmt, ...) 1063 { 1064 va_list ap; 1065 1066 va_start(ap, fmt); 1067 vfprintf(stderr, fmt, ap); 1068 va_end(ap); 1069 fprintf(stderr, "\n"); 1070 } 1071