]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/bsnmp/lib/snmpagent.c
Fix bsnmpd remote denial of service vulnerability. [SA-14:01]
[FreeBSD/releng/9.2.git] / contrib / bsnmp / lib / snmpagent.c
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(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         return (snmp_fix_encoding(resp_b, resp));
280 }
281
282 static struct snmp_node *
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
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
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
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
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
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
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
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
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 *
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
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
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 }