xref: /openbsd-src/usr.sbin/rpki-client/x509.c (revision 1a8dbaac879b9f3335ad7fb25429ce63ac1d6bac)
1 /*	$OpenBSD: x509.c,v 1.14 2020/09/12 15:46:48 claudio Exp $ */
2 /*
3  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/socket.h>
19 
20 #include <assert.h>
21 #include <err.h>
22 #include <stdarg.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 
28 #include <openssl/x509v3.h>
29 
30 #include "extern.h"
31 
32 /*
33  * Wrapper around ASN1_get_object() that preserves the current start
34  * state and returns a more meaningful value.
35  * Return zero on failure, non-zero on success.
36  */
37 static int
38 ASN1_frame(const char *fn, size_t sz,
39     const unsigned char **cnt, long *cntsz, int *tag)
40 {
41 	int	 ret, pcls;
42 
43 	assert(cnt != NULL && *cnt != NULL);
44 	assert(sz > 0);
45 	ret = ASN1_get_object(cnt, cntsz, tag, &pcls, sz);
46 	if ((ret & 0x80)) {
47 		cryptowarnx("%s: ASN1_get_object", fn);
48 		return 0;
49 	}
50 	return ASN1_object_size((ret & 0x01) ? 2 : 0, *cntsz, *tag);
51 }
52 
53 /*
54  * Parse X509v3 authority key identifier (AKI), RFC 6487 sec. 4.8.3.
55  * Returns the AKI or NULL if it could not be parsed.
56  * The AKI is formatted as aa:bb:cc:dd, with each being a hex value.
57  */
58 char *
59 x509_get_aki_ext(X509_EXTENSION *ext, const char *fn)
60 {
61 	const unsigned char	*d;
62 	const ASN1_TYPE		*t;
63 	const ASN1_OCTET_STRING	*os = NULL;
64 	ASN1_SEQUENCE_ANY	*seq = NULL;
65 	int			 dsz, ptag;
66 	long			 i, plen;
67 	char			 buf[4];
68 	char			*res = NULL;
69 
70 	assert(NID_authority_key_identifier ==
71 	    OBJ_obj2nid(X509_EXTENSION_get_object(ext)));
72 	os = X509_EXTENSION_get_data(ext);
73 	assert(os != NULL);
74 
75 	d = os->data;
76 	dsz = os->length;
77 
78 	if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) {
79 		cryptowarnx("%s: RFC 6487 section 4.8.3: AKI: "
80 		    "failed ASN.1 sub-sequence parse", fn);
81 		goto out;
82 	}
83 	if (sk_ASN1_TYPE_num(seq) != 1) {
84 		warnx("%s: RFC 6487 section 4.8.3: AKI: "
85 		    "want 1 element, have %d", fn, sk_ASN1_TYPE_num(seq));
86 		goto out;
87 	}
88 
89 	t = sk_ASN1_TYPE_value(seq, 0);
90 	if (t->type != V_ASN1_OTHER) {
91 		warnx("%s: RFC 6487 section 4.8.3: AKI: "
92 		    "want ASN.1 external, have %s (NID %d)",
93 		    fn, ASN1_tag2str(t->type), t->type);
94 		goto out;
95 	}
96 
97 	d = t->value.asn1_string->data;
98 	dsz = t->value.asn1_string->length;
99 
100 	if (!ASN1_frame(fn, dsz, &d, &plen, &ptag))
101 		goto out;
102 
103 	/* Make room for [hex1, hex2, ":"]*, NUL. */
104 
105 	if ((res = calloc(plen * 3 + 1, 1)) == NULL)
106 		err(1, NULL);
107 
108 	for (i = 0; i < plen; i++) {
109 		snprintf(buf, sizeof(buf), "%02X:", d[i]);
110 		strlcat(res, buf, plen * 3 + 1);
111 	}
112 	res[plen * 3 - 1] = '\0';
113 out:
114 	sk_ASN1_TYPE_pop_free(seq, ASN1_TYPE_free);
115 	return res;
116 }
117 
118 /*
119  * Parse X509v3 subject key identifier (SKI), RFC 6487 sec. 4.8.2.
120  * Returns the SKI or NULL if it could not be parsed.
121  * The SKI is formatted as aa:bb:cc:dd, with each being a hex value.
122  */
123 char *
124 x509_get_ski_ext(X509_EXTENSION *ext, const char *fn)
125 {
126 	const unsigned char	*d;
127 	const ASN1_OCTET_STRING	*os;
128 	ASN1_OCTET_STRING	*oss = NULL;
129 	int			 i, dsz;
130 	char			 buf[4];
131 	char			*res = NULL;
132 
133 	assert(NID_subject_key_identifier ==
134 	    OBJ_obj2nid(X509_EXTENSION_get_object(ext)));
135 
136 	os = X509_EXTENSION_get_data(ext);
137 	assert(os != NULL);
138 	d = os->data;
139 	dsz = os->length;
140 
141 	if ((oss = d2i_ASN1_OCTET_STRING(NULL, &d, dsz)) == NULL) {
142 		cryptowarnx("%s: RFC 6487 section 4.8.2: SKI: "
143 		    "failed ASN.1 octet string parse", fn);
144 		goto out;
145 	}
146 
147 	d = oss->data;
148 	dsz = oss->length;
149 
150 	if (dsz != 20) {
151 		warnx("%s: RFC 6487 section 4.8.2: SKI: "
152 		    "want 20 B SHA1 hash, have %d B", fn, dsz);
153 		goto out;
154 	}
155 
156 	/* Make room for [hex1, hex2, ":"]*, NUL. */
157 
158 	if ((res = calloc(dsz * 3 + 1, 1)) == NULL)
159 		err(1, NULL);
160 
161 	for (i = 0; i < dsz; i++) {
162 		snprintf(buf, sizeof(buf), "%02X:", d[i]);
163 		strlcat(res, buf, dsz * 3 + 1);
164 	}
165 	res[dsz * 3 - 1] = '\0';
166 out:
167 	ASN1_OCTET_STRING_free(oss);
168 	return res;
169 }
170 
171 /*
172  * Wraps around x509_get_ski_ext and x509_get_aki_ext.
173  * Returns zero on failure (out pointers are NULL) or non-zero on
174  * success (out pointers must be freed).
175  */
176 int
177 x509_get_ski_aki(X509 *x, const char *fn, char **ski, char **aki)
178 {
179 	X509_EXTENSION		*ext = NULL;
180 	const ASN1_OBJECT	*obj;
181 	int			 extsz, i;
182 
183 	*ski = *aki = NULL;
184 
185 	if ((extsz = X509_get_ext_count(x)) < 0)
186 		cryptoerrx("X509_get_ext_count");
187 
188 	for (i = 0; i < extsz; i++) {
189 		ext = X509_get_ext(x, i);
190 		assert(ext != NULL);
191 		obj = X509_EXTENSION_get_object(ext);
192 		assert(obj != NULL);
193 		switch (OBJ_obj2nid(obj)) {
194 		case NID_subject_key_identifier:
195 			free(*ski);
196 			*ski = x509_get_ski_ext(ext, fn);
197 			break;
198 		case NID_authority_key_identifier:
199 			free(*aki);
200 			*aki = x509_get_aki_ext(ext, fn);
201 			break;
202 		}
203 	}
204 
205 	if (*aki == NULL) {
206 		cryptowarnx("%s: RFC 6487 section 4.8.3: AKI: "
207 		    "missing AKI X509 extension", fn);
208 		free(*ski);
209 		*ski = NULL;
210 		return 0;
211 	}
212 	if (*ski == NULL) {
213 		cryptowarnx("%s: RFC 6487 section 4.8.2: AKI: "
214 		    "missing SKI X509 extension", fn);
215 		free(*aki);
216 		*aki = NULL;
217 		return 0;
218 	}
219 
220 	return 1;
221 }
222 
223 /*
224  * Parse the very specific subset of information in the CRL distribution
225  * point extension.
226  * See RFC 6487, sectoin 4.8.6 for details.
227  * Returns NULL on failure, the crl URI on success which has to be freed
228  * after use.
229  */
230 char *
231 x509_get_crl(X509 *x, const char *fn)
232 {
233 	STACK_OF(DIST_POINT)	*crldp;
234 	DIST_POINT		*dp;
235 	GENERAL_NAME		*name;
236 	char			*crl;
237 
238 	crldp = X509_get_ext_d2i(x, NID_crl_distribution_points, NULL, NULL);
239 	if (crldp == NULL) {
240 		warnx("%s: RFC 6487 section 4.8.6: CRL: "
241 		    "no CRL distribution point extension", fn);
242 		return NULL;
243 	}
244 
245 	if (sk_DIST_POINT_num(crldp) != 1) {
246 		warnx("%s: RFC 6487 section 4.8.6: CRL: "
247 		    "want 1 element, have %d", fn,
248 		    sk_DIST_POINT_num(crldp));
249 		return NULL;
250 	}
251 
252 	dp = sk_DIST_POINT_value(crldp, 0);
253 	if (dp->distpoint == NULL) {
254 		warnx("%s: RFC 6487 section 4.8.6: CRL: "
255 		    "no distribution point name", fn);
256 		return NULL;
257 	}
258 	if (dp->distpoint->type != 0) {
259 		warnx("%s: RFC 6487 section 4.8.6: CRL: "
260 		    "expected GEN_OTHERNAME, have %d", fn, dp->distpoint->type);
261 		return NULL;
262 	}
263 
264 	if (sk_GENERAL_NAME_num(dp->distpoint->name.fullname) != 1) {
265 		warnx("%s: RFC 6487 section 4.8.6: CRL: "
266 		    "want 1 full name, have %d", fn,
267 		    sk_GENERAL_NAME_num(dp->distpoint->name.fullname));
268 		return NULL;
269 	}
270 
271 	name = sk_GENERAL_NAME_value(dp->distpoint->name.fullname, 0);
272 	if (name->type != GEN_URI) {
273 		warnx("%s: RFC 6487 section 4.8.6: CRL: "
274 		    "want URI type, have %d", fn, name->type);
275 		return NULL;
276 	}
277 
278 	crl = strndup(ASN1_STRING_get0_data(name->d.uniformResourceIdentifier),
279 	    ASN1_STRING_length(name->d.uniformResourceIdentifier));
280 	if (crl == NULL)
281 		err(1, NULL);
282 
283 	return crl;
284 }
285 
286 char *
287 x509_crl_get_aki(X509_CRL *crl)
288 {
289 	X509_EXTENSION *ext;
290 	int loc;
291 
292 	loc = X509_CRL_get_ext_by_NID(crl, NID_authority_key_identifier, -1);
293 	if (loc == -1) {
294 		warnx("%s: CRL without AKI extension", __func__);
295 		return NULL;
296 	}
297 	ext = X509_CRL_get_ext(crl, loc);
298 
299 	return x509_get_aki_ext(ext, "x509_crl_get_aki");
300 }
301