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