xref: /dragonfly/contrib/tcpdump/print-snmp.c (revision 59c07fbdf8168fa08c76c515186d561b5a92690c)
1 /*
2  * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
3  *     John Robert LoVerso. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  *
28  * This implementation has been influenced by the CMU SNMP release,
29  * by Steve Waldbusser.  However, this shares no code with that system.
30  * Additional ASN.1 insight gained from Marshall T. Rose's _The_Open_Book_.
31  * Earlier forms of this implementation were derived and/or inspired by an
32  * awk script originally written by C. Philip Wood of LANL (but later
33  * heavily modified by John Robert LoVerso).  The copyright notice for
34  * that work is preserved below, even though it may not rightly apply
35  * to this file.
36  *
37  * Support for SNMPv2c/SNMPv3 and the ability to link the module against
38  * the libsmi was added by J. Schoenwaelder, Copyright (c) 1999.
39  *
40  * This started out as a very simple program, but the incremental decoding
41  * (into the BE structure) complicated things.
42  *
43  #                            Los Alamos National Laboratory
44  #
45  #        Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
46  #        This software was produced under a U.S. Government contract
47  #        (W-7405-ENG-36) by Los Alamos National Laboratory, which is
48  #        operated by the     University of California for the U.S. Department
49  #        of Energy.  The U.S. Government is licensed to use, reproduce,
50  #        and distribute this software.  Permission is granted to the
51  #        public to copy and use this software without charge, provided
52  #        that this Notice and any statement of authorship are reproduced
53  #        on all copies.  Neither the Government nor the University makes
54  #        any warranty, express or implied, or assumes any liability or
55  #        responsibility for the use of this software.
56  #        @(#)snmp.awk.x      1.1 (LANL) 1/15/90
57  */
58 
59 /* \summary: Simple Network Management Protocol (SNMP) printer */
60 
61 #ifdef HAVE_CONFIG_H
62 #include <config.h>
63 #endif
64 
65 #include "netdissect-stdinc.h"
66 
67 #include <stdio.h>
68 #include <string.h>
69 
70 #ifdef USE_LIBSMI
71 #include <smi.h>
72 #endif
73 
74 #include "netdissect-ctype.h"
75 
76 #include "netdissect.h"
77 #include "extract.h"
78 
79 #undef OPAQUE  /* defined in <wingdi.h> */
80 
81 
82 /*
83  * Universal ASN.1 types
84  * (we only care about the tag values for those allowed in the Internet SMI)
85  */
86 static const char *Universal[] = {
87           "U-0",
88           "Boolean",
89           "Integer",
90 #define INTEGER 2
91           "Bitstring",
92           "String",
93 #define STRING 4
94           "Null",
95 #define ASN_NULL 5
96           "ObjID",
97 #define OBJECTID 6
98           "ObjectDes",
99           "U-8","U-9","U-10","U-11",    /* 8-11 */
100           "U-12","U-13","U-14","U-15",  /* 12-15 */
101           "Sequence",
102 #define SEQUENCE 16
103           "Set"
104 };
105 
106 /*
107  * Application-wide ASN.1 types from the Internet SMI and their tags
108  */
109 static const char *Application[] = {
110           "IpAddress",
111 #define IPADDR 0
112           "Counter",
113 #define COUNTER 1
114           "Gauge",
115 #define GAUGE 2
116           "TimeTicks",
117 #define TIMETICKS 3
118           "Opaque",
119 #define OPAQUE 4
120           "C-5",
121           "Counter64"
122 #define COUNTER64 6
123 };
124 
125 /*
126  * Context-specific ASN.1 types for the SNMP PDUs and their tags
127  */
128 static const char *Context[] = {
129           "GetRequest",
130 #define GETREQ 0
131           "GetNextRequest",
132 #define GETNEXTREQ 1
133           "GetResponse",
134 #define GETRESP 2
135           "SetRequest",
136 #define SETREQ 3
137           "Trap",
138 #define TRAP 4
139           "GetBulk",
140 #define GETBULKREQ 5
141           "Inform",
142 #define INFORMREQ 6
143           "V2Trap",
144 #define V2TRAP 7
145           "Report"
146 #define REPORT 8
147 };
148 
149 #define NOTIFY_CLASS(x)           (x == TRAP || x == V2TRAP || x == INFORMREQ)
150 #define READ_CLASS(x)       (x == GETREQ || x == GETNEXTREQ || x == GETBULKREQ)
151 #define WRITE_CLASS(x)            (x == SETREQ)
152 #define RESPONSE_CLASS(x)   (x == GETRESP)
153 #define INTERNAL_CLASS(x)   (x == REPORT)
154 
155 /*
156  * Context-specific ASN.1 types for the SNMP Exceptions and their tags
157  */
158 static const char *Exceptions[] = {
159           "noSuchObject",
160 #define NOSUCHOBJECT 0
161           "noSuchInstance",
162 #define NOSUCHINSTANCE 1
163           "endOfMibView",
164 #define ENDOFMIBVIEW 2
165 };
166 
167 /*
168  * Private ASN.1 types
169  * The Internet SMI does not specify any
170  */
171 static const char *Private[] = {
172           "P-0"
173 };
174 
175 /*
176  * error-status values for any SNMP PDU
177  */
178 static const char *ErrorStatus[] = {
179           "noError",
180           "tooBig",
181           "noSuchName",
182           "badValue",
183           "readOnly",
184           "genErr",
185           "noAccess",
186           "wrongType",
187           "wrongLength",
188           "wrongEncoding",
189           "wrongValue",
190           "noCreation",
191           "inconsistentValue",
192           "resourceUnavailable",
193           "commitFailed",
194           "undoFailed",
195           "authorizationError",
196           "notWritable",
197           "inconsistentName"
198 };
199 #define DECODE_ErrorStatus(e) \
200           ( e >= 0 && (size_t)e < sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \
201                     ? ErrorStatus[e] \
202                     : (snprintf(errbuf, sizeof(errbuf), "err=%u", e), errbuf))
203 
204 /*
205  * generic-trap values in the SNMP Trap-PDU
206  */
207 static const char *GenericTrap[] = {
208           "coldStart",
209           "warmStart",
210           "linkDown",
211           "linkUp",
212           "authenticationFailure",
213           "egpNeighborLoss",
214           "enterpriseSpecific"
215 #define GT_ENTERPRISE 6
216 };
217 #define DECODE_GenericTrap(t) \
218           ( t >= 0 && (size_t)t < sizeof(GenericTrap)/sizeof(GenericTrap[0]) \
219                     ? GenericTrap[t] \
220                     : (snprintf(buf, sizeof(buf), "gt=%d", t), buf))
221 
222 /*
223  * ASN.1 type class table
224  * Ties together the preceding Universal, Application, Context, and Private
225  * type definitions.
226  */
227 #define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */
228 static const struct {
229           const char          *name;
230           const char          **Id;
231               int   numIDs;
232     } Class[] = {
233           defineCLASS(Universal),
234 #define   UNIVERSAL 0
235           defineCLASS(Application),
236 #define   APPLICATION         1
237           defineCLASS(Context),
238 #define   CONTEXT             2
239           defineCLASS(Private),
240 #define   PRIVATE             3
241           defineCLASS(Exceptions),
242 #define EXCEPTIONS  4
243 };
244 
245 /*
246  * defined forms for ASN.1 types
247  */
248 static const char *Form[] = {
249           "Primitive",
250 #define PRIMITIVE   0
251           "Constructed",
252 #define CONSTRUCTED 1
253 };
254 
255 /*
256  * A structure for the OID tree for the compiled-in MIB.
257  * This is stored as a general-order tree.
258  */
259 static struct obj {
260           const char          *desc;              /* name of object */
261           u_char    oid;                          /* sub-id following parent */
262           u_char    type;                         /* object type (unused) */
263           struct obj *child, *next;     /* child and next sibling pointers */
264 } *objp = NULL;
265 
266 /*
267  * Include the compiled in SNMP MIB.  "mib.h" is produced by feeding
268  * RFC-1156 format files into "makemib".  "mib.h" MUST define at least
269  * a value for `mibroot'.
270  *
271  * In particular, this is gross, as this is including initialized structures,
272  * and by right shouldn't be an "include" file.
273  */
274 #include "mib.h"
275 
276 /*
277  * This defines a list of OIDs which will be abbreviated on output.
278  * Currently, this includes the prefixes for the Internet MIB, the
279  * private enterprises tree, and the experimental tree.
280  */
281 #define OID_FIRST_OCTET(x, y) (((x)*40) + (y))    /* X.690 8.19.4 */
282 
283 #ifndef NO_ABREV_MIB
284 static const uint8_t mib_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 2, 1 };
285 #endif
286 #ifndef NO_ABREV_ENTER
287 static const uint8_t enterprises_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 4, 1 };
288 #endif
289 #ifndef NO_ABREV_EXPERI
290 static const uint8_t experimental_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 3 };
291 #endif
292 #ifndef NO_ABBREV_SNMPMODS
293 static const uint8_t snmpModules_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 6, 3 };
294 #endif
295 
296 #define OBJ_ABBREV_ENTRY(prefix, obj) \
297           { prefix, &_ ## obj ## _obj, obj ## _oid, sizeof (obj ## _oid) }
298 static const struct obj_abrev {
299           const char *prefix;           /* prefix for this abrev */
300           struct obj *node;             /* pointer into object table */
301           const uint8_t *oid;           /* ASN.1 encoded OID */
302           size_t oid_len;                         /* length of OID */
303 } obj_abrev_list[] = {
304 #ifndef NO_ABREV_MIB
305           /* .iso.org.dod.internet.mgmt.mib */
306           OBJ_ABBREV_ENTRY("",          mib),
307 #endif
308 #ifndef NO_ABREV_ENTER
309           /* .iso.org.dod.internet.private.enterprises */
310           OBJ_ABBREV_ENTRY("E:",        enterprises),
311 #endif
312 #ifndef NO_ABREV_EXPERI
313           /* .iso.org.dod.internet.experimental */
314           OBJ_ABBREV_ENTRY("X:",        experimental),
315 #endif
316 #ifndef NO_ABBREV_SNMPMODS
317           /* .iso.org.dod.internet.snmpV2.snmpModules */
318           OBJ_ABBREV_ENTRY("S:",        snmpModules),
319 #endif
320           { 0,0,0,0 }
321 };
322 
323 /*
324  * This is used in the OID print routine to walk down the object tree
325  * rooted at `mibroot'.
326  */
327 #define OBJ_PRINT(o, suppressdot) \
328 { \
329           if (objp) { \
330                     do { \
331                               if ((o) == objp->oid) \
332                                         break; \
333                     } while ((objp = objp->next) != NULL); \
334           } \
335           if (objp) { \
336                     ND_PRINT(suppressdot?"%s":".%s", objp->desc); \
337                     objp = objp->child; \
338           } else \
339                     ND_PRINT(suppressdot?"%u":".%u", (o)); \
340 }
341 
342 /*
343  * This is the definition for the Any-Data-Type storage used purely for
344  * temporary internal representation while decoding an ASN.1 data stream.
345  */
346 struct be {
347           uint32_t asnlen;
348           union {
349                     const uint8_t *raw;
350                     int32_t integer;
351                     uint32_t uns;
352                     const u_char *str;
353                     uint64_t uns64;
354           } data;
355           u_short id;
356           u_char form, class;           /* tag info */
357           u_char type;
358 #define BE_ANY                255
359 #define BE_NONE               0
360 #define BE_NULL               1
361 #define BE_OCTET    2
362 #define BE_OID                3
363 #define BE_INT                4
364 #define BE_UNS                5
365 #define BE_STR                6
366 #define BE_SEQ                7
367 #define BE_INETADDR 8
368 #define BE_PDU                9
369 #define BE_UNS64    10
370 #define BE_NOSUCHOBJECT       128
371 #define BE_NOSUCHINST         129
372 #define BE_ENDOFMIBVIEW       130
373 };
374 
375 /*
376  * SNMP versions recognized by this module
377  */
378 static const char *SnmpVersion[] = {
379           "SNMPv1",
380 #define SNMP_VERSION_1        0
381           "SNMPv2c",
382 #define SNMP_VERSION_2        1
383           "SNMPv2u",
384 #define SNMP_VERSION_2U       2
385           "SNMPv3"
386 #define SNMP_VERSION_3        3
387 };
388 
389 /*
390  * Defaults for SNMP PDU components
391  */
392 #define DEF_COMMUNITY "public"
393 
394 /*
395  * constants for ASN.1 decoding
396  */
397 #define OIDMUX 40
398 #define ASNLEN_INETADDR 4
399 #define ASN_SHIFT7 7
400 #define ASN_SHIFT8 8
401 #define ASN_BIT8 0x80
402 #define ASN_LONGLEN 0x80
403 
404 #define ASN_ID_BITS 0x1f
405 #define ASN_FORM_BITS 0x20
406 #define ASN_FORM_SHIFT 5
407 #define ASN_CLASS_BITS 0xc0
408 #define ASN_CLASS_SHIFT 6
409 
410 #define ASN_ID_EXT 0x1f                 /* extension ID in tag field */
411 
412 /*
413  * This decodes the next ASN.1 object in the stream pointed to by "p"
414  * (and of real-length "len") and stores the intermediate data in the
415  * provided BE object.
416  *
417  * This returns -l if it fails (i.e., the ASN.1 stream is not valid).
418  * O/w, this returns the number of bytes parsed from "p".
419  */
420 static int
asn1_parse(netdissect_options * ndo,const u_char * p,u_int len,struct be * elem)421 asn1_parse(netdissect_options *ndo,
422            const u_char *p, u_int len, struct be *elem)
423 {
424           u_char form, class, id;
425           u_int i, hdr;
426 
427           elem->asnlen = 0;
428           elem->type = BE_ANY;
429           if (len < 1) {
430                     ND_PRINT("[nothing to parse]");
431                     return -1;
432           }
433 
434           /*
435            * it would be nice to use a bit field, but you can't depend on them.
436            *  +---+---+---+---+---+---+---+---+
437            *  + class |frm|        id         |
438            *  +---+---+---+---+---+---+---+---+
439            *    7   6   5   4   3   2   1   0
440            */
441           id = GET_U_1(p) & ASN_ID_BITS;                    /* lower 5 bits, range 00-1f */
442 #ifdef notdef
443           form = (GET_U_1(p) & 0xe0) >> 5;        /* move upper 3 bits to lower 3 */
444           class = form >> 1;            /* bits 7&6 -> bits 1&0, range 0-3 */
445           form &= 0x1;                            /* bit 5 -> bit 0, range 0-1 */
446 #else
447           form = (u_char)(GET_U_1(p) & ASN_FORM_BITS) >> ASN_FORM_SHIFT;
448           class = (u_char)(GET_U_1(p) & ASN_CLASS_BITS) >> ASN_CLASS_SHIFT;
449 #endif
450           elem->form = form;
451           elem->class = class;
452           elem->id = id;
453           p++; len--; hdr = 1;
454           /* extended tag field */
455           if (id == ASN_ID_EXT) {
456                     /*
457                      * The ID follows, as a sequence of octets with the
458                      * 8th bit set and the remaining 7 bits being
459                      * the next 7 bits of the value, terminated with
460                      * an octet with the 8th bit not set.
461                      *
462                      * First, assemble all the octets with the 8th
463                      * bit set.  XXX - this doesn't handle a value
464                      * that won't fit in 32 bits.
465                      */
466                     id = 0;
467                     while (GET_U_1(p) & ASN_BIT8) {
468                               if (len < 1) {
469                                         ND_PRINT("[Xtagfield?]");
470                                         return -1;
471                               }
472                               id = (id << 7) | (GET_U_1(p) & ~ASN_BIT8);
473                               len--;
474                               hdr++;
475                               p++;
476                     }
477                     if (len < 1) {
478                               ND_PRINT("[Xtagfield?]");
479                               return -1;
480                     }
481                     elem->id = id = (id << 7) | GET_U_1(p);
482                     --len;
483                     ++hdr;
484                     ++p;
485           }
486           if (len < 1) {
487                     ND_PRINT("[no asnlen]");
488                     return -1;
489           }
490           elem->asnlen = GET_U_1(p);
491           p++; len--; hdr++;
492           if (elem->asnlen & ASN_BIT8) {
493                     uint32_t noct = elem->asnlen % ASN_BIT8;
494                     elem->asnlen = 0;
495                     if (len < noct) {
496                               ND_PRINT("[asnlen? %d<%d]", len, noct);
497                               return -1;
498                     }
499                     ND_TCHECK_LEN(p, noct);
500                     for (; noct != 0; len--, hdr++, noct--) {
501                               elem->asnlen = (elem->asnlen << ASN_SHIFT8) | GET_U_1(p);
502                               p++;
503                     }
504           }
505           if (len < elem->asnlen) {
506                     ND_PRINT("[len%d<asnlen%u]", len, elem->asnlen);
507                     return -1;
508           }
509           if (form >= sizeof(Form)/sizeof(Form[0])) {
510                     ND_PRINT("[form?%d]", form);
511                     return -1;
512           }
513           if (class >= sizeof(Class)/sizeof(Class[0])) {
514                     ND_PRINT("[class?%c/%d]", *Form[form], class);
515                     return -1;
516           }
517           if ((int)id >= Class[class].numIDs) {
518                     ND_PRINT("[id?%c/%s/%d]", *Form[form], Class[class].name, id);
519                     return -1;
520           }
521           ND_TCHECK_LEN(p, elem->asnlen);
522 
523           switch (form) {
524           case PRIMITIVE:
525                     switch (class) {
526                     case UNIVERSAL:
527                               switch (id) {
528                               case STRING:
529                                         elem->type = BE_STR;
530                                         elem->data.str = p;
531                                         break;
532 
533                               case INTEGER: {
534                                         int32_t data;
535                                         elem->type = BE_INT;
536                                         data = 0;
537 
538                                         if (elem->asnlen == 0) {
539                                                   ND_PRINT("[asnlen=0]");
540                                                   return -1;
541                                         }
542                                         if (GET_U_1(p) & ASN_BIT8)    /* negative */
543                                                   data = -1;
544                                         for (i = elem->asnlen; i != 0; p++, i--)
545                                                   data = (data << ASN_SHIFT8) | GET_U_1(p);
546                                         elem->data.integer = data;
547                                         break;
548                               }
549 
550                               case OBJECTID:
551                                         elem->type = BE_OID;
552                                         elem->data.raw = (const uint8_t *)p;
553                                         break;
554 
555                               case ASN_NULL:
556                                         elem->type = BE_NULL;
557                                         elem->data.raw = NULL;
558                                         break;
559 
560                               default:
561                                         elem->type = BE_OCTET;
562                                         elem->data.raw = (const uint8_t *)p;
563                                         ND_PRINT("[P/U/%s]", Class[class].Id[id]);
564                                         break;
565                               }
566                               break;
567 
568                     case APPLICATION:
569                               switch (id) {
570                               case IPADDR:
571                                         elem->type = BE_INETADDR;
572                                         elem->data.raw = (const uint8_t *)p;
573                                         break;
574 
575                               case COUNTER:
576                               case GAUGE:
577                               case TIMETICKS: {
578                                         uint32_t data;
579                                         elem->type = BE_UNS;
580                                         data = 0;
581                                         for (i = elem->asnlen; i != 0; p++, i--)
582                                                   data = (data << 8) + GET_U_1(p);
583                                         elem->data.uns = data;
584                                         break;
585                               }
586 
587                               case COUNTER64: {
588                                         uint64_t data64;
589                                       elem->type = BE_UNS64;
590                                         data64 = 0;
591                                         for (i = elem->asnlen; i != 0; p++, i--)
592                                                   data64 = (data64 << 8) + GET_U_1(p);
593                                         elem->data.uns64 = data64;
594                                         break;
595                               }
596 
597                               default:
598                                         elem->type = BE_OCTET;
599                                         elem->data.raw = (const uint8_t *)p;
600                                         ND_PRINT("[P/A/%s]",
601                                                   Class[class].Id[id]);
602                                         break;
603                               }
604                               break;
605 
606                     case CONTEXT:
607                               switch (id) {
608                               case NOSUCHOBJECT:
609                                         elem->type = BE_NOSUCHOBJECT;
610                                         elem->data.raw = NULL;
611                                         break;
612 
613                               case NOSUCHINSTANCE:
614                                         elem->type = BE_NOSUCHINST;
615                                         elem->data.raw = NULL;
616                                         break;
617 
618                               case ENDOFMIBVIEW:
619                                         elem->type = BE_ENDOFMIBVIEW;
620                                         elem->data.raw = NULL;
621                                         break;
622                               }
623                               break;
624 
625                     default:
626                               ND_PRINT("[P/%s/%s]", Class[class].name, Class[class].Id[id]);
627                               elem->type = BE_OCTET;
628                               elem->data.raw = (const uint8_t *)p;
629                               break;
630                     }
631                     break;
632 
633           case CONSTRUCTED:
634                     switch (class) {
635                     case UNIVERSAL:
636                               switch (id) {
637                               case SEQUENCE:
638                                         elem->type = BE_SEQ;
639                                         elem->data.raw = (const uint8_t *)p;
640                                         break;
641 
642                               default:
643                                         elem->type = BE_OCTET;
644                                         elem->data.raw = (const uint8_t *)p;
645                                         ND_PRINT("C/U/%s", Class[class].Id[id]);
646                                         break;
647                               }
648                               break;
649 
650                     case CONTEXT:
651                               elem->type = BE_PDU;
652                               elem->data.raw = (const uint8_t *)p;
653                               break;
654 
655                     default:
656                               elem->type = BE_OCTET;
657                               elem->data.raw = (const uint8_t *)p;
658                               ND_PRINT("C/%s/%s", Class[class].name, Class[class].Id[id]);
659                               break;
660                     }
661                     break;
662           }
663           p += elem->asnlen;
664           len -= elem->asnlen;
665           return elem->asnlen + hdr;
666 
667 trunc:
668           nd_print_trunc(ndo);
669           return -1;
670 }
671 
672 static int
asn1_print_octets(netdissect_options * ndo,struct be * elem)673 asn1_print_octets(netdissect_options *ndo, struct be *elem)
674 {
675           const u_char *p = (const u_char *)elem->data.raw;
676           uint32_t asnlen = elem->asnlen;
677           uint32_t i;
678 
679           ND_TCHECK_LEN(p, asnlen);
680           for (i = asnlen; i != 0; p++, i--)
681                     ND_PRINT("_%.2x", GET_U_1(p));
682           return 0;
683 
684 trunc:
685           nd_print_trunc(ndo);
686           return -1;
687 }
688 
689 static int
asn1_print_string(netdissect_options * ndo,struct be * elem)690 asn1_print_string(netdissect_options *ndo, struct be *elem)
691 {
692           int printable = 1, first = 1;
693           const u_char *p;
694           uint32_t asnlen = elem->asnlen;
695           uint32_t i;
696 
697           p = elem->data.str;
698           ND_TCHECK_LEN(p, asnlen);
699           for (i = asnlen; printable && i != 0; p++, i--)
700                     printable = ND_ASCII_ISPRINT(GET_U_1(p));
701           p = elem->data.str;
702           if (printable) {
703                     ND_PRINT("\"");
704                     if (nd_printn(ndo, p, asnlen, ndo->ndo_snapend)) {
705                               ND_PRINT("\"");
706                               goto trunc;
707                     }
708                     ND_PRINT("\"");
709           } else {
710                     for (i = asnlen; i != 0; p++, i--) {
711                               ND_PRINT(first ? "%.2x" : "_%.2x", GET_U_1(p));
712                               first = 0;
713                     }
714           }
715           return 0;
716 
717 trunc:
718           nd_print_trunc(ndo);
719           return -1;
720 }
721 
722 /*
723  * Display the ASN.1 object represented by the BE object.
724  * This used to be an integral part of asn1_parse() before the intermediate
725  * BE form was added.
726  */
727 static int
asn1_print(netdissect_options * ndo,struct be * elem)728 asn1_print(netdissect_options *ndo,
729            struct be *elem)
730 {
731           const u_char *p;
732           uint32_t asnlen = elem->asnlen;
733           uint32_t i;
734 
735           switch (elem->type) {
736 
737           case BE_OCTET:
738                     if (asn1_print_octets(ndo, elem) == -1)
739                               return -1;
740                     break;
741 
742           case BE_NULL:
743                     break;
744 
745           case BE_OID: {
746                     int o = 0, first = -1;
747 
748                     p = (const u_char *)elem->data.raw;
749                     i = asnlen;
750                     if (!ndo->ndo_nflag && asnlen > 2) {
751                               const struct obj_abrev *a = &obj_abrev_list[0];
752                               for (; a->node; a++) {
753                                         if (i < a->oid_len)
754                                                   continue;
755                                         if (!ND_TTEST_LEN(p, a->oid_len))
756                                                   continue;
757                                         if (memcmp(a->oid, p, a->oid_len) == 0) {
758                                                   objp = a->node->child;
759                                                   i -= a->oid_len;
760                                                   p += a->oid_len;
761                                                   ND_PRINT("%s", a->prefix);
762                                                   first = 1;
763                                                   break;
764                                         }
765                               }
766                     }
767 
768                     for (; i != 0; p++, i--) {
769                               o = (o << ASN_SHIFT7) + (GET_U_1(p) & ~ASN_BIT8);
770                               if (GET_U_1(p) & ASN_LONGLEN)
771                                       continue;
772 
773                               /*
774                                * first subitem encodes two items with
775                                * 1st*OIDMUX+2nd
776                                * (see X.690:1997 clause 8.19 for the details)
777                                */
778                               if (first < 0) {
779                                       int s;
780                                         if (!ndo->ndo_nflag)
781                                                   objp = mibroot;
782                                         first = 0;
783                                         s = o / OIDMUX;
784                                         if (s > 2) s = 2;
785                                         OBJ_PRINT(s, first);
786                                         o -= s * OIDMUX;
787                               }
788                               OBJ_PRINT(o, first);
789                               if (--first < 0)
790                                         first = 0;
791                               o = 0;
792                     }
793                     break;
794           }
795 
796           case BE_INT:
797                     ND_PRINT("%d", elem->data.integer);
798                     break;
799 
800           case BE_UNS:
801                     ND_PRINT("%u", elem->data.uns);
802                     break;
803 
804           case BE_UNS64:
805                     ND_PRINT("%" PRIu64, elem->data.uns64);
806                     break;
807 
808           case BE_STR:
809                     if (asn1_print_string(ndo, elem) == -1)
810                               return -1;
811                     break;
812 
813           case BE_SEQ:
814                     ND_PRINT("Seq(%u)", elem->asnlen);
815                     break;
816 
817           case BE_INETADDR:
818                     if (asnlen != ASNLEN_INETADDR)
819                               ND_PRINT("[inetaddr len!=%d]", ASNLEN_INETADDR);
820                     p = (const u_char *)elem->data.raw;
821                     ND_TCHECK_LEN(p, asnlen);
822                     for (i = asnlen; i != 0; p++, i--) {
823                               ND_PRINT((i == asnlen) ? "%u" : ".%u", GET_U_1(p));
824                     }
825                     break;
826 
827           case BE_NOSUCHOBJECT:
828           case BE_NOSUCHINST:
829           case BE_ENDOFMIBVIEW:
830                     ND_PRINT("[%s]", Class[EXCEPTIONS].Id[elem->id]);
831                     break;
832 
833           case BE_PDU:
834                     ND_PRINT("%s(%u)", Class[CONTEXT].Id[elem->id], elem->asnlen);
835                     break;
836 
837           case BE_ANY:
838                     ND_PRINT("[BE_ANY!?]");
839                     break;
840 
841           default:
842                     ND_PRINT("[be!?]");
843                     break;
844           }
845           return 0;
846 
847 trunc:
848           nd_print_trunc(ndo);
849           return -1;
850 }
851 
852 #ifdef notdef
853 /*
854  * This is a brute force ASN.1 printer: recurses to dump an entire structure.
855  * This will work for any ASN.1 stream, not just an SNMP PDU.
856  *
857  * By adding newlines and spaces at the correct places, this would print in
858  * Rose-Normal-Form.
859  *
860  * This is not currently used.
861  */
862 static void
asn1_decode(u_char * p,u_int length)863 asn1_decode(u_char *p, u_int length)
864 {
865           struct be elem;
866           int i = 0;
867 
868           while (i >= 0 && length > 0) {
869                     i = asn1_parse(ndo, p, length, &elem);
870                     if (i >= 0) {
871                               ND_PRINT(" ");
872                               if (asn1_print(ndo, &elem) < 0)
873                                         return;
874                               if (elem.type == BE_SEQ || elem.type == BE_PDU) {
875                                         ND_PRINT(" {");
876                                         asn1_decode(elem.data.raw, elem.asnlen);
877                                         ND_PRINT(" }");
878                               }
879                               length -= i;
880                               p += i;
881                     }
882           }
883 }
884 #endif
885 
886 #ifdef USE_LIBSMI
887 
888 struct smi2be {
889     SmiBasetype basetype;
890     int be;
891 };
892 
893 static const struct smi2be smi2betab[] = {
894     { SMI_BASETYPE_INTEGER32,           BE_INT },
895     { SMI_BASETYPE_OCTETSTRING,                   BE_STR },
896     { SMI_BASETYPE_OCTETSTRING,                   BE_INETADDR },
897     { SMI_BASETYPE_OBJECTIDENTIFIER,    BE_OID },
898     { SMI_BASETYPE_UNSIGNED32,                    BE_UNS },
899     { SMI_BASETYPE_INTEGER64,           BE_NONE },
900     { SMI_BASETYPE_UNSIGNED64,                    BE_UNS64 },
901     { SMI_BASETYPE_FLOAT32,             BE_NONE },
902     { SMI_BASETYPE_FLOAT64,             BE_NONE },
903     { SMI_BASETYPE_FLOAT128,            BE_NONE },
904     { SMI_BASETYPE_ENUM,                BE_INT },
905     { SMI_BASETYPE_BITS,                BE_STR },
906     { SMI_BASETYPE_UNKNOWN,             BE_NONE }
907 };
908 
909 static int
smi_decode_oid(netdissect_options * ndo,struct be * elem,unsigned int * oid,unsigned int oidsize,unsigned int * oidlen)910 smi_decode_oid(netdissect_options *ndo,
911                struct be *elem, unsigned int *oid,
912                unsigned int oidsize, unsigned int *oidlen)
913 {
914           const u_char *p = (const u_char *)elem->data.raw;
915           uint32_t asnlen = elem->asnlen;
916           uint32_t i = asnlen;
917           int o = 0, first = -1;
918           unsigned int firstval;
919 
920           for (*oidlen = 0; i != 0; p++, i--) {
921                   o = (o << ASN_SHIFT7) + (GET_U_1(p) & ~ASN_BIT8);
922                     if (GET_U_1(p) & ASN_LONGLEN)
923                         continue;
924 
925                     /*
926                      * first subitem encodes two items with 1st*OIDMUX+2nd
927                      * (see X.690:1997 clause 8.19 for the details)
928                      */
929                     if (first < 0) {
930                               first = 0;
931                               firstval = o / OIDMUX;
932                               if (firstval > 2) firstval = 2;
933                               o -= firstval * OIDMUX;
934                               if (*oidlen < oidsize) {
935                                   oid[(*oidlen)++] = firstval;
936                               }
937                     }
938                     if (*oidlen < oidsize) {
939                               oid[(*oidlen)++] = o;
940                     }
941                     o = 0;
942           }
943           return 0;
944 }
945 
smi_check_type(SmiBasetype basetype,int be)946 static int smi_check_type(SmiBasetype basetype, int be)
947 {
948     int i;
949 
950     for (i = 0; smi2betab[i].basetype != SMI_BASETYPE_UNKNOWN; i++) {
951           if (smi2betab[i].basetype == basetype && smi2betab[i].be == be) {
952               return 1;
953           }
954     }
955 
956     return 0;
957 }
958 
smi_check_a_range(SmiType * smiType,SmiRange * smiRange,struct be * elem)959 static int smi_check_a_range(SmiType *smiType, SmiRange *smiRange,
960                                    struct be *elem)
961 {
962     int ok = 1;
963 
964     switch (smiType->basetype) {
965     case SMI_BASETYPE_OBJECTIDENTIFIER:
966     case SMI_BASETYPE_OCTETSTRING:
967           if (smiRange->minValue.value.unsigned32
968               == smiRange->maxValue.value.unsigned32) {
969               ok = (elem->asnlen == smiRange->minValue.value.unsigned32);
970           } else {
971               ok = (elem->asnlen >= smiRange->minValue.value.unsigned32
972                       && elem->asnlen <= smiRange->maxValue.value.unsigned32);
973           }
974           break;
975 
976     case SMI_BASETYPE_INTEGER32:
977           ok = (elem->data.integer >= smiRange->minValue.value.integer32
978                 && elem->data.integer <= smiRange->maxValue.value.integer32);
979           break;
980 
981     case SMI_BASETYPE_UNSIGNED32:
982           ok = (elem->data.uns >= smiRange->minValue.value.unsigned32
983                 && elem->data.uns <= smiRange->maxValue.value.unsigned32);
984           break;
985 
986     case SMI_BASETYPE_UNSIGNED64:
987           /* XXX */
988           break;
989 
990           /* case SMI_BASETYPE_INTEGER64: SMIng */
991           /* case SMI_BASETYPE_FLOAT32: SMIng */
992           /* case SMI_BASETYPE_FLOAT64: SMIng */
993           /* case SMI_BASETYPE_FLOAT128: SMIng */
994 
995     case SMI_BASETYPE_ENUM:
996     case SMI_BASETYPE_BITS:
997     case SMI_BASETYPE_UNKNOWN:
998           ok = 1;
999           break;
1000 
1001     default:
1002           ok = 0;
1003           break;
1004     }
1005 
1006     return ok;
1007 }
1008 
smi_check_range(SmiType * smiType,struct be * elem)1009 static int smi_check_range(SmiType *smiType, struct be *elem)
1010 {
1011         SmiRange *smiRange;
1012           int ok = 1;
1013 
1014           for (smiRange = smiGetFirstRange(smiType);
1015                smiRange;
1016                smiRange = smiGetNextRange(smiRange)) {
1017 
1018               ok = smi_check_a_range(smiType, smiRange, elem);
1019 
1020               if (ok) {
1021                     break;
1022               }
1023           }
1024 
1025           if (ok) {
1026               SmiType *parentType;
1027               parentType = smiGetParentType(smiType);
1028               if (parentType) {
1029                     ok = smi_check_range(parentType, elem);
1030               }
1031           }
1032 
1033           return ok;
1034 }
1035 
1036 static SmiNode *
smi_print_variable(netdissect_options * ndo,struct be * elem,int * status)1037 smi_print_variable(netdissect_options *ndo,
1038                    struct be *elem, int *status)
1039 {
1040           unsigned int oid[128], oidlen;
1041           SmiNode *smiNode = NULL;
1042           unsigned int i;
1043 
1044           if (!nd_smi_module_loaded) {
1045                     *status = asn1_print(ndo, elem);
1046                     return NULL;
1047           }
1048           *status = smi_decode_oid(ndo, elem, oid, sizeof(oid) / sizeof(unsigned int),
1049               &oidlen);
1050           if (*status < 0)
1051                     return NULL;
1052           smiNode = smiGetNodeByOID(oidlen, oid);
1053           if (! smiNode) {
1054                     *status = asn1_print(ndo, elem);
1055                     return NULL;
1056           }
1057           if (ndo->ndo_vflag) {
1058                     ND_PRINT("%s::", smiGetNodeModule(smiNode)->name);
1059           }
1060           ND_PRINT("%s", smiNode->name);
1061           if (smiNode->oidlen < oidlen) {
1062                     for (i = smiNode->oidlen; i < oidlen; i++) {
1063                               ND_PRINT(".%u", oid[i]);
1064                     }
1065           }
1066           *status = 0;
1067           return smiNode;
1068 }
1069 
1070 static int
smi_print_value(netdissect_options * ndo,SmiNode * smiNode,u_short pduid,struct be * elem)1071 smi_print_value(netdissect_options *ndo,
1072                 SmiNode *smiNode, u_short pduid, struct be *elem)
1073 {
1074           unsigned int i, oid[128], oidlen;
1075           SmiType *smiType;
1076           SmiNamedNumber *nn;
1077           int done = 0;
1078 
1079           if (! smiNode || ! (smiNode->nodekind
1080                                   & (SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN))) {
1081               return asn1_print(ndo, elem);
1082           }
1083 
1084           if (elem->type == BE_NOSUCHOBJECT
1085               || elem->type == BE_NOSUCHINST
1086               || elem->type == BE_ENDOFMIBVIEW) {
1087               return asn1_print(ndo, elem);
1088           }
1089 
1090           if (NOTIFY_CLASS(pduid) && smiNode->access < SMI_ACCESS_NOTIFY) {
1091               ND_PRINT("[notNotifyable]");
1092           }
1093 
1094           if (READ_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_ONLY) {
1095               ND_PRINT("[notReadable]");
1096           }
1097 
1098           if (WRITE_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_WRITE) {
1099               ND_PRINT("[notWritable]");
1100           }
1101 
1102           if (RESPONSE_CLASS(pduid)
1103               && smiNode->access == SMI_ACCESS_NOT_ACCESSIBLE) {
1104               ND_PRINT("[noAccess]");
1105           }
1106 
1107           smiType = smiGetNodeType(smiNode);
1108           if (! smiType) {
1109               return asn1_print(ndo, elem);
1110           }
1111 
1112           if (! smi_check_type(smiType->basetype, elem->type)) {
1113               ND_PRINT("[wrongType]");
1114           }
1115 
1116           if (! smi_check_range(smiType, elem)) {
1117               ND_PRINT("[outOfRange]");
1118           }
1119 
1120           /* resolve bits to named bits */
1121 
1122           /* check whether instance identifier is valid */
1123 
1124           /* apply display hints (integer, octetstring) */
1125 
1126           /* convert instance identifier to index type values */
1127 
1128           switch (elem->type) {
1129           case BE_OID:
1130                   if (smiType->basetype == SMI_BASETYPE_BITS) {
1131                             /* print bit labels */
1132                     } else {
1133                               if (nd_smi_module_loaded &&
1134                                   smi_decode_oid(ndo, elem, oid,
1135                                                      sizeof(oid)/sizeof(unsigned int),
1136                                                      &oidlen) == 0) {
1137                                         smiNode = smiGetNodeByOID(oidlen, oid);
1138                                         if (smiNode) {
1139                                                 if (ndo->ndo_vflag) {
1140                                                             ND_PRINT("%s::", smiGetNodeModule(smiNode)->name);
1141                                                   }
1142                                                   ND_PRINT("%s", smiNode->name);
1143                                                   if (smiNode->oidlen < oidlen) {
1144                                                           for (i = smiNode->oidlen;
1145                                                                  i < oidlen; i++) {
1146                                                                     ND_PRINT(".%u", oid[i]);
1147                                                             }
1148                                                   }
1149                                                   done++;
1150                                         }
1151                               }
1152                     }
1153                     break;
1154 
1155           case BE_INT:
1156                   if (smiType->basetype == SMI_BASETYPE_ENUM) {
1157                             for (nn = smiGetFirstNamedNumber(smiType);
1158                                    nn;
1159                                    nn = smiGetNextNamedNumber(nn)) {
1160                                        if (nn->value.value.integer32
1161                                              == elem->data.integer) {
1162                                                  ND_PRINT("%s", nn->name);
1163                                                    ND_PRINT("(%d)", elem->data.integer);
1164                                                    done++;
1165                                                    break;
1166                                         }
1167                               }
1168                     }
1169                     break;
1170           }
1171 
1172           if (! done) {
1173                     return asn1_print(ndo, elem);
1174           }
1175           return 0;
1176 }
1177 #endif
1178 
1179 /*
1180  * General SNMP header
1181  *        SEQUENCE {
1182  *                  version INTEGER {version-1(0)},
1183  *                  community OCTET STRING,
1184  *                  data ANY  -- PDUs
1185  *        }
1186  * PDUs for all but Trap: (see rfc1157 from page 15 on)
1187  *        SEQUENCE {
1188  *                  request-id INTEGER,
1189  *                  error-status INTEGER,
1190  *                  error-index INTEGER,
1191  *                  varbindlist SEQUENCE OF
1192  *                            SEQUENCE {
1193  *                                      name ObjectName,
1194  *                                      value ObjectValue
1195  *                            }
1196  *        }
1197  * PDU for Trap:
1198  *        SEQUENCE {
1199  *                  enterprise OBJECT IDENTIFIER,
1200  *                  agent-addr NetworkAddress,
1201  *                  generic-trap INTEGER,
1202  *                  specific-trap INTEGER,
1203  *                  time-stamp TimeTicks,
1204  *                  varbindlist SEQUENCE OF
1205  *                            SEQUENCE {
1206  *                                      name ObjectName,
1207  *                                      value ObjectValue
1208  *                            }
1209  *        }
1210  */
1211 
1212 /*
1213  * Decode SNMP varBind
1214  */
1215 static void
varbind_print(netdissect_options * ndo,u_short pduid,const u_char * np,u_int length)1216 varbind_print(netdissect_options *ndo,
1217               u_short pduid, const u_char *np, u_int length)
1218 {
1219           struct be elem;
1220           int count = 0, ind;
1221 #ifdef USE_LIBSMI
1222           SmiNode *smiNode = NULL;
1223 #endif
1224           int status;
1225 
1226           /* Sequence of varBind */
1227           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1228                     return;
1229           if (elem.type != BE_SEQ) {
1230                     ND_PRINT("[!SEQ of varbind]");
1231                     asn1_print(ndo, &elem);
1232                     return;
1233           }
1234           if ((u_int)count < length)
1235                     ND_PRINT("[%d extra after SEQ of varbind]", length - count);
1236           /* descend */
1237           length = elem.asnlen;
1238           np = (const u_char *)elem.data.raw;
1239 
1240           for (ind = 1; length > 0; ind++) {
1241                     const u_char *vbend;
1242                     u_int vblength;
1243 
1244                     ND_PRINT(" ");
1245 
1246                     /* Sequence */
1247                     if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1248                               return;
1249                     if (elem.type != BE_SEQ) {
1250                               ND_PRINT("[!varbind]");
1251                               asn1_print(ndo, &elem);
1252                               return;
1253                     }
1254                     vbend = np + count;
1255                     vblength = length - count;
1256                     /* descend */
1257                     length = elem.asnlen;
1258                     np = (const u_char *)elem.data.raw;
1259 
1260                     /* objName (OID) */
1261                     if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1262                               return;
1263                     if (elem.type != BE_OID) {
1264                               ND_PRINT("[objName!=OID]");
1265                               asn1_print(ndo, &elem);
1266                               return;
1267                     }
1268 #ifdef USE_LIBSMI
1269                     smiNode = smi_print_variable(ndo, &elem, &status);
1270 #else
1271                     status = asn1_print(ndo, &elem);
1272 #endif
1273                     if (status < 0)
1274                               return;
1275                     length -= count;
1276                     np += count;
1277 
1278                     if (pduid != GETREQ && pduid != GETNEXTREQ
1279                         && pduid != GETBULKREQ)
1280                               ND_PRINT("=");
1281 
1282                     /* objVal (ANY) */
1283                     if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1284                               return;
1285                     if (pduid == GETREQ || pduid == GETNEXTREQ
1286                         || pduid == GETBULKREQ) {
1287                               if (elem.type != BE_NULL) {
1288                                         ND_PRINT("[objVal!=NULL]");
1289                                         if (asn1_print(ndo, &elem) < 0)
1290                                                   return;
1291                               }
1292                     } else {
1293                             if (elem.type != BE_NULL) {
1294 #ifdef USE_LIBSMI
1295                                         status = smi_print_value(ndo, smiNode, pduid, &elem);
1296 #else
1297                                         status = asn1_print(ndo, &elem);
1298 #endif
1299                               }
1300                               if (status < 0)
1301                                         return;
1302                     }
1303                     length = vblength;
1304                     np = vbend;
1305           }
1306 }
1307 
1308 /*
1309  * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, SetRequest,
1310  * GetBulk, Inform, V2Trap, and Report
1311  */
1312 static void
snmppdu_print(netdissect_options * ndo,u_short pduid,const u_char * np,u_int length)1313 snmppdu_print(netdissect_options *ndo,
1314               u_short pduid, const u_char *np, u_int length)
1315 {
1316           struct be elem;
1317           int count = 0, error_status;
1318 
1319           /* reqId (Integer) */
1320           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1321                     return;
1322           if (elem.type != BE_INT) {
1323                     ND_PRINT("[reqId!=INT]");
1324                     asn1_print(ndo, &elem);
1325                     return;
1326           }
1327           if (ndo->ndo_vflag)
1328                     ND_PRINT("R=%d ", elem.data.integer);
1329           length -= count;
1330           np += count;
1331 
1332           /* errorStatus (Integer) */
1333           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1334                     return;
1335           if (elem.type != BE_INT) {
1336                     ND_PRINT("[errorStatus!=INT]");
1337                     asn1_print(ndo, &elem);
1338                     return;
1339           }
1340           error_status = 0;
1341           if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1342               || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1343               && elem.data.integer != 0) {
1344                     char errbuf[20];
1345                     ND_PRINT("[errorStatus(%s)!=0]",
1346                               DECODE_ErrorStatus(elem.data.integer));
1347           } else if (pduid == GETBULKREQ) {
1348                     ND_PRINT(" N=%d", elem.data.integer);
1349           } else if (elem.data.integer != 0) {
1350                     char errbuf[20];
1351                     ND_PRINT(" %s", DECODE_ErrorStatus(elem.data.integer));
1352                     error_status = elem.data.integer;
1353           }
1354           length -= count;
1355           np += count;
1356 
1357           /* errorIndex (Integer) */
1358           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1359                     return;
1360           if (elem.type != BE_INT) {
1361                     ND_PRINT("[errorIndex!=INT]");
1362                     asn1_print(ndo, &elem);
1363                     return;
1364           }
1365           if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
1366               || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
1367               && elem.data.integer != 0)
1368                     ND_PRINT("[errorIndex(%d)!=0]", elem.data.integer);
1369           else if (pduid == GETBULKREQ)
1370                     ND_PRINT(" M=%d", elem.data.integer);
1371           else if (elem.data.integer != 0) {
1372                     if (!error_status)
1373                               ND_PRINT("[errorIndex(%d) w/o errorStatus]", elem.data.integer);
1374                     else
1375                               ND_PRINT("@%d", elem.data.integer);
1376           } else if (error_status) {
1377                     ND_PRINT("[errorIndex==0]");
1378           }
1379           length -= count;
1380           np += count;
1381 
1382           varbind_print(ndo, pduid, np, length);
1383 }
1384 
1385 /*
1386  * Decode SNMP Trap PDU
1387  */
1388 static void
trappdu_print(netdissect_options * ndo,const u_char * np,u_int length)1389 trappdu_print(netdissect_options *ndo,
1390               const u_char *np, u_int length)
1391 {
1392           struct be elem;
1393           int count = 0, generic;
1394 
1395           ND_PRINT(" ");
1396 
1397           /* enterprise (oid) */
1398           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1399                     return;
1400           if (elem.type != BE_OID) {
1401                     ND_PRINT("[enterprise!=OID]");
1402                     asn1_print(ndo, &elem);
1403                     return;
1404           }
1405           if (asn1_print(ndo, &elem) < 0)
1406                     return;
1407           length -= count;
1408           np += count;
1409 
1410           ND_PRINT(" ");
1411 
1412           /* agent-addr (inetaddr) */
1413           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1414                     return;
1415           if (elem.type != BE_INETADDR) {
1416                     ND_PRINT("[agent-addr!=INETADDR]");
1417                     asn1_print(ndo, &elem);
1418                     return;
1419           }
1420           if (asn1_print(ndo, &elem) < 0)
1421                     return;
1422           length -= count;
1423           np += count;
1424 
1425           /* generic-trap (Integer) */
1426           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1427                     return;
1428           if (elem.type != BE_INT) {
1429                     ND_PRINT("[generic-trap!=INT]");
1430                     asn1_print(ndo, &elem);
1431                     return;
1432           }
1433           generic = elem.data.integer;
1434           {
1435                     char buf[20];
1436                     ND_PRINT(" %s", DECODE_GenericTrap(generic));
1437           }
1438           length -= count;
1439           np += count;
1440 
1441           /* specific-trap (Integer) */
1442           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1443                     return;
1444           if (elem.type != BE_INT) {
1445                     ND_PRINT("[specific-trap!=INT]");
1446                     asn1_print(ndo, &elem);
1447                     return;
1448           }
1449           if (generic != GT_ENTERPRISE) {
1450                     if (elem.data.integer != 0)
1451                               ND_PRINT("[specific-trap(%d)!=0]", elem.data.integer);
1452           } else
1453                     ND_PRINT(" s=%d", elem.data.integer);
1454           length -= count;
1455           np += count;
1456 
1457           ND_PRINT(" ");
1458 
1459           /* time-stamp (TimeTicks) */
1460           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1461                     return;
1462           if (elem.type != BE_UNS) {                        /* XXX */
1463                     ND_PRINT("[time-stamp!=TIMETICKS]");
1464                     asn1_print(ndo, &elem);
1465                     return;
1466           }
1467           if (asn1_print(ndo, &elem) < 0)
1468                     return;
1469           length -= count;
1470           np += count;
1471 
1472           varbind_print(ndo, TRAP, np, length);
1473 }
1474 
1475 /*
1476  * Decode arbitrary SNMP PDUs.
1477  */
1478 static void
pdu_print(netdissect_options * ndo,const u_char * np,u_int length,int version)1479 pdu_print(netdissect_options *ndo,
1480           const u_char *np, u_int length, int version)
1481 {
1482           struct be pdu;
1483           int count = 0;
1484 
1485           /* PDU (Context) */
1486           if ((count = asn1_parse(ndo, np, length, &pdu)) < 0)
1487                     return;
1488           if (pdu.type != BE_PDU) {
1489                     ND_PRINT("[no PDU]");
1490                     return;
1491           }
1492           if ((u_int)count < length)
1493                     ND_PRINT("[%d extra after PDU]", length - count);
1494           if (ndo->ndo_vflag) {
1495                     ND_PRINT("{ ");
1496           }
1497           if (asn1_print(ndo, &pdu) < 0)
1498                     return;
1499           ND_PRINT(" ");
1500           /* descend into PDU */
1501           length = pdu.asnlen;
1502           np = (const u_char *)pdu.data.raw;
1503 
1504           if (version == SNMP_VERSION_1 &&
1505               (pdu.id == GETBULKREQ || pdu.id == INFORMREQ ||
1506                pdu.id == V2TRAP || pdu.id == REPORT)) {
1507                   ND_PRINT("[v2 PDU in v1 message]");
1508                     return;
1509           }
1510 
1511           if (version == SNMP_VERSION_2 && pdu.id == TRAP) {
1512                     ND_PRINT("[v1 PDU in v2 message]");
1513                     return;
1514           }
1515 
1516           switch (pdu.id) {
1517           case TRAP:
1518                     trappdu_print(ndo, np, length);
1519                     break;
1520           case GETREQ:
1521           case GETNEXTREQ:
1522           case GETRESP:
1523           case SETREQ:
1524           case GETBULKREQ:
1525           case INFORMREQ:
1526           case V2TRAP:
1527           case REPORT:
1528                     snmppdu_print(ndo, pdu.id, np, length);
1529                     break;
1530           }
1531 
1532           if (ndo->ndo_vflag) {
1533                     ND_PRINT(" } ");
1534           }
1535 }
1536 
1537 /*
1538  * Decode a scoped SNMP PDU.
1539  */
1540 static void
scopedpdu_print(netdissect_options * ndo,const u_char * np,u_int length,int version)1541 scopedpdu_print(netdissect_options *ndo,
1542                 const u_char *np, u_int length, int version)
1543 {
1544           struct be elem;
1545           int count = 0;
1546 
1547           /* Sequence */
1548           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1549                     return;
1550           if (elem.type != BE_SEQ) {
1551                     ND_PRINT("[!scoped PDU]");
1552                     asn1_print(ndo, &elem);
1553                     return;
1554           }
1555           length = elem.asnlen;
1556           np = (const u_char *)elem.data.raw;
1557 
1558           /* contextEngineID (OCTET STRING) */
1559           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1560                     return;
1561           if (elem.type != BE_STR) {
1562                     ND_PRINT("[contextEngineID!=STR]");
1563                     asn1_print(ndo, &elem);
1564                     return;
1565           }
1566           length -= count;
1567           np += count;
1568 
1569           ND_PRINT("E=");
1570           if (asn1_print_octets(ndo, &elem) == -1)
1571                     return;
1572           ND_PRINT(" ");
1573 
1574           /* contextName (OCTET STRING) */
1575           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1576                     return;
1577           if (elem.type != BE_STR) {
1578                     ND_PRINT("[contextName!=STR]");
1579                     asn1_print(ndo, &elem);
1580                     return;
1581           }
1582           length -= count;
1583           np += count;
1584 
1585           ND_PRINT("C=");
1586           if (asn1_print_string(ndo, &elem) == -1)
1587                     return;
1588           ND_PRINT(" ");
1589 
1590           pdu_print(ndo, np, length, version);
1591 }
1592 
1593 /*
1594  * Decode SNMP Community Header (SNMPv1 and SNMPv2c)
1595  */
1596 static void
community_print(netdissect_options * ndo,const u_char * np,u_int length,int version)1597 community_print(netdissect_options *ndo,
1598                 const u_char *np, u_int length, int version)
1599 {
1600           struct be elem;
1601           int count = 0;
1602 
1603           /* Community (String) */
1604           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1605                     return;
1606           if (elem.type != BE_STR) {
1607                     ND_PRINT("[comm!=STR]");
1608                     asn1_print(ndo, &elem);
1609                     return;
1610           }
1611           /* default community */
1612           if (!(elem.asnlen == sizeof(DEF_COMMUNITY) - 1 &&
1613               strncmp((const char *)elem.data.str, DEF_COMMUNITY,
1614                       sizeof(DEF_COMMUNITY) - 1) == 0)) {
1615                     /* ! "public" */
1616                     ND_PRINT("C=");
1617                     if (asn1_print_string(ndo, &elem) == -1)
1618                               return;
1619                     ND_PRINT(" ");
1620           }
1621           length -= count;
1622           np += count;
1623 
1624           pdu_print(ndo, np, length, version);
1625 }
1626 
1627 /*
1628  * Decode SNMPv3 User-based Security Message Header (SNMPv3)
1629  */
1630 static void
usm_print(netdissect_options * ndo,const u_char * np,u_int length)1631 usm_print(netdissect_options *ndo,
1632           const u_char *np, u_int length)
1633 {
1634         struct be elem;
1635           int count = 0;
1636 
1637           /* Sequence */
1638           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1639                     return;
1640           if (elem.type != BE_SEQ) {
1641                     ND_PRINT("[!usm]");
1642                     asn1_print(ndo, &elem);
1643                     return;
1644           }
1645           length = elem.asnlen;
1646           np = (const u_char *)elem.data.raw;
1647 
1648           /* msgAuthoritativeEngineID (OCTET STRING) */
1649           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1650                     return;
1651           if (elem.type != BE_STR) {
1652                     ND_PRINT("[msgAuthoritativeEngineID!=STR]");
1653                     asn1_print(ndo, &elem);
1654                     return;
1655           }
1656           length -= count;
1657           np += count;
1658 
1659           /* msgAuthoritativeEngineBoots (INTEGER) */
1660           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1661                     return;
1662           if (elem.type != BE_INT) {
1663                     ND_PRINT("[msgAuthoritativeEngineBoots!=INT]");
1664                     asn1_print(ndo, &elem);
1665                     return;
1666           }
1667           if (ndo->ndo_vflag)
1668                     ND_PRINT("B=%d ", elem.data.integer);
1669           length -= count;
1670           np += count;
1671 
1672           /* msgAuthoritativeEngineTime (INTEGER) */
1673           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1674                     return;
1675           if (elem.type != BE_INT) {
1676                     ND_PRINT("[msgAuthoritativeEngineTime!=INT]");
1677                     asn1_print(ndo, &elem);
1678                     return;
1679           }
1680           if (ndo->ndo_vflag)
1681                     ND_PRINT("T=%d ", elem.data.integer);
1682           length -= count;
1683           np += count;
1684 
1685           /* msgUserName (OCTET STRING) */
1686           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1687                     return;
1688           if (elem.type != BE_STR) {
1689                     ND_PRINT("[msgUserName!=STR]");
1690                     asn1_print(ndo, &elem);
1691                     return;
1692           }
1693           length -= count;
1694         np += count;
1695 
1696           ND_PRINT("U=");
1697           if (asn1_print_string(ndo, &elem) == -1)
1698                     return;
1699           ND_PRINT(" ");
1700 
1701           /* msgAuthenticationParameters (OCTET STRING) */
1702           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1703                     return;
1704           if (elem.type != BE_STR) {
1705                     ND_PRINT("[msgAuthenticationParameters!=STR]");
1706                     asn1_print(ndo, &elem);
1707                     return;
1708           }
1709           length -= count;
1710         np += count;
1711 
1712           /* msgPrivacyParameters (OCTET STRING) */
1713           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1714                     return;
1715           if (elem.type != BE_STR) {
1716                     ND_PRINT("[msgPrivacyParameters!=STR]");
1717                     asn1_print(ndo, &elem);
1718                     return;
1719           }
1720           length -= count;
1721         np += count;
1722 
1723           if ((u_int)count < length)
1724                     ND_PRINT("[%d extra after usm SEQ]", length - count);
1725 }
1726 
1727 /*
1728  * Decode SNMPv3 Message Header (SNMPv3)
1729  */
1730 static void
v3msg_print(netdissect_options * ndo,const u_char * np,u_int length)1731 v3msg_print(netdissect_options *ndo,
1732             const u_char *np, u_int length)
1733 {
1734           struct be elem;
1735           int count = 0;
1736           u_char flags;
1737           int model;
1738           const u_char *xnp = np;
1739           int xlength = length;
1740 
1741           /* Sequence */
1742           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1743                     return;
1744           if (elem.type != BE_SEQ) {
1745                     ND_PRINT("[!message]");
1746                     asn1_print(ndo, &elem);
1747                     return;
1748           }
1749           length = elem.asnlen;
1750           np = (const u_char *)elem.data.raw;
1751 
1752           if (ndo->ndo_vflag) {
1753                     ND_PRINT("{ ");
1754           }
1755 
1756           /* msgID (INTEGER) */
1757           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1758                     return;
1759           if (elem.type != BE_INT) {
1760                     ND_PRINT("[msgID!=INT]");
1761                     asn1_print(ndo, &elem);
1762                     return;
1763           }
1764           length -= count;
1765           np += count;
1766 
1767           /* msgMaxSize (INTEGER) */
1768           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1769                     return;
1770           if (elem.type != BE_INT) {
1771                     ND_PRINT("[msgMaxSize!=INT]");
1772                     asn1_print(ndo, &elem);
1773                     return;
1774           }
1775           length -= count;
1776           np += count;
1777 
1778           /* msgFlags (OCTET STRING) */
1779           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1780                     return;
1781           if (elem.type != BE_STR) {
1782                     ND_PRINT("[msgFlags!=STR]");
1783                     asn1_print(ndo, &elem);
1784                     return;
1785           }
1786           if (elem.asnlen != 1) {
1787                     ND_PRINT("[msgFlags size %d]", elem.asnlen);
1788                     return;
1789           }
1790           flags = GET_U_1(elem.data.str);
1791           if (flags != 0x00 && flags != 0x01 && flags != 0x03
1792               && flags != 0x04 && flags != 0x05 && flags != 0x07) {
1793                     ND_PRINT("[msgFlags=0x%02X]", flags);
1794                     return;
1795           }
1796           length -= count;
1797           np += count;
1798 
1799           ND_PRINT("F=%s%s%s ",
1800                     flags & 0x01 ? "a" : "",
1801                     flags & 0x02 ? "p" : "",
1802                     flags & 0x04 ? "r" : "");
1803 
1804           /* msgSecurityModel (INTEGER) */
1805           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1806                     return;
1807           if (elem.type != BE_INT) {
1808                     ND_PRINT("[msgSecurityModel!=INT]");
1809                     asn1_print(ndo, &elem);
1810                     return;
1811           }
1812           model = elem.data.integer;
1813           length -= count;
1814           np += count;
1815 
1816           if ((u_int)count < length)
1817                     ND_PRINT("[%d extra after message SEQ]", length - count);
1818 
1819           if (ndo->ndo_vflag) {
1820                     ND_PRINT("} ");
1821           }
1822 
1823           if (model == 3) {
1824               if (ndo->ndo_vflag) {
1825                     ND_PRINT("{ USM ");
1826               }
1827           } else {
1828               ND_PRINT("[security model %d]", model);
1829             return;
1830           }
1831 
1832           np = xnp + (np - xnp);
1833           length = xlength - (np - xnp);
1834 
1835           /* msgSecurityParameters (OCTET STRING) */
1836           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1837                     return;
1838           if (elem.type != BE_STR) {
1839                     ND_PRINT("[msgSecurityParameters!=STR]");
1840                     asn1_print(ndo, &elem);
1841                     return;
1842           }
1843           length -= count;
1844           np += count;
1845 
1846           if (model == 3) {
1847               usm_print(ndo, elem.data.str, elem.asnlen);
1848               if (ndo->ndo_vflag) {
1849                     ND_PRINT("} ");
1850               }
1851           }
1852 
1853           if (ndo->ndo_vflag) {
1854               ND_PRINT("{ ScopedPDU ");
1855           }
1856 
1857           scopedpdu_print(ndo, np, length, 3);
1858 
1859           if (ndo->ndo_vflag) {
1860                     ND_PRINT("} ");
1861           }
1862 }
1863 
1864 /*
1865  * Decode SNMP header and pass on to PDU printing routines
1866  */
1867 void
snmp_print(netdissect_options * ndo,const u_char * np,u_int length)1868 snmp_print(netdissect_options *ndo,
1869            const u_char *np, u_int length)
1870 {
1871           struct be elem;
1872           int count = 0;
1873           int version = 0;
1874 
1875           ndo->ndo_protocol = "snmp";
1876           ND_PRINT(" ");
1877 
1878           /* initial Sequence */
1879           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1880                     return;
1881           if (elem.type != BE_SEQ) {
1882                     ND_PRINT("[!init SEQ]");
1883                     asn1_print(ndo, &elem);
1884                     return;
1885           }
1886           if ((u_int)count < length)
1887                     ND_PRINT("[%d extra after iSEQ]", length - count);
1888           /* descend */
1889           length = elem.asnlen;
1890           np = (const u_char *)elem.data.raw;
1891 
1892           /* Version (INTEGER) */
1893           if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
1894                     return;
1895           if (elem.type != BE_INT) {
1896                     ND_PRINT("[version!=INT]");
1897                     asn1_print(ndo, &elem);
1898                     return;
1899           }
1900 
1901           switch (elem.data.integer) {
1902           case SNMP_VERSION_1:
1903           case SNMP_VERSION_2:
1904           case SNMP_VERSION_3:
1905                     if (ndo->ndo_vflag)
1906                               ND_PRINT("{ %s ", SnmpVersion[elem.data.integer]);
1907                     break;
1908           default:
1909                   ND_PRINT("SNMP [version = %d]", elem.data.integer);
1910                     return;
1911           }
1912           version = elem.data.integer;
1913           length -= count;
1914           np += count;
1915 
1916           switch (version) {
1917           case SNMP_VERSION_1:
1918         case SNMP_VERSION_2:
1919                     community_print(ndo, np, length, version);
1920                     break;
1921           case SNMP_VERSION_3:
1922                     v3msg_print(ndo, np, length);
1923                     break;
1924           default:
1925                     ND_PRINT("[version = %d]", elem.data.integer);
1926                     break;
1927           }
1928 
1929           if (ndo->ndo_vflag) {
1930                     ND_PRINT("} ");
1931           }
1932 }
1933