|
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 } |