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