xref: /openbsd-src/usr.sbin/rpki-client/x509.c (revision f1dd7b858388b4a23f4f67a4957ec5ff656ebbe8)
1 /*	$OpenBSD: x509.c,v 1.21 2021/04/01 06:43:23 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 <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include <openssl/x509v3.h>
28 
29 #include "extern.h"
30 
31 /*
32  * Parse X509v3 authority key identifier (AKI), RFC 6487 sec. 4.8.3.
33  * Returns the AKI or NULL if it could not be parsed.
34  * The AKI is formatted as a hex string.
35  */
36 char *
37 x509_get_aki(X509 *x, int ta, const char *fn)
38 {
39 	const unsigned char	*d;
40 	AUTHORITY_KEYID		*akid;
41 	ASN1_OCTET_STRING	*os;
42 	int			 dsz, crit;
43 	char			*res = NULL;
44 
45 	akid = X509_get_ext_d2i(x, NID_authority_key_identifier, &crit, NULL);
46 	if (akid == NULL) {
47 		if (!ta)
48 			warnx("%s: RFC 6487 section 4.8.3: AKI: "
49 			    "extension missing", fn);
50 		return NULL;
51 	}
52 	if (crit != 0) {
53 		warnx("%s: RFC 6487 section 4.8.3: "
54 		    "AKI: extension not non-critical", fn);
55 		goto out;
56 	}
57 	if (akid->issuer != NULL || akid->serial != NULL) {
58 		warnx("%s: RFC 6487 section 4.8.3: AKI: "
59 		    "authorityCertIssuer or authorityCertSerialNumber present",
60 		    fn);
61 		goto out;
62 	}
63 
64 	os = akid->keyid;
65 	if (os == NULL) {
66 		warnx("%s: RFC 6487 section 4.8.3: AKI: "
67 		    "Key Identifier missing", fn);
68 		goto out;
69 	}
70 
71 	d = os->data;
72 	dsz = os->length;
73 
74 	if (dsz != SHA_DIGEST_LENGTH) {
75 		warnx("%s: RFC 6487 section 4.8.2: AKI: "
76 		    "want %d bytes SHA1 hash, have %d bytes",
77 		    fn, SHA_DIGEST_LENGTH, dsz);
78 		goto out;
79 	}
80 
81 	res = hex_encode(d, dsz);
82 out:
83 	AUTHORITY_KEYID_free(akid);
84 	return res;
85 }
86 
87 /*
88  * Parse X509v3 subject key identifier (SKI), RFC 6487 sec. 4.8.2.
89  * Returns the SKI or NULL if it could not be parsed.
90  * The SKI is formatted as a hex string.
91  */
92 char *
93 x509_get_ski(X509 *x, const char *fn)
94 {
95 	const unsigned char	*d;
96 	ASN1_OCTET_STRING	*os;
97 	int			 dsz, crit;
98 	char			*res = NULL;
99 
100 	os = X509_get_ext_d2i(x, NID_subject_key_identifier, &crit, NULL);
101 	if (os == NULL) {
102 		warnx("%s: RFC 6487 section 4.8.2: SKI: extension missing", fn);
103 		return NULL;
104 	}
105 	if (crit != 0) {
106 		warnx("%s: RFC 6487 section 4.8.2: "
107 		    "SKI: extension not non-critical", fn);
108 		goto out;
109 	}
110 
111 	d = os->data;
112 	dsz = os->length;
113 
114 	if (dsz != SHA_DIGEST_LENGTH) {
115 		warnx("%s: RFC 6487 section 4.8.2: SKI: "
116 		    "want %d bytes SHA1 hash, have %d bytes",
117 		    fn, SHA_DIGEST_LENGTH, dsz);
118 		goto out;
119 	}
120 
121 	res = hex_encode(d, dsz);
122 out:
123 	ASN1_OCTET_STRING_free(os);
124 	return res;
125 }
126 
127 /*
128  * Parse the Authority Information Access (AIA) extension
129  * See RFC 6487, section 4.8.7 for details.
130  * Returns NULL on failure, on success returns the AIA URI
131  * (which has to be freed after use).
132  */
133 char *
134 x509_get_aia(X509 *x, const char *fn)
135 {
136 	ACCESS_DESCRIPTION		*ad;
137 	AUTHORITY_INFO_ACCESS		*info;
138 	char				*aia = NULL;
139 	int				 crit;
140 
141 	info = X509_get_ext_d2i(x, NID_info_access, &crit, NULL);
142 	if (info == NULL) {
143 		warnx("%s: RFC 6487 section 4.8.7: AIA: extension missing", fn);
144 		return NULL;
145 	}
146 	if (crit != 0) {
147 		warnx("%s: RFC 6487 section 4.8.7: "
148 		    "AIA: extension not non-critical", fn);
149 		goto out;
150 	}
151 	if (sk_ACCESS_DESCRIPTION_num(info) != 1) {
152 		warnx("%s: RFC 6487 section 4.8.7: AIA: "
153 		    "want 1 element, have %d", fn,
154 		    sk_ACCESS_DESCRIPTION_num(info));
155 		goto out;
156 	}
157 
158 	ad = sk_ACCESS_DESCRIPTION_value(info, 0);
159 	if (OBJ_obj2nid(ad->method) != NID_ad_ca_issuers) {
160 		warnx("%s: RFC 6487 section 4.8.7: AIA: "
161 		    "expected caIssuers, have %d", fn, OBJ_obj2nid(ad->method));
162 		goto out;
163 	}
164 	if (ad->location->type != GEN_URI) {
165 		warnx("%s: RFC 6487 section 4.8.7: AIA: "
166 		    "want GEN_URI type, have %d", fn, ad->location->type);
167 		goto out;
168 	}
169 
170 	aia = strndup(
171 	    ASN1_STRING_get0_data(ad->location->d.uniformResourceIdentifier),
172 	    ASN1_STRING_length(ad->location->d.uniformResourceIdentifier));
173 	if (aia == NULL)
174 		err(1, NULL);
175 
176 out:
177 	AUTHORITY_INFO_ACCESS_free(info);
178 	return aia;
179 }
180 
181 /*
182  * Parse the very specific subset of information in the CRL distribution
183  * point extension.
184  * See RFC 6487, sectoin 4.8.6 for details.
185  * Returns NULL on failure, the crl URI on success which has to be freed
186  * after use.
187  */
188 char *
189 x509_get_crl(X509 *x, const char *fn)
190 {
191 	CRL_DIST_POINTS		*crldp;
192 	DIST_POINT		*dp;
193 	GENERAL_NAME		*name;
194 	char			*crl = NULL;
195 	int			 crit;
196 
197 	crldp = X509_get_ext_d2i(x, NID_crl_distribution_points, &crit, NULL);
198 	if (crldp == NULL) {
199 		warnx("%s: RFC 6487 section 4.8.6: CRL: "
200 		    "no CRL distribution point extension", fn);
201 		return NULL;
202 	}
203 	if (crit != 0) {
204 		warnx("%s: RFC 6487 section 4.8.6: "
205 		    "CRL distribution point: extension not non-critical", fn);
206 		goto out;
207 	}
208 
209 	if (sk_DIST_POINT_num(crldp) != 1) {
210 		warnx("%s: RFC 6487 section 4.8.6: CRL: "
211 		    "want 1 element, have %d", fn,
212 		    sk_DIST_POINT_num(crldp));
213 		goto out;
214 	}
215 
216 	dp = sk_DIST_POINT_value(crldp, 0);
217 	if (dp->distpoint == NULL) {
218 		warnx("%s: RFC 6487 section 4.8.6: CRL: "
219 		    "no distribution point name", fn);
220 		goto out;
221 	}
222 	if (dp->distpoint->type != 0) {
223 		warnx("%s: RFC 6487 section 4.8.6: CRL: "
224 		    "expected GEN_OTHERNAME, have %d", fn, dp->distpoint->type);
225 		goto out;
226 	}
227 
228 	if (sk_GENERAL_NAME_num(dp->distpoint->name.fullname) != 1) {
229 		warnx("%s: RFC 6487 section 4.8.6: CRL: "
230 		    "want 1 full name, have %d", fn,
231 		    sk_GENERAL_NAME_num(dp->distpoint->name.fullname));
232 		goto out;
233 	}
234 
235 	name = sk_GENERAL_NAME_value(dp->distpoint->name.fullname, 0);
236 	if (name->type != GEN_URI) {
237 		warnx("%s: RFC 6487 section 4.8.6: CRL: "
238 		    "want URI type, have %d", fn, name->type);
239 		goto out;
240 	}
241 
242 	crl = strndup(ASN1_STRING_get0_data(name->d.uniformResourceIdentifier),
243 	    ASN1_STRING_length(name->d.uniformResourceIdentifier));
244 	if (crl == NULL)
245 		err(1, NULL);
246 
247 out:
248 	CRL_DIST_POINTS_free(crldp);
249 	return crl;
250 }
251 
252 /*
253  * Parse X509v3 authority key identifier (AKI) from the CRL.
254  * This is matched against the string from x509_get_ski() above.
255  * Returns the AKI or NULL if it could not be parsed.
256  * The AKI is formatted as a hex string.
257  */
258 char *
259 x509_crl_get_aki(X509_CRL *crl, const char *fn)
260 {
261 	const unsigned char	*d;
262 	AUTHORITY_KEYID		*akid;
263 	ASN1_OCTET_STRING	*os;
264 	int			 dsz, crit;
265 	char			*res = NULL;
266 
267 	akid = X509_CRL_get_ext_d2i(crl, NID_authority_key_identifier, &crit,
268 	    NULL);
269 	if (akid == NULL) {
270 		warnx("%s: RFC 6487 section 4.8.3: AKI: extension missing", fn);
271 		return NULL;
272 	}
273 	if (crit != 0) {
274 		warnx("%s: RFC 6487 section 4.8.3: "
275 		    "AKI: extension not non-critical", fn);
276 		goto out;
277 	}
278 	if (akid->issuer != NULL || akid->serial != NULL) {
279 		warnx("%s: RFC 6487 section 4.8.3: AKI: "
280 		    "authorityCertIssuer or authorityCertSerialNumber present",
281 		    fn);
282 		goto out;
283 	}
284 
285 	os = akid->keyid;
286 	if (os == NULL) {
287 		warnx("%s: RFC 6487 section 4.8.3: AKI: "
288 		    "Key Identifier missing", fn);
289 		goto out;
290 	}
291 
292 	d = os->data;
293 	dsz = os->length;
294 
295 	if (dsz != SHA_DIGEST_LENGTH) {
296 		warnx("%s: RFC 6487 section 4.8.2: AKI: "
297 		    "want %d bytes SHA1 hash, have %d bytes",
298 		    fn, SHA_DIGEST_LENGTH, dsz);
299 		goto out;
300 	}
301 
302 	res = hex_encode(d, dsz);
303 out:
304 	AUTHORITY_KEYID_free(akid);
305 	return res;
306 }
307