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 *
snmp_init_context(void)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 *
find_node(const struct snmp_value * value,enum snmp_syntax * errp)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 *
find_subnode(const struct snmp_value * value)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
snmp_pdu_create_response(const struct snmp_pdu * pdu,struct snmp_pdu * resp)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
snmp_get(struct snmp_pdu * pdu,struct asn_buf * resp_b,struct snmp_pdu * resp,void * data)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 *
next_node(const struct snmp_value * value,int * pnext)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
do_getnext(struct context * context,const struct snmp_value * inb,struct snmp_value * outb,struct snmp_pdu * pdu)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
snmp_getnext(struct snmp_pdu * pdu,struct asn_buf * resp_b,struct snmp_pdu * resp,void * data)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
snmp_getbulk(struct snmp_pdu * pdu,struct asn_buf * resp_b,struct snmp_pdu * resp,void * data)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
rollback(struct context * context,struct snmp_pdu * pdu,u_int i)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
snmp_dep_commit(struct snmp_context * ctx)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
snmp_dep_rollback(struct snmp_context * ctx)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
snmp_dep_finish(struct snmp_context * ctx)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
snmp_set(struct snmp_pdu * pdu,struct asn_buf * resp_b,struct snmp_pdu * resp,void * data)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 *
snmp_dep_lookup(struct snmp_context * ctx,const struct asn_oid * obj,const struct asn_oid * idx,size_t len,snmp_depop_t func)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
snmp_make_errresp(const struct snmp_pdu * pdu,struct asn_buf * pdu_b,struct asn_buf * resp_b)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
snmp_debug_func(const char * fmt,...)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