| |
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
| |
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
| |
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
4 |
| |
5 /* |
| |
6 * Code for dealing with X509.V3 extensions. |
| |
7 */ |
| |
8 |
| |
9 #include "cert.h" |
| |
10 #include "secitem.h" |
| |
11 #include "secoid.h" |
| |
12 #include "secder.h" |
| |
13 #include "secasn1.h" |
| |
14 #include "certxutl.h" |
| |
15 #include "secerr.h" |
| |
16 |
| |
17 SECStatus |
| |
18 CERT_FindCertExtensionByOID(CERTCertificate *cert, SECItem *oid, |
| |
19 SECItem *value) |
| |
20 { |
| |
21 return (cert_FindExtensionByOID (cert->extensions, oid, value)); |
| |
22 } |
| |
23 |
| |
24 |
| |
25 SECStatus |
| |
26 CERT_FindCertExtension(const CERTCertificate *cert, int tag, SECItem *value) |
| |
27 { |
| |
28 return (cert_FindExtension (cert->extensions, tag, value)); |
| |
29 } |
| |
30 |
| |
31 static void |
| |
32 SetExts(void *object, CERTCertExtension **exts) |
| |
33 { |
| |
34 CERTCertificate *cert = (CERTCertificate *)object; |
| |
35 |
| |
36 cert->extensions = exts; |
| |
37 DER_SetUInteger (cert->arena, &(cert->version), SEC_CERTIFICATE_VERSION_3); |
| |
38 } |
| |
39 |
| |
40 void * |
| |
41 CERT_StartCertExtensions(CERTCertificate *cert) |
| |
42 { |
| |
43 return (cert_StartExtensions ((void *)cert, cert->arena, SetExts)); |
| |
44 } |
| |
45 |
| |
46 /* find the given extension in the certificate of the Issuer of 'cert' */ |
| |
47 SECStatus |
| |
48 CERT_FindIssuerCertExtension(CERTCertificate *cert, int tag, SECItem *value) |
| |
49 { |
| |
50 CERTCertificate *issuercert; |
| |
51 SECStatus rv; |
| |
52 |
| |
53 issuercert = CERT_FindCertByName(cert->dbhandle, &cert->derIssuer); |
| |
54 if ( issuercert ) { |
| |
55 rv = cert_FindExtension(issuercert->extensions, tag, value); |
| |
56 CERT_DestroyCertificate(issuercert); |
| |
57 } else { |
| |
58 rv = SECFailure; |
| |
59 } |
| |
60 |
| |
61 return(rv); |
| |
62 } |
| |
63 |
| |
64 /* find a URL extension in the cert or its CA |
| |
65 * apply the base URL string if it exists |
| |
66 */ |
| |
67 char * |
| |
68 CERT_FindCertURLExtension(CERTCertificate *cert, int tag, int catag) |
| |
69 { |
| |
70 SECStatus rv; |
| |
71 SECItem urlitem = {siBuffer,0}; |
| |
72 SECItem baseitem = {siBuffer,0}; |
| |
73 SECItem urlstringitem = {siBuffer,0}; |
| |
74 SECItem basestringitem = {siBuffer,0}; |
| |
75 PLArenaPool *arena = NULL; |
| |
76 PRBool hasbase; |
| |
77 char *urlstring; |
| |
78 char *str; |
| |
79 int len; |
| |
80 unsigned int i; |
| |
81 |
| |
82 urlstring = NULL; |
| |
83 |
| |
84 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
| |
85 if ( ! arena ) { |
| |
86 goto loser; |
| |
87 } |
| |
88 |
| |
89 hasbase = PR_FALSE; |
| |
90 |
| |
91 rv = cert_FindExtension(cert->extensions, tag, &urlitem); |
| |
92 if ( rv == SECSuccess ) { |
| |
93 rv = cert_FindExtension(cert->extensions, SEC_OID_NS_CERT_EXT_BASE_URL, |
| |
94 &baseitem); |
| |
95 if ( rv == SECSuccess ) { |
| |
96 hasbase = PR_TRUE; |
| |
97 } |
| |
98 |
| |
99 } else if ( catag ) { |
| |
100 /* if the cert doesn't have the extensions, see if the issuer does */ |
| |
101 rv = CERT_FindIssuerCertExtension(cert, catag, &urlitem); |
| |
102 if ( rv != SECSuccess ) { |
| |
103 goto loser; |
| |
104 } |
| |
105 rv = CERT_FindIssuerCertExtension(cert, SEC_OID_NS_CERT_EXT_BASE_URL, |
| |
106 &baseitem); |
| |
107 if ( rv == SECSuccess ) { |
| |
108 hasbase = PR_TRUE; |
| |
109 } |
| |
110 } else { |
| |
111 goto loser; |
| |
112 } |
| |
113 |
| |
114 rv = SEC_QuickDERDecodeItem(arena, &urlstringitem, |
| |
115 SEC_ASN1_GET(SEC_IA5StringTemplate), &urlitem); |
| |
116 |
| |
117 if ( rv != SECSuccess ) { |
| |
118 goto loser; |
| |
119 } |
| |
120 if ( hasbase ) { |
| |
121 rv = SEC_QuickDERDecodeItem(arena, &basestringitem, |
| |
122 SEC_ASN1_GET(SEC_IA5StringTemplate), |
| |
123 &baseitem); |
| |
124 |
| |
125 if ( rv != SECSuccess ) { |
| |
126 goto loser; |
| |
127 } |
| |
128 } |
| |
129 |
| |
130 len = urlstringitem.len + ( hasbase ? basestringitem.len : 0 ) + 1; |
| |
131 |
| |
132 str = urlstring = (char *)PORT_Alloc(len); |
| |
133 if ( urlstring == NULL ) { |
| |
134 goto loser; |
| |
135 } |
| |
136 |
| |
137 /* copy the URL base first */ |
| |
138 if ( hasbase ) { |
| |
139 |
| |
140 /* if the urlstring has a : in it, then we assume it is an absolute |
| |
141 * URL, and will not get the base string pre-pended |
| |
142 */ |
| |
143 for ( i = 0; i < urlstringitem.len; i++ ) { |
| |
144 if ( urlstringitem.data[i] == ':' ) { |
| |
145 goto nobase; |
| |
146 } |
| |
147 } |
| |
148 |
| |
149 PORT_Memcpy(str, basestringitem.data, basestringitem.len); |
| |
150 str += basestringitem.len; |
| |
151 |
| |
152 } |
| |
153 |
| |
154 nobase: |
| |
155 /* copy the rest (or all) of the URL */ |
| |
156 PORT_Memcpy(str, urlstringitem.data, urlstringitem.len); |
| |
157 str += urlstringitem.len; |
| |
158 |
| |
159 *str = '\0'; |
| |
160 goto done; |
| |
161 |
| |
162 loser: |
| |
163 if ( urlstring ) { |
| |
164 PORT_Free(urlstring); |
| |
165 } |
| |
166 |
| |
167 urlstring = NULL; |
| |
168 done: |
| |
169 if ( arena ) { |
| |
170 PORT_FreeArena(arena, PR_FALSE); |
| |
171 } |
| |
172 if ( baseitem.data ) { |
| |
173 PORT_Free(baseitem.data); |
| |
174 } |
| |
175 if ( urlitem.data ) { |
| |
176 PORT_Free(urlitem.data); |
| |
177 } |
| |
178 |
| |
179 return(urlstring); |
| |
180 } |
| |
181 |
| |
182 /* |
| |
183 * get the value of the Netscape Certificate Type Extension |
| |
184 */ |
| |
185 SECStatus |
| |
186 CERT_FindNSCertTypeExtension(CERTCertificate *cert, SECItem *retItem) |
| |
187 { |
| |
188 |
| |
189 return (CERT_FindBitStringExtension |
| |
190 (cert->extensions, SEC_OID_NS_CERT_EXT_CERT_TYPE, retItem)); |
| |
191 } |
| |
192 |
| |
193 |
| |
194 /* |
| |
195 * get the value of a string type extension |
| |
196 */ |
| |
197 char * |
| |
198 CERT_FindNSStringExtension(CERTCertificate *cert, int oidtag) |
| |
199 { |
| |
200 SECItem wrapperItem, tmpItem = {siBuffer,0}; |
| |
201 SECStatus rv; |
| |
202 PLArenaPool *arena = NULL; |
| |
203 char *retstring = NULL; |
| |
204 |
| |
205 wrapperItem.data = NULL; |
| |
206 tmpItem.data = NULL; |
| |
207 |
| |
208 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
| |
209 |
| |
210 if ( ! arena ) { |
| |
211 goto loser; |
| |
212 } |
| |
213 |
| |
214 rv = cert_FindExtension(cert->extensions, oidtag, |
| |
215 &wrapperItem); |
| |
216 if ( rv != SECSuccess ) { |
| |
217 goto loser; |
| |
218 } |
| |
219 |
| |
220 rv = SEC_QuickDERDecodeItem(arena, &tmpItem, |
| |
221 SEC_ASN1_GET(SEC_IA5StringTemplate), &wrapperItem); |
| |
222 |
| |
223 if ( rv != SECSuccess ) { |
| |
224 goto loser; |
| |
225 } |
| |
226 |
| |
227 retstring = (char *)PORT_Alloc(tmpItem.len + 1 ); |
| |
228 if ( retstring == NULL ) { |
| |
229 goto loser; |
| |
230 } |
| |
231 |
| |
232 PORT_Memcpy(retstring, tmpItem.data, tmpItem.len); |
| |
233 retstring[tmpItem.len] = '\0'; |
| |
234 |
| |
235 loser: |
| |
236 if ( arena ) { |
| |
237 PORT_FreeArena(arena, PR_FALSE); |
| |
238 } |
| |
239 |
| |
240 if ( wrapperItem.data ) { |
| |
241 PORT_Free(wrapperItem.data); |
| |
242 } |
| |
243 |
| |
244 return(retstring); |
| |
245 } |
| |
246 |
| |
247 /* |
| |
248 * get the value of the X.509 v3 Key Usage Extension |
| |
249 */ |
| |
250 SECStatus |
| |
251 CERT_FindKeyUsageExtension(CERTCertificate *cert, SECItem *retItem) |
| |
252 { |
| |
253 |
| |
254 return (CERT_FindBitStringExtension(cert->extensions, |
| |
255 SEC_OID_X509_KEY_USAGE, retItem)); |
| |
256 } |
| |
257 |
| |
258 /* |
| |
259 * get the value of the X.509 v3 Key Usage Extension |
| |
260 */ |
| |
261 SECStatus |
| |
262 CERT_FindSubjectKeyIDExtension(CERTCertificate *cert, SECItem *retItem) |
| |
263 { |
| |
264 |
| |
265 SECStatus rv; |
| |
266 SECItem encodedValue = {siBuffer, NULL, 0 }; |
| |
267 SECItem decodedValue = {siBuffer, NULL, 0 }; |
| |
268 |
| |
269 rv = cert_FindExtension |
| |
270 (cert->extensions, SEC_OID_X509_SUBJECT_KEY_ID, &encodedValue); |
| |
271 if (rv == SECSuccess) { |
| |
272 PLArenaPool * tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
| |
273 if (tmpArena) { |
| |
274 rv = SEC_QuickDERDecodeItem(tmpArena, &decodedValue, |
| |
275 SEC_ASN1_GET(SEC_OctetStringTemplate), |
| |
276 &encodedValue); |
| |
277 if (rv == SECSuccess) { |
| |
278 rv = SECITEM_CopyItem(NULL, retItem, &decodedValue); |
| |
279 } |
| |
280 PORT_FreeArena(tmpArena, PR_FALSE); |
| |
281 } else { |
| |
282 rv = SECFailure; |
| |
283 } |
| |
284 } |
| |
285 SECITEM_FreeItem(&encodedValue, PR_FALSE); |
| |
286 return rv; |
| |
287 } |
| |
288 |
| |
289 SECStatus |
| |
290 CERT_FindBasicConstraintExten(CERTCertificate *cert, |
| |
291 CERTBasicConstraints *value) |
| |
292 { |
| |
293 SECItem encodedExtenValue; |
| |
294 SECStatus rv; |
| |
295 |
| |
296 encodedExtenValue.data = NULL; |
| |
297 encodedExtenValue.len = 0; |
| |
298 |
| |
299 rv = cert_FindExtension(cert->extensions, SEC_OID_X509_BASIC_CONSTRAINTS, |
| |
300 &encodedExtenValue); |
| |
301 if ( rv != SECSuccess ) { |
| |
302 return (rv); |
| |
303 } |
| |
304 |
| |
305 rv = CERT_DecodeBasicConstraintValue (value, &encodedExtenValue); |
| |
306 |
| |
307 /* free the raw extension data */ |
| |
308 PORT_Free(encodedExtenValue.data); |
| |
309 encodedExtenValue.data = NULL; |
| |
310 |
| |
311 return(rv); |
| |
312 } |
| |
313 |
| |
314 CERTAuthKeyID * |
| |
315 CERT_FindAuthKeyIDExten (PLArenaPool *arena, CERTCertificate *cert) |
| |
316 { |
| |
317 SECItem encodedExtenValue; |
| |
318 SECStatus rv; |
| |
319 CERTAuthKeyID *ret; |
| |
320 |
| |
321 encodedExtenValue.data = NULL; |
| |
322 encodedExtenValue.len = 0; |
| |
323 |
| |
324 rv = cert_FindExtension(cert->extensions, SEC_OID_X509_AUTH_KEY_ID, |
| |
325 &encodedExtenValue); |
| |
326 if ( rv != SECSuccess ) { |
| |
327 return (NULL); |
| |
328 } |
| |
329 |
| |
330 ret = CERT_DecodeAuthKeyID (arena, &encodedExtenValue); |
| |
331 |
| |
332 PORT_Free(encodedExtenValue.data); |
| |
333 encodedExtenValue.data = NULL; |
| |
334 |
| |
335 return(ret); |
| |
336 } |
| |
337 |
| |
338 SECStatus |
| |
339 CERT_CheckCertUsage(CERTCertificate *cert, unsigned char usage) |
| |
340 { |
| |
341 SECItem keyUsage; |
| |
342 SECStatus rv; |
| |
343 |
| |
344 /* There is no extension, v1 or v2 certificate */ |
| |
345 if (cert->extensions == NULL) { |
| |
346 return (SECSuccess); |
| |
347 } |
| |
348 |
| |
349 keyUsage.data = NULL; |
| |
350 |
| |
351 /* This code formerly ignored the Key Usage extension if it was |
| |
352 ** marked non-critical. That was wrong. Since we do understand it, |
| |
353 ** we are obligated to honor it, whether or not it is critical. |
| |
354 */ |
| |
355 rv = CERT_FindKeyUsageExtension(cert, &keyUsage); |
| |
356 if (rv == SECFailure) { |
| |
357 rv = (PORT_GetError () == SEC_ERROR_EXTENSION_NOT_FOUND) ? |
| |
358 SECSuccess : SECFailure; |
| |
359 } else if (!(keyUsage.data[0] & usage)) { |
| |
360 PORT_SetError (SEC_ERROR_CERT_USAGES_INVALID); |
| |
361 rv = SECFailure; |
| |
362 } |
| |
363 PORT_Free (keyUsage.data); |
| |
364 return (rv); |
| |
365 } |