security/nss/lib/certdb/certv3.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial