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