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