xref: /freebsd/contrib/libder/libder/libder_type.c (revision 35c0a8c449fd2b7f75029ebed5e10852240f0865)
1 /*-
2  * Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  */
6 
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include <string.h>
10 
11 #include "libder_private.h"
12 
13 uint8_t
libder_type_simple_abi(const struct libder_tag * type)14 libder_type_simple_abi(const struct libder_tag *type)
15 {
16 
17 	return (libder_type_simple(type));
18 }
19 
20 /*
21  * We'll likely expose this in the form of libder_type_import(), which validates
22  * and allocates a tag.
23  */
24 LIBDER_PRIVATE struct libder_tag *
libder_type_alloc(void)25 libder_type_alloc(void)
26 {
27 
28 	return (calloc(1, sizeof(struct libder_tag)));
29 }
30 
31 struct libder_tag *
libder_type_dup(struct libder_ctx * ctx,const struct libder_tag * dtype)32 libder_type_dup(struct libder_ctx *ctx, const struct libder_tag *dtype)
33 {
34 	struct libder_tag *type;
35 
36 	type = libder_type_alloc();
37 	if (type == NULL) {
38 		libder_set_error(ctx, LDE_NOMEM);
39 		return (NULL);
40 	}
41 
42 	memcpy(type, dtype, sizeof(*dtype));
43 
44 	if (type->tag_encoded) {
45 		uint8_t *tdata;
46 
47 		/* Deep copy the tag data. */
48 		tdata = malloc(type->tag_size);
49 		if (tdata == NULL) {
50 			libder_set_error(ctx, LDE_NOMEM);
51 
52 			/*
53 			 * Don't accidentally free the caller's buffer; it may
54 			 * be an external user of the API.
55 			 */
56 			type->tag_long = NULL;
57 			type->tag_size = 0;
58 			libder_type_free(type);
59 			return (NULL);
60 		}
61 
62 		memcpy(tdata, dtype->tag_long, dtype->tag_size);
63 		type->tag_long = tdata;
64 	}
65 
66 	return (type);
67 }
68 
69 struct libder_tag *
libder_type_alloc_simple(struct libder_ctx * ctx,uint8_t typeval)70 libder_type_alloc_simple(struct libder_ctx *ctx, uint8_t typeval)
71 {
72 	struct libder_tag *type;
73 
74 	type = libder_type_alloc();
75 	if (type == NULL) {
76 		libder_set_error(ctx, LDE_NOMEM);
77 		return (NULL);
78 	}
79 
80 	type->tag_size = sizeof(typeval);
81 	type->tag_class = BER_TYPE_CLASS(typeval);
82 	type->tag_constructed = BER_TYPE_CONSTRUCTED(typeval);
83 	type->tag_short = BER_TYPE(typeval);
84 	return (type);
85 }
86 
87 LIBDER_PRIVATE void
libder_type_release(struct libder_tag * type)88 libder_type_release(struct libder_tag *type)
89 {
90 
91 	if (type->tag_encoded) {
92 		free(type->tag_long);
93 		type->tag_long = NULL;
94 
95 		/*
96 		 * Leaving type->tag_encoded set in case it helps us catch some
97 		 * bogus re-use of the type; we'd surface that as a null ptr
98 		 * deref as they think they should be using tag_long.
99 		 */
100 	}
101 }
102 
103 void
libder_type_free(struct libder_tag * type)104 libder_type_free(struct libder_tag *type)
105 {
106 
107 	if (type == NULL)
108 		return;
109 
110 	libder_type_release(type);
111 	free(type);
112 }
113 
114 LIBDER_PRIVATE void
libder_normalize_type(struct libder_ctx * ctx,struct libder_tag * type)115 libder_normalize_type(struct libder_ctx *ctx, struct libder_tag *type)
116 {
117 	uint8_t tagval;
118 	size_t offset;
119 
120 	if (!type->tag_encoded || !DER_NORMALIZING(ctx, TAGS))
121 		return;
122 
123 	/*
124 	 * Strip any leading 0's off -- not possible in strict mode.
125 	 */
126 	for (offset = 0; offset < type->tag_size - 1; offset++) {
127 		if ((type->tag_long[offset] & 0x7f) != 0)
128 			break;
129 	}
130 
131 	assert(offset == 0 || !ctx->strict);
132 	if (offset != 0) {
133 		type->tag_size -= offset;
134 		memmove(&type->tag_long[0], &type->tag_long[offset],
135 		    type->tag_size);
136 	}
137 
138 	/*
139 	 * We might be able to strip it down to a unencoded tag_short, if only
140 	 * the lower 5 bits are in use.
141 	 */
142 	if (type->tag_size != 1 || (type->tag_long[0] & ~0x1e) != 0)
143 		return;
144 
145 	tagval = type->tag_long[0];
146 
147 	free(type->tag_long);
148 	type->tag_short = tagval;
149 	type->tag_encoded = false;
150 }
151