1 /*
2  * ProFTPD - mod_snmp SMI routines
3  * Copyright (c) 2008-2016 TJ Saunders
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18  *
19  * As a special exemption, TJ Saunders and other respective copyright holders
20  * give permission to link this program with OpenSSL, and distribute the
21  * resulting executable, without including the source code for OpenSSL in the
22  * source distribution.
23  */
24 
25 #include "mod_snmp.h"
26 #include "asn1.h"
27 #include "smi.h"
28 #include "mib.h"
29 #include "msg.h"
30 
31 static const char *trace_channel = "snmp.smi";
32 
snmp_smi_get_varstr(pool * p,unsigned char var_type)33 const char *snmp_smi_get_varstr(pool *p, unsigned char var_type) {
34   const char *varstr = "unknown";
35 
36   switch (var_type) {
37     case SNMP_SMI_INTEGER:
38       varstr = "INTEGER";
39       break;
40 
41     case SNMP_SMI_STRING:
42       varstr = "STRING";
43       break;
44 
45     case SNMP_SMI_OID:
46       varstr = "OID";
47       break;
48 
49     case SNMP_SMI_NULL:
50       varstr = "NULL";
51       break;
52 
53     case SNMP_SMI_IPADDR:
54       varstr = "IPADDR";
55       break;
56 
57     case SNMP_SMI_COUNTER32:
58       varstr = "COUNTER32";
59       break;
60 
61     case SNMP_SMI_GAUGE32:
62       varstr = "GAUGE32";
63       break;
64 
65     case SNMP_SMI_TIMETICKS:
66       varstr = "TIMETICKS";
67       break;
68 
69     case SNMP_SMI_OPAQUE:
70       varstr = "OPAQUE";
71       break;
72 
73     case SNMP_SMI_COUNTER64:
74       varstr = "COUNTER64";
75       break;
76 
77     case SNMP_SMI_NO_SUCH_OBJECT:
78       varstr = "NO_SUCH_OBJECT";
79       break;
80 
81     case SNMP_SMI_NO_SUCH_INSTANCE:
82       varstr = "NO_SUCH_INSTANCE";
83       break;
84 
85     case SNMP_SMI_END_OF_MIB_VIEW:
86       varstr = "END_OF_MIB_VIEW";
87       break;
88   }
89 
90   return varstr;
91 }
92 
snmp_smi_alloc_var(pool * p,oid_t * name,unsigned int namelen)93 struct snmp_var *snmp_smi_alloc_var(pool *p, oid_t *name,
94     unsigned int namelen) {
95   pool *sub_pool;
96   struct snmp_var *var;
97 
98   sub_pool = pr_pool_create_sz(p, 64);
99   var = pcalloc(sub_pool, sizeof(struct snmp_var));
100   var->pool = sub_pool;
101   var->next = NULL;
102 
103   /* Default type for newly-allocated variables. */
104   var->smi_type = SNMP_SMI_NULL;
105 
106   var->namelen = namelen;
107 
108   if (var->namelen == 0) {
109     /* Not sure why a caller would do this, but... */
110     return var;
111   }
112 
113   /* Even though the name argument may be NULL, we still allocate the space.
114    * Why?  Because when reading off variables from a message, we may not
115    * know the name when we are allocating the struct, but we will know at
116    * some point after that.
117    */
118   var->name = pcalloc(var->pool, sizeof(oid_t) * var->namelen);
119 
120   if (name != NULL) {
121     memmove(var->name, name, sizeof(oid_t) * var->namelen);
122   }
123 
124   return var;
125 }
126 
snmp_smi_create_var(pool * p,oid_t * name,unsigned int namelen,unsigned char smi_type,int32_t int_value,char * str_value,size_t str_valuelen)127 struct snmp_var *snmp_smi_create_var(pool *p, oid_t *name, unsigned int namelen,
128     unsigned char smi_type, int32_t int_value, char *str_value,
129     size_t str_valuelen) {
130   struct snmp_var *var = NULL;
131 
132   switch (smi_type) {
133     case SNMP_SMI_INTEGER:
134     case SNMP_SMI_COUNTER32:
135     case SNMP_SMI_GAUGE32:
136     case SNMP_SMI_TIMETICKS:
137       var = snmp_smi_create_int(p, name, namelen, smi_type, int_value);
138       break;
139 
140     case SNMP_SMI_STRING:
141     case SNMP_SMI_IPADDR:
142       var = snmp_smi_create_string(p, name, namelen, smi_type, str_value,
143         str_valuelen);
144       break;
145 
146     default:
147       pr_trace_msg(trace_channel, 16,
148         "unable to create variable for SMI type %s",
149       snmp_smi_get_varstr(p, smi_type));
150       errno = ENOENT;
151       break;
152   }
153 
154   return var;
155 }
156 
snmp_smi_create_int(pool * p,oid_t * name,unsigned int namelen,unsigned char smi_type,int32_t value)157 struct snmp_var *snmp_smi_create_int(pool *p, oid_t *name, unsigned int namelen,
158     unsigned char smi_type, int32_t value) {
159   struct snmp_var *var;
160 
161   var = snmp_smi_alloc_var(p, name, namelen);
162   var->valuelen = sizeof(value);
163   var->value.integer = palloc(var->pool, var->valuelen);
164   *(var->value.integer) = value;
165   var->smi_type = smi_type;
166 
167   pr_trace_msg(trace_channel, 19,
168     "created SMI variable %s, value %d", snmp_smi_get_varstr(p, smi_type),
169     value);
170   return var;
171 }
172 
snmp_smi_create_string(pool * p,oid_t * name,unsigned int namelen,unsigned char smi_type,char * value,size_t valuelen)173 struct snmp_var *snmp_smi_create_string(pool *p, oid_t *name,
174     unsigned int namelen, unsigned char smi_type, char *value,
175     size_t valuelen) {
176   struct snmp_var *var;
177 
178   if (value == NULL) {
179     errno = EINVAL;
180     return NULL;
181   }
182 
183   var = snmp_smi_alloc_var(p, name, namelen);
184   var->valuelen = valuelen;
185   var->value.string = pstrndup(var->pool, value, var->valuelen);
186   var->smi_type = smi_type;
187 
188   pr_trace_msg(trace_channel, 19,
189     "created SMI variable %s, value '%s'", snmp_smi_get_varstr(p, smi_type),
190     value);
191   return var;
192 }
193 
snmp_smi_create_oid(pool * p,oid_t * name,unsigned int namelen,unsigned char smi_type,oid_t * value,unsigned int valuelen)194 struct snmp_var *snmp_smi_create_oid(pool *p, oid_t *name,
195     unsigned int namelen, unsigned char smi_type, oid_t *value,
196     unsigned int valuelen) {
197   struct snmp_var *var;
198 
199   if (value == NULL) {
200     errno = EINVAL;
201     return NULL;
202   }
203 
204   if (smi_type != SNMP_SMI_OID) {
205     errno = EINVAL;
206     return NULL;
207   }
208 
209   var = snmp_smi_alloc_var(p, name, namelen);
210 
211   /* The valuelen argument is the number of sub-ids, NOT the number of bytes,
212    * for an OID.
213    */
214   var->valuelen = valuelen;
215   var->value.oid = palloc(var->pool, sizeof(oid_t) * var->valuelen);
216   memmove(var->value.oid, value, sizeof(oid_t) * var->valuelen);
217   var->smi_type = smi_type;
218 
219   pr_trace_msg(trace_channel, 19,
220     "created SMI variable %s, value %s", snmp_smi_get_varstr(p, smi_type),
221     snmp_asn1_get_oidstr(p, value, valuelen));
222   return var;
223 }
224 
snmp_smi_create_exception(pool * p,oid_t * name,unsigned int namelen,unsigned char smi_type)225 struct snmp_var *snmp_smi_create_exception(pool *p, oid_t *name,
226     unsigned int namelen, unsigned char smi_type) {
227   struct snmp_var *var;
228 
229   /* Check that the SMI type is one of the allowed "exceptions"
230    * (terminology from RFC 1905).
231    */
232   switch (smi_type) {
233     case SNMP_SMI_NO_SUCH_OBJECT:
234     case SNMP_SMI_NO_SUCH_INSTANCE:
235     case SNMP_SMI_END_OF_MIB_VIEW:
236       break;
237 
238     default:
239       errno = EINVAL;
240       return NULL;
241   }
242 
243   var = snmp_smi_alloc_var(p, name, namelen);
244   var->valuelen = 0;
245   var->smi_type = smi_type;
246 
247   pr_trace_msg(trace_channel, 19,
248     "created SMI variable %s", snmp_smi_get_varstr(p, smi_type));
249   return var;
250 }
251 
252 /* Note: This will duplicate the entire varlist represented by the head
253  * variable.
254  */
snmp_smi_dup_var(pool * p,struct snmp_var * src_var)255 struct snmp_var *snmp_smi_dup_var(pool *p, struct snmp_var *src_var) {
256   struct snmp_var *head_var = NULL, *iter_var = NULL, *tail_var = NULL;
257   unsigned int var_count = 0;
258 
259   for (iter_var = src_var; iter_var; iter_var = iter_var->next) {
260     struct snmp_var *var;
261 
262     pr_signals_handle();
263 
264     var = snmp_smi_alloc_var(p, iter_var->name, iter_var->namelen);
265     var->smi_type = iter_var->smi_type;
266     var->valuelen = iter_var->valuelen;
267 
268     if (var->valuelen > 0) {
269       switch (var->smi_type) {
270         case SNMP_SMI_INTEGER:
271           var->value.integer = palloc(var->pool, var->valuelen);
272           memmove(var->value.integer, iter_var->value.integer, var->valuelen);
273           break;
274 
275         case SNMP_SMI_STRING:
276           var->value.string = pcalloc(var->pool, var->valuelen);
277           memmove(var->value.string, iter_var->value.string, var->valuelen);
278           break;
279 
280         case SNMP_SMI_OID:
281           var->value.oid = palloc(var->pool, var->valuelen);
282           memmove(var->value.oid, iter_var->value.oid, var->valuelen);
283           break;
284 
285         default:
286           pr_trace_msg(trace_channel, 1,
287             "unable to dup variable '%s': unsupported",
288             snmp_asn1_get_tagstr(p, var->smi_type));
289 
290           /* XXX Destroy the entire chain? */
291           destroy_pool(var->pool);
292           pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
293           errno = EINVAL;
294           return NULL;
295       }
296     }
297 
298     if (head_var == NULL) {
299       head_var = var;
300     }
301 
302     if (tail_var != NULL) {
303       tail_var->next = var;
304     }
305 
306     tail_var = var;
307     var_count++;
308 
309     pr_trace_msg(trace_channel, 19,
310       "cloned SMI variable %s", snmp_smi_get_varstr(p, iter_var->smi_type));
311   }
312 
313   pr_trace_msg(trace_channel, 19, "cloned %u SMI %s", var_count,
314     var_count != 1 ? "variables" : "variable");
315   return head_var;
316 }
317 
318 /* Decode a list of SNMPv2 variable bindings. */
snmp_smi_read_vars(pool * p,unsigned char ** buf,size_t * buflen,struct snmp_var ** varlist,int snmp_version)319 int snmp_smi_read_vars(pool *p, unsigned char **buf, size_t *buflen,
320     struct snmp_var **varlist, int snmp_version) {
321   struct snmp_var *var = NULL, *head = NULL, *tail = NULL;
322   unsigned char asn1_type;
323   unsigned int total_varlen = 0;
324   int res, var_count = 0;
325 
326   res = snmp_asn1_read_header(p, buf, buflen, &asn1_type, &total_varlen, 0);
327   if (res < 0) {
328     return -1;
329   }
330 
331   /* If this isn't a constructed sequence, error out. */
332   if (asn1_type != (SNMP_ASN1_TYPE_SEQUENCE|SNMP_ASN1_CONSTRUCT)) {
333     pr_trace_msg(trace_channel, 1,
334       "unable to parse tag (%s) as list of variables",
335       snmp_asn1_get_tagstr(p, asn1_type));
336     pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
337     errno = EINVAL;
338     return -1;
339   }
340 
341   pr_trace_msg(trace_channel, 17, "reading %s variables (%u bytes)",
342     snmp_msg_get_versionstr(snmp_version), total_varlen);
343 
344   while (*buflen > 0) {
345     unsigned int varlen;
346     unsigned char *obj_start = NULL;
347     size_t obj_startlen = 0;
348 
349     pr_signals_handle();
350 
351     res = snmp_asn1_read_header(p, buf, buflen, &asn1_type, &varlen, 0);
352     if (res < 0) {
353       return -1;
354     }
355 
356     /* If this isn't a constructed sequence, error out. */
357     if (asn1_type != (SNMP_ASN1_TYPE_SEQUENCE|SNMP_ASN1_CONSTRUCT)) {
358       pr_trace_msg(trace_channel, 1,
359         "unable to parse tag (%s) as variable binding",
360         snmp_asn1_get_tagstr(p, asn1_type));
361       pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
362       errno = EINVAL;
363       return -1;
364     }
365 
366     /* We don't know the name of this variable yet. */
367     var = snmp_smi_alloc_var(p, NULL, SNMP_SMI_MAX_NAMELEN);
368 
369     /* Read the variable name/OID. */
370     res = snmp_asn1_read_oid(p, buf, buflen, &asn1_type, var->name,
371       &(var->namelen));
372     if (res < 0) {
373       destroy_pool(var->pool);
374       return -1;
375     }
376 
377     if (asn1_type != (SNMP_ASN1_CLASS_UNIVERSAL|SNMP_ASN1_PRIMITIVE|SNMP_ASN1_TYPE_OID)) {
378       pr_trace_msg(trace_channel, 1,
379         "expected OID tag, read tag (%s) from variable list",
380         snmp_asn1_get_tagstr(p, asn1_type));
381 
382       destroy_pool(var->pool);
383       pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
384       errno = EINVAL;
385       return -1;
386     }
387 
388     if (pr_trace_get_level(trace_channel) >= 19) {
389       struct snmp_mib *mib;
390       int lacks_instance_id = FALSE;
391 
392       mib = snmp_mib_get_by_oid(var->name, var->namelen, &lacks_instance_id);
393       if (mib != NULL) {
394         pr_trace_msg(trace_channel, 19,
395           "read variable OID %s (%u sub-ids, name %s)",
396           snmp_asn1_get_oidstr(p, var->name, var->namelen), var->namelen,
397           mib->instance_name);
398 
399       } else {
400         pr_trace_msg(trace_channel, 19,
401           "read variable OID %s (%u sub-ids)",
402           snmp_asn1_get_oidstr(p, var->name, var->namelen), var->namelen);
403       }
404     }
405 
406     obj_start = *buf;
407     obj_startlen = *buflen;
408 
409     /* Now that we know the name/OID of the variable, let's find out what
410      * type of variable it is.
411      *
412      * This is effectively a peek, since the following reader functions
413      * will also want to read the tag/length header values.
414      */
415     res = snmp_asn1_read_header(p, &obj_start, &obj_startlen, &(var->smi_type),
416       &(var->valuelen), 0);
417     if (res < 0) {
418       destroy_pool(var->pool);
419       return -1;
420     }
421 
422     pr_trace_msg(trace_channel, 19,
423       "read SMI variable %s, data len %u bytes",
424       snmp_smi_get_varstr(p, var->smi_type), var->valuelen);
425 
426     /* Now read in the value */
427     switch (var->smi_type) {
428       case SNMP_SMI_INTEGER:
429         res = snmp_asn1_read_int(p, buf, buflen,
430           &(var->smi_type), var->value.integer, 0);
431         if (res == 0) {
432           pr_trace_msg(trace_channel, 19,
433             "read INTEGER variable (value %d)", *((int *) var->value.integer));
434         }
435         break;
436 
437       case SNMP_SMI_COUNTER32:
438       case SNMP_SMI_GAUGE32:
439       case SNMP_SMI_TIMETICKS:
440         res = snmp_asn1_read_uint(p, buf, buflen,
441           &(var->smi_type), (unsigned long *) var->value.integer);
442         if (res == 0) {
443           pr_trace_msg(trace_channel, 19,
444             "read %s variable (value %u)",
445             snmp_smi_get_varstr(p, var->smi_type),
446             *((unsigned int *) var->value.integer));
447         }
448         break;
449 
450       case SNMP_SMI_STRING:
451       case SNMP_SMI_IPADDR:
452       case SNMP_SMI_OPAQUE:
453         res = snmp_asn1_read_string(p, buf, buflen,
454           &(var->smi_type), &(var->value.string), &(var->valuelen));
455         if (res == 0) {
456           pr_trace_msg(trace_channel, 19,
457             "read %s variable (value '%.*s')",
458             snmp_smi_get_varstr(p, var->smi_type),
459             var->valuelen, var->value.string);
460         }
461         break;
462 
463       case SNMP_SMI_OID:
464         res = snmp_asn1_read_oid(p, buf, buflen,
465           &(var->smi_type), var->value.oid, &(var->valuelen));
466         if (res == 0) {
467           pr_trace_msg(trace_channel, 19,
468             "read %s variable (%u sub-ids, value %s)",
469             snmp_smi_get_varstr(p, var->smi_type), var->valuelen,
470             snmp_asn1_get_oidstr(p, var->value.oid, var->valuelen));
471         }
472         break;
473 
474       case SNMP_SMI_NULL:
475         res = snmp_asn1_read_null(p, buf, buflen, &(var->smi_type));
476         if (res == 0) {
477           pr_trace_msg(trace_channel, 19, "read %s variable",
478             snmp_smi_get_varstr(p, var->smi_type));
479         }
480         break;
481 
482       case SNMP_SMI_NO_SUCH_OBJECT:
483       case SNMP_SMI_NO_SUCH_INSTANCE:
484       case SNMP_SMI_END_OF_MIB_VIEW:
485         pr_trace_msg(trace_channel, 19, "read %s variable",
486           snmp_smi_get_varstr(p, var->smi_type));
487         break;
488 
489       case SNMP_SMI_COUNTER64:
490         pr_trace_msg(trace_channel, 1,
491           "unable to handle COUNTER64 variable (%x)", var->smi_type);
492         /* fallthrough */
493 
494       default:
495         pr_trace_msg(trace_channel, 1,
496           "unable to read variable type %x", var->smi_type);
497         destroy_pool(var->pool);
498         pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
499         errno = EINVAL;
500         return -1;
501     }
502 
503     if (res < 0) {
504       return -1;
505     }
506 
507     /* Add the variable to the end of the list. */
508     if (tail != NULL) {
509       tail->next = var;
510       tail = var;
511 
512     } else {
513       head = tail = var;
514     }
515 
516     var_count++;
517   }
518 
519   *varlist = head;
520   return var_count;
521 }
522 
523 /* Encode an SNMPv2 variable binding.
524  *
525  * As per RFC 1905 Protocol Operations for SNMPv2:
526  *
527  * VarBind ::=
528  *   SEQUENCE {
529  *     name ObjectName
530  *     CHOICE {
531  *       value ObjectSyntax
532  *       unSpecified NULL
533  *       noSuchObject[0] NULL
534  *       noSuchInstance[1] NULL
535  *       endOfMibView[2] NULL
536  *     }
537  *   }
538  */
snmp_smi_write_vars(pool * p,unsigned char ** buf,size_t * buflen,struct snmp_var * varlist,int snmp_version)539 int snmp_smi_write_vars(pool *p, unsigned char **buf, size_t *buflen,
540     struct snmp_var *varlist, int snmp_version) {
541   struct snmp_var *iter;
542   unsigned char asn1_type, *list_hdr_start, *list_hdr_end;
543   size_t list_hdr_startlen;
544   unsigned int asn1_len;
545   int res;
546 
547   /* Write the header for the varlist. */
548   asn1_type = (SNMP_ASN1_TYPE_SEQUENCE|SNMP_ASN1_CONSTRUCT);
549   asn1_len = 0;
550 
551   list_hdr_start = *buf;
552   list_hdr_startlen = *buflen;
553 
554   res = snmp_asn1_write_header(p, buf, buflen, asn1_type, asn1_len, 0);
555   if (res < 0) {
556     return -1;
557   }
558 
559   list_hdr_end = *buf;
560 
561   for (iter = varlist; iter; iter = iter->next) {
562     unsigned char *var_hdr_start = NULL, *var_hdr_end = NULL;
563     size_t var_hdr_startlen;
564 
565     pr_signals_handle();
566 
567     /* Write the header for this variable. */
568     asn1_type = (SNMP_ASN1_TYPE_SEQUENCE|SNMP_ASN1_CONSTRUCT);
569     asn1_len = 0;
570 
571     var_hdr_start = *buf;
572     var_hdr_startlen = *buflen;
573 
574     res = snmp_asn1_write_header(p, buf, buflen, asn1_type, asn1_len, 0);
575     if (res < 0) {
576       return -1;
577     }
578 
579     var_hdr_end = *buf;
580 
581     asn1_type = (SNMP_ASN1_CLASS_UNIVERSAL|SNMP_ASN1_PRIMITIVE|SNMP_ASN1_TYPE_OID);
582     res = snmp_asn1_write_oid(p, buf, buflen, asn1_type, iter->name,
583       iter->namelen);
584     if (res < 0) {
585       return -1;
586     }
587 
588     switch (iter->smi_type) {
589       case SNMP_SMI_INTEGER:
590         res = snmp_asn1_write_int(p, buf, buflen, iter->smi_type,
591           *((long *) iter->value.integer), 0);
592         break;
593 
594       case SNMP_SMI_COUNTER32:
595       case SNMP_SMI_GAUGE32:
596       case SNMP_SMI_TIMETICKS:
597         res = snmp_asn1_write_uint(p, buf, buflen, iter->smi_type,
598           *((unsigned long *) iter->value.integer));
599         break;
600 
601       case SNMP_SMI_STRING:
602       case SNMP_SMI_IPADDR:
603       case SNMP_SMI_OPAQUE:
604         res = snmp_asn1_write_string(p, buf, buflen, iter->smi_type,
605           iter->value.string, iter->valuelen);
606         break;
607 
608       case SNMP_SMI_OID:
609         res = snmp_asn1_write_oid(p, buf, buflen, iter->smi_type,
610           iter->value.oid, iter->valuelen);
611         break;
612 
613       case SNMP_SMI_NO_SUCH_OBJECT:
614       case SNMP_SMI_NO_SUCH_INSTANCE:
615       case SNMP_SMI_END_OF_MIB_VIEW:
616         if (snmp_version == SNMP_PROTOCOL_VERSION_1) {
617           /* SNMPv1 does not support the other error codes. */
618           res = snmp_asn1_write_null(p, buf, buflen, SNMP_SMI_NO_SUCH_OBJECT);
619 
620         } else {
621           res = snmp_asn1_write_exception(p, buf, buflen, iter->smi_type, 0);
622         }
623 
624         break;
625 
626       case SNMP_SMI_NULL:
627         res = snmp_asn1_write_null(p, buf, buflen, iter->smi_type);
628         break;
629 
630       case SNMP_SMI_COUNTER64:
631         pr_trace_msg(trace_channel, 1, "%s",
632           "unable to encode COUNTER64 SMI variable");
633         /* fall through */
634 
635       default:
636         /* Unsupported type */
637         pr_trace_msg(trace_channel, 1, "%s",
638           "unable to encode unsupported SMI variable type");
639         pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
640         errno = ENOSYS;
641         return -1;
642     }
643 
644     if (res < 0) {
645       return -1;
646     }
647 
648     /* Rewrite the header, this time with the appropriate length. */
649     asn1_type = (SNMP_ASN1_TYPE_SEQUENCE|SNMP_ASN1_CONSTRUCT);
650     asn1_len = (*buf - var_hdr_end);
651 
652     pr_trace_msg(trace_channel, 18,
653       "updating variable header to have length %u", asn1_len);
654     res = snmp_asn1_write_header(p, &var_hdr_start, &var_hdr_startlen,
655       asn1_type, asn1_len, 0);
656     if (res < 0) {
657       return -1;
658     }
659   }
660 
661   /* Rewrite the varlist header, this time with the length of all of the
662    * variables.
663    */
664 
665   asn1_type = (SNMP_ASN1_TYPE_SEQUENCE|SNMP_ASN1_CONSTRUCT);
666   asn1_len = (*buf - list_hdr_end);
667 
668   pr_trace_msg(trace_channel, 18,
669     "updating variable bindings list header to have length %u", asn1_len);
670   res = snmp_asn1_write_header(p, &list_hdr_start, &list_hdr_startlen,
671     asn1_type, asn1_len, 0);
672   if (res < 0) {
673     return -1;
674   }
675 
676   return 0;
677 }
678 
snmp_smi_util_add_list_var(struct snmp_var ** head,struct snmp_var ** tail,struct snmp_var * var)679 unsigned int snmp_smi_util_add_list_var(struct snmp_var **head,
680     struct snmp_var **tail, struct snmp_var *var) {
681   unsigned int count = 0;
682   struct snmp_var *iter_var;
683 
684   if (*head == NULL) {
685     *head = var;
686   }
687 
688   if (*tail != NULL) {
689     (*tail)->next = var;
690   }
691 
692   (*tail) = var;
693 
694   for (iter_var = *head; iter_var; iter_var = iter_var->next) {
695     count++;
696   }
697 
698   return count;
699 }
700 
701