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 PDU Encoding
11  *
12  * Complies with:
13  *
14  * RFC 1902: Structure of Management Information for SNMPv2
15  *
16  */
17 
18 /**********************************************************************
19  *
20  *           Copyright 1997 by Carnegie Mellon University
21  *
22  *                       All Rights Reserved
23  *
24  * Permission to use, copy, modify, and distribute this software and its
25  * documentation for any purpose and without fee is hereby granted,
26  * provided that the above copyright notice appear in all copies and that
27  * both that copyright notice and this permission notice appear in
28  * supporting documentation, and that the name of CMU not be
29  * used in advertising or publicity pertaining to distribution of the
30  * software without specific, written prior permission.
31  *
32  * CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
33  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
34  * CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
35  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
36  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
37  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
38  * SOFTWARE.
39  *
40  * Author: Ryan Troll <ryan+@andrew.cmu.edu>
41  *
42  **********************************************************************/
43 
44 #include "squid.h"
45 
46 #if HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif
49 #if HAVE_STDLIB_H
50 #include <stdlib.h>
51 #endif
52 #if HAVE_SYS_TYPES_H
53 #include <sys/types.h>
54 #endif
55 #if HAVE_CTYPE_H
56 #include <ctype.h>
57 #endif
58 #if HAVE_GNUMALLOC_H
59 #include <gnumalloc.h>
60 #elif HAVE_MALLOC_H
61 #include <malloc.h>
62 #endif
63 #if HAVE_MEMORY_H
64 #include <memory.h>
65 #endif
66 #if HAVE_STRING_H
67 #include <string.h>
68 #endif
69 #if HAVE_STRINGS_H
70 #include <strings.h>
71 #endif
72 #if HAVE_BSTRING_H
73 #include <bstring.h>
74 #endif
75 #if HAVE_SYS_SOCKET_H
76 #include <sys/socket.h>
77 #endif
78 #if HAVE_NETINET_IN_H
79 #include <netinet/in.h>
80 #endif
81 #if HAVE_ARPA_INET_H
82 #include <arpa/inet.h>
83 #endif
84 #if HAVE_SYS_TIME_H
85 #include <sys/time.h>
86 #endif
87 #if HAVE_NETDB_H
88 #include <netdb.h>
89 #endif
90 
91 #include "asn1.h"
92 #include "snmp.h"
93 #include "snmp_api_error.h"
94 #include "snmp_error.h"
95 #include "snmp_msg.h"
96 #include "snmp_pdu.h"
97 #include "snmp_vars.h"
98 
99 #include "util.h"
100 
101 /* #define DEBUG_PDU 1 */
102 /* #define DEBUG_PDU_DECODE 1 */
103 /* #define DEBUG_PDU_ENCODE 1 */
104 
105 #define ASN_PARSE_ERROR(x) {  return(x); }
106 
107 /**********************************************************************/
108 
109 /* Create a PDU.
110  */
111 
112 struct snmp_pdu *
snmp_pdu_create(int command)113 snmp_pdu_create(int command) {
114     struct snmp_pdu *pdu;
115 
116 #if DEBUG_PDU
117     snmplib_debug(8, "PDU:  Creating\n");
118 #endif
119 
120     pdu = (struct snmp_pdu *) xmalloc(sizeof(struct snmp_pdu));
121     if (pdu == NULL) {
122         snmp_set_api_error(SNMPERR_OS_ERR);
123         return (NULL);
124     }
125     memset((char *) pdu, '\0', sizeof(struct snmp_pdu));
126 
127     pdu->command = command;
128     pdu->errstat = SNMP_DEFAULT_ERRSTAT;
129     pdu->errindex = SNMP_DEFAULT_ERRINDEX;
130     pdu->address.sin_addr.s_addr = SNMP_DEFAULT_ADDRESS;
131     pdu->enterprise = NULL;
132     pdu->enterprise_length = 0;
133     pdu->variables = NULL;
134 
135 #if DEBUG_PDU
136     snmplib_debug(8, "PDU:  Created %x\n", (unsigned int) pdu);
137 #endif
138 
139     return (pdu);
140 }
141 
142 /**********************************************************************/
143 
144 /* Clone an existing PDU.
145  */
146 struct snmp_pdu *
snmp_pdu_clone(struct snmp_pdu * Src)147 snmp_pdu_clone(struct snmp_pdu *Src) {
148     struct snmp_pdu *Dest;
149 
150 #if DEBUG_PDU
151     snmplib_debug(8, "PDU %x:  Cloning\n", (unsigned int) Src);
152 #endif
153 
154     Dest = (struct snmp_pdu *) xmalloc(sizeof(struct snmp_pdu));
155     if (Dest == NULL) {
156         snmp_set_api_error(SNMPERR_OS_ERR);
157         return (NULL);
158     }
159     memcpy((char *) Dest, (char *) Src, sizeof(struct snmp_pdu));
160 
161 #if DEBUG_PDU
162     snmplib_debug(8, "PDU %x:  Created %x\n", (unsigned int) Src, (unsigned int) Dest);
163 #endif
164     return (Dest);
165 }
166 
167 /**********************************************************************/
168 
169 /*
170  * If there was an error in the input pdu, creates a clone of the pdu
171  * that includes all the variables except the one marked by the errindex.
172  * The command is set to the input command and the reqid, errstat, and
173  * errindex are set to default values.
174  * If the error status didn't indicate an error, the error index didn't
175  * indicate a variable, the pdu wasn't a get response message, or there
176  * would be no remaining variables, this function will return NULL.
177  * If everything was successful, a pointer to the fixed cloned pdu will
178  * be returned.
179  */
180 struct snmp_pdu *
snmp_pdu_fix(struct snmp_pdu * pdu,int command)181 snmp_pdu_fix(struct snmp_pdu *pdu, int command) {
182     return (snmp_fix_pdu(pdu, command));
183 }
184 
185 struct snmp_pdu *
snmp_fix_pdu(struct snmp_pdu * pdu,int command)186 snmp_fix_pdu(struct snmp_pdu *pdu, int command) {
187     struct variable_list *var, *newvar;
188     struct snmp_pdu *newpdu;
189     int i;
190     int copied = 0;
191 
192 #if DEBUG_PDU
193     snmplib_debug(8, "PDU %x:  Fixing.  Err index is %d\n",
194                   (unsigned int) pdu, (unsigned int) pdu->errindex);
195 #endif
196 
197     if (pdu->command != SNMP_PDU_RESPONSE ||
198             pdu->errstat == SNMP_ERR_NOERROR ||
199             pdu->errindex <= 0) {
200         snmp_set_api_error(SNMPERR_UNABLE_TO_FIX);
201         return (NULL);
202     }
203     /* clone the pdu */
204     newpdu = snmp_pdu_clone(pdu);
205     if (newpdu == NULL)
206         return (NULL);
207 
208     newpdu->variables = 0;
209     newpdu->command = command;
210     newpdu->reqid = SNMP_DEFAULT_REQID;
211     newpdu->errstat = SNMP_DEFAULT_ERRSTAT;
212     newpdu->errindex = SNMP_DEFAULT_ERRINDEX;
213 
214     /* Loop through the variables, removing whatever isn't necessary */
215 
216     var = pdu->variables;
217     i = 1;
218 
219     /* skip first variable if necessary */
220     if (pdu->errindex == i) {
221         var = var->next_variable;
222         i++;
223     }
224     if (var != NULL) {
225 
226         /* VAR is the first uncopied variable */
227 
228         /* Clone this variable */
229         newpdu->variables = snmp_var_clone(var);
230         if (newpdu->variables == NULL) {
231             snmp_pdu_free(newpdu);
232             return (NULL);
233         }
234         copied++;
235 
236         newvar = newpdu->variables;
237 
238         /* VAR has been copied to NEWVAR. */
239         while (var->next_variable) {
240 
241             /* Skip the item that was bad */
242             if (++i == pdu->errindex) {
243                 var = var->next_variable;
244                 continue;
245             }
246             /* Copy this var */
247             newvar->next_variable = snmp_var_clone(var->next_variable);
248             if (newvar->next_variable == NULL) {
249                 snmp_pdu_free(newpdu);
250                 return (NULL);
251             }
252             /* Move to the next one */
253             newvar = newvar->next_variable;
254             var = var->next_variable;
255             copied++;
256         }
257         newvar->next_variable = NULL;
258     }
259     /* If we didn't copy anything, free the new pdu. */
260     if (i < pdu->errindex || copied == 0) {
261         snmp_free_pdu(newpdu);
262         snmp_set_api_error(SNMPERR_UNABLE_TO_FIX);
263         return (NULL);
264     }
265 #if DEBUG_PDU
266     snmplib_debug(8, "PDU %x:  Fixed PDU is %x\n",
267                   (unsigned int) pdu, (unsigned int) newpdu);
268 #endif
269     return (newpdu);
270 }
271 
272 /**********************************************************************/
273 
274 void
snmp_pdu_free(struct snmp_pdu * pdu)275 snmp_pdu_free(struct snmp_pdu *pdu)
276 {
277     snmp_free_pdu(pdu);
278 }
279 
280 /*
281  * Frees the pdu and any xmalloc'd data associated with it.
282  */
283 void
snmp_free_pdu(struct snmp_pdu * pdu)284 snmp_free_pdu(struct snmp_pdu *pdu)
285 {
286     struct variable_list *vp, *ovp;
287 
288     vp = pdu->variables;
289     while (vp) {
290         ovp = vp;
291         vp = vp->next_variable;
292         snmp_var_free(ovp);
293     }
294 
295     if (pdu->enterprise)
296         xfree((char *) pdu->enterprise);
297     xfree((char *) pdu);
298 }
299 
300 /**********************************************************************/
301 
302 /* Encode this PDU into DestBuf.
303  *
304  * Returns a pointer to the next byte in the buffer (where the Variable
305  * Bindings belong.)
306  */
307 
308 /*
309  * RFC 1902: Structure of Management Information for SNMPv2
310  *
311  *   PDU ::=
312  *    SEQUENCE {
313  *      request-id   INTEGER32
314  *      error-status INTEGER
315  *      error-index  INTEGER
316  *      Variable Bindings
317  *    }
318  *
319  * BulkPDU ::=
320  *    SEQUENCE {
321  *      request-id      INTEGER32
322  *      non-repeaters   INTEGER
323  *      max-repetitions INTEGER
324  *      Variable Bindings
325  *    }
326  */
327 
328 /*
329  * RFC 1157: A Simple Network Management Protocol (SNMP)
330  *
331  *   PDU ::=
332  *    SEQUENCE {
333  *      request-id   INTEGER
334  *      error-status INTEGER
335  *      error-index  INTEGER
336  *      Variable Bindings
337  *    }
338  *
339  *   TrapPDU ::=
340  *    SEQUENCE {
341  *      enterprise    NetworkAddress
342  *      generic-trap  INTEGER
343  *      specific-trap INTEGER
344  *      time-stamp    TIMETICKS
345  *      Variable Bindings
346  *    }
347  */
348 
349 u_char *
snmp_pdu_encode(u_char * DestBuf,int * DestBufLen,struct snmp_pdu * PDU)350 snmp_pdu_encode(u_char * DestBuf, int *DestBufLen,
351                 struct snmp_pdu *PDU)
352 {
353     u_char *bufp;
354 
355 #if DEBUG_PDU_ENCODE
356     snmplib_debug(8, "PDU: Encoding %d\n", PDU->command);
357 #endif
358 
359     /* ASN.1 Header */
360     switch (PDU->command) {
361 
362         /**********************************************************************/
363 #if TRP_REQ_MSG
364     case TRP_REQ_MSG:
365 
366         /* SNMPv1 Trap */
367 
368         /* enterprise */
369         bufp = asn_build_objid(DestBuf, DestBufLen,
370                                (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
371                                (oid *) PDU->enterprise, PDU->enterprise_length);
372         if (bufp == NULL)
373             return (NULL);
374 
375         /* agent-addr */
376         bufp = asn_build_string(bufp, DestBufLen,
377                                 (u_char) (SMI_IPADDRESS | ASN_PRIMITIVE),
378                                 (u_char *) & PDU->agent_addr.sin_addr.s_addr,
379                                 sizeof(PDU->agent_addr.sin_addr.s_addr));
380         if (bufp == NULL)
381             return (NULL);
382 
383         /* generic trap */
384         bufp = asn_build_int(bufp, DestBufLen,
385                              (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
386                              (int *) &PDU->trap_type, sizeof(PDU->trap_type));
387         if (bufp == NULL)
388             return (NULL);
389 
390         /* specific trap */
391         bufp = asn_build_int(bufp, DestBufLen,
392                              (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
393                              (int *) &PDU->specific_type,
394                              sizeof(PDU->specific_type));
395         if (bufp == NULL)
396             return (NULL);
397 
398         /* timestamp */
399         bufp = asn_build_unsigned_int(bufp, DestBufLen,
400                                       (u_char) (SMI_TIMETICKS | ASN_PRIMITIVE),
401                                       &PDU->time, sizeof(PDU->time));
402         if (bufp == NULL)
403             return (NULL);
404         break;
405 #endif
406 
407     /**********************************************************************/
408 
409     case SNMP_PDU_GETBULK:
410 
411         /* SNMPv2 Bulk Request */
412 
413         /* request id */
414         bufp = asn_build_int(DestBuf, DestBufLen,
415                              (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
416                              &PDU->reqid, sizeof(PDU->reqid));
417         if (bufp == NULL)
418             return (NULL);
419 
420         /* non-repeaters */
421         bufp = asn_build_int(bufp, DestBufLen,
422                              (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
423                              &PDU->non_repeaters,
424                              sizeof(PDU->non_repeaters));
425         if (bufp == NULL)
426             return (NULL);
427 
428         /* max-repetitions */
429         bufp = asn_build_int(bufp, DestBufLen,
430                              (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
431                              &PDU->max_repetitions,
432                              sizeof(PDU->max_repetitions));
433         if (bufp == NULL)
434             return (NULL);
435         break;
436 
437     /**********************************************************************/
438 
439     default:
440 
441         /* Normal PDU Encoding */
442 
443         /* request id */
444 #if DEBUG_PDU_ENCODE
445         snmplib_debug(8, "PDU: Request ID %d (0x%x)\n", PDU->reqid, DestBuf);
446 #endif
447         bufp = asn_build_int(DestBuf, DestBufLen,
448                              (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
449                              &PDU->reqid, sizeof(PDU->reqid));
450         if (bufp == NULL)
451             return (NULL);
452 
453         /* error status */
454 #if DEBUG_PDU_ENCODE
455         snmplib_debug(8, "PDU: Error Status %d (0x%x)\n", PDU->errstat, bufp);
456 #endif
457         bufp = asn_build_int(bufp, DestBufLen,
458                              (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
459                              &PDU->errstat, sizeof(PDU->errstat));
460         if (bufp == NULL)
461             return (NULL);
462 
463         /* error index */
464 #if DEBUG_PDU_ENCODE
465         snmplib_debug(8, "PDU: Error index %d (0x%x)\n", PDU->errindex, bufp);
466 #endif
467         bufp = asn_build_int(bufp, DestBufLen,
468                              (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
469                              &PDU->errindex, sizeof(PDU->errindex));
470         if (bufp == NULL)
471             return (NULL);
472         break;
473     }               /* End of encoding */
474 
475     return (bufp);
476 }
477 
478 /**********************************************************************/
479 
480 /* Decodes PDU from Packet into PDU.
481  *
482  * Returns a pointer to the next byte of the packet, which is where the
483  * Variable Bindings start.
484  */
485 u_char *
snmp_pdu_decode(u_char * Packet,int * Length,struct snmp_pdu * PDU)486 snmp_pdu_decode(u_char * Packet,    /* data */
487                 int *Length,        /* &length */
488                 struct snmp_pdu * PDU)
489 {   /* pdu */
490     u_char *bufp;
491     u_char PDUType;
492     u_char ASNType;
493 #if UNUSED_CODE
494     int four;
495     oid objid[MAX_NAME_LEN];
496 #endif
497 
498     bufp = asn_parse_header(Packet, Length, &PDUType);
499     if (bufp == NULL)
500         ASN_PARSE_ERROR(NULL);
501 
502 #if DEBUG_PDU_DECODE
503     snmplib_debug(8, "PDU Type: %d\n", PDUType);
504 #endif
505 
506     PDU->command = PDUType;
507     switch (PDUType) {
508 
509 #if TRP_REQ_MSG
510     case TRP_REQ_MSG:
511 
512         /* SNMPv1 Trap Message */
513 
514         /* enterprise */
515         PDU->enterprise_length = MAX_NAME_LEN;
516         bufp = asn_parse_objid(bufp, Length,
517                                &ASNType, objid, &PDU->enterprise_length);
518         if (bufp == NULL)
519             ASN_PARSE_ERROR(NULL);
520 
521         PDU->enterprise = (oid *) xmalloc(PDU->enterprise_length * sizeof(oid));
522         if (PDU->enterprise == NULL) {
523             snmp_set_api_error(SNMPERR_OS_ERR);
524             return (NULL);
525         }
526         memcpy((char *) PDU->enterprise, (char *) objid,
527                PDU->enterprise_length * sizeof(oid));
528 
529         /* Agent-addr */
530         four = 4;
531         bufp = asn_parse_string(bufp, Length,
532                                 &ASNType,
533                                 (u_char *) & PDU->agent_addr.sin_addr.s_addr,
534                                 &four);
535         if (bufp == NULL)
536             ASN_PARSE_ERROR(NULL);
537 
538         /* Generic trap */
539         bufp = asn_parse_int(bufp, Length,
540                              &ASNType,
541                              (int *) &PDU->trap_type,
542                              sizeof(PDU->trap_type));
543         if (bufp == NULL)
544             ASN_PARSE_ERROR(NULL);
545 
546         /* Specific Trap */
547         bufp = asn_parse_int(bufp, Length,
548                              &ASNType,
549                              (int *) &PDU->specific_type,
550                              sizeof(PDU->specific_type));
551         if (bufp == NULL)
552             ASN_PARSE_ERROR(NULL);
553 
554         /* Timestamp */
555         bufp = asn_parse_unsigned_int(bufp, Length,
556                                       &ASNType,
557                                       &PDU->time, sizeof(PDU->time));
558         if (bufp == NULL)
559             ASN_PARSE_ERROR(NULL);
560         break;
561 #endif
562 
563     /**********************************************************************/
564 
565     case SNMP_PDU_GETBULK:
566 
567         /* SNMPv2 Bulk Request */
568 
569         /* request id */
570         bufp = asn_parse_int(bufp, Length,
571                              &ASNType,
572                              &PDU->reqid, sizeof(PDU->reqid));
573         if (bufp == NULL)
574             ASN_PARSE_ERROR(NULL);
575 
576         /* non-repeaters */
577         bufp = asn_parse_int(bufp, Length,
578                              &ASNType,
579                              &PDU->non_repeaters, sizeof(PDU->non_repeaters));
580         if (bufp == NULL)
581             ASN_PARSE_ERROR(NULL);
582 
583         /* max-repetitions */
584         bufp = asn_parse_int(bufp, Length,
585                              &ASNType,
586                              &PDU->max_repetitions, sizeof(PDU->max_repetitions));
587         if (bufp == NULL)
588             ASN_PARSE_ERROR(NULL);
589         break;
590 
591     /**********************************************************************/
592 
593     default:
594 
595         /* Normal PDU Encoding */
596 
597         /* request id */
598         bufp = asn_parse_int(bufp, Length,
599                              &ASNType,
600                              &PDU->reqid, sizeof(PDU->reqid));
601         if (bufp == NULL)
602             ASN_PARSE_ERROR(NULL);
603 
604 #if DEBUG_PDU_DECODE
605         snmplib_debug(8, "PDU Request ID: %d\n", PDU->reqid);
606 #endif
607 
608         /* error status */
609         bufp = asn_parse_int(bufp, Length,
610                              &ASNType,
611                              &PDU->errstat, sizeof(PDU->errstat));
612         if (bufp == NULL)
613             ASN_PARSE_ERROR(NULL);
614 
615 #if DEBUG_PDU_DECODE
616         snmplib_debug(8, "PDU Error Status: %d\n", PDU->errstat);
617 #endif
618 
619         /* error index */
620         bufp = asn_parse_int(bufp, Length,
621                              &ASNType,
622                              &PDU->errindex, sizeof(PDU->errindex));
623         if (bufp == NULL)
624             ASN_PARSE_ERROR(NULL);
625 
626 #if DEBUG_PDU_DECODE
627         snmplib_debug(8, "PDU Error Index: %d\n", PDU->errindex);
628 #endif
629 
630         break;
631     }
632 
633     return (bufp);
634 }
635 
636 /*
637  * Add a null variable with the requested name to the end of the list of
638  * variables for this pdu.
639  */
640 void
snmp_add_null_var(struct snmp_pdu * pdu,oid * name,int name_length)641 snmp_add_null_var(struct snmp_pdu *pdu, oid * name, int name_length)
642 {
643     struct variable_list *vars;
644     struct variable_list *ptr;
645 
646     vars = snmp_var_new(name, name_length);
647     if (vars == NULL) {
648         perror("snmp_add_null_var:xmalloc");
649         return;
650     }
651     if (pdu->variables == NULL) {
652         pdu->variables = vars;
653     } else {
654 
655         /* Insert at the end */
656         for (ptr = pdu->variables;
657                 ptr->next_variable;
658                 ptr = ptr->next_variable)
659             /*EXIT */ ;
660         ptr->next_variable = vars;
661     }
662 
663     return;
664 }
665 
666