1 /*
2  * ProFTPD - mod_snmp ASN.1 support
3  * Copyright (c) 2008-2020 TJ Saunders
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18  *
19  * As a special exemption, TJ Saunders and other respective copyright holders
20  * give permission to link this program with OpenSSL, and distribute the
21  * resulting executable, without including the source code for OpenSSL in the
22  * source distribution.
23  */
24 
25 #include "mod_snmp.h"
26 #include "asn1.h"
27 #include "mib.h"
28 
29 static const char *trace_channel = "snmp.asn1";
30 
31 /* Set an arbitrary max limit of 512K on ASN.1 objects */
32 #define SNMP_ASN1_MAX_OBJECT_LEN	(512 * 1024)
33 
asn1_typestr(unsigned char byte)34 static const char *asn1_typestr(unsigned char byte) {
35   unsigned char asn1_type;
36   const char *typestr = "(unknown)";
37 
38   asn1_type = byte;
39 
40   /* Clear any Class and P/C bits */
41   asn1_type &= ~(SNMP_ASN1_CLASS_APPLICATION|SNMP_ASN1_CLASS_CONTEXT|SNMP_ASN1_CLASS_PRIVATE);
42   asn1_type &= ~(SNMP_ASN1_CONSTRUCT);
43 
44   switch (asn1_type) {
45     case SNMP_ASN1_TYPE_BOOLEAN:
46       typestr = "BOOLEAN";
47       break;
48 
49     case SNMP_ASN1_TYPE_INTEGER:
50       typestr = "INTEGER";
51       break;
52 
53     case SNMP_ASN1_TYPE_BITSTRING:
54       typestr = "BITSTRING";
55       break;
56 
57     case SNMP_ASN1_TYPE_OCTETSTRING:
58       typestr = "OCTETSTRING";
59       break;
60 
61     case SNMP_ASN1_TYPE_NULL:
62       typestr = "NULL";
63       break;
64 
65     case SNMP_ASN1_TYPE_OID:
66       typestr = "OID";
67       break;
68 
69     case SNMP_ASN1_TYPE_SEQUENCE:
70       typestr = "SEQUENCE";
71       break;
72 
73     case SNMP_ASN1_TYPE_SET:
74       typestr = "SET";
75       break;
76   }
77 
78   return typestr;
79 }
80 
asn1_classstr(unsigned char asn1_type)81 static const char *asn1_classstr(unsigned char asn1_type) {
82   const char *class_str = "Universal";
83 
84   if (asn1_type & SNMP_ASN1_CLASS_APPLICATION) {
85     class_str = "Application";
86 
87   } else if (asn1_type & SNMP_ASN1_CLASS_CONTEXT) {
88     class_str = "Context";
89 
90   } else if (asn1_type & SNMP_ASN1_CLASS_PRIVATE) {
91     class_str = "Private";
92   }
93 
94   return class_str;
95 }
96 
asn1_pcstr(unsigned char asn1_type)97 static const char *asn1_pcstr(unsigned char asn1_type) {
98   const char *pcstr = "Primitive";
99 
100   if (asn1_type & SNMP_ASN1_CONSTRUCT) {
101     pcstr = "Construct";
102   }
103 
104   return pcstr;
105 }
106 
snmp_asn1_get_oidstr(pool * p,oid_t * asn1_oid,unsigned int asn1_oidlen)107 const char *snmp_asn1_get_oidstr(pool *p, oid_t *asn1_oid,
108     unsigned int asn1_oidlen) {
109   register unsigned int i;
110   char *oidstr = "";
111 
112   if (asn1_oidlen == 0) {
113     return oidstr;
114   }
115 
116   for (i = 0; i < asn1_oidlen; i++) {
117     char buf[16];
118 
119     memset(buf, '\0', sizeof(buf));
120     pr_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long) asn1_oid[i]);
121 
122     oidstr = pstrcat(p, oidstr, buf, NULL);
123 
124     /* Skip the trailing '.' in the OID string. */
125     if (i != (asn1_oidlen-1)) {
126       oidstr = pstrcat(p, oidstr, ".", NULL);
127     }
128   }
129 
130   return oidstr;
131 }
132 
snmp_asn1_get_tagstr(pool * p,unsigned char asn1_type)133 const char *snmp_asn1_get_tagstr(pool *p, unsigned char asn1_type) {
134   const char *tagstr;
135 
136   tagstr = pstrcat(p, "type '", asn1_typestr(asn1_type), "', class '",
137     asn1_classstr(asn1_type), "', ", asn1_pcstr(asn1_type), NULL);
138   return tagstr;
139 }
140 
asn1_read_byte(pool * p,unsigned char ** buf,size_t * buflen,unsigned char * byte)141 static int asn1_read_byte(pool *p, unsigned char **buf, size_t *buflen,
142     unsigned char *byte) {
143 
144   if (*buflen < sizeof(unsigned char)) {
145     (void) pr_log_writefile(snmp_logfd, MOD_SNMP_VERSION,
146       "ASN.1 format error: unable to read type (buflen = %lu)",
147       (unsigned long) *buflen);
148     pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
149     errno = EINVAL;
150     return -1;
151   }
152 
153   memmove(byte, *buf, sizeof(unsigned char));
154   (*buf) += sizeof(unsigned char);
155   (*buflen) -= sizeof(unsigned char);
156 
157   return 0;
158 }
159 
asn1_read_type(pool * p,unsigned char ** buf,size_t * buflen,unsigned char * asn1_type,int flags)160 static int asn1_read_type(pool *p, unsigned char **buf, size_t *buflen,
161     unsigned char *asn1_type, int flags) {
162   unsigned char byte = 0;
163   int res;
164 
165   res = asn1_read_byte(p, buf, buflen, &byte);
166   if (res < 0) {
167     return -1;
168   }
169 
170   *asn1_type = byte;
171 
172   if (!(flags & SNMP_ASN1_FL_NO_TRACE_TYPESTR)) {
173     pr_trace_msg(trace_channel, 18,
174       "read ASN.1 type 0x%02x (%s)", *asn1_type, asn1_typestr(*asn1_type));
175 
176   } else {
177     pr_trace_msg(trace_channel, 18, "read byte 0x%02x", *asn1_type);
178   }
179 
180   return 0;
181 }
182 
asn1_read_len(pool * p,unsigned char ** buf,size_t * buflen,unsigned int * asn1_len)183 static int asn1_read_len(pool *p, unsigned char **buf, size_t *buflen,
184     unsigned int *asn1_len) {
185   unsigned char byte = 0;
186 
187   if (*buflen < sizeof(unsigned char)) {
188     (void) pr_log_writefile(snmp_logfd, MOD_SNMP_VERSION,
189       "ASN.1 format error: unable to read length (buflen = %lu)",
190       (unsigned long) *buflen);
191     pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
192     errno = EINVAL;
193     return -1;
194   }
195 
196   /* First we check the first byte in the buffer, the 'length' byte. */
197   memmove(&byte, *buf, sizeof(unsigned char));
198   (*buf) += sizeof(unsigned char);
199   (*buflen) -= sizeof(unsigned char);
200 
201   if (byte & SNMP_ASN1_LEN_LONG) {
202     byte &= ~SNMP_ASN1_LEN_LONG;
203 
204     /* The high bit was set, indicating a "long" length value, spread
205      * out over the next bytes.
206      */
207     if (byte == 0) {
208       (void) pr_log_writefile(snmp_logfd, MOD_SNMP_VERSION,
209         "ASN.1 format error: invalid ASN1 length value %c", byte);
210       pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
211       errno = EINVAL;
212       return -1;
213     }
214 
215     if (byte > sizeof(unsigned int)) {
216       (void) pr_log_writefile(snmp_logfd, MOD_SNMP_VERSION,
217         "ASN.1 format error: invalid ASN1 length value %c (> %lu)", byte,
218         (unsigned long) sizeof(unsigned int));
219       pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
220       errno = EINVAL;
221       return -1;
222     }
223 
224     *asn1_len = 0;
225     memmove(asn1_len, *buf, (size_t) byte);
226     (*buf) += byte;
227     (*buflen) -= byte;
228 
229     *asn1_len = ntohl(*asn1_len);
230     *asn1_len >>= (8 * ((sizeof(unsigned int)) - byte));
231 
232   } else {
233     *asn1_len = (unsigned int) byte;
234   }
235 
236   pr_trace_msg(trace_channel, 18, "read ASN.1 length %u", *asn1_len);
237   return 0;
238 }
239 
snmp_asn1_read_header(pool * p,unsigned char ** buf,size_t * buflen,unsigned char * asn1_type,unsigned int * asn1_len,int flags)240 int snmp_asn1_read_header(pool *p, unsigned char **buf, size_t *buflen,
241     unsigned char *asn1_type, unsigned int *asn1_len, int flags) {
242   unsigned int objlen;
243   int res;
244 
245   /* XXX Currently don't support extension octets.  We check this by looking
246    * at the first byte of data to see if extension length bit is set.
247    */
248   if ((*buf)[0] == SNMP_ASN1_LEN_EXTENSION) {
249     pr_trace_msg(trace_channel, 3,
250       "failed reading object header: extension length bit set (%c)", (*buf)[0]);
251 
252     pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
253     errno = EPERM;
254     return -1;
255   }
256 
257   /* Type */
258   res = asn1_read_type(p, buf, buflen, asn1_type, flags);
259   if (res < 0) {
260     return -1;
261   }
262 
263   /* Length */
264   res = asn1_read_len(p, buf, buflen, &objlen);
265   if (res < 0) {
266     return -1;
267   }
268 
269   /* Sanity check on the object length, to make sure it is not absurd. */
270   if (objlen > SNMP_ASN1_MAX_OBJECT_LEN) {
271     pr_trace_msg(trace_channel, 3,
272       "failed reading object header: object length (%u bytes) is greater "
273       "than max object length (%u bytes)", objlen, SNMP_ASN1_MAX_OBJECT_LEN);
274     pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
275     errno = EINVAL;
276     return -1;
277   }
278 
279   if (objlen > *buflen) {
280     pr_trace_msg(trace_channel, 3,
281       "failed reading object header: object length (%u bytes) is greater "
282       "than remaining data (%lu bytes)", objlen, (unsigned long) (*buflen));
283 
284     pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
285     errno = EINVAL;
286     return -1;
287   }
288 
289   *asn1_len = objlen;
290   return 0;
291 }
292 
293 /* ASN.1 integer ::= 0x02 objlen byte {byte}* */
snmp_asn1_read_int(pool * p,unsigned char ** buf,size_t * buflen,unsigned char * asn1_type,long * asn1_int,int flags)294 int snmp_asn1_read_int(pool *p, unsigned char **buf, size_t *buflen,
295     unsigned char *asn1_type, long *asn1_int, int flags) {
296   unsigned int objlen = 0;
297   long objval = 0;
298   int res;
299 
300   /* Type */
301   res = asn1_read_type(p, buf, buflen, asn1_type, 0);
302   if (res < 0) {
303     return -1;
304   }
305 
306   /*  Check that we actually read an INTEGER as expected. */
307   if (!(*asn1_type & SNMP_ASN1_TYPE_INTEGER)) {
308     pr_trace_msg(trace_channel, 3,
309       "unable to read INTEGER (received type '%s')",
310       snmp_asn1_get_tagstr(p, *asn1_type));
311     errno = EINVAL;
312     return -1;
313   }
314 
315   /* Length */
316   res = asn1_read_len(p, buf, buflen, &objlen);
317   if (res < 0) {
318     return -1;
319   }
320 
321   /* Make sure there'e enough remaining data for the object. */
322   if (objlen > *buflen) {
323     pr_trace_msg(trace_channel, 3,
324       "failed reading object header: object length (%u bytes) is greater "
325       "than remaining data (%lu bytes)", objlen, (unsigned long) (*buflen));
326 
327     pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
328     errno = EINVAL;
329     return -1;
330   }
331 
332   if ((*buf)[0] & 0x80) {
333     /* The integer is negative; the negative sign bit is set. */
334 
335     if (flags & SNMP_ASN1_FL_UNSIGNED) {
336       objval = ~objval;
337 
338     } else {
339       objval = -1;
340     }
341   }
342 
343   /* Pull objlen bytes out of the buffer, building up the value. */
344   while (objlen--) {
345     unsigned char byte = 0;
346 
347     pr_signals_handle();
348 
349     res = asn1_read_byte(p, buf, buflen, &byte);
350     if (res < 0) {
351       return -1;
352     }
353 
354     objval = (objval << 8) | byte;
355   }
356 
357   *asn1_int = objval;
358   return 0;
359 }
360 
snmp_asn1_read_uint(pool * p,unsigned char ** buf,size_t * buflen,unsigned char * asn1_type,unsigned long * asn1_uint)361 int snmp_asn1_read_uint(pool *p, unsigned char **buf, size_t *buflen,
362     unsigned char *asn1_type, unsigned long *asn1_uint) {
363   long asn1_int;
364   int flags, res;
365 
366   flags = SNMP_ASN1_FL_UNSIGNED;
367 
368   res = snmp_asn1_read_int(p, buf, buflen, asn1_type, &asn1_int, flags);
369   if (res < 0) {
370     return -1;
371   }
372 
373   /* Check that the actual integer value read in is unsigned/not negative.
374    * If it's negative, log a warning -- but proceed, and simply handle the
375    * value as if it's unsigned, as the caller requested.
376    */
377   if (asn1_int < 0) {
378     pr_trace_msg(trace_channel, 1,
379       "ASN.1 integer value (%ld) is not unsigned as expected", asn1_int);
380   }
381 
382   *asn1_uint = (unsigned int) asn1_int;
383   return 0;
384 }
385 
386 /* ASN.1 null ::= 0x05 0x00 */
snmp_asn1_read_null(pool * p,unsigned char ** buf,size_t * buflen,unsigned char * asn1_type)387 int snmp_asn1_read_null(pool *p, unsigned char **buf, size_t *buflen,
388     unsigned char *asn1_type) {
389   unsigned int objlen;
390   int res;
391 
392   /* Type */
393   res = asn1_read_type(p, buf, buflen, asn1_type, 0);
394   if (res < 0) {
395     return -1;
396   }
397 
398   /* Check that the type is actually a NULL, as expected. */
399   if (!(*asn1_type & SNMP_ASN1_TYPE_NULL)) {
400     pr_trace_msg(trace_channel, 3,
401       "unable to read NULL (received type '%s')",
402       snmp_asn1_get_tagstr(p, *asn1_type));
403     errno = EINVAL;
404     return -1;
405   }
406 
407   res = asn1_read_len(p, buf, buflen, &objlen);
408   if (res < 0) {
409     return -1;
410   }
411 
412   /* Check that the object len is zero, as expected. */
413   if (objlen != 0) {
414     pr_trace_msg(trace_channel, 3,
415       "failed reading NULL object: object length (%u bytes) is not zero, "
416       "as expected", objlen);
417 
418     pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
419     errno = EINVAL;
420     return -1;
421   }
422 
423   return 0;
424 }
425 
426 /* ASN.1 OID ::= 0x06 asnlength subidentifier {subidentifier}*
427  * subidentifier ::= {leadingbyte}* lastbyte
428  * leadingbyte ::= 1 7bitvalue
429  * lastbyte ::= 0 7bitvalue
430  */
snmp_asn1_read_oid(pool * p,unsigned char ** buf,size_t * buflen,unsigned char * asn1_type,oid_t * asn1_oid,unsigned int * asn1_oidlen)431 int snmp_asn1_read_oid(pool *p, unsigned char **buf, size_t *buflen,
432     unsigned char *asn1_type, oid_t *asn1_oid, unsigned int *asn1_oidlen) {
433 
434   /* We leave room at the start of the OID memory for expansion into two
435    * bytes from the first byte of buffer data.
436    */
437   oid_t *oid_ptr = asn1_oid + 1;
438 
439   unsigned int objlen, len, sub_id;
440   int res;
441 
442   /* Type */
443   res = asn1_read_type(p, buf, buflen, asn1_type, 0);
444   if (res < 0) {
445     return -1;
446   }
447 
448   /* Check that asn1_type is actually for an OID, as expected. */
449   if (!(*asn1_type & SNMP_ASN1_TYPE_OID)) {
450     pr_trace_msg(trace_channel, 3,
451       "unable to read OID (received type '%s')",
452       snmp_asn1_get_tagstr(p, *asn1_type));
453     errno = EINVAL;
454     return -1;
455   }
456 
457   /* Length */
458   res = asn1_read_len(p, buf, buflen, &objlen);
459   if (res < 0) {
460     return -1;
461   }
462 
463   /* Is there enough data remaining in the buffer for the indicated object? */
464   if (objlen > *buflen) {
465     pr_trace_msg(trace_channel, 3,
466       "failed reading OID object: object length (%u bytes) is greater "
467       "than remaining data (%lu bytes)", objlen, (unsigned long) (*buflen));
468 
469     pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
470     errno = EINVAL;
471     return -1;
472   }
473 
474   /* Handle invalid OID encodings of the form 06 00 robustly. */
475   if (objlen == 0) {
476     memset(asn1_oid, 0, sizeof(oid_t));
477   }
478 
479   /* XXX Not sure I like this section; it presumes that the value in
480    * *asn1_oidlen is already initialized to something sane.  Is that a valid
481    * assumption?
482    */
483 
484   len = objlen;
485   (*asn1_oidlen)--;		/* account for expansion of first byte */
486   while (len > 0 && (*asn1_oidlen)-- > 0) {
487     unsigned char byte = 0;
488 
489     pr_signals_handle();
490 
491     sub_id = 0;
492 
493     do {
494       res = asn1_read_byte(p, buf, buflen, &byte);
495       if (res < 0) {
496         return -1;
497       }
498 
499       /* Shift and add in the low order 7 bits */
500       sub_id = (sub_id << 7) + (byte & ~0x80);
501       len--;
502 
503     } while (byte & 0x80);
504 
505     if (sub_id > SNMP_ASN1_OID_MAX_ID) {
506       pr_trace_msg(trace_channel, 3,
507         "failed reading OID object: sub-identifer (%u is greater "
508         "than maximum allowed OID value (%u)", sub_id, SNMP_ASN1_OID_MAX_ID);
509 
510       pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
511       errno = EINVAL;
512       return -1;
513     }
514 
515     *oid_ptr++ = (oid_t) sub_id;
516   }
517 
518   /* The first two subidentifiers are encoded into the first component with
519    * the value (X * 40) + Y, where:
520    *
521    *  X is the value of the first subidentifier.
522    *  Y is the value of the second subidentifier.
523    */
524   sub_id = (unsigned int) asn1_oid[1];
525   if (sub_id == 0x2b) {
526     asn1_oid[0] = 1;
527     asn1_oid[1] = 3;
528 
529   } else {
530     asn1_oid[1] = (unsigned char) (sub_id % 40);
531     asn1_oid[0] = (unsigned char) ((sub_id - asn1_oid[1]) / 40);
532   }
533 
534   *asn1_oidlen = (unsigned int) (oid_ptr - asn1_oid);
535   return 0;
536 }
537 
538 /* ASN.1 octet string ::= primitive-string | compound-string
539  * primitive-string ::= 0x04 asnlength byte {byte}*
540  * compound-string ::= 0x24 asnlength string {string}*
541  */
snmp_asn1_read_string(pool * p,unsigned char ** buf,size_t * buflen,unsigned char * asn1_type,char ** asn1_str,unsigned int * asn1_strlen)542 int snmp_asn1_read_string(pool *p, unsigned char **buf, size_t *buflen,
543     unsigned char *asn1_type, char **asn1_str, unsigned int *asn1_strlen) {
544   unsigned int objlen;
545   int res;
546 
547   /* Type */
548   res = asn1_read_type(p, buf, buflen, asn1_type, 0);
549   if (res < 0) {
550     return -1;
551   }
552 
553   /* Check the type to see if it actually is OCTET_STRING, as expected.
554    * XXX What about compound strings, bitstrings?
555    */
556   if (!(*asn1_type & SNMP_ASN1_TYPE_OCTETSTRING)) {
557     pr_trace_msg(trace_channel, 3,
558       "unable to read OCTET_STRING (received type '%s')",
559       snmp_asn1_get_tagstr(p, *asn1_type));
560     errno = EINVAL;
561     return -1;
562   }
563 
564   /* Length */
565   res = asn1_read_len(p, buf, buflen, &objlen);
566   if (res < 0) {
567     return -1;
568   }
569 
570   /* Is there enough data remaining in the buffer for the indicated object? */
571   if (objlen > *buflen) {
572     pr_trace_msg(trace_channel, 3,
573       "failed reading OCTET_STRING object: object length (%u bytes) is greater "
574       "than remaining data (%lu bytes)", objlen, (unsigned long) (*buflen));
575 
576     pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
577     errno = EINVAL;
578     return -1;
579   }
580 
581   *asn1_strlen = objlen;
582   *asn1_str = pstrndup(p, (char *) *buf, objlen);
583   (*buf) += objlen;
584   (*buflen) -= objlen;
585 
586   return 0;
587 }
588 
asn1_write_byte(unsigned char ** buf,size_t * buflen,unsigned char byte)589 static int asn1_write_byte(unsigned char **buf, size_t *buflen,
590     unsigned char byte) {
591 
592   if (*buflen < sizeof(unsigned char)) {
593     (void) pr_log_writefile(snmp_logfd, MOD_SNMP_VERSION,
594       "ASN.1 format error: unable to write byte %c (buflen = %lu)", byte,
595       (unsigned long) *buflen);
596     pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
597     errno = EINVAL;
598     return -1;
599   }
600 
601   memmove(*buf, &byte, sizeof(unsigned char));
602   (*buf) += sizeof(unsigned char);
603   (*buflen) -= sizeof(unsigned char);
604 
605   return 0;
606 }
607 
asn1_write_type(unsigned char ** buf,size_t * buflen,unsigned char asn1_type,int flags)608 static int asn1_write_type(unsigned char **buf, size_t *buflen,
609     unsigned char asn1_type, int flags) {
610   int res;
611 
612   res = asn1_write_byte(buf, buflen, asn1_type);
613   if (res < 0) {
614     return -1;
615   }
616 
617   if (!(flags & SNMP_ASN1_FL_NO_TRACE_TYPESTR)) {
618     pr_trace_msg(trace_channel, 18,
619       "wrote ASN.1 type 0x%02x (%s)", asn1_type, asn1_typestr(asn1_type));
620 
621   } else {
622     pr_trace_msg(trace_channel, 18, "wrote byte 0x%02x", asn1_type);
623   }
624 
625   return 0;
626 }
627 
asn1_write_len(unsigned char ** buf,size_t * buflen,unsigned int asn1_len,int flags)628 static int asn1_write_len(unsigned char **buf, size_t *buflen,
629     unsigned int asn1_len, int flags) {
630   int res;
631 
632   if (flags & SNMP_ASN1_FL_KNOWN_LEN) {
633     pr_trace_msg(trace_channel, 19, "writing ASN.1 known length %u", asn1_len);
634 
635     /* No indefinite lengths sent. */
636     if (asn1_len < SNMP_ASN1_LEN_LONG) {
637 
638       /* For this length, we only need one byte. */
639       if (*buflen < sizeof(unsigned char)) {
640         pr_trace_msg(trace_channel, 1,
641           "ASN.1 format error: unable to write length %u (buflen = %lu)",
642           asn1_len, (unsigned long) *buflen);
643 
644         pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
645         errno = EINVAL;
646         return -1;
647       }
648 
649       res = asn1_write_byte(buf, buflen, (unsigned char) asn1_len);
650       if (res < 0) {
651         return -1;
652       }
653 
654     } else if (asn1_len <= 0xff) {
655       unsigned char first_byte, last_byte;
656 
657       /* For this length, we need two bytes. */
658       if (*buflen < (2 * sizeof(unsigned char))) {
659         pr_trace_msg(trace_channel, 1,
660           "ASN.1 format error: unable to write length %u (buflen = %lu)",
661           asn1_len, (unsigned long) *buflen);
662 
663         pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
664         errno = EINVAL;
665         return -1;
666       }
667 
668       first_byte = (unsigned char) (0x01|SNMP_ASN1_LEN_LONG);
669       res = asn1_write_byte(buf, buflen, first_byte);
670       if (res < 0) {
671         return -1;
672       }
673 
674       last_byte = (unsigned char) asn1_len;
675       res = asn1_write_byte(buf, buflen, last_byte);
676       if (res < 0) {
677         return -1;
678       }
679 
680     } else {
681       unsigned char first_byte;
682       unsigned short len;
683 
684       /* Length is 0xff (255) < asn1_len <= 0xffff (65535) */
685 
686       /* For this length, we need three bytes. */
687       if (*buflen < (3 * sizeof(unsigned char))) {
688         pr_trace_msg(trace_channel, 1,
689           "ASN.1 format error: unable to write length %u (buflen = %lu)",
690           asn1_len, (unsigned long) *buflen);
691 
692         pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
693         errno = EINVAL;
694         return -1;
695       }
696 
697       first_byte = (unsigned char) (0x02|SNMP_ASN1_LEN_LONG);
698       res = asn1_write_byte(buf, buflen, first_byte);
699       if (res < 0) {
700         return -1;
701       }
702 
703       len = (unsigned short) asn1_len;
704       len = htons(len);
705 
706       memmove(*buf, &len, sizeof(unsigned short));
707       (*buf) += sizeof(unsigned short);
708       (*buflen) -= sizeof(unsigned short);
709     }
710 
711   } else {
712     unsigned char first_byte;
713     unsigned short len;
714 
715     pr_trace_msg(trace_channel, 19, "writing ASN.1 unknown length %u",
716       asn1_len);
717 
718     /* We don't know if this is the true length.  Make sure it's large
719      * enough (i.e. three bytes) for later.
720      */
721     if (*buflen < (3 * sizeof(unsigned char))) {
722       pr_trace_msg(trace_channel, 1,
723         "ASN.1 format error: unable to write length %u (buflen = %lu)",
724         asn1_len, (unsigned long) *buflen);
725 
726       pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
727       errno = EINVAL;
728       return -1;
729     }
730 
731     first_byte = (unsigned char) (0x02|SNMP_ASN1_LEN_LONG);
732     res = asn1_write_byte(buf, buflen, first_byte);
733     if (res < 0) {
734       return -1;
735     }
736 
737     len = (unsigned short) asn1_len;
738     len = htons(len);
739 
740     memmove(*buf, &len, sizeof(unsigned short));
741     (*buf) += sizeof(unsigned short);
742     (*buflen) -= sizeof(unsigned short);
743   }
744 
745   pr_trace_msg(trace_channel, 18, "wrote ASN.1 length %u", asn1_len);
746   return 0;
747 }
748 
snmp_asn1_write_header(pool * p,unsigned char ** buf,size_t * buflen,unsigned char asn1_type,unsigned int asn1_len,int flags)749 int snmp_asn1_write_header(pool *p, unsigned char **buf, size_t *buflen,
750     unsigned char asn1_type, unsigned int asn1_len, int flags) {
751   int res;
752 
753   res = asn1_write_type(buf, buflen, asn1_type, flags);
754   if (res < 0) {
755     return -1;
756   }
757 
758   res = asn1_write_len(buf, buflen, asn1_len, flags);
759   return res;
760 }
761 
762 /* Why does the caller have to provide the ASN.1 type, if we know that they
763  * want to write an INTEGER?
764  *
765  * Answer: Callers need to include the Class and Primitive/Constructed bits
766  * in the ASN.1 type value as well, e.g.:
767  *
768  *  asn1_type = SNMP_ASN1_TYPE_INTEGER;
769  *  asn1_type |= SNMP_ASN1_PRIMITIVE;
770  *  asn1_type |= SNMP_ASN1_CLASS_UNIVERSAL;
771  *
772  * ASN.1 integer ::= 0x02 asnlength byte {byte}*
773  */
snmp_asn1_write_int(pool * p,unsigned char ** buf,size_t * buflen,unsigned char asn1_type,long asn1_int,int flags)774 int snmp_asn1_write_int(pool *p, unsigned char **buf, size_t *buflen,
775     unsigned char asn1_type, long asn1_int, int flags) {
776   unsigned int asn1_intsz;
777   unsigned long bitmask;
778   long objval;
779   int res;
780 
781   /* XXX Check that asn1_type is INTEGER, as expected? */
782 
783   asn1_intsz = (unsigned int) sizeof(long);
784   flags |= SNMP_ASN1_FL_KNOWN_LEN;
785 
786   /* Truncate "unnecessary" bytes off of the most significant end of this
787    * 2's complement integer.  There should be no sequence of 9 consecutive 1's
788    * or 0's at the most significant end of the integer.
789    *
790    * bitmask is 0xff800000 on a big-endian machine.
791    */
792 
793   objval = asn1_int;
794   bitmask = (unsigned long) 0x1ff << ((8 * (sizeof(long) - 1)) - 1);
795 
796   while (((objval & bitmask) == 0 ||
797           (objval & bitmask) == bitmask) &&
798          asn1_intsz > 1) {
799     pr_signals_handle();
800 
801     asn1_intsz--;
802     objval <<= 8;
803   }
804 
805   res = snmp_asn1_write_header(p, buf, buflen, asn1_type, asn1_intsz, flags);
806   if (res < 0) {
807     return -1;
808   }
809 
810   /* Is there enough room remaining in the buffer for the object? */
811   if (*buflen < asn1_intsz) {
812     pr_trace_msg(trace_channel, 3,
813       "failed writing INTEGER object: object length (%u bytes) is greater "
814       "than remaining buffer (%lu bytes)", asn1_intsz,
815       (unsigned long) (*buflen));
816 
817     pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
818     errno = EINVAL;
819     return -1;
820   }
821 
822   /* At this point, bitmask is 0xff000000 on a big-endian machine. */
823   bitmask = (unsigned long) 0xff << (8 * (sizeof(long) - 1));
824 
825   while (asn1_intsz--) {
826     unsigned char byte = 0;
827 
828     pr_signals_handle();
829 
830     byte = (unsigned char) ((objval & bitmask) >> (8 * (sizeof(long) - 1)));
831     res = asn1_write_byte(buf, buflen, byte);
832     if (res < 0) {
833       return -1;
834     }
835 
836     objval <<= 8;
837   }
838 
839   pr_trace_msg(trace_channel, 18, "wrote ASN.1 value %ld", asn1_int);
840   return 0;
841 }
842 
843 /* ASN.1 integer ::= 0x02 asnlength byte {byte}* */
snmp_asn1_write_uint(pool * p,unsigned char ** buf,size_t * buflen,unsigned char asn1_type,unsigned long asn1_uint)844 int snmp_asn1_write_uint(pool *p, unsigned char **buf, size_t *buflen,
845     unsigned char asn1_type, unsigned long asn1_uint) {
846   unsigned int asn1_uintsz, bitmask;
847   int add_null_byte = FALSE, flags, res;
848 
849   /* XXX Check that asn1_type is INTEGER, as expected? */
850 
851   asn1_uintsz = (unsigned int) sizeof(unsigned int);
852   flags = SNMP_ASN1_FL_KNOWN_LEN;
853 
854   /* Truncate "unnecessary" bytes off of the most significant end of this
855    * 2's complement integer.
856    *
857    * There should be no sequence of 9 consecutive 1's or 0's at the most
858    * significant end of the integer.  The 1's case is taken care of below by
859    * adding a null byte.
860    *
861    * bitmask is 0x80000000 on a big-endian machine
862    */
863   bitmask = (unsigned int) 0x80 << (8 * (sizeof(unsigned int) - 1));
864 
865   if ((asn1_uint & bitmask) != 0) {
866     /* Add a null byte if MSB is set, to prevent sign extension. */
867     add_null_byte = TRUE;
868     asn1_uintsz++;
869   }
870 
871   /* bitmask is 0xff800000 on a big-endian machine */
872   bitmask = (unsigned int) 0x1ff << ((8 * (sizeof(unsigned int) - 1)) - 1);
873 
874   while ((asn1_uint & bitmask) == 0 &&
875          asn1_uintsz > 1) {
876     pr_signals_handle();
877 
878     asn1_uintsz--;
879     asn1_uint <<= 8;
880   }
881 
882   res = snmp_asn1_write_header(p, buf, buflen, asn1_type, asn1_uintsz, flags);
883   if (res < 0) {
884     return -1;
885   }
886 
887   /* Is there enough room remaining in the buffer for the object? */
888   if (*buflen < asn1_uintsz) {
889     pr_trace_msg(trace_channel, 3,
890       "failed writing INTEGER object: object length (%u bytes) is greater "
891       "than remaining buffer (%lu bytes)", asn1_uintsz,
892       (unsigned long) (*buflen));
893 
894     pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
895     errno = EINVAL;
896     return -1;
897   }
898 
899   if (add_null_byte) {
900     res = asn1_write_byte(buf, buflen, 0);
901     if (res < 0) {
902       return -1;
903     }
904 
905     asn1_uintsz--;
906   }
907 
908   /* At this point, bitmask is 0xff000000 on a big-endian machine. */
909   bitmask = (unsigned int) 0xff << (8 * (sizeof(unsigned int) - 1));
910   while (asn1_uintsz--) {
911     unsigned char byte = 0;
912 
913     pr_signals_handle();
914 
915     byte = (unsigned char) ((asn1_uint & bitmask) >> (8 * (sizeof(unsigned int) - 1)));
916     res = asn1_write_byte(buf, buflen, byte);
917     if (res < 0) {
918       return -1;
919     }
920 
921     asn1_uint <<= 8;
922   }
923 
924   pr_trace_msg(trace_channel, 18, "wrote ASN.1 value %lu", asn1_uint);
925   return 0;
926 }
927 
928 /* ASN.1 null ::= 0x05 0x00 */
snmp_asn1_write_null(pool * p,unsigned char ** buf,size_t * buflen,unsigned char asn1_type)929 int snmp_asn1_write_null(pool *p, unsigned char **buf, size_t *buflen,
930   unsigned char asn1_type) {
931   int flags, res;
932 
933   flags = SNMP_ASN1_FL_KNOWN_LEN;
934 
935   /* XXX Check that asn1_type is NULL, as expected? */
936 
937   res = snmp_asn1_write_header(p, buf, buflen, asn1_type, 0, flags);
938   if (res < 0) {
939     return -1;
940   }
941 
942   pr_trace_msg(trace_channel, 18, "%s", "wrote ASN.1 value null");
943   return res;
944 }
945 
946 /* ASN.1 objid ::= 0x06 asnlength subidentifier {subidentifier}*
947  * subidentifier ::= {leadingbyte}* lastbyte
948  * leadingbyte ::= 1 7bitvalue
949  * lastbyte ::= 0 7bitvalue
950  */
snmp_asn1_write_oid(pool * p,unsigned char ** buf,size_t * buflen,unsigned char asn1_type,oid_t * asn1_oid,unsigned int asn1_oidlen)951 int snmp_asn1_write_oid(pool *p, unsigned char **buf, size_t *buflen,
952     unsigned char asn1_type, oid_t *asn1_oid, unsigned int asn1_oidlen) {
953   register unsigned int i;
954   unsigned char oid_lens[SNMP_ASN1_OID_MAX_LEN];
955   unsigned int asn1_len;
956   oid_t *oid_ptr = asn1_oid, sub_id, first_sub_id;
957   int flags, res;
958 
959   flags = SNMP_ASN1_FL_KNOWN_LEN;
960 
961   /* XXX Check that asn1_type is OID, as expected? */
962 
963   /* ISO/IEC 8825 - Specification of Basic Encoding Rules for Abstract Syntax
964    * Notation One (ASN.1) dictates that the first two sub-identifiers are
965    * encoded into the first identifier using the the equation:
966    *
967    *  subid = ((first * 40) + second)
968    *
969    * Pad the OBJECT IDENTIFIER to at least two sub-identifiers.
970    */
971 
972   /* Make sure that there are at least 2 sub-identifiers. */
973   if (asn1_oidlen == 0) {
974     /* If not, make the OID have two sub-identifiers, both valued zero. */
975     sub_id = 0;
976     asn1_oidlen = 0;
977 
978   } else if (asn1_oid[0] > 2) {
979     /* Bad first sub-identifier value.
980      *
981      * The first sub-identifiers are limited to ccitt(0), iso(1), and
982      * joint-iso-ccitt(2) as per RFC 2578.
983      */
984     (void) pr_log_writefile(snmp_logfd, MOD_SNMP_VERSION,
985       "invalid first sub-identifier (%lu) in OID", (unsigned long) asn1_oid[0]);
986     pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
987     errno = EINVAL;
988     return -1;
989 
990   } else if (asn1_oidlen > SNMP_MIB_MAX_OIDLEN) {
991     /* OID is too long for us. */
992     (void) pr_log_writefile(snmp_logfd, MOD_SNMP_VERSION,
993       "OID sub-identifier count (%u) exceeds max supported (%u)", asn1_oidlen,
994       SNMP_MIB_MAX_OIDLEN);
995     pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
996     errno = EINVAL;
997     return -1;
998 
999   } else if (asn1_oidlen == 1) {
1000     /* Encode the first sub-identifier. */
1001 
1002     sub_id = (oid_ptr[0] * 40);
1003     asn1_oidlen = 2;
1004     oid_ptr++;
1005 
1006   } else {
1007     /* Combine the first two values. */
1008 
1009     sub_id = ((oid_ptr[0] * 40) + oid_ptr[1]);
1010     oid_ptr += 2;
1011   }
1012 
1013   first_sub_id = sub_id;
1014 
1015   /* Determine how many bytes are needed for the encoded value. */
1016   for (i = 1, asn1_len = 0;;) {
1017     pr_signals_handle();
1018 
1019     if (sub_id < (unsigned int) 0x80) {
1020       oid_lens[i] = 1;
1021       asn1_len += 1;
1022 
1023     } else if (sub_id < (unsigned int) 0x4000) {
1024       oid_lens[i] = 2;
1025       asn1_len += 2;
1026 
1027     } else if (sub_id < (unsigned int) 0x200000) {
1028       oid_lens[i] = 3;
1029       asn1_len += 3;
1030 
1031     } else if (sub_id < (unsigned int) 0x10000000) {
1032       oid_lens[i] = 4;
1033       asn1_len += 4;
1034 
1035     } else {
1036       oid_lens[i] = 5;
1037       asn1_len += 5;
1038     }
1039 
1040     i++;
1041 
1042     if (i >= asn1_oidlen) {
1043       break;
1044     }
1045 
1046     sub_id = *oid_ptr++;
1047   }
1048 
1049   res = snmp_asn1_write_header(p, buf, buflen, asn1_type, asn1_len, flags);
1050   if (res < 0) {
1051     return -1;
1052   }
1053 
1054   /* Is there enough room remaining in the buffer for the object? */
1055   if (*buflen < asn1_len) {
1056     (void) pr_log_writefile(snmp_logfd, MOD_SNMP_VERSION,
1057       "failed writing OID object: object length (%u bytes) is greater "
1058       "than remaining buffer (%lu bytes)", asn1_len, (unsigned long) (*buflen));
1059 
1060     pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
1061     errno = EINVAL;
1062     return -1;
1063   }
1064 
1065   /* Write in the encoded OID value. */
1066   for (i = 1, sub_id = first_sub_id, oid_ptr = asn1_oid + 2;
1067        i < asn1_oidlen; i++) {
1068     unsigned char byte = 0;
1069 
1070     if (i != 1) {
1071       sub_id = *oid_ptr++;
1072     }
1073 
1074     switch (oid_lens[i]) {
1075       case 1:
1076         byte = (unsigned char) sub_id;
1077         res = asn1_write_byte(buf, buflen, byte);
1078         if (res < 0) {
1079           return -1;
1080         }
1081 
1082         break;
1083 
1084       case 2:
1085         byte = (unsigned char) ((sub_id >> 7) | 0x80);
1086         res = asn1_write_byte(buf, buflen, byte);
1087         if (res < 0) {
1088           return -1;
1089         }
1090 
1091         byte = (unsigned char) (sub_id & 0x07f);
1092         res = asn1_write_byte(buf, buflen, byte);
1093         if (res < 0) {
1094           return -1;
1095         }
1096 
1097         break;
1098 
1099       case 3:
1100         byte = (unsigned char) ((sub_id >> 14) | 0x80);
1101         res = asn1_write_byte(buf, buflen, byte);
1102         if (res < 0) {
1103           return -1;
1104         }
1105 
1106         byte = (unsigned char) ((sub_id >> 7 & 0x07f) | 0x80);
1107         res = asn1_write_byte(buf, buflen, byte);
1108         if (res < 0) {
1109           return -1;
1110         }
1111 
1112         byte = (unsigned char) (sub_id & 0x07f);
1113         res = asn1_write_byte(buf, buflen, byte);
1114         if (res < 0) {
1115           return -1;
1116         }
1117 
1118         break;
1119 
1120       case 4:
1121         byte = (unsigned char) ((sub_id >> 21) | 0x80);
1122         res = asn1_write_byte(buf, buflen, byte);
1123         if (res < 0) {
1124           return -1;
1125         }
1126 
1127         byte = (unsigned char) ((sub_id >> 14 & 0x07f) | 0x80);
1128         res = asn1_write_byte(buf, buflen, byte);
1129         if (res < 0) {
1130           return -1;
1131         }
1132 
1133         byte = (unsigned char) ((sub_id >> 7 & 0x07f) | 0x80);
1134         res = asn1_write_byte(buf, buflen, byte);
1135         if (res < 0) {
1136           return -1;
1137         }
1138 
1139         byte = (unsigned char) (sub_id & 0x07f);
1140         res = asn1_write_byte(buf, buflen, byte);
1141         if (res < 0) {
1142           return -1;
1143         }
1144 
1145         break;
1146 
1147       case 5:
1148         byte = (unsigned char) ((sub_id >> 28) | 0x80);
1149         res = asn1_write_byte(buf, buflen, byte);
1150         if (res < 0) {
1151           return -1;
1152         }
1153 
1154         byte = (unsigned char) ((sub_id >> 21 & 0x07f) | 0x80);
1155         res = asn1_write_byte(buf, buflen, byte);
1156         if (res < 0) {
1157           return -1;
1158         }
1159 
1160         byte = (unsigned char) ((sub_id >> 14 & 0x07f) | 0x80);
1161         res = asn1_write_byte(buf, buflen, byte);
1162         if (res < 0) {
1163           return -1;
1164         }
1165 
1166         byte = (unsigned char) ((sub_id >> 7 & 0x07f) | 0x80);
1167         res = asn1_write_byte(buf, buflen, byte);
1168         if (res < 0) {
1169           return -1;
1170         }
1171 
1172         byte = (unsigned char) (sub_id & 0x07f);
1173         res = asn1_write_byte(buf, buflen, byte);
1174         if (res < 0) {
1175           return -1;
1176         }
1177 
1178         break;
1179     }
1180   }
1181 
1182   pr_trace_msg(trace_channel, 18, "wrote ASN.1 value %s (%u bytes)",
1183     snmp_asn1_get_oidstr(p, asn1_oid, asn1_oidlen), asn1_len);
1184   return 0;
1185 }
1186 
1187 /* ASN.1 octet string ::= primitive-string | compound-string
1188  * primitive-string ::= 0x04 asnlength byte {byte}*
1189  * compound-string ::= 0x24 asnlength string {string}*
1190  *
1191  * Note: this code will never send a compound string.
1192  */
snmp_asn1_write_string(pool * p,unsigned char ** buf,size_t * buflen,unsigned char asn1_type,const char * asn1_str,unsigned int asn1_strlen)1193 int snmp_asn1_write_string(pool *p, unsigned char **buf, size_t *buflen,
1194     unsigned char asn1_type, const char *asn1_str, unsigned int asn1_strlen) {
1195   int flags, res;
1196 
1197   flags = SNMP_ASN1_FL_KNOWN_LEN;
1198 
1199   /* XXX Check that asn1_type is OCTET_STRING, as expected? */
1200 
1201   res = snmp_asn1_write_header(p, buf, buflen, asn1_type, asn1_strlen, flags);
1202   if (res < 0) {
1203     return -1;
1204   }
1205 
1206   /* Is there enough room remaining in the buffer for the object? */
1207   if (*buflen < asn1_strlen) {
1208     pr_trace_msg(trace_channel, 3,
1209       "failed writing STRING object: object length (%lu bytes) is greater "
1210       "than remaining buffer (%lu bytes)", (unsigned long) asn1_strlen,
1211       (unsigned long) (*buflen));
1212 
1213     pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
1214     errno = EINVAL;
1215     return -1;
1216   }
1217 
1218   memmove(*buf, asn1_str, asn1_strlen);
1219   (*buf) += asn1_strlen;
1220   (*buflen) -= asn1_strlen;
1221 
1222   pr_trace_msg(trace_channel, 18, "wrote ASN.1 value '%.*s' (%u bytes)",
1223     (int) asn1_strlen, asn1_str, asn1_strlen);
1224   return 0;
1225 }
1226 
1227 /* ASN.1 variable exception ::= 0x8i 0x00, where i the exception identifier:
1228  * noSuchObject(0), noSuchInstance(1), endOfMibView(2).
1229  */
snmp_asn1_write_exception(pool * p,unsigned char ** buf,size_t * buflen,unsigned char asn1_type,unsigned char asn1_ex)1230 int snmp_asn1_write_exception(pool *p, unsigned char **buf, size_t *buflen,
1231     unsigned char asn1_type, unsigned char asn1_ex) {
1232   int flags, res;
1233 
1234   flags = SNMP_ASN1_FL_KNOWN_LEN;
1235 
1236   /* XXX Check that asn1_type is EXCEPTION, as expected? */
1237 
1238   res = snmp_asn1_write_header(p, buf, buflen, asn1_type, asn1_ex, flags);
1239   if (res < 0) {
1240     return -1;
1241   }
1242 
1243   pr_trace_msg(trace_channel, 18, "wrote ASN.1 value %u", asn1_ex);
1244   return res;
1245 }
1246