1 /*
2  * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /*
10  * SNMP Variable Binding.  Complies with:
11  *
12  * RFC 1905: Protocol Operations for SNMPv2
13  *
14  */
15 
16 /**********************************************************************
17  *
18  *           Copyright 1997 by Carnegie Mellon University
19  *
20  *                       All Rights Reserved
21  *
22  * Permission to use, copy, modify, and distribute this software and its
23  * documentation for any purpose and without fee is hereby granted,
24  * provided that the above copyright notice appear in all copies and that
25  * both that copyright notice and this permission notice appear in
26  * supporting documentation, and that the name of CMU not be
27  * used in advertising or publicity pertaining to distribution of the
28  * software without specific, written prior permission.
29  *
30  * CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
31  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
32  * CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
33  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
34  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
35  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
36  * SOFTWARE.
37  *
38  * Author: Ryan Troll <ryan+@andrew.cmu.edu>
39  *
40  **********************************************************************/
41 
42 #include "squid.h"
43 
44 #if HAVE_UNISTD_H
45 #include <unistd.h>
46 #endif
47 #if HAVE_STDLIB_H
48 #include <stdlib.h>
49 #endif
50 #if HAVE_SYS_TYPES_H
51 #include <sys/types.h>
52 #endif
53 #if HAVE_CTYPE_H
54 #include <ctype.h>
55 #endif
56 #if HAVE_GNUMALLOC_H
57 #include <gnumalloc.h>
58 #elif HAVE_MALLOC_H
59 #include <malloc.h>
60 #endif
61 #if HAVE_MEMORY_H
62 #include <memory.h>
63 #endif
64 #if HAVE_STRING_H
65 #include <string.h>
66 #endif
67 #if HAVE_STRINGS_H
68 #include <strings.h>
69 #endif
70 #if HAVE_BSTRING_H
71 #include <bstring.h>
72 #endif
73 #if HAVE_SYS_SOCKET_H
74 #include <sys/socket.h>
75 #endif
76 #if HAVE_NETINET_IN_H
77 #include <netinet/in.h>
78 #endif
79 #if HAVE_ARPA_INET_H
80 #include <arpa/inet.h>
81 #endif
82 #if HAVE_SYS_TIME_H
83 #include <sys/time.h>
84 #endif
85 #if HAVE_NETDB_H
86 #include <netdb.h>
87 #endif
88 
89 #include "asn1.h"
90 #include "snmp.h"
91 #include "snmp_vars.h"
92 #if 0
93 #include "mibii.h"
94 #endif
95 #include "snmp_api_error.h"
96 #include "snmp_msg.h"
97 #include "snmp_pdu.h"
98 
99 #include "util.h"
100 
101 /* #define DEBUG_VARS 1 */
102 /* #define DEBUG_VARS_MALLOC 1 */
103 /* #define DEBUG_VARS_DECODE 1 */
104 /* #define DEBUG_VARS_ENCODE 1 */
105 
106 /* Create a new variable_list structure representing oid Name of length Len.
107  *
108  * Returns NULL upon error.
109  */
110 
111 struct variable_list *
snmp_var_new(oid * Name,int Len)112 snmp_var_new(oid * Name, int Len) {
113     struct variable_list *New;
114 
115 #if DEBUG_VARS
116     printf("VARS: Creating.\n");
117 #endif
118 
119     New = xmalloc(sizeof(*New));
120     /* XXX xmalloc never returns NULL */
121     if (New == NULL) {
122         snmp_set_api_error(SNMPERR_OS_ERR);
123         return (NULL);
124     }
125     memset(New, '\0', sizeof(struct variable_list));
126     /*  New->next_variable = NULL; */
127 
128     New->type = ASN_NULL;
129     New->name_length = Len;
130 
131     if (New->name_length == 0) {
132         New->name = NULL;
133         return (New);
134     }
135     New->name = (oid *) xmalloc(Len * sizeof(oid));
136     /* XXX xmalloc never returns NULL */
137     if (New->name == NULL) {
138         xfree(New);
139         snmp_set_api_error(SNMPERR_OS_ERR);
140         return (NULL);
141     }
142 #if DEBUG_VARS
143     printf("VARS: Copying name, size (%d)\n", Len);
144 #endif
145 
146     /* Only copy a name if it was specified. */
147     if (Name)
148         memcpy((char *) New->name, (char *) Name, Len * sizeof(oid));
149 
150     return (New);
151 }
152 
153 struct variable_list *
snmp_var_new_integer(oid * Name,int Len,int ival,unsigned char type)154 snmp_var_new_integer(oid * Name, int Len, int ival, unsigned char type) {
155     variable_list *v = snmp_var_new(Name, Len);
156     v->val_len = sizeof(int);
157     v->val.integer = xmalloc(sizeof(int));
158     v->type = type;
159     *(v->val.integer) = ival;
160     return v;
161 }
162 
163 /* Clone a variable list.
164  *
165  * Returns NULL upon error.
166  */
167 
168 struct variable_list *
snmp_var_clone(struct variable_list * Src)169 snmp_var_clone(struct variable_list *Src) {
170     struct variable_list *Dest;
171 
172 #if DEBUG_VARS
173     printf("VARS: Cloning.\n");
174 #endif
175 
176     Dest = (struct variable_list *) xmalloc(sizeof(struct variable_list));
177     if (Dest == NULL) {
178         snmp_set_api_error(SNMPERR_OS_ERR);
179         return (NULL);
180     }
181 #if DEBUG_VARS
182     printf("VARS: Copying entire variable list.  (Size %d)\n",
183            sizeof(struct variable_list));
184 #endif
185 
186     memcpy((char *) Dest, (char *) Src, sizeof(struct variable_list));
187 
188     if (Src->name != NULL) {
189         Dest->name = (oid *) xmalloc(Src->name_length * sizeof(oid));
190         if (Dest->name == NULL) {
191             snmp_set_api_error(SNMPERR_OS_ERR);
192             xfree(Dest);
193             return (NULL);
194         }
195 #if DEBUG_VARS
196         printf("VARS: Copying name OID. (Size %d)\n", Src->name_length);
197 #endif
198         memcpy((char *) Dest->name, (char *) Src->name,
199                Src->name_length * sizeof(oid));
200     }
201     /* CISCO Catalyst 2900 returns NULL strings as data of length 0. */
202     if ((Src->val.string != NULL) &&
203             (Src->val_len)) {
204         Dest->val.string = (u_char *) xmalloc(Src->val_len);
205         if (Dest->val.string == NULL) {
206             snmp_set_api_error(SNMPERR_OS_ERR);
207             xfree(Dest->name);
208             xfree(Dest);
209             return (NULL);
210         }
211 #if DEBUG_VARS
212         printf("VARS: Copying value (Size %d)\n", Src->val_len);
213 #endif
214         memcpy((char *) Dest->val.string, (char *) Src->val.string, Src->val_len);
215     }
216 #if DEBUG_VARS
217     printf("VARS: Cloned %x.\n", (unsigned int) Dest);
218 #endif
219 #if DEBUG_VARS_MALLOC
220     printf("VARS: Cloned  (%x)\n", (unsigned int) Dest);
221     printf("VARS: Name is (%x)\n", (unsigned int) Dest->name);
222 #endif
223 
224     return (Dest);
225 }
226 
227 /* Free a variable_list.
228  */
229 void
snmp_var_free(struct variable_list * Ptr)230 snmp_var_free(struct variable_list *Ptr)
231 {
232     if (Ptr->name)
233         xfree((char *) Ptr->name);
234 
235     if (Ptr->val.string)
236         xfree((char *) Ptr->val.string);
237     else if (Ptr->val.integer)
238         xfree((char *) Ptr->val.integer);
239 
240     xfree(Ptr);
241 }
242 
243 /**********************************************************************/
244 
245 /* Build a variable binding.
246  *
247  * RFC 1905: Protocol Operations for SNMPv2
248  *
249  * VarBind ::=
250  *   SEQUENCE {
251  *     name ObjectName
252  *     CHOICE {
253  *       value ObjectSyntax
254  *       unSpecified NULL
255  *       noSuchObject[0] NULL
256  *       noSuchInstance[1] NULL
257  *       endOfMibView[2] NULL
258  *     }
259  *   }
260  */
261 u_char *
snmp_var_EncodeVarBind(u_char * Buffer,int * BufLenP,variable_list * VarList,int Version)262 snmp_var_EncodeVarBind(u_char * Buffer, int *BufLenP,
263                        variable_list * VarList,
264                        int Version)
265 {
266     struct variable_list *Vars;
267     u_char *bufp;
268     u_char *HeaderStart;
269     u_char *HeaderEnd;
270     int FakeArg = *BufLenP;
271 
272     bufp = Buffer;
273 
274     for (Vars = VarList; Vars; Vars = Vars->next_variable) {
275 
276         /* Build the header for this variable
277          *
278          * Use Maximum size.
279          */
280         HeaderStart = bufp;
281         HeaderEnd = asn_build_header(HeaderStart, BufLenP,
282                                      (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
283                                      FakeArg);
284         if (HeaderEnd == NULL)
285             return (NULL);
286 
287         /* Now, let's put the Object Identifier into the buffer */
288         bufp = asn_build_objid(HeaderEnd, BufLenP,
289                                (u_char) (ASN_UNIVERSAL |
290                                          ASN_PRIMITIVE |
291                                          ASN_OBJECT_ID),
292                                Vars->name, Vars->name_length);
293         if (bufp == NULL)
294             return (NULL);
295 
296         /* Now put the data in */
297         switch (Vars->type) {
298 
299         case ASN_INTEGER:
300             bufp = asn_build_int(bufp,
301                                  BufLenP, Vars->type,
302                                  (int *) Vars->val.integer, Vars->val_len);
303             break;
304 
305         case SMI_COUNTER32:
306         case SMI_GAUGE32:
307         /*  case SMI_UNSIGNED32: */
308         case SMI_TIMETICKS:
309             bufp = asn_build_unsigned_int(bufp, BufLenP,
310                                           Vars->type,
311                                           (u_int *) Vars->val.integer, Vars->val_len);
312             break;
313 
314         case ASN_OCTET_STR:
315         case SMI_IPADDRESS:
316         case SMI_OPAQUE:
317             bufp = asn_build_string(bufp, BufLenP, Vars->type,
318                                     Vars->val.string, Vars->val_len);
319             break;
320 
321         case ASN_OBJECT_ID:
322             bufp = asn_build_objid(bufp, BufLenP, Vars->type,
323                                    (oid *) Vars->val.objid, Vars->val_len / sizeof(oid));
324             break;
325 
326         case SMI_NOSUCHINSTANCE:
327         case SMI_NOSUCHOBJECT:
328         case SMI_ENDOFMIBVIEW:
329             if (Version == SNMP_VERSION_1) {
330                 /* SNMP Version 1 does not support these error codes. */
331                 bufp = asn_build_null(bufp, BufLenP, SMI_NOSUCHOBJECT);
332             } else {
333                 bufp = asn_build_exception(bufp, BufLenP, Vars->type);
334             }
335             break;
336 
337         case ASN_NULL:
338             bufp = asn_build_null(bufp, BufLenP, Vars->type);
339             break;
340 
341         case SMI_COUNTER64:
342             snmplib_debug(2, "Unable to encode type SMI_COUNTER64!\n");
343         /* Fall through */
344 
345         default:
346             snmp_set_api_error(SNMPERR_UNSUPPORTED_TYPE);
347             return (NULL);
348         }
349 
350         /* ASSERT:  bufp should now point to the next valid byte. */
351         if (bufp == NULL)
352             return (NULL);
353 
354         /* Rebuild the header with the appropriate length */
355         HeaderEnd = asn_build_header(HeaderStart, &FakeArg,
356                                      (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
357                                      (bufp - HeaderEnd));
358 
359         /* Returns NULL */
360         if (HeaderEnd == NULL)
361             return (NULL);
362 
363     }
364 
365     /* or the end of the entire thing */
366     return (bufp);
367 }
368 
369 /* Parse all Vars from the buffer */
370 u_char *
snmp_var_DecodeVarBind(u_char * Buffer,int * BufLen,struct variable_list ** VarP,int Version)371 snmp_var_DecodeVarBind(u_char * Buffer, int *BufLen,
372                        struct variable_list ** VarP,
373                        int Version)
374 {
375     struct variable_list *Var = NULL, **VarLastP;
376     u_char *bufp, *tmp;
377     u_char VarBindType;
378     u_char *DataPtr;
379     int DataLen;
380     oid TmpBuf[MAX_NAME_LEN];
381     memset(TmpBuf, 0, MAX_NAME_LEN * sizeof(*TmpBuf));
382 
383     int AllVarLen = *BufLen;
384     int ThisVarLen = 0;
385 
386     VarLastP = VarP;
387 #if DEBUG_VARS_DECODE
388     printf("VARS: Decoding buffer of length %d\n", *BufLen);
389 #endif
390 
391     /* Now parse the variables */
392     bufp = asn_parse_header(Buffer, &AllVarLen, &VarBindType);
393     if (bufp == NULL)
394         return (NULL);
395 
396     if (VarBindType != (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR)) {
397         snmp_set_api_error(SNMPERR_PDU_PARSE);
398         return (NULL);
399     }
400 #if DEBUG_VARS_DECODE
401     printf("VARS: All Variable length %d\n", AllVarLen);
402 #endif
403 
404 #define PARSE_ERROR { snmp_var_free(Var); return(NULL); }
405 
406     /* We know how long the variable list is.  Parse it. */
407     while ((int) AllVarLen > 0) {
408 
409         /* Create a new variable */
410         Var = snmp_var_new(NULL, MAX_NAME_LEN);
411         if (Var == NULL)
412             return (NULL);
413 
414         /* Parse the header to find out the length of this variable. */
415         ThisVarLen = AllVarLen;
416         tmp = asn_parse_header(bufp, &ThisVarLen, &VarBindType);
417         if (tmp == NULL)
418             PARSE_ERROR;
419 
420         /* Now that we know the length , figure out how it relates to
421          * the entire variable list
422          */
423         AllVarLen = AllVarLen - (ThisVarLen + (tmp - bufp));
424         bufp = tmp;
425 
426         /* Is it valid? */
427         if (VarBindType != (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR)) {
428             snmp_set_api_error(SNMPERR_PDU_PARSE);
429             PARSE_ERROR;
430         }
431 #if DEBUG_VARS_DECODE
432         printf("VARS: Header type 0x%x (%d bytes left)\n", VarBindType, ThisVarLen);
433 #endif
434 
435         /* Parse the OBJID */
436         bufp = asn_parse_objid(bufp, &ThisVarLen, &VarBindType,
437                                Var->name, &(Var->name_length));
438         if (bufp == NULL)
439             PARSE_ERROR;
440 
441         if (VarBindType != (u_char) (ASN_UNIVERSAL |
442                                      ASN_PRIMITIVE |
443                                      ASN_OBJECT_ID)) {
444             snmp_set_api_error(SNMPERR_PDU_PARSE);
445             PARSE_ERROR;
446         }
447 #if DEBUG_VARS_DECODE
448         printf("VARS: Decoded OBJID (%d bytes). (%d bytes left)\n",
449                Var->name_length, ThisVarLen);
450 #endif
451 
452         /* Keep a pointer to this object */
453         DataPtr = bufp;
454         DataLen = ThisVarLen;
455 
456         /* find out type of object */
457         bufp = asn_parse_header(bufp, &ThisVarLen, &(Var->type));
458         if (bufp == NULL)
459             PARSE_ERROR;
460         ThisVarLen = DataLen;
461 
462 #if DEBUG_VARS_DECODE
463         printf("VARS: Data type %d\n", Var->type);
464 #endif
465 
466         /* Parse the type */
467 
468         switch ((short) Var->type) {
469 
470         case ASN_INTEGER:
471             Var->val.integer = (int *) xmalloc(sizeof(int));
472             if (Var->val.integer == NULL) {
473                 snmp_set_api_error(SNMPERR_OS_ERR);
474                 PARSE_ERROR;
475             }
476             Var->val_len = sizeof(int);
477             bufp = asn_parse_int(DataPtr, &ThisVarLen,
478                                  &Var->type, (int *) Var->val.integer,
479                                  Var->val_len);
480 #if DEBUG_VARS_DECODE
481             printf("VARS: Decoded integer '%d' (%d bytes left)\n",
482                    *(Var->val.integer), ThisVarLen);
483 #endif
484             break;
485 
486         case SMI_COUNTER32:
487         case SMI_GAUGE32:
488         /*  case SMI_UNSIGNED32: */
489         case SMI_TIMETICKS:
490             Var->val.integer = (int *) xmalloc(sizeof(u_int));
491             if (Var->val.integer == NULL) {
492                 snmp_set_api_error(SNMPERR_OS_ERR);
493                 PARSE_ERROR;
494             }
495             Var->val_len = sizeof(u_int);
496             bufp = asn_parse_unsigned_int(DataPtr, &ThisVarLen,
497                                           &Var->type, (u_int *) Var->val.integer,
498                                           Var->val_len);
499 #if DEBUG_VARS_DECODE
500             printf("VARS: Decoded timeticks '%d' (%d bytes left)\n",
501                    *(Var->val.integer), ThisVarLen);
502 #endif
503             break;
504 
505         case ASN_OCTET_STR:
506         case SMI_IPADDRESS:
507         case SMI_OPAQUE:
508             Var->val_len = *&ThisVarLen;    /* String is this at most */
509             Var->val.string = (u_char *) xmalloc((unsigned) Var->val_len);
510             if (Var->val.string == NULL) {
511                 snmp_set_api_error(SNMPERR_OS_ERR);
512                 PARSE_ERROR;
513             }
514             int terminatorPos = Var->val_len - 1;
515             bufp = asn_parse_string(DataPtr, &ThisVarLen,
516                                     &Var->type, Var->val.string,
517                                     &Var->val_len);
518             if (Var->val_len < terminatorPos) {
519                 terminatorPos = Var->val_len;
520             }
521             Var->val.string[terminatorPos] = '\0';
522 #if DEBUG_VARS_DECODE
523             printf("VARS: Decoded string '%s' (length %d) (%d bytes left)\n",
524                    (Var->val.string), Var->val_len, ThisVarLen);
525 #endif
526             break;
527 
528         case ASN_OBJECT_ID:
529             Var->val_len = MAX_NAME_LEN;
530             bufp = asn_parse_objid(DataPtr, &ThisVarLen,
531                                    &Var->type, TmpBuf, &Var->val_len);
532             Var->val_len *= sizeof(oid);
533             Var->val.objid = (oid *) xmalloc((unsigned) Var->val_len);
534             if (Var->val.integer == NULL) {
535                 snmp_set_api_error(SNMPERR_OS_ERR);
536                 PARSE_ERROR;
537             }
538             /* Only copy if we successfully decoded something */
539             if (bufp) {
540                 memcpy((char *) Var->val.objid, (char *) TmpBuf, Var->val_len);
541             }
542 #if DEBUG_VARS_DECODE
543             printf("VARS: Decoded OBJID (length %d) (%d bytes left)\n",
544                    Var->val_len, ThisVarLen);
545 #endif
546             break;
547 
548         case ASN_NULL:
549         case SMI_NOSUCHINSTANCE:
550         case SMI_NOSUCHOBJECT:
551         case SMI_ENDOFMIBVIEW:
552             break;
553 
554         case SMI_COUNTER64:
555             snmplib_debug(2, "Unable to parse type SMI_COUNTER64!\n");
556             snmp_set_api_error(SNMPERR_UNSUPPORTED_TYPE);
557             PARSE_ERROR;
558 
559         default:
560             snmplib_debug(2, "bad type returned (%x)\n", Var->type);
561             snmp_set_api_error(SNMPERR_PDU_PARSE);
562             PARSE_ERROR;
563         }           /* End of var type switch */
564 
565         if (bufp == NULL)
566             PARSE_ERROR;
567 
568 #if DEBUG_VARS_DECODE
569         printf("VARS:  Adding to list.\n");
570 #endif
571         /* Add variable to the list */
572         *VarLastP = Var;
573         VarLastP = &(Var->next_variable);
574     }
575 #undef PARSE_ERROR
576 
577     return (bufp);
578 }
579 
580