/* $OpenBSD: ber_test.c,v 1.20 2019/10/24 12:39:26 tb Exp $ */ /* * Copyright (c) Rob Pierce * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #define SUCCEED 0 #define FAIL 1 struct test_vector { int fail; /* 1 means test is expected to fail */ int memcheck; /* 1 when short forms used */ char title[128]; size_t length; unsigned char input[1024]; }; struct test_vector test_vectors[] = { { SUCCEED, 1, "boolean", 3, { 0x01, 0x01, 0xff }, }, { FAIL, 0, "boolean (constructed - expected failure)", 3, { 0x21, 0x01, 0xff }, }, { FAIL, 0, "boolean (more than 1 content octet - expected failure)", 4, { 0x01, 0x02, 0x00, 0xff }, }, { SUCCEED, 1, "enum", 3, { 0x0a, 0x01, 0x00 }, }, { FAIL, 0, "enum (constructed - expected failure)", 3, { 0x2a, 0x01, 0x00 }, }, { FAIL, 0, "enum minimal contents octets (expected failure)", 4, { 0x0a, 0x02, 0x00, 0x01 }, }, { SUCCEED, 1, "integer (zero)", 3, { 0x02, 0x01, 0x00 }, }, { FAIL, 0, "integer (constructed - expected failure)", 3, { 0x22, 0x01, 0x01 }, }, { SUCCEED, 1, "positive integer", 3, { 0x02, 0x01, 0x63 }, }, { SUCCEED, 1, "large positive integer", 5, { 0x02, 0x03, 0x01, 0x00, 0x00 }, }, { SUCCEED, 1, "negative integer", 4, { 0x02, 0x02, 0xff, 0x7f }, }, { FAIL, 0, "integer minimal contents octets (expected failure)", 4, { 0x02, 0x02, 0x00, 0x01 }, }, { SUCCEED, 1, "bit string", 6, { 0x03, 0x04, 0xde, 0xad, 0xbe, 0xef }, }, { SUCCEED, 1, "octet string", 10, { 0x04, 0x08, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }, }, { SUCCEED, 1, "null", 2, { 0x05, 0x00 }, }, { FAIL, 0, "null (constructed - expected failure)", 2, { 0x25, 0x00 }, }, { SUCCEED, 1, "object identifier", 8, { 0x06, 0x06, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d } }, { SUCCEED, 1, "sequence", /* ldap */ 14, { 0x30, 0x0c, 0x02, 0x01, 0x01, 0x60, 0x07, 0x02, 0x01, 0x03, 0x04, 0x00, 0x80, 0x00 } }, { SUCCEED, 1, "ldap bind", 30, { 0x30, 0x1c, 0x02, 0x01, 0x01, 0x60, 0x17, 0x02, 0x01, 0x03, 0x04, 0x08, 0x63, 0x6e, 0x3d, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x80, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64 } }, { SUCCEED, 1, "ldap search", 37, { 0x30, 0x23, 0x02, 0x01, 0x01, 0x60, 0x1e, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x04, 0x02, 0x63, 0x6e } }, { SUCCEED, 1, "snmpd encode", 15, { 0x30, 0x0d, 0x02, 0x01, 0x01, 0x02, 0x02, 0x20, 0x00, 0x04, 0x01, 0x01, 0x02, 0x01, 0x03 } }, { SUCCEED, 1, "set with integer and boolean", 8, { 0x31, 0x06, 0x02, 0x01, 0x04, 0x01, 0x01, 0xff } }, { FAIL, 0, "indefinite encoding (expected failure)", 4, { 0x30, 0x80, 0x00, 0x00 } }, { FAIL, 0, "reserved for future use (expected failure)", 4, { 0x30, 0xff, 0x01, 0x01 } }, { FAIL, 0, "long form tagging prohibited (expected failure)", 5, { 0x1f, 0x80, 0x02, 0x01, 0x01 }, }, { SUCCEED, 0, "max long form length octets (i.e. 4 bytes)", 7, { 0x02, 0x84, 0x00, 0x00, 0x00, 0x01, 0x01 }, }, { FAIL, 0, "overflow long form length octets (expected failure)", 8, { 0x02, 0x85, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01 }, }, { FAIL, 0, "incorrect length - not enough data (expected failure)", 3, { 0x02, 0x02, 0x01 } } }; static void hexdump(const unsigned char *buf, size_t len) { size_t i; for (i = 1; i < len; i++) fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "": "\n"); fprintf(stderr, " 0x%02hhx", buf[i - 1]); fprintf(stderr, "\n"); } unsigned int ldap_application(struct ber_element *elm) { return BER_TYPE_OCTETSTRING; } static int test(int i) { int pos, b; char *string; void *p = NULL; ssize_t len = 0; struct ber_element *elm = NULL, *ptr = NULL; struct ber ber; long long val; void *bstring = NULL; struct ber_oid oid; struct ber_octetstring ostring; bzero(&ber, sizeof(ber)); ober_set_readbuf(&ber, test_vectors[i].input, test_vectors[i].length); ober_set_application(&ber, ldap_application); elm = ober_read_elements(&ber, elm); if (elm == NULL && test_vectors[i].fail && (errno == EINVAL || errno == ERANGE || errno == ECANCELED)) return 0; else if (elm != NULL && test_vectors[i].fail) { printf("expected failure of ober_read_elements did not occur\n"); return 1; } else if (elm == NULL) { printf("unexpectedly failed ober_read_elements\n"); return 1; } /* * short form tagged elements start at the 3rd octet (i.e. position 2). */ if (test_vectors[i].memcheck) { pos = ober_getpos(elm); if (pos != 2) { printf("unexpected element position within " "byte stream\n"); return 1; } } switch (elm->be_encoding) { case BER_TYPE_EOC: if (ober_get_eoc(elm) == -1) { printf("failed (eoc) encoding check\n"); return 1; } if (ober_scanf_elements(elm, ".", &val) == -1) { printf("failed (eoc) ober_scanf_elements\n"); return 1; } break; case BER_TYPE_BOOLEAN: if (ober_get_boolean(elm, &b) == -1) { printf("failed (boolean) encoding check\n"); return 1; } if (ober_scanf_elements(elm, "b", &b) == -1) { printf("failed (boolean) ober_scanf_elements\n"); return 1; } break; case BER_TYPE_INTEGER: if (ober_get_integer(elm, &val) == -1) { printf("failed (int) encoding check\n"); return 1; } if (ober_scanf_elements(elm, "i", &val) == -1) { printf("failed (int) ober_scanf_elements (i)\n"); return 1; } if (ober_scanf_elements(elm, "d", &val) == -1) { printf("failed (int) ober_scanf_elements (d)\n"); return 1; } break; case BER_TYPE_ENUMERATED: if (ober_get_enumerated(elm, &val) == -1) { printf("failed (enum) encoding check\n"); return 1; } if (ober_scanf_elements(elm, "E", &val) == -1) { printf("failed (enum) ober_scanf_elements (E)\n"); return 1; } break; case BER_TYPE_BITSTRING: if (ober_get_bitstring(elm, &bstring, &len) == -1) { printf("failed (bit string) encoding check\n"); return 1; } break; case BER_TYPE_OCTETSTRING: if (ober_get_ostring(elm, &ostring) == -1) { printf("failed (octet string) encoding check\n"); return 1; } if (ober_scanf_elements(elm, "s", &string) == -1) { printf("failed (octet string) ober_scanf_elements\n"); return 1; } break; case BER_TYPE_NULL: if (ober_get_null(elm) == -1) { printf("failed (null) encoding check\n"); return 1; } if (ober_scanf_elements(elm, "0", &val) == -1) { printf("failed (null) ober_scanf_elements\n"); return 1; } break; case BER_TYPE_OBJECT: /* OID */ if (ober_get_oid(elm, &oid) == -1) { printf("failed (oid) encoding check\n"); return 1; } if (ober_scanf_elements(elm, "o", &oid) == -1) { printf("failed (oid) ober_scanf_elements\n"); return 1; } break; case BER_TYPE_SET: case BER_TYPE_SEQUENCE: if (elm->be_sub != NULL) { ptr = elm->be_sub; if (ober_getpos(ptr) <= pos) { printf("unexpected element position within " "byte stream\n"); return 1; } } else { printf("expected sub element was not present\n"); return 1; } break; default: printf("failed with unknown encoding (%ud)\n", elm->be_encoding); return 1; } /* * additional testing on short form encoding */ if (test_vectors[i].memcheck) { len = ober_calc_len(elm); if (len != test_vectors[i].length) { printf("failed to calculate length\n"); return 1; } ber.br_wbuf = NULL; len = ober_write_elements(&ber, elm); if (len != test_vectors[i].length) { printf("failed length check (was %zd want " "%zd)\n", len, test_vectors[i].length); return 1; } if (memcmp(ber.br_wbuf, test_vectors[i].input, test_vectors[i].length) != 0) { printf("failed byte stream compare\n"); printf("Got:\n"); hexdump(ber.br_wbuf, len); printf("Expected:\n"); hexdump(test_vectors[i].input, test_vectors[i].length); return 1; } ober_free(&ber); } ober_free_elements(elm); return 0; } int test_ber_printf_elements_integer(void) { int val = 1, len = 0; struct ber_element *elm = NULL; struct ber ber; unsigned char exp[3] = { 0x02, 0x01, 0x01 }; elm = ober_printf_elements(elm, "d", val); if (elm == NULL) { printf("failed ober_printf_elements\n"); return 1; } bzero(&ber, sizeof(ber)); ber.br_wbuf = NULL; len = ober_write_elements(&ber, elm); if (len != sizeof(exp)) { printf("failed length check (was %d want %zd)\n", len, sizeof(exp)); return 1; } if (memcmp(ber.br_wbuf, exp, len) != 0) { printf("failed (int) byte stream compare\n"); return 1; } ober_free_elements(elm); ober_free(&ber); return 0; } #define LDAP_REQ_BIND 0 #define LDAP_REQ_SEARCH 0 #define VERSION 3 #define LDAP_AUTH_SIMPLE 0 int test_ber_printf_elements_ldap_bind(void) { int len = 0, msgid = 1; char *binddn = "cn=admin"; char *bindcred = "password"; struct ber_element *root = NULL, *elm = NULL; struct ber ber; unsigned char exp[] = { 0x30, 0x1c, 0x02, 0x01, 0x01, 0x60, 0x17, 0x02, 0x01, 0x03, 0x04, 0x08, 0x63, 0x6e, 0x3d, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x80, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64 }; if ((root = ober_add_sequence(NULL)) == NULL) return 1; elm = ober_printf_elements(root, "d{tdsst", msgid, BER_CLASS_APP, LDAP_REQ_BIND, VERSION, binddn, bindcred, BER_CLASS_CONTEXT, LDAP_AUTH_SIMPLE); if (elm == NULL) { printf("failed ober_printf_elements\n"); return 1; } bzero(&ber, sizeof(ber)); ber.br_wbuf = NULL; ober_set_application(&ber, ldap_application); len = ober_write_elements(&ber, root); if (len != sizeof(exp)) { printf("failed length check (was %d want %zd)\n", len, sizeof(exp)); return 1; } if (memcmp(ber.br_wbuf, exp, len) != 0) { printf("failed (ldap bind) byte stream compare\n"); hexdump(ber.br_wbuf, len); return 1; } ober_free_elements(elm); ober_free(&ber); return 0; } int test_ber_printf_elements_ldap_search(void) { int len = 0, msgid = 1; int sizelimit = 0, timelimit = 0; int typesonly = 0; long long scope = 0, deref = 0; char *basedn = "ou=people"; char *filter = "cn"; struct ber_element *root = NULL, *elm = NULL, *felm = NULL; struct ber ber; unsigned char exp[] = { 0x30, 0x23, 0x02, 0x01, 0x01, 0x60, 0x1e, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x04, 0x02, 0x63, 0x6e }; if ((root = ober_add_sequence(NULL)) == NULL) return 1; elm = ober_printf_elements(root, "d{tsEEddbs", msgid, BER_CLASS_APP, LDAP_REQ_SEARCH, basedn, scope, deref, sizelimit, timelimit, typesonly, filter); if (elm == NULL) { printf("failed ober_printf_elements\n"); return 1; } bzero(&ber, sizeof(ber)); ber.br_wbuf = NULL; ober_set_application(&ber, ldap_application); len = ober_write_elements(&ber, root); if (len != sizeof(exp)) { printf("failed length check (was %d want %zd)\n", len, sizeof(exp)); return 1; } if (memcmp(ber.br_wbuf, exp, len) != 0) { printf("failed (ldap search) byte stream compare\n"); hexdump(ber.br_wbuf, len); return 1; } ober_free_elements(elm); ober_free(&ber); return 0; } int test_ber_printf_elements_snmp_v3_encode(void) { int len = 0; u_int8_t f = 0x01; /* verbose */ long long secmodel = 3; /* USM */ long long msgid = 1, max_msg_size = 8192; struct ber_element *elm = NULL; struct ber ber; unsigned char exp[] = { 0x30, 0x0d, 0x02, 0x01, 0x01, 0x02, 0x02, 0x20, 0x00, 0x04, 0x01, 0x01, 0x02, 0x01, 0x03 }; elm = ober_printf_elements(elm, "{iixi}", msgid, max_msg_size, &f, sizeof(f), secmodel); if (elm == NULL) { printf("failed ober_printf_elements\n"); return 1; } bzero(&ber, sizeof(ber)); ber.br_wbuf = NULL; len = ober_write_elements(&ber, elm); if (len != sizeof(exp)) { printf("failed length check (was %d want %zd)\n", len, sizeof(exp)); return 1; } if (memcmp(ber.br_wbuf, exp, len) != 0) { printf("failed (snmp_v3_encode) byte stream compare\n"); hexdump(ber.br_wbuf, len); return 1; } ober_free_elements(elm); ober_free(&ber); return 0; } int test_ber_null(void) { long long val; struct ber_element *elm = NULL; /* scanning into a null ber_element should fail */ if (ober_scanf_elements(elm, "0", &val) != -1) { printf("failed (null ber_element) ober_scanf_elements empty\n"); goto fail; } if ((elm = ober_printf_elements(elm, "{d}", 1)) == NULL) { printf("failed (null ber_element) ober_printf_elements\n"); } /* * Scanning after the last valid element should be able to descend back * into the parent level. */ if (ober_scanf_elements(elm, "{i}", &val) != 0) { printf("failed (null ber_element) ober_scanf_elements valid\n"); goto fail; } /* * Scanning for a non-existent element should fail, even if it's just a * skip. */ if (ober_scanf_elements(elm, "{lS}", &val) != -1) { printf("failed (null ber_element) ober_scanf_elements invalid\n"); goto fail; } ober_free_elements(elm); return 0; fail: ober_free_elements(elm); return 1; } int main(void) { extern char *__progname; ssize_t len = 0; int i, ret = 0; /* * drive test vectors for ber byte stream input validation, etc. */ for (i = 0; i < sizeof(test_vectors) / sizeof(test_vectors[0]); i++) { if (test(i) != 0) { printf("FAILED: %s\n", test_vectors[i].title); ret = 1; } else printf("SUCCESS: %s\n", test_vectors[i].title); } /* * run standalone functions for ber byte stream creation, etc. * (e.g. ldap, snmpd) */ if (test_ber_printf_elements_integer() != 0) { printf("FAILED: test_ber_printf_elements_integer\n"); ret = 1; } else printf("SUCCESS: test_ber_printf_elements_integer\n"); if (test_ber_printf_elements_ldap_bind() != 0) { printf("FAILED: test_ber_printf_elements_ldap_bind\n"); ret = 1; } else printf("SUCCESS: test_ber_printf_elements_ldap_bind\n"); if (test_ber_printf_elements_ldap_search() != 0) { printf("FAILED: test_ber_printf_elements_ldap_search\n"); ret = 1; } else printf("SUCCESS: test_ber_printf_elements_ldap_search\n"); if (test_ber_printf_elements_snmp_v3_encode() != 0) { printf("FAILED: test_ber_printf_elements_snmpd_v3_encode\n"); ret = 1; } else printf("SUCCESS: test_ber_printf_elements_snmpd_v3_encode\n"); if (test_ber_null() != 0) { printf("FAILED: test_ber_null\n"); ret = 1; } else printf("SUCCESS: test_ber_null\n"); if (ret != 0) { printf("FAILED: %s\n", __progname); return 1; } return 0; }