xref: /openbsd/usr.sbin/tcpdump/print-snmp.c (revision 771fbea0)
1 /*	$OpenBSD: print-snmp.c,v 1.25 2020/01/24 22:46:37 procter Exp $	*/
2 
3 /*
4  * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
5  *     John Robert LoVerso. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  *
30  * This implementation has been influenced by the CMU SNMP release,
31  * by Steve Waldbusser.  However, this shares no code with that system.
32  * Additional ASN.1 insight gained from Marshall T. Rose's _The_Open_Book_.
33  * Earlier forms of this implementation were derived and/or inspired by an
34  * awk script originally written by C. Philip Wood of LANL (but later
35  * heavily modified by John Robert LoVerso).  The copyright notice for
36  * that work is preserved below, even though it may not rightly apply
37  * to this file.
38  *
39  * This started out as a very simple program, but the incremental decoding
40  * (into the BE structure) complicated things.
41  *
42  #			Los Alamos National Laboratory
43  #
44  #	Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
45  #	This software was produced under a U.S. Government contract
46  #	(W-7405-ENG-36) by Los Alamos National Laboratory, which is
47  #	operated by the	University of California for the U.S. Department
48  #	of Energy.  The U.S. Government is licensed to use, reproduce,
49  #	and distribute this software.  Permission is granted to the
50  #	public to copy and use this software without charge, provided
51  #	that this Notice and any statement of authorship are reproduced
52  #	on all copies.  Neither the Government nor the University makes
53  #	any warranty, express or implied, or assumes any liability or
54  #	responsibility for the use of this software.
55  #	@(#)snmp.awk.x	1.1 (LANL) 1/15/90
56  */
57 
58 #include <sys/time.h>
59 
60 #include <ctype.h>
61 #include <stdio.h>
62 #include <string.h>
63 
64 #include "interface.h"
65 #include "addrtoname.h"
66 
67 /*
68  * Universal ASN.1 types
69  * (we only care about the tag values for those allowed in the Internet SMI)
70  */
71 char *Universal[] = {
72 	"U-0",
73 	"Boolean",
74 	"Integer",
75 #define INTEGER 2
76 	"Bitstring",
77 	"String",
78 #define STRING 4
79 	"Null",
80 #define ASN_NULL 5
81 	"ObjID",
82 #define OBJECTID 6
83 	"ObjectDes",
84 	"U-8","U-9","U-10","U-11",	/* 8-11 */
85 	"U-12","U-13","U-14","U-15",	/* 12-15 */
86 	"Sequence",
87 #define SEQUENCE 16
88 	"Set"
89 };
90 
91 /*
92  * Application-wide ASN.1 types from the Internet SMI and their tags
93  */
94 char *Application[] = {
95 	"IpAddress",
96 #define IPADDR 0
97 	"Counter",
98 #define COUNTER 1
99 	"Gauge",
100 #define GAUGE 2
101 	"TimeTicks",
102 #define TIMETICKS 3
103 	"Opaque",
104 #define OPAQUE 4
105 	"NsapAddress",
106 #define NSAPADDR 5
107 	"Counter64",
108 #define COUNTER64 6
109 	"UInteger32"
110 #define UINTEGER32 7
111 };
112 
113 /*
114  * Context-specific ASN.1 types for the SNMP PDUs and their tags
115  */
116 char *Context[] = {
117 	"GetRequest",
118 #define GETREQ 0
119 	"GetNextRequest",
120 #define GETNEXTREQ 1
121 	"GetResponse",
122 #define GETRESP 2
123 	"SetRequest",
124 #define SETREQ 3
125 	"Trap",
126 #define TRAP 4
127 	"GetBulkReq",
128 #define GETBULKREQ 5
129 	"InformReq",
130 #define INFORMREQ 6
131 	"TrapV2",
132 #define TRAPV2 7
133 	"Report"
134 #define REPORT 8
135 };
136 
137 /*
138  * Private ASN.1 types
139  * The Internet SMI does not specify any
140  */
141 char *Private[] = {
142 	"P-0"
143 };
144 
145 /*
146  * error-status values for any SNMP PDU
147  */
148 char *ErrorStatus[] = {
149 	"noError",
150 	"tooBig",
151 	"noSuchName",
152 	"badValue",
153 	"readOnly",
154 	"genErr",
155 	"noAccess",
156 	"wrongType",
157 	"wrongLength",
158 	"wrongEnc",
159 	"wrongValue",
160 	"noCreation",
161 	"inconValue",
162 	"resUnavail",
163 	"commitFailed",
164 	"undoFailed",
165 	"authError",
166 	"notWritable",
167 	"inconName"
168 };
169 #define DECODE_ErrorStatus(e) \
170 	( e >= 0 && e <= sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \
171 	? ErrorStatus[e] : (snprintf(errbuf, sizeof(errbuf), "err=%u", e), errbuf))
172 
173 /*
174  * generic-trap values in the SNMP Trap-PDU
175  */
176 char *GenericTrap[] = {
177 	"coldStart",
178 	"warmStart",
179 	"linkDown",
180 	"linkUp",
181 	"authenticationFailure",
182 	"egpNeighborLoss",
183 	"enterpriseSpecific"
184 #define GT_ENTERPRISE 6
185 };
186 #define DECODE_GenericTrap(t) \
187 	( t >= 0 && t < sizeof(GenericTrap)/sizeof(GenericTrap[0]) \
188 	? GenericTrap[t] : (snprintf(buf, sizeof(buf), "gt=%d", t), buf))
189 
190 /*
191  * ASN.1 type class table
192  * Ties together the preceding Universal, Application, Context, and Private
193  * type definitions.
194  */
195 #define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */
196 struct {
197 	char	*name;
198 	char	**Id;
199 	    int	numIDs;
200     } Class[] = {
201 	defineCLASS(Universal),
202 #define	UNIVERSAL	0
203 	defineCLASS(Application),
204 #define	APPLICATION	1
205 	defineCLASS(Context),
206 #define	CONTEXT		2
207 	defineCLASS(Private),
208 #define	PRIVATE		3
209 };
210 
211 /*
212  * defined forms for ASN.1 types
213  */
214 char *Form[] = {
215 	"Primitive",
216 #define PRIMITIVE	0
217 	"Constructed",
218 #define CONSTRUCTED	1
219 };
220 
221 /*
222  * A structure for the OID tree for the compiled-in MIB.
223  * This is stored as a general-order tree.
224  */
225 struct obj {
226 	char	*desc;			/* name of object */
227 	u_int	oid;			/* sub-id following parent */
228 	u_char	type;			/* object type (unused) */
229 	struct obj *child, *next;	/* child and next sibling pointers */
230 } *objp = NULL;
231 
232 /*
233  * Include the compiled in SNMP MIB.  "mib.h" is produced by feeding
234  * RFC-1156 format files into "makemib".  "mib.h" MUST define at least
235  * a value for `mibroot'.
236  *
237  * In particular, this is gross, as this is including initialized structures,
238  * and by right shouldn't be an "include" file.
239  */
240 #include "mib.h"
241 
242 /*
243  * This defines a list of OIDs which will be abbreviated on output.
244  * Currently, this includes the prefixes for the Internet MIB, the
245  * private enterprises tree, and the experimental tree.
246  */
247 struct obj_abrev {
248 	char *prefix;			/* prefix for this abrev */
249 	struct obj *node;		/* pointer into object table */
250 	char *oid;			/* ASN.1 encoded OID */
251 } obj_abrev_list[] = {
252 #ifndef NO_ABREV_MIB
253 	/* .iso.org.dod.internet.mgmt.mib */
254 	{ "",	&_mib_obj,		"\53\6\1\2\1" },
255 #endif
256 #ifndef NO_ABREV_ENTER
257 	/* .iso.org.dod.internet.private.enterprises */
258 	{ "E:",	&_enterprises_obj,	"\53\6\1\4\1" },
259 #endif
260 #ifndef NO_ABREV_EXPERI
261 	/* .iso.org.dod.internet.experimental */
262 	{ "X:",	&_experimental_obj,	"\53\6\1\3" },
263 #endif
264 #ifndef NO_ABREV_SNMPMIBOBJECTS
265 	/* .iso.org.dod.internet.snmpV2.snmpModules.snmpMIB.snmpMIBObjects */
266 	{ "S:", &_snmpmibobjects_obj,	"\53\6\1\6\3\1\1" },
267 #endif
268 	{ 0,0,0 }
269 };
270 
271 /*
272  * This is used in the OID print routine to walk down the object tree
273  * rooted at `mibroot'.
274  */
275 #define OBJ_PRINT(o, suppressdot) \
276 { \
277 	if (objp) { \
278 		do { \
279 			if ((o) == objp->oid) \
280 				break; \
281 		} while ((objp = objp->next) != NULL); \
282 	} \
283 	if (objp) { \
284 		printf(suppressdot?"%s":".%s", objp->desc); \
285 		objp = objp->child; \
286 	} else \
287 		printf(suppressdot?"%u":".%u", (o)); \
288 }
289 
290 /*
291  * This is the definition for the Any-Data-Type storage used purely for
292  * temporary internal representation while decoding an ASN.1 data stream.
293  */
294 struct be {
295 	u_int32_t asnlen;
296 	union {
297 		caddr_t raw;
298 		int32_t integer;
299 		u_int32_t uns;
300 		u_int64_t uns64;
301 		const u_char *str;
302 	} data;
303 	u_short id;
304 	u_char form, class;		/* tag info */
305 	u_char type;
306 #define BE_ANY		255
307 #define BE_NONE		0
308 #define BE_NULL		1
309 #define BE_OCTET	2
310 #define BE_OID		3
311 #define BE_INT		4
312 #define BE_UNS		5
313 #define BE_STR		6
314 #define BE_SEQ		7
315 #define BE_INETADDR	8
316 #define BE_PDU		9
317 #define BE_UNS64	10
318 };
319 
320 
321 /*
322  * SNMP components
323  */
324 static int snmp3_print_usmparams(const u_char *, u_int);
325 
326 enum snmp_version {
327 	SNMP_V1 = 0,
328 	SNMP_V2C = 1,
329 	SNMP_V3 = 3
330 };
331 #define SNMP3_AUTH(f) (f & 0x01)
332 #define SNMP3_PRIV(f) (f & 0x02)
333 #define SNMP3_REPORT(f) (f & 0x04)
334 
335 struct snmp3_sm {
336 	int id;
337 	char *name;
338 	int (*parse_params)(const u_char *, u_int);
339 } snmp3_securitymodel[] = {
340 	{3, "USM", snmp3_print_usmparams},
341 	{0, NULL, NULL}
342 };
343 
344 /*
345  * Defaults for SNMP PDU components
346  */
347 #define DEF_COMMUNITY "public"
348 
349 /*
350  * constants for ASN.1 decoding
351  */
352 #define OIDMUX 40
353 #define ASNLEN_INETADDR 4
354 #define ASN_SHIFT7 7
355 #define ASN_SHIFT8 8
356 #define ASN_BIT8 0x80
357 #define ASN_LONGLEN 0x80
358 
359 #define ASN_ID_BITS 0x1f
360 #define ASN_FORM_BITS 0x20
361 #define ASN_FORM_SHIFT 5
362 #define ASN_CLASS_BITS 0xc0
363 #define ASN_CLASS_SHIFT 6
364 
365 #define ASN_ID_EXT 0x1f		/* extension ID in tag field */
366 
367 /*
368  * truncated==1 means the packet was complete, but we don't have all of
369  * it to decode.
370  */
371 static int truncated;
372 
373 /*
374  * This decodes the next ASN.1 object in the stream pointed to by "p"
375  * (and of real-length "len") and stores the intermediate data in the
376  * provided BE object.
377  *
378  * This returns -l if it fails (i.e., the ASN.1 stream is not valid).
379  * O/w, this returns the number of bytes parsed from "p".
380  */
381 static int
382 asn1_parse(const u_char *p, u_int len, struct be *elem)
383 {
384 	u_char form, class, id;
385 	int i, hdr;
386 
387 	elem->asnlen = 0;
388 	elem->type = BE_ANY;
389 	if (len < 1) {
390 		if (truncated)
391 			printf("[|snmp]");
392 		else
393 			printf("[nothing to parse]");
394 		return -1;
395 	}
396 
397 	/*
398 	 * it would be nice to use a bit field, but you can't depend on them.
399 	 *  +---+---+---+---+---+---+---+---+
400 	 *  + class |frm|        id         |
401 	 *  +---+---+---+---+---+---+---+---+
402 	 *    7   6   5   4   3   2   1   0
403 	 */
404 	id = *p & ASN_ID_BITS;		/* lower 5 bits, range 00-1f */
405 #ifdef notdef
406 	form = (*p & 0xe0) >> 5;	/* move upper 3 bits to lower 3 */
407 	class = form >> 1;		/* bits 7&6 -> bits 1&0, range 0-3 */
408 	form &= 0x1;			/* bit 5 -> bit 0, range 0-1 */
409 #else
410 	form = (u_char)(*p & ASN_FORM_BITS) >> ASN_FORM_SHIFT;
411 	class = (u_char)(*p & ASN_CLASS_BITS) >> ASN_CLASS_SHIFT;
412 #endif
413 	elem->form = form;
414 	elem->class = class;
415 	elem->id = id;
416 	if (vflag > 1)
417 		printf("|%.2x", *p);
418 	p++; len--; hdr = 1;
419 	/* extended tag field */
420 	if (id == ASN_ID_EXT) {
421 		for (id = 0; *p & ASN_BIT8 && len > 0; len--, hdr++, p++) {
422 			if (vflag > 1)
423 				printf("|%.2x", *p);
424 			id = (id << 7) | (*p & ~ASN_BIT8);
425 		}
426 		if (len == 0 && *p & ASN_BIT8) {
427 			if (truncated)
428 				printf("[|snmp]");
429 			else
430 				printf("[Xtagfield?]");
431 			return -1;
432 		}
433 		elem->id = id = (id << 7) | *p;
434 		--len;
435 		++hdr;
436 		++p;
437 	}
438 	if (len < 1) {
439 		if (truncated)
440 			printf("[|snmp]");
441 		else
442 			printf("[no asnlen]");
443 		return -1;
444 	}
445 	elem->asnlen = *p;
446 	if (vflag > 1)
447 		printf("|%.2x", *p);
448 	p++; len--; hdr++;
449 	if (elem->asnlen & ASN_BIT8) {
450 		int noct = elem->asnlen % ASN_BIT8;
451 		elem->asnlen = 0;
452 		if (len < noct) {
453 			if (truncated)
454 				printf("[|snmp]");
455 			else
456 				printf("[asnlen? %d<%d]", len, noct);
457 			return -1;
458 		}
459 		for (; noct-- > 0; len--, hdr++) {
460 			if (vflag > 1)
461 				printf("|%.2x", *p);
462 			elem->asnlen = (elem->asnlen << ASN_SHIFT8) | *p++;
463 		}
464 	}
465 	if (len < elem->asnlen) {
466 		if (!truncated) {
467 			printf("[len%d<asnlen%u]", len, elem->asnlen);
468 			return -1;
469 		}
470 		/* maybe should check at least 4? */
471 		elem->asnlen = len;
472 	}
473 	if (form >= sizeof(Form)/sizeof(Form[0])) {
474 		if (truncated)
475 			printf("[|snmp]");
476 		else
477 			printf("[form?%d]", form);
478 		return -1;
479 	}
480 	if (class >= sizeof(Class)/sizeof(Class[0])) {
481 		if (truncated)
482 			printf("[|snmp]");
483 		else
484 			printf("[class?%c/%d]", *Form[form], class);
485 		return -1;
486 	}
487 	if ((int)id >= Class[class].numIDs) {
488 		if (truncated)
489 			printf("[|snmp]");
490 		else
491 			printf("[id?%c/%s/%d]", *Form[form],
492 			    Class[class].name, id);
493 		return -1;
494 	}
495 
496 	switch (form) {
497 	case PRIMITIVE:
498 		switch (class) {
499 		case UNIVERSAL:
500 			switch (id) {
501 			case STRING:
502 				elem->type = BE_STR;
503 				elem->data.str = p;
504 				break;
505 
506 			case INTEGER: {
507 				int32_t data;
508 				elem->type = BE_INT;
509 				data = 0;
510 
511 				if (*p & ASN_BIT8)	/* negative */
512 					data = -1;
513 				for (i = elem->asnlen; i-- > 0; p++)
514 					data = (data << ASN_SHIFT8) | *p;
515 				elem->data.integer = data;
516 				break;
517 			}
518 
519 			case OBJECTID:
520 				elem->type = BE_OID;
521 				elem->data.raw = (caddr_t)p;
522 				break;
523 
524 			case ASN_NULL:
525 				elem->type = BE_NULL;
526 				elem->data.raw = NULL;
527 				break;
528 
529 			default:
530 				elem->type = BE_OCTET;
531 				elem->data.raw = (caddr_t)p;
532 				printf("[P/U/%s]",
533 					Class[class].Id[id]);
534 				break;
535 			}
536 			break;
537 
538 		case APPLICATION:
539 			switch (id) {
540 			case IPADDR:
541 				elem->type = BE_INETADDR;
542 				elem->data.raw = (caddr_t)p;
543 				break;
544 
545 			case COUNTER:
546 			case GAUGE:
547 			case TIMETICKS:
548 			case OPAQUE:
549 			case NSAPADDR:
550 			case UINTEGER32: {
551 				u_int32_t data;
552 				elem->type = BE_UNS;
553 				data = 0;
554 				for (i = elem->asnlen; i-- > 0; p++)
555 					data = (data << 8) + *p;
556 				elem->data.uns = data;
557 				break;
558 			}
559 
560 			case COUNTER64: {
561 				u_int64_t data;
562 				elem->type = BE_UNS64;
563 				data = 0;
564 				for (i = elem->asnlen; i-- > 0; p++)
565 					data = (data << 8) + *p;
566 				elem->data.uns64 = data;
567 				break;
568 			}
569 
570 			default:
571 				elem->type = BE_OCTET;
572 				elem->data.raw = (caddr_t)p;
573 				printf("[P/A/%s]",
574 					Class[class].Id[id]);
575 				break;
576 			}
577 			break;
578 
579 		default:
580 			elem->type = BE_OCTET;
581 			elem->data.raw = (caddr_t)p;
582 			printf("[P/%s/%s]",
583 				Class[class].name, Class[class].Id[id]);
584 			break;
585 		}
586 		break;
587 
588 	case CONSTRUCTED:
589 		switch (class) {
590 		case UNIVERSAL:
591 			switch (id) {
592 			case SEQUENCE:
593 				elem->type = BE_SEQ;
594 				elem->data.raw = (caddr_t)p;
595 				break;
596 
597 			default:
598 				elem->type = BE_OCTET;
599 				elem->data.raw = (caddr_t)p;
600 				printf("C/U/%s", Class[class].Id[id]);
601 				break;
602 			}
603 			break;
604 
605 		case CONTEXT:
606 			elem->type = BE_PDU;
607 			elem->data.raw = (caddr_t)p;
608 			break;
609 
610 		default:
611 			elem->type = BE_OCTET;
612 			elem->data.raw = (caddr_t)p;
613 			printf("C/%s/%s",
614 				Class[class].name, Class[class].Id[id]);
615 			break;
616 		}
617 		break;
618 	}
619 	p += elem->asnlen;
620 	len -= elem->asnlen;
621 	return elem->asnlen + hdr;
622 }
623 
624 /*
625  * Display the ASN.1 object represented by the BE object.
626  * This used to be an integral part of asn1_parse() before the intermediate
627  * BE form was added.
628  */
629 static void
630 asn1_print(struct be *elem)
631 {
632 	u_char *p = (u_char *)elem->data.raw;
633 	u_int32_t asnlen = elem->asnlen;
634 	int i;
635 
636 	switch (elem->type) {
637 	case BE_OCTET:
638 		for (i = asnlen; i-- > 0; p++)
639 			printf("_%.2x", *p);
640 		break;
641 	case BE_NULL:
642 		break;
643 	case BE_OID: {
644 	int o = 0, first = -1, i = asnlen;
645 
646 		if (!nflag && asnlen > 2) {
647 			struct obj_abrev *a = &obj_abrev_list[0];
648 			for (; a->node; a++) {
649 				if (!memcmp(a->oid, (char *)p,
650 				    strlen(a->oid))) {
651 					objp = a->node->child;
652 					i -= strlen(a->oid);
653 					p += strlen(a->oid);
654 					printf("%s", a->prefix);
655 					first = 1;
656 					break;
657 				}
658 			}
659 		}
660 		for (; i-- > 0; p++) {
661 			o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
662 			if (*p & ASN_LONGLEN)
663 				continue;
664 
665 			/*
666 			 * first subitem encodes two items with 1st*OIDMUX+2nd
667 			 */
668 			if (first < 0) {
669 				if (!nflag)
670 					objp = mibroot;
671 				first = 0;
672 				OBJ_PRINT(o/OIDMUX, first);
673 				o %= OIDMUX;
674 			}
675 			OBJ_PRINT(o, first);
676 			if (--first < 0)
677 				first = 0;
678 			o = 0;
679 		}
680 		break;
681 	}
682 	case BE_INT:
683 		printf("%d", elem->data.integer);
684 		break;
685 	case BE_UNS:
686 		printf("%d", elem->data.uns);
687 		break;
688 	case BE_UNS64:
689 		printf("%lld", elem->data.uns64);
690 		break;
691 	case BE_STR: {
692 		int printable = 1, first = 1;
693 		const u_char *p = elem->data.str;
694 		for (i = asnlen; printable && i-- > 0; p++)
695 			printable = isprint(*p) || isspace(*p);
696 		p = elem->data.str;
697 		if (printable) {
698 			putchar('"');
699 			(void)fn_print(p, p + asnlen);
700 			putchar('"');
701 		} else
702 			for (i = asnlen; i-- > 0; p++) {
703 				printf(first ? "%.2x" : "_%.2x", *p);
704 				first = 0;
705 			}
706 		break;
707 	}
708 	case BE_SEQ:
709 		printf("Seq(%u)", elem->asnlen);
710 		break;
711 	case BE_INETADDR: {
712 		char sep;
713 		if (asnlen != ASNLEN_INETADDR)
714 			printf("[inetaddr len!=%d]", ASNLEN_INETADDR);
715 		sep='[';
716 		for (i = asnlen; i-- > 0; p++) {
717 			printf("%c%u", sep, *p);
718 			sep='.';
719 		}
720 		putchar(']');
721 		break;
722 	}
723 	case BE_PDU:
724 		printf("%s(%u)",
725 			Class[CONTEXT].Id[elem->id], elem->asnlen);
726 		break;
727 	case BE_ANY:
728 		printf("[BE_ANY!?]");
729 		break;
730 	default:
731 		printf("[be!?]");
732 		break;
733 	}
734 }
735 
736 #ifdef notdef
737 /*
738  * This is a brute force ASN.1 printer: recurses to dump an entire structure.
739  * This will work for any ASN.1 stream, not just an SNMP PDU.
740  *
741  * By adding newlines and spaces at the correct places, this would print in
742  * Rose-Normal-Form.
743  *
744  * This is not currently used.
745  */
746 static void
747 asn1_decode(u_char *p, u_int length)
748 {
749 	struct be elem;
750 	int i = 0;
751 
752 	while (i >= 0 && length > 0) {
753 		i = asn1_parse(p, length, &elem);
754 		if (i >= 0) {
755 			printf(" ");
756 			asn1_print(&elem);
757 			if (elem.type == BE_SEQ || elem.type == BE_PDU) {
758 				printf(" {");
759 				asn1_decode(elem.data.raw, elem.asnlen);
760 				printf(" }");
761 			}
762 			length -= i;
763 			p += i;
764 		}
765 	}
766 }
767 #endif
768 
769 /*
770  * General SNMP header
771  *	SEQUENCE {
772  *		version INTEGER {version-1(0)},
773  *		community OCTET STRING,
774  *		data ANY	-- PDUs
775  *	}
776  * PDUs for all but Trap: (see rfc1157 from page 15 on)
777  *	SEQUENCE {
778  *		request-id INTEGER,
779  *		error-status INTEGER,
780  *		error-index INTEGER,
781  *		varbindlist SEQUENCE OF
782  *			SEQUENCE {
783  *				name ObjectName,
784  *				value ObjectValue
785  *			}
786  *	}
787  * PDU for Trap:
788  *	SEQUENCE {
789  *		enterprise OBJECT IDENTIFIER,
790  *		agent-addr NetworkAddress,
791  *		generic-trap INTEGER,
792  *		specific-trap INTEGER,
793  *		time-stamp TimeTicks,
794  *		varbindlist SEQUENCE OF
795  *			SEQUENCE {
796  *				name ObjectName,
797  *				value ObjectValue
798  *			}
799  *	}
800  */
801 
802 /*
803  * Decode SNMP varBind
804  */
805 static void
806 varbind_print(u_char pduid, const u_char *np, u_int length, int error)
807 {
808 	struct be elem;
809 	int count = 0, ind;
810 
811 	/* Sequence of varBind */
812 	if ((count = asn1_parse(np, length, &elem)) < 0)
813 		return;
814 	if (elem.type != BE_SEQ) {
815 		printf("[!SEQ of varbind]");
816 		asn1_print(&elem);
817 		return;
818 	}
819 	if (count < length)
820 		printf("[%d extra after SEQ of varbind]", length - count);
821 	/* descend */
822 	length = elem.asnlen;
823 	np = (u_char *)elem.data.raw;
824 
825 	for (ind = 1; length > 0; ind++) {
826 		const u_char *vbend;
827 		u_int vblength;
828 
829 		putchar(' ');
830 		/* Sequence */
831 		if ((count = asn1_parse(np, length, &elem)) < 0)
832 			return;
833 		if (elem.type != BE_SEQ) {
834 			printf("[!varbind]");
835 			asn1_print(&elem);
836 			return;
837 		}
838 		vbend = np + count;
839 		vblength = length - count;
840 		/* descend */
841 		length = elem.asnlen;
842 		np = (u_char *)elem.data.raw;
843 
844 		/* objName (OID) */
845 		if ((count = asn1_parse(np, length, &elem)) < 0)
846 			return;
847 		if (elem.type != BE_OID) {
848 			printf("[objName!=OID]");
849 			asn1_print(&elem);
850 			return;
851 		}
852 		if (!error || ind == error)
853 			asn1_print(&elem);
854 		length -= count;
855 		np += count;
856 
857 		if (pduid != GETREQ && pduid != GETNEXTREQ && !error)
858 				printf("=");
859 
860 		/* objVal (ANY) */
861 		if ((count = asn1_parse(np, length, &elem)) < 0)
862 			return;
863 		if (pduid == GETREQ || pduid == GETNEXTREQ || pduid == GETBULKREQ) {
864 			if (elem.type != BE_NULL) {
865 				printf("[objVal!=NULL]");
866 				asn1_print(&elem);
867 			}
868 		} else {
869 			if (error && ind == error && elem.type != BE_NULL)
870 				printf("[err objVal!=NULL]");
871 			if (!error || ind == error)
872 				asn1_print(&elem);
873 		}
874 
875 		length = vblength;
876 		np = vbend;
877 	}
878 }
879 
880 /*
881  * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, and SetRequest
882  */
883 static void
884 snmppdu_print(u_char pduid, const u_char *np, u_int length)
885 {
886 	struct be elem;
887 	int count = 0, error;
888 
889 	/* reqId (Integer) */
890 	if ((count = asn1_parse(np, length, &elem)) < 0)
891 		return;
892 	if (elem.type != BE_INT) {
893 		printf("[reqId!=INT]");
894 		asn1_print(&elem);
895 		return;
896 	}
897 	if (vflag)
898 		printf(" reqId=%d", elem.data.integer);
899 	length -= count;
900 	np += count;
901 
902 	/* errorStatus (Integer) */
903 	if ((count = asn1_parse(np, length, &elem)) < 0)
904 		return;
905 	if (elem.type != BE_INT) {
906 		printf("[errorStatus!=INT]");
907 		asn1_print(&elem);
908 		return;
909 	}
910 	error = 0;
911 	if ((pduid == GETREQ || pduid == GETNEXTREQ)
912 	    && elem.data.integer != 0) {
913 		char errbuf[20];
914 		printf("[errorStatus(%s)!=0]",
915 			DECODE_ErrorStatus(elem.data.integer));
916 	} else if (pduid == GETBULKREQ)
917 		printf(" non-repeaters=%d", elem.data.integer);
918 	else if (elem.data.integer != 0) {
919 		char errbuf[20];
920 		printf(" %s", DECODE_ErrorStatus(elem.data.integer));
921 		error = elem.data.integer;
922 	}
923 	length -= count;
924 	np += count;
925 
926 	/* errorIndex (Integer) */
927 	if ((count = asn1_parse(np, length, &elem)) < 0)
928 		return;
929 	if (elem.type != BE_INT) {
930 		printf("[errorIndex!=INT]");
931 		asn1_print(&elem);
932 		return;
933 	}
934 	if ((pduid == GETREQ || pduid == GETNEXTREQ)
935 	    && elem.data.integer != 0)
936 		printf("[errorIndex(%d)!=0]", elem.data.integer);
937 	else if (pduid == GETBULKREQ)
938 		printf(" max-repetitions=%d", elem.data.integer);
939 	else if (elem.data.integer != 0) {
940 		if (!error)
941 			printf("[errorIndex(%d) w/o errorStatus]",
942 				elem.data.integer);
943 		else {
944 			printf("@%d", elem.data.integer);
945 			error = elem.data.integer;
946 		}
947 	} else if (error) {
948 		printf("[errorIndex==0]");
949 		error = 0;
950 	}
951 	length -= count;
952 	np += count;
953 
954 	varbind_print(pduid, np, length, error);
955 	return;
956 }
957 
958 /*
959  * Decode SNMP Trap PDU
960  */
961 static void
962 trap_print(const u_char *np, u_int length)
963 {
964 	struct be elem;
965 	int count = 0, generic;
966 
967 	putchar(' ');
968 
969 	/* enterprise (oid) */
970 	if ((count = asn1_parse(np, length, &elem)) < 0)
971 		return;
972 	if (elem.type != BE_OID) {
973 		printf("[enterprise!=OID]");
974 		asn1_print(&elem);
975 		return;
976 	}
977 	asn1_print(&elem);
978 	length -= count;
979 	np += count;
980 
981 	putchar(' ');
982 
983 	/* agent-addr (inetaddr) */
984 	if ((count = asn1_parse(np, length, &elem)) < 0)
985 		return;
986 	if (elem.type != BE_INETADDR) {
987 		printf("[agent-addr!=INETADDR]");
988 		asn1_print(&elem);
989 		return;
990 	}
991 	asn1_print(&elem);
992 	length -= count;
993 	np += count;
994 
995 	/* generic-trap (Integer) */
996 	if ((count = asn1_parse(np, length, &elem)) < 0)
997 		return;
998 	if (elem.type != BE_INT) {
999 		printf("[generic-trap!=INT]");
1000 		asn1_print(&elem);
1001 		return;
1002 	}
1003 	generic = elem.data.integer;
1004 	{
1005 		char buf[20];
1006 		printf(" %s", DECODE_GenericTrap(generic));
1007 	}
1008 	length -= count;
1009 	np += count;
1010 
1011 	/* specific-trap (Integer) */
1012 	if ((count = asn1_parse(np, length, &elem)) < 0)
1013 		return;
1014 	if (elem.type != BE_INT) {
1015 		printf("[specific-trap!=INT]");
1016 		asn1_print(&elem);
1017 		return;
1018 	}
1019 	if (generic != GT_ENTERPRISE) {
1020 		if (elem.data.integer != 0)
1021 			printf("[specific-trap(%d)!=0]", elem.data.integer);
1022 	} else
1023 		printf(" s=%d", elem.data.integer);
1024 	length -= count;
1025 	np += count;
1026 
1027 	putchar(' ');
1028 
1029 	/* time-stamp (TimeTicks) */
1030 	if ((count = asn1_parse(np, length, &elem)) < 0)
1031 		return;
1032 	if (elem.type != BE_UNS) {			/* XXX */
1033 		printf("[time-stamp!=TIMETICKS]");
1034 		asn1_print(&elem);
1035 		return;
1036 	}
1037 	asn1_print(&elem);
1038 	length -= count;
1039 	np += count;
1040 
1041 	varbind_print (TRAP, np, length, 0);
1042 	return;
1043 }
1044 
1045 /*
1046  * Decode SNMP header and pass on to PDU printing routines
1047  */
1048 static void
1049 snmp12_print(const u_char *np, u_int length)
1050 {
1051 	struct be elem;
1052 	int count;
1053 
1054 	/* Community (String) */
1055 	if ((count = asn1_parse(np, length, &elem)) < 0)
1056 		return;
1057 	if (elem.type != BE_STR) {
1058 		printf("[comm!=STR]");
1059 		asn1_print(&elem);
1060 		return;
1061 	}
1062 	/* default community */
1063 	if (strncmp((char *)elem.data.str, DEF_COMMUNITY,
1064 	    sizeof(DEF_COMMUNITY) - 1))
1065 		/* ! "public" */
1066 		printf("C=%.*s ", (int)elem.asnlen, elem.data.str);
1067 	length -= count;
1068 	np += count;
1069 
1070 	/* PDU (Context) */
1071 	if ((count = asn1_parse(np, length, &elem)) < 0)
1072 		return;
1073 	if (elem.type != BE_PDU) {
1074 		printf("[no PDU]");
1075 		return;
1076 	}
1077 	if (count < length)
1078 		printf("[%d extra after PDU]", length - count);
1079 	asn1_print(&elem);
1080 	/* descend into PDU */
1081 	length = elem.asnlen;
1082 	np = (u_char *)elem.data.raw;
1083 
1084 	switch (elem.id) {
1085 	case TRAP:
1086 		trap_print(np, length);
1087 		break;
1088 	case GETREQ:
1089 	case GETNEXTREQ:
1090 	case GETRESP:
1091 	case SETREQ:
1092 	case GETBULKREQ:
1093 	case INFORMREQ:
1094 	case TRAPV2:
1095 	case REPORT:
1096 		snmppdu_print(elem.id, np, length);
1097 		break;
1098 	}
1099 	return;
1100 }
1101 
1102 static int
1103 snmp3_print_usmparams(const u_char *np, u_int length)
1104 {
1105 	struct be elem;
1106 	int count;
1107 	int i;
1108 
1109 	if ((count = asn1_parse(np, length, &elem)) < 0)
1110 		return -1;
1111 	if (elem.type != BE_SEQ) {
1112 		printf("[!usmSM SEQ]");
1113 		asn1_print(&elem);
1114 		return -1;
1115 	}
1116 	if (count < length) {
1117 		printf("[%d extra after usmSM]", length - count);
1118 		return -1;
1119 	}
1120 	/* descend */
1121 	length = elem.asnlen;
1122 	np = (u_char *)elem.data.raw;
1123 
1124 	/* msgAuthoritativeEngineID */
1125 	if ((count = asn1_parse(np, length, &elem)) < 0)
1126 		return -1;
1127 	if (elem.type != BE_STR) {
1128 		printf("[umsEID!=STR]");
1129 		asn1_print(&elem);
1130 		return -1;
1131 	}
1132 	if (vflag && elem.asnlen > 0) {
1133 		printf("umsEID=0x");
1134 		for (i = 0; i < elem.asnlen; i++)
1135 			printf("%02hhX", elem.data.str[i]);
1136 		putchar(' ');
1137 	}
1138 	length -= count;
1139 	np += count;
1140 
1141 	/* msgAuthoritativeEngineBoots */
1142 	if ((count = asn1_parse(np, length, &elem)) < 0)
1143 		return -1;
1144 	if (elem.type != BE_INT) {
1145 		printf("[EBoots!=INT]");
1146 		asn1_print(&elem);
1147 		return -1;
1148 	}
1149 	if (vflag)
1150 		printf("EBoots=%d ", elem.data.integer);
1151 	length -= count;
1152 	np += count;
1153 
1154 	/* msgAuthoritativeEngineTime */
1155 	if ((count = asn1_parse(np, length, &elem)) < 0)
1156 		return -1;
1157 	if (elem.type != BE_INT) {
1158 		printf("[ETime!=INT]");
1159 		asn1_print(&elem);
1160 		return -1;
1161 	}
1162 	if (vflag)
1163 		printf("ETime=%d ", elem.data.integer);
1164 	length -= count;
1165 	np += count;
1166 
1167 	if ((count = asn1_parse(np, length, &elem)) < 0)
1168 		return -1;
1169 	if (elem.type != BE_STR) {
1170 		printf("[User!=STR]");
1171 		asn1_print(&elem);
1172 		return -1;
1173 	}
1174 	if (elem.asnlen > 0) {
1175 		printf("User=");
1176 		asn1_print(&elem);
1177 		putchar(' ');
1178 	}
1179 	length -= count;
1180 	np += count;
1181 
1182 	/* msgAuthenticationParameters */
1183 	if ((count = asn1_parse(np, length, &elem)) < 0)
1184 		return -1;
1185 	if (elem.type != BE_STR) {
1186 		printf("[AuthParam!=STR]");
1187 		asn1_print(&elem);
1188 		return -1;
1189 	}
1190 	/* Can't validate msgAuthenticationParameters without pass */
1191 	length -= count;
1192 	np += count;
1193 
1194 	/* msgPrivacyParameters */
1195 	if ((count = asn1_parse(np, length, &elem)) < 0)
1196 		return -1;
1197 	if (elem.type != BE_STR) {
1198 		printf("[PrivParam!=STR]");
1199 		asn1_print(&elem);
1200 		return -1;
1201 	}
1202 	/* Salt is not useful if we can't decrypt */
1203 	if (length - count != 0) {
1204 		printf("[%d extra after usmSM]", length - count);
1205 		return -1;
1206 	}
1207 	return 0;
1208 }
1209 
1210 static void
1211 snmp3_print(const u_char *np, u_int length)
1212 {
1213 	struct be elem;
1214 	struct snmp3_sm *sm = NULL;
1215 	int count;
1216 	int sublen;
1217 	int i;
1218 	int authpriv;
1219 	u_char *subnp;
1220 
1221 	/* Header sequence */
1222 	if ((count = asn1_parse(np, length, &elem)) < 0)
1223 		return;
1224 	if (elem.type != BE_SEQ) {
1225 		printf("[!header SEQ]");
1226 		asn1_print(&elem);
1227 		return;
1228 	}
1229 	np += count;
1230 	length -= count;
1231 	/* descend */
1232 	/* msgID */
1233 	sublen = elem.asnlen;
1234 	subnp = (u_char *)elem.data.raw;
1235 	if ((count = asn1_parse(subnp, sublen, &elem)) < 0)
1236 		return;
1237 	if (elem.type != BE_INT) {
1238 		printf("[msgID!=INT]");
1239 		asn1_print(&elem);
1240 		return;
1241 	}
1242 	if (vflag)
1243 		printf("msgID=%d ", elem.data.integer);
1244 	sublen -= count;
1245 	subnp += count;
1246 
1247 	/* msgMaxSize */
1248 	if ((count = asn1_parse(subnp, sublen, &elem)) < 0)
1249 		return;
1250 	if (elem.type != BE_INT) {
1251 		printf("[msgMS!=INT]");
1252 		asn1_print(&elem);
1253 		return;
1254 	}
1255 	if (vflag)
1256 		printf("msgMS=%d ", elem.data.integer);
1257 	sublen -= count;
1258 	subnp += count;
1259 
1260 	/* msgFlags */
1261 	if ((count = asn1_parse(subnp, sublen, &elem)) < 0)
1262 		return;
1263 	if (elem.type != BE_STR) {
1264 		printf("[msgFl!=STR]");
1265 		asn1_print(&elem);
1266 		return;
1267 	}
1268 	if (elem.asnlen != 1)
1269 		printf("[%d extra after msgFl]", elem.asnlen - 1);
1270 	authpriv = *elem.data.str & 0x3;
1271 	if (vflag && (*elem.data.str & 0x7) != 0) {
1272 		printf("(%suth%sPriv%s) ",
1273 		    SNMP3_AUTH(*elem.data.str) ? "a" : "noA",
1274 		    SNMP3_PRIV(*elem.data.str) ? "" : "No",
1275 		    SNMP3_REPORT(*elem.data.str) ? "|Reportable" : ""
1276 		);
1277 	}
1278 	sublen -= count;
1279 	subnp += count;
1280 
1281 	/* msgSecurityModel */
1282 	if ((count = asn1_parse(subnp, sublen, &elem)) < 0)
1283 		return;
1284 	if (elem.type != BE_INT) {
1285 		printf("[msgSM!=INT]");
1286 		asn1_print(&elem);
1287 		return;
1288 	}
1289 	for (i = 0; snmp3_securitymodel[i].id != 0; i++) {
1290 		if (snmp3_securitymodel[i].id == elem.data.integer) {
1291 			sm = &(snmp3_securitymodel[i]);
1292 			break;
1293 		}
1294 	}
1295 	if (vflag) {
1296 		if (sm != NULL && nflag == 0)
1297 			printf("msgSM=%s ", sm->name);
1298 		else
1299 			printf("msgSM=%d ", elem.data.integer);
1300 	}
1301 	if (sublen - count != 0) {
1302 		printf("[%d extra after header]", sublen - count);
1303 		return;
1304 	}
1305 
1306 	/* ascend */
1307 	if ((count = asn1_parse(np, length, &elem)) < 0)
1308 		return;
1309 	if (elem.type != BE_STR) {
1310 		printf("msgSP!=STR]");
1311 		asn1_print(&elem);
1312 		return;
1313 	}
1314 	if (sm != NULL && sm->parse_params != NULL) {
1315 		if (sm->parse_params(elem.data.raw, elem.asnlen) == -1)
1316 			return;
1317 	}
1318 	length -= count;
1319 	np += count;
1320 
1321 	if (SNMP3_PRIV(authpriv) != 0) {
1322 		printf("[encrypted PDU]");
1323 		return;
1324 	}
1325 
1326 	/* msgData */
1327 	if ((count = asn1_parse(np, length, &elem)) < 0)
1328 		return;
1329 	if (elem.type != BE_SEQ) {
1330 		printf("[ScPDU!=SEQ]");
1331 		asn1_print(&elem);
1332 		return;
1333 	}
1334 	if (count < length)
1335 		printf("[%d extra after ScPDU]", length - count);
1336 	/* descend */
1337 	length = elem.asnlen;
1338 	np = (u_char *)elem.data.raw;
1339 
1340 	/* contextEngineID */
1341 	if ((count = asn1_parse(np, length, &elem)) < 0)
1342 		return;
1343 	if (elem.type != BE_STR) {
1344 		printf("ctxEID!=STR]");
1345 		asn1_print(&elem);
1346 		return;
1347 	}
1348 	if (vflag && elem.asnlen > 0) {
1349 		printf("ctxEID=0x");
1350 		for (i = 0; i < elem.asnlen; i++)
1351 			printf("%02hhX", elem.data.str[i]);
1352 		putchar(' ');
1353 	}
1354 	length -= count;
1355 	np += count;
1356 
1357 	/* contextName */
1358 	if ((count = asn1_parse(np, length, &elem)) < 0)
1359 		return;
1360 	if (elem.type != BE_STR) {
1361 		printf("[ctxEName!=STR]");
1362 		asn1_print(&elem);
1363 		return;
1364 	}
1365 	if (vflag && elem.asnlen > 0) {
1366 		printf("ctxName=");
1367 		asn1_print(&elem);
1368 		putchar(' ');
1369 	}
1370 	length -= count;
1371 	np += count;
1372 
1373 	/* Data */
1374 	if ((count = asn1_parse(np, length, &elem)) < 0)
1375 		return;
1376 	if (elem.type != BE_PDU) {
1377 		printf("[data!=PDU]");
1378 		asn1_print(&elem);
1379 		return;
1380 	}
1381 	if (count < length)
1382 		printf("[%d extra after PDU]", length - count);
1383 	asn1_print(&elem);
1384 	/* descend into PDU */
1385 	length = elem.asnlen;
1386 	np = (u_char *)elem.data.raw;
1387 	switch (elem.id) {
1388 	case TRAP:
1389 		trap_print(np, length);
1390 		break;
1391 	case GETREQ:
1392 	case GETNEXTREQ:
1393 	case GETRESP:
1394 	case SETREQ:
1395 	case GETBULKREQ:
1396 	case INFORMREQ:
1397 	case TRAPV2:
1398 	case REPORT:
1399 		snmppdu_print(elem.id, np, length);
1400 		break;
1401 	}
1402 }
1403 
1404 void
1405 snmp_print(const u_char *np, u_int length)
1406 {
1407 	struct be elem;
1408 	int count = 0;
1409 
1410 	truncated = 0;
1411 
1412 	/* truncated packet? */
1413 	if (np + length > snapend) {
1414 		truncated = 1;
1415 		length = snapend - np;
1416 	}
1417 
1418 	/* initial Sequence */
1419 	if ((count = asn1_parse(np, length, &elem)) < 0)
1420 		return;
1421 	if (elem.type != BE_SEQ) {
1422 		printf("[!init SEQ]");
1423 		asn1_print(&elem);
1424 		return;
1425 	}
1426 	if (count < length)
1427 		printf("[%d extra after iSEQ]", length - count);
1428 	/* descend */
1429 	length = elem.asnlen;
1430 	np = (u_char *)elem.data.raw;
1431 	/* Version (Integer) */
1432 	if ((count = asn1_parse(np, length, &elem)) < 0)
1433 		return;
1434 	if (elem.type != BE_INT) {
1435 		printf("[version!=INT]");
1436 		asn1_print(&elem);
1437 		return;
1438 	}
1439 	length -= count;
1440 	np += count;
1441 	switch (elem.data.integer) {
1442 	case SNMP_V1:
1443 	case SNMP_V2C:
1444 		if (vflag)
1445 			printf("SNMPv%s ", elem.data.integer == SNMP_V1 ?
1446 			    "1" : "2c");
1447 		snmp12_print(np, length);
1448 		return;
1449 	case SNMP_V3:
1450 		if (vflag)
1451 			printf("SNMPv3 ");
1452 		snmp3_print(np, length);
1453 		return;
1454 	default:
1455 		printf("[snmp version(%d)]", elem.data.integer);
1456 		return;
1457 	}
1458 }
1459