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 resp->type = SNMP_PDU_RESPONSE; 175 resp->request_id = pdu->request_id; 176 resp->version = pdu->version; 177 178 if (resp->version != SNMP_V3) 179 return; 180 181 memcpy(&resp->engine, &pdu->engine, sizeof(pdu->engine)); 182 memcpy(&resp->user, &pdu->user, sizeof(pdu->user)); 183 snmp_pdu_init_secparams(resp); 184 resp->identifier = pdu->identifier; 185 resp->security_model = pdu->security_model; 186 resp->context_engine_len = pdu->context_engine_len; 187 memcpy(resp->context_engine, pdu->context_engine, 188 resp->context_engine_len); 189 strlcpy(resp->context_name, pdu->context_name, 190 sizeof(resp->context_name)); 191 } 192 193 /* 194 * Execute a GET operation. The tree is rooted at the global 'root'. 195 * Build the response PDU on the fly. If the return code is SNMP_RET_ERR 196 * the pdu error status and index will be set. 197 */ 198 enum snmp_ret 199 snmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b, 200 struct snmp_pdu *resp, void *data) 201 { 202 int ret; 203 u_int i; 204 struct snmp_node *tp; 205 enum snmp_syntax except; 206 struct context context; 207 enum asn_err err; 208 209 memset(&context, 0, sizeof(context)); 210 context.ctx.data = data; 211 212 snmp_pdu_create_response(pdu, resp); 213 214 if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK) 215 /* cannot even encode header - very bad */ 216 return (SNMP_RET_IGN); 217 218 for (i = 0; i < pdu->nbindings; i++) { 219 resp->bindings[i].var = pdu->bindings[i].var; 220 if ((tp = find_node(&pdu->bindings[i], &except)) == NULL) { 221 if (pdu->version == SNMP_V1) { 222 if (TR(GET)) 223 snmp_debug("get: nosuchname"); 224 pdu->error_status = SNMP_ERR_NOSUCHNAME; 225 pdu->error_index = i + 1; 226 snmp_pdu_free(resp); 227 return (SNMP_RET_ERR); 228 } 229 if (TR(GET)) 230 snmp_debug("get: exception %u", except); 231 resp->bindings[i].syntax = except; 232 233 } else { 234 /* call the action to fetch the value. */ 235 resp->bindings[i].syntax = tp->syntax; 236 ret = (*tp->op)(&context.ctx, &resp->bindings[i], 237 tp->oid.len, tp->index, SNMP_OP_GET); 238 if (TR(GET)) 239 snmp_debug("get: action returns %d", ret); 240 241 if (ret == SNMP_ERR_NOSUCHNAME) { 242 if (pdu->version == SNMP_V1) { 243 pdu->error_status = SNMP_ERR_NOSUCHNAME; 244 pdu->error_index = i + 1; 245 snmp_pdu_free(resp); 246 return (SNMP_RET_ERR); 247 } 248 if (TR(GET)) 249 snmp_debug("get: exception noSuchInstance"); 250 resp->bindings[i].syntax = SNMP_SYNTAX_NOSUCHINSTANCE; 251 252 } else if (ret != SNMP_ERR_NOERROR) { 253 pdu->error_status = SNMP_ERR_GENERR; 254 pdu->error_index = i + 1; 255 snmp_pdu_free(resp); 256 return (SNMP_RET_ERR); 257 } 258 } 259 resp->nbindings++; 260 261 err = snmp_binding_encode(resp_b, &resp->bindings[i]); 262 263 if (err == ASN_ERR_EOBUF) { 264 pdu->error_status = SNMP_ERR_TOOBIG; 265 pdu->error_index = 0; 266 snmp_pdu_free(resp); 267 return (SNMP_RET_ERR); 268 } 269 if (err != ASN_ERR_OK) { 270 if (TR(GET)) 271 snmp_debug("get: binding encoding: %u", err); 272 pdu->error_status = SNMP_ERR_GENERR; 273 pdu->error_index = i + 1; 274 snmp_pdu_free(resp); 275 return (SNMP_RET_ERR); 276 } 277 } 278 279 if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { 280 snmp_debug("get: failed to encode PDU"); 281 return (SNMP_RET_ERR); 282 } 283 284 return (SNMP_RET_OK); 285 } 286 287 static struct snmp_node * 288 next_node(const struct snmp_value *value, int *pnext) 289 { 290 struct snmp_node *tp; 291 292 if (TR(FIND)) 293 snmp_debug("next: searching %s", 294 asn_oid2str_r(&value->var, oidbuf)); 295 296 *pnext = 0; 297 for (tp = tree; tp < tree + tree_size; tp++) { 298 if (asn_is_suboid(&tp->oid, &value->var)) { 299 /* the tree OID is a sub-oid of the requested OID. */ 300 if (tp->type == SNMP_NODE_LEAF) { 301 if (tp->oid.len == value->var.len) { 302 /* request for scalar type */ 303 if (TR(FIND)) 304 snmp_debug("next: found scalar %s", 305 asn_oid2str_r(&tp->oid, oidbuf)); 306 return (tp); 307 } 308 /* try next */ 309 } else { 310 if (TR(FIND)) 311 snmp_debug("next: found column %s", 312 asn_oid2str_r(&tp->oid, oidbuf)); 313 return (tp); 314 } 315 } else if (asn_is_suboid(&value->var, &tp->oid) || 316 asn_compare_oid(&tp->oid, &value->var) >= 0) { 317 if (TR(FIND)) 318 snmp_debug("next: found %s", 319 asn_oid2str_r(&tp->oid, oidbuf)); 320 *pnext = 1; 321 return (tp); 322 } 323 } 324 325 if (TR(FIND)) 326 snmp_debug("next: failed"); 327 328 return (NULL); 329 } 330 331 static enum snmp_ret 332 do_getnext(struct context *context, const struct snmp_value *inb, 333 struct snmp_value *outb, struct snmp_pdu *pdu) 334 { 335 const struct snmp_node *tp; 336 int ret, next; 337 338 if ((tp = next_node(inb, &next)) == NULL) 339 goto eofMib; 340 341 /* retain old variable if we are doing a GETNEXT on an exact 342 * matched leaf only */ 343 if (tp->type == SNMP_NODE_LEAF || next) 344 outb->var = tp->oid; 345 else 346 outb->var = inb->var; 347 348 for (;;) { 349 outb->syntax = tp->syntax; 350 if (tp->type == SNMP_NODE_LEAF) { 351 /* make a GET operation */ 352 outb->var.subs[outb->var.len++] = 0; 353 ret = (*tp->op)(&context->ctx, outb, tp->oid.len, 354 tp->index, SNMP_OP_GET); 355 } else { 356 /* make a GETNEXT */ 357 ret = (*tp->op)(&context->ctx, outb, tp->oid.len, 358 tp->index, SNMP_OP_GETNEXT); 359 } 360 if (ret != SNMP_ERR_NOSUCHNAME) { 361 /* got something */ 362 if (ret != SNMP_ERR_NOERROR && TR(GETNEXT)) 363 snmp_debug("getnext: %s returns %u", 364 asn_oid2str(&outb->var), ret); 365 break; 366 } 367 368 /* object has no data - try next */ 369 if (++tp == tree + tree_size) 370 break; 371 372 if (TR(GETNEXT)) 373 snmp_debug("getnext: no data - avancing to %s", 374 asn_oid2str(&tp->oid)); 375 376 outb->var = tp->oid; 377 } 378 379 if (ret == SNMP_ERR_NOSUCHNAME) { 380 eofMib: 381 outb->var = inb->var; 382 if (pdu->version == SNMP_V1) { 383 pdu->error_status = SNMP_ERR_NOSUCHNAME; 384 return (SNMP_RET_ERR); 385 } 386 outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW; 387 388 } else if (ret != SNMP_ERR_NOERROR) { 389 pdu->error_status = SNMP_ERR_GENERR; 390 return (SNMP_RET_ERR); 391 } 392 return (SNMP_RET_OK); 393 } 394 395 396 /* 397 * Execute a GETNEXT operation. The tree is rooted at the global 'root'. 398 * Build the response PDU on the fly. The return is: 399 */ 400 enum snmp_ret 401 snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b, 402 struct snmp_pdu *resp, void *data) 403 { 404 struct context context; 405 u_int i; 406 enum asn_err err; 407 enum snmp_ret result; 408 409 memset(&context, 0, sizeof(context)); 410 context.ctx.data = data; 411 412 snmp_pdu_create_response(pdu, resp); 413 414 if (snmp_pdu_encode_header(resp_b, resp)) 415 return (SNMP_RET_IGN); 416 417 for (i = 0; i < pdu->nbindings; i++) { 418 result = do_getnext(&context, &pdu->bindings[i], 419 &resp->bindings[i], pdu); 420 421 if (result != SNMP_RET_OK) { 422 pdu->error_index = i + 1; 423 snmp_pdu_free(resp); 424 return (result); 425 } 426 427 resp->nbindings++; 428 429 err = snmp_binding_encode(resp_b, &resp->bindings[i]); 430 431 if (err == ASN_ERR_EOBUF) { 432 pdu->error_status = SNMP_ERR_TOOBIG; 433 pdu->error_index = 0; 434 snmp_pdu_free(resp); 435 return (SNMP_RET_ERR); 436 } 437 if (err != ASN_ERR_OK) { 438 if (TR(GET)) 439 snmp_debug("getnext: binding encoding: %u", err); 440 pdu->error_status = SNMP_ERR_GENERR; 441 pdu->error_index = i + 1; 442 snmp_pdu_free(resp); 443 return (SNMP_RET_ERR); 444 } 445 } 446 447 if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { 448 snmp_debug("getnext: failed to encode PDU"); 449 return (SNMP_RET_ERR); 450 } 451 452 return (SNMP_RET_OK); 453 } 454 455 enum snmp_ret 456 snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b, 457 struct snmp_pdu *resp, void *data) 458 { 459 struct context context; 460 u_int i; 461 int cnt; 462 u_int non_rep; 463 int eomib; 464 enum snmp_ret result; 465 enum asn_err err; 466 467 memset(&context, 0, sizeof(context)); 468 context.ctx.data = data; 469 470 snmp_pdu_create_response(pdu, resp); 471 472 if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK) 473 /* cannot even encode header - very bad */ 474 return (SNMP_RET_IGN); 475 476 if ((non_rep = pdu->error_status) > pdu->nbindings) 477 non_rep = pdu->nbindings; 478 479 /* non-repeaters */ 480 for (i = 0; i < non_rep; i++) { 481 result = do_getnext(&context, &pdu->bindings[i], 482 &resp->bindings[resp->nbindings], pdu); 483 484 if (result != SNMP_RET_OK) { 485 pdu->error_index = i + 1; 486 snmp_pdu_free(resp); 487 return (result); 488 } 489 490 err = snmp_binding_encode(resp_b, 491 &resp->bindings[resp->nbindings++]); 492 493 if (err == ASN_ERR_EOBUF) 494 goto done; 495 496 if (err != ASN_ERR_OK) { 497 if (TR(GET)) 498 snmp_debug("getnext: binding encoding: %u", err); 499 pdu->error_status = SNMP_ERR_GENERR; 500 pdu->error_index = i + 1; 501 snmp_pdu_free(resp); 502 return (SNMP_RET_ERR); 503 } 504 } 505 506 if (non_rep == pdu->nbindings) 507 goto done; 508 509 /* repeates */ 510 for (cnt = 0; cnt < pdu->error_index; cnt++) { 511 eomib = 1; 512 for (i = non_rep; i < pdu->nbindings; i++) { 513 514 if (resp->nbindings == SNMP_MAX_BINDINGS) 515 /* PDU is full */ 516 goto done; 517 518 if (cnt == 0) 519 result = do_getnext(&context, &pdu->bindings[i], 520 &resp->bindings[resp->nbindings], pdu); 521 else 522 result = do_getnext(&context, 523 &resp->bindings[resp->nbindings - 524 (pdu->nbindings - non_rep)], 525 &resp->bindings[resp->nbindings], pdu); 526 527 if (result != SNMP_RET_OK) { 528 pdu->error_index = i + 1; 529 snmp_pdu_free(resp); 530 return (result); 531 } 532 if (resp->bindings[resp->nbindings].syntax != 533 SNMP_SYNTAX_ENDOFMIBVIEW) 534 eomib = 0; 535 536 err = snmp_binding_encode(resp_b, 537 &resp->bindings[resp->nbindings++]); 538 539 if (err == ASN_ERR_EOBUF) 540 goto done; 541 542 if (err != ASN_ERR_OK) { 543 if (TR(GET)) 544 snmp_debug("getnext: binding encoding: %u", err); 545 pdu->error_status = SNMP_ERR_GENERR; 546 pdu->error_index = i + 1; 547 snmp_pdu_free(resp); 548 return (SNMP_RET_ERR); 549 } 550 } 551 if (eomib) 552 break; 553 } 554 555 done: 556 if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { 557 snmp_debug("getnext: failed to encode PDU"); 558 return (SNMP_RET_ERR); 559 } 560 561 return (SNMP_RET_OK); 562 } 563 564 /* 565 * Rollback a SET operation. Failed index is 'i'. 566 */ 567 static void 568 rollback(struct context *context, struct snmp_pdu *pdu, u_int i) 569 { 570 struct snmp_value *b; 571 const struct snmp_node *np; 572 int ret; 573 574 while (i-- > 0) { 575 b = &pdu->bindings[i]; 576 np = context->node[i]; 577 578 context->ctx.scratch = &context->scratch[i]; 579 580 ret = (*np->op)(&context->ctx, b, np->oid.len, np->index, 581 SNMP_OP_ROLLBACK); 582 583 if (ret != SNMP_ERR_NOERROR) { 584 snmp_error("set: rollback failed (%d) on variable %s " 585 "index %u", ret, asn_oid2str(&b->var), i); 586 if (pdu->version != SNMP_V1) { 587 pdu->error_status = SNMP_ERR_UNDO_FAILED; 588 pdu->error_index = 0; 589 } 590 } 591 } 592 } 593 594 /* 595 * Commit dependencies. 596 */ 597 int 598 snmp_dep_commit(struct snmp_context *ctx) 599 { 600 struct context *context = (struct context *)ctx; 601 int ret; 602 603 TAILQ_FOREACH(context->depend, &context->dlist, link) { 604 ctx->dep = &context->depend->dep; 605 606 if (TR(SET)) 607 snmp_debug("set: dependency commit %s", 608 asn_oid2str(&ctx->dep->obj)); 609 610 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT); 611 612 if (ret != SNMP_ERR_NOERROR) { 613 if (TR(SET)) 614 snmp_debug("set: dependency failed %d", ret); 615 return (ret); 616 } 617 } 618 return (SNMP_ERR_NOERROR); 619 } 620 621 /* 622 * Rollback dependencies 623 */ 624 int 625 snmp_dep_rollback(struct snmp_context *ctx) 626 { 627 struct context *context = (struct context *)ctx; 628 int ret, ret1; 629 char objbuf[ASN_OIDSTRLEN]; 630 char idxbuf[ASN_OIDSTRLEN]; 631 632 ret1 = SNMP_ERR_NOERROR; 633 while ((context->depend = 634 TAILQ_PREV(context->depend, depend_list, link)) != NULL) { 635 ctx->dep = &context->depend->dep; 636 637 if (TR(SET)) 638 snmp_debug("set: dependency rollback %s", 639 asn_oid2str(&ctx->dep->obj)); 640 641 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK); 642 643 if (ret != SNMP_ERR_NOERROR) { 644 snmp_debug("set: dep rollback returns %u: %s %s", ret, 645 asn_oid2str_r(&ctx->dep->obj, objbuf), 646 asn_oid2str_r(&ctx->dep->idx, idxbuf)); 647 if (ret1 == SNMP_ERR_NOERROR) 648 ret1 = ret; 649 } 650 } 651 return (ret1); 652 } 653 654 void 655 snmp_dep_finish(struct snmp_context *ctx) 656 { 657 struct context *context = (struct context *)ctx; 658 struct depend *d; 659 660 while ((d = TAILQ_FIRST(&context->dlist)) != NULL) { 661 ctx->dep = &d->dep; 662 (void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH); 663 TAILQ_REMOVE(&context->dlist, d, link); 664 free(d); 665 } 666 } 667 668 /* 669 * Do a SET operation. 670 */ 671 enum snmp_ret 672 snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b, 673 struct snmp_pdu *resp, void *data) 674 { 675 int ret; 676 u_int i; 677 enum asn_err asnerr; 678 struct context context; 679 const struct snmp_node *np; 680 struct snmp_value *b; 681 enum snmp_syntax except; 682 683 memset(&context, 0, sizeof(context)); 684 TAILQ_INIT(&context.dlist); 685 context.ctx.data = data; 686 687 snmp_pdu_create_response(pdu, resp); 688 689 if (snmp_pdu_encode_header(resp_b, resp)) 690 return (SNMP_RET_IGN); 691 692 /* 693 * 1. Find all nodes, check that they are writeable and 694 * that the syntax is ok, copy over the binding to the response. 695 */ 696 for (i = 0; i < pdu->nbindings; i++) { 697 b = &pdu->bindings[i]; 698 699 if ((np = context.node[i] = find_node(b, &except)) == NULL) { 700 /* not found altogether or LEAF with wrong index */ 701 if (TR(SET)) 702 snmp_debug("set: node not found %s", 703 asn_oid2str_r(&b->var, oidbuf)); 704 if (pdu->version == SNMP_V1) { 705 pdu->error_index = i + 1; 706 pdu->error_status = SNMP_ERR_NOSUCHNAME; 707 } else if ((np = find_subnode(b)) != NULL) { 708 /* 2. intermediate object */ 709 pdu->error_index = i + 1; 710 pdu->error_status = SNMP_ERR_NOT_WRITEABLE; 711 } else if (except == SNMP_SYNTAX_NOSUCHOBJECT) { 712 pdu->error_index = i + 1; 713 pdu->error_status = SNMP_ERR_NO_ACCESS; 714 } else { 715 pdu->error_index = i + 1; 716 pdu->error_status = SNMP_ERR_NO_CREATION; 717 } 718 snmp_pdu_free(resp); 719 return (SNMP_RET_ERR); 720 } 721 /* 722 * 2. write/createable? 723 * Can check this for leafs only, because in v2 we have 724 * to differentiate between NOT_WRITEABLE and NO_CREATION 725 * and only the action routine for COLUMNS knows, whether 726 * a column exists. 727 */ 728 if (np->type == SNMP_NODE_LEAF && 729 !(np->flags & SNMP_NODE_CANSET)) { 730 if (pdu->version == SNMP_V1) { 731 pdu->error_index = i + 1; 732 pdu->error_status = SNMP_ERR_NOSUCHNAME; 733 } else { 734 pdu->error_index = i + 1; 735 pdu->error_status = SNMP_ERR_NOT_WRITEABLE; 736 } 737 snmp_pdu_free(resp); 738 return (SNMP_RET_ERR); 739 } 740 /* 741 * 3. Ensure the right syntax 742 */ 743 if (np->syntax != b->syntax) { 744 if (pdu->version == SNMP_V1) { 745 pdu->error_index = i + 1; 746 pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */ 747 } else { 748 pdu->error_index = i + 1; 749 pdu->error_status = SNMP_ERR_WRONG_TYPE; 750 } 751 snmp_pdu_free(resp); 752 return (SNMP_RET_ERR); 753 } 754 /* 755 * 4. Copy binding 756 */ 757 if (snmp_value_copy(&resp->bindings[i], b)) { 758 pdu->error_index = i + 1; 759 pdu->error_status = SNMP_ERR_GENERR; 760 snmp_pdu_free(resp); 761 return (SNMP_RET_ERR); 762 } 763 asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]); 764 if (asnerr == ASN_ERR_EOBUF) { 765 pdu->error_index = i + 1; 766 pdu->error_status = SNMP_ERR_TOOBIG; 767 snmp_pdu_free(resp); 768 return (SNMP_RET_ERR); 769 } else if (asnerr != ASN_ERR_OK) { 770 pdu->error_index = i + 1; 771 pdu->error_status = SNMP_ERR_GENERR; 772 snmp_pdu_free(resp); 773 return (SNMP_RET_ERR); 774 } 775 resp->nbindings++; 776 } 777 778 context.ctx.code = SNMP_RET_OK; 779 780 /* 781 * 2. Call the SET method for each node. If a SET fails, rollback 782 * everything. Map error codes depending on the version. 783 */ 784 for (i = 0; i < pdu->nbindings; i++) { 785 b = &pdu->bindings[i]; 786 np = context.node[i]; 787 788 context.ctx.var_index = i + 1; 789 context.ctx.scratch = &context.scratch[i]; 790 791 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index, 792 SNMP_OP_SET); 793 794 if (TR(SET)) 795 snmp_debug("set: action %s returns %d", np->name, ret); 796 797 if (pdu->version == SNMP_V1) { 798 switch (ret) { 799 case SNMP_ERR_NO_ACCESS: 800 ret = SNMP_ERR_NOSUCHNAME; 801 break; 802 case SNMP_ERR_WRONG_TYPE: 803 /* should no happen */ 804 ret = SNMP_ERR_BADVALUE; 805 break; 806 case SNMP_ERR_WRONG_LENGTH: 807 ret = SNMP_ERR_BADVALUE; 808 break; 809 case SNMP_ERR_WRONG_ENCODING: 810 /* should not happen */ 811 ret = SNMP_ERR_BADVALUE; 812 break; 813 case SNMP_ERR_WRONG_VALUE: 814 ret = SNMP_ERR_BADVALUE; 815 break; 816 case SNMP_ERR_NO_CREATION: 817 ret = SNMP_ERR_NOSUCHNAME; 818 break; 819 case SNMP_ERR_INCONS_VALUE: 820 ret = SNMP_ERR_BADVALUE; 821 break; 822 case SNMP_ERR_RES_UNAVAIL: 823 ret = SNMP_ERR_GENERR; 824 break; 825 case SNMP_ERR_COMMIT_FAILED: 826 ret = SNMP_ERR_GENERR; 827 break; 828 case SNMP_ERR_UNDO_FAILED: 829 ret = SNMP_ERR_GENERR; 830 break; 831 case SNMP_ERR_AUTH_ERR: 832 /* should not happen */ 833 ret = SNMP_ERR_GENERR; 834 break; 835 case SNMP_ERR_NOT_WRITEABLE: 836 ret = SNMP_ERR_NOSUCHNAME; 837 break; 838 case SNMP_ERR_INCONS_NAME: 839 ret = SNMP_ERR_BADVALUE; 840 break; 841 } 842 } 843 if (ret != SNMP_ERR_NOERROR) { 844 pdu->error_index = i + 1; 845 pdu->error_status = ret; 846 847 rollback(&context, pdu, i); 848 snmp_pdu_free(resp); 849 850 context.ctx.code = SNMP_RET_ERR; 851 852 goto errout; 853 } 854 } 855 856 /* 857 * 3. Call dependencies 858 */ 859 if (TR(SET)) 860 snmp_debug("set: set operations ok"); 861 862 if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) { 863 pdu->error_status = ret; 864 pdu->error_index = context.ctx.var_index; 865 866 if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) { 867 if (pdu->version != SNMP_V1) { 868 pdu->error_status = SNMP_ERR_UNDO_FAILED; 869 pdu->error_index = 0; 870 } 871 } 872 rollback(&context, pdu, i); 873 snmp_pdu_free(resp); 874 875 context.ctx.code = SNMP_RET_ERR; 876 877 goto errout; 878 } 879 880 /* 881 * 4. Commit and copy values from the original packet to the response. 882 * This is not the commit operation from RFC 1905 but rather an 883 * 'FREE RESOURCES' operation. It shouldn't fail. 884 */ 885 if (TR(SET)) 886 snmp_debug("set: commiting"); 887 888 for (i = 0; i < pdu->nbindings; i++) { 889 b = &resp->bindings[i]; 890 np = context.node[i]; 891 892 context.ctx.var_index = i + 1; 893 context.ctx.scratch = &context.scratch[i]; 894 895 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index, 896 SNMP_OP_COMMIT); 897 898 if (ret != SNMP_ERR_NOERROR) 899 snmp_error("set: commit failed (%d) on" 900 " variable %s index %u", ret, 901 asn_oid2str_r(&b->var, oidbuf), i); 902 } 903 904 if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { 905 snmp_error("set: fix_encoding failed"); 906 snmp_pdu_free(resp); 907 context.ctx.code = SNMP_RET_IGN; 908 } 909 910 /* 911 * Done 912 */ 913 errout: 914 snmp_dep_finish(&context.ctx); 915 916 if (TR(SET)) 917 snmp_debug("set: returning %d", context.ctx.code); 918 919 return (context.ctx.code); 920 } 921 /* 922 * Lookup a dependency. If it doesn't exist, create one 923 */ 924 struct snmp_dependency * 925 snmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj, 926 const struct asn_oid *idx, size_t len, snmp_depop_t func) 927 { 928 struct context *context; 929 struct depend *d; 930 931 context = (struct context *)(void *) 932 ((char *)ctx - offsetof(struct context, ctx)); 933 if (TR(DEPEND)) { 934 snmp_debug("depend: looking for %s", asn_oid2str(obj)); 935 if (idx) 936 snmp_debug("depend: index is %s", asn_oid2str(idx)); 937 } 938 TAILQ_FOREACH(d, &context->dlist, link) 939 if (asn_compare_oid(obj, &d->dep.obj) == 0 && 940 ((idx == NULL && d->dep.idx.len == 0) || 941 (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) { 942 if(TR(DEPEND)) 943 snmp_debug("depend: found"); 944 return (&d->dep); 945 } 946 947 if(TR(DEPEND)) 948 snmp_debug("depend: creating"); 949 950 if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL) 951 return (NULL); 952 memset(&d->dep, 0, len); 953 954 d->dep.obj = *obj; 955 if (idx == NULL) 956 d->dep.idx.len = 0; 957 else 958 d->dep.idx = *idx; 959 d->len = len; 960 d->func = func; 961 962 TAILQ_INSERT_TAIL(&context->dlist, d, link); 963 964 return (&d->dep); 965 } 966 967 /* 968 * Make an error response from a PDU. We do this without decoding the 969 * variable bindings. This means we can sent the junk back to a caller 970 * that has sent us junk in the first place. 971 */ 972 enum snmp_ret 973 snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b, 974 struct asn_buf *resp_b) 975 { 976 u_char type; 977 asn_len_t len; 978 struct snmp_pdu resp; 979 enum asn_err err; 980 enum snmp_code code; 981 982 snmp_pdu_create_response(pdu, &resp); 983 984 if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK) 985 return (SNMP_RET_IGN); 986 987 if (pdu->version == SNMP_V3) { 988 if (resp.user.priv_proto != SNMP_PRIV_NOPRIV && 989 (asn_get_header(pdu_b, &type, &resp.scoped_len) != ASN_ERR_OK 990 || type != ASN_TYPE_OCTETSTRING)) { 991 snmp_error("cannot decode encrypted pdu"); 992 return (SNMP_RET_IGN); 993 } 994 995 if (asn_get_sequence(pdu_b, &len) != ASN_ERR_OK) { 996 snmp_error("cannot decode scoped pdu header"); 997 return (SNMP_RET_IGN); 998 } 999 1000 len = SNMP_ENGINE_ID_SIZ; 1001 if (asn_get_octetstring(pdu_b, (u_char *)resp.context_engine, 1002 &len) != ASN_ERR_OK) { 1003 snmp_error("cannot decode msg context engine"); 1004 return (SNMP_RET_IGN); 1005 } 1006 resp.context_engine_len = len; 1007 len = SNMP_CONTEXT_NAME_SIZ; 1008 if (asn_get_octetstring(pdu_b, (u_char *)resp.context_name, 1009 &len) != ASN_ERR_OK) { 1010 snmp_error("cannot decode msg context name"); 1011 return (SNMP_RET_IGN); 1012 } 1013 resp.context_name[len] = '\0'; 1014 } 1015 1016 1017 if (asn_get_header(pdu_b, &type, &len) != ASN_ERR_OK) { 1018 snmp_error("cannot get pdu header"); 1019 return (SNMP_RET_IGN); 1020 } 1021 1022 if ((type & ~ASN_TYPE_MASK) != 1023 (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT)) { 1024 snmp_error("bad pdu header tag"); 1025 return (SNMP_RET_IGN); 1026 } 1027 1028 err = snmp_parse_pdus_hdr(pdu_b, &resp, &len); 1029 if (ASN_ERR_STOPPED(err)) 1030 return (SNMP_RET_IGN); 1031 if (pdu_b->asn_len < len) 1032 return (SNMP_RET_IGN); 1033 pdu_b->asn_len = len; 1034 1035 /* now we have the bindings left - construct new message */ 1036 resp.error_status = pdu->error_status; 1037 resp.error_index = pdu->error_index; 1038 resp.type = SNMP_PDU_RESPONSE; 1039 1040 code = snmp_pdu_encode_header(resp_b, &resp); 1041 if (code != SNMP_CODE_OK) 1042 return (SNMP_RET_IGN); 1043 1044 if (pdu_b->asn_len > resp_b->asn_len) 1045 /* too short */ 1046 return (SNMP_RET_IGN); 1047 (void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len); 1048 resp_b->asn_len -= pdu_b->asn_len; 1049 resp_b->asn_ptr += pdu_b->asn_len; 1050 1051 code = snmp_fix_encoding(resp_b, &resp); 1052 if (code != SNMP_CODE_OK) 1053 return (SNMP_RET_IGN); 1054 1055 return (SNMP_RET_OK); 1056 } 1057 1058 static void 1059 snmp_debug_func(const char *fmt, ...) 1060 { 1061 va_list ap; 1062 1063 va_start(ap, fmt); 1064 vfprintf(stderr, fmt, ap); 1065 va_end(ap); 1066 fprintf(stderr, "\n"); 1067 } 1068