michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * X.509 v3 Basic Constraints Extension michael@0: */ michael@0: michael@0: #include "prtypes.h" michael@0: #include /* for LONG_MAX */ michael@0: #include "seccomon.h" michael@0: #include "secdert.h" michael@0: #include "secoidt.h" michael@0: #include "secasn1t.h" michael@0: #include "secasn1.h" michael@0: #include "certt.h" michael@0: #include "secder.h" michael@0: #include "prprf.h" michael@0: #include "secerr.h" michael@0: michael@0: typedef struct EncodedContext{ michael@0: SECItem isCA; michael@0: SECItem pathLenConstraint; michael@0: SECItem encodedValue; michael@0: PLArenaPool *arena; michael@0: }EncodedContext; michael@0: michael@0: static const SEC_ASN1Template CERTBasicConstraintsTemplate[] = { michael@0: { SEC_ASN1_SEQUENCE, michael@0: 0, NULL, sizeof(EncodedContext) }, michael@0: { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN, /* XXX DER_DEFAULT */ michael@0: offsetof(EncodedContext,isCA)}, michael@0: { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER, michael@0: offsetof(EncodedContext,pathLenConstraint) }, michael@0: { 0, } michael@0: }; michael@0: michael@0: static unsigned char hexTrue = 0xff; michael@0: static unsigned char hexFalse = 0x00; michael@0: michael@0: #define GEN_BREAK(status) rv = status; break; michael@0: michael@0: SECStatus CERT_EncodeBasicConstraintValue michael@0: (PLArenaPool *arena, CERTBasicConstraints *value, SECItem *encodedValue) michael@0: { michael@0: EncodedContext encodeContext; michael@0: PLArenaPool *our_pool = NULL; michael@0: SECStatus rv = SECSuccess; michael@0: michael@0: do { michael@0: PORT_Memset (&encodeContext, 0, sizeof (encodeContext)); michael@0: if (!value->isCA && value->pathLenConstraint >= 0) { michael@0: PORT_SetError (SEC_ERROR_EXTENSION_VALUE_INVALID); michael@0: GEN_BREAK (SECFailure); michael@0: } michael@0: michael@0: encodeContext.arena = arena; michael@0: if (value->isCA == PR_TRUE) { michael@0: encodeContext.isCA.data = &hexTrue ; michael@0: encodeContext.isCA.len = 1; michael@0: } michael@0: michael@0: /* If the pathLenConstraint is less than 0, then it should be michael@0: * omitted from the encoding. michael@0: */ michael@0: if (value->isCA && value->pathLenConstraint >= 0) { michael@0: our_pool = PORT_NewArena (SEC_ASN1_DEFAULT_ARENA_SIZE); michael@0: if (our_pool == NULL) { michael@0: PORT_SetError (SEC_ERROR_NO_MEMORY); michael@0: GEN_BREAK (SECFailure); michael@0: } michael@0: if (SEC_ASN1EncodeUnsignedInteger michael@0: (our_pool, &encodeContext.pathLenConstraint, michael@0: (unsigned long)value->pathLenConstraint) == NULL) { michael@0: PORT_SetError (SEC_ERROR_NO_MEMORY); michael@0: GEN_BREAK (SECFailure); michael@0: } michael@0: } michael@0: if (SEC_ASN1EncodeItem (arena, encodedValue, &encodeContext, michael@0: CERTBasicConstraintsTemplate) == NULL) { michael@0: GEN_BREAK (SECFailure); michael@0: } michael@0: } while (0); michael@0: if (our_pool) michael@0: PORT_FreeArena (our_pool, PR_FALSE); michael@0: return(rv); michael@0: michael@0: } michael@0: michael@0: SECStatus CERT_DecodeBasicConstraintValue michael@0: (CERTBasicConstraints *value, const SECItem *encodedValue) michael@0: { michael@0: EncodedContext decodeContext; michael@0: PLArenaPool *our_pool; michael@0: SECStatus rv = SECSuccess; michael@0: michael@0: do { michael@0: PORT_Memset (&decodeContext, 0, sizeof (decodeContext)); michael@0: /* initialize the value just in case we got "0x30 00", or when the michael@0: pathLenConstraint is omitted. michael@0: */ michael@0: decodeContext.isCA.data =&hexFalse; michael@0: decodeContext.isCA.len = 1; michael@0: michael@0: our_pool = PORT_NewArena (SEC_ASN1_DEFAULT_ARENA_SIZE); michael@0: if (our_pool == NULL) { michael@0: PORT_SetError (SEC_ERROR_NO_MEMORY); michael@0: GEN_BREAK (SECFailure); michael@0: } michael@0: michael@0: rv = SEC_QuickDERDecodeItem michael@0: (our_pool, &decodeContext, CERTBasicConstraintsTemplate, encodedValue); michael@0: if (rv == SECFailure) michael@0: break; michael@0: michael@0: value->isCA = decodeContext.isCA.data michael@0: ? (PRBool)(decodeContext.isCA.data[0] != 0) michael@0: : PR_FALSE; michael@0: if (decodeContext.pathLenConstraint.data == NULL) { michael@0: /* if the pathLenConstraint is not encoded, and the current setting michael@0: is CA, then the pathLenConstraint should be set to a negative number michael@0: for unlimited certificate path. michael@0: */ michael@0: if (value->isCA) michael@0: value->pathLenConstraint = CERT_UNLIMITED_PATH_CONSTRAINT; michael@0: } else if (value->isCA) { michael@0: long len = DER_GetInteger (&decodeContext.pathLenConstraint); michael@0: if (len < 0 || len == LONG_MAX) { michael@0: PORT_SetError (SEC_ERROR_BAD_DER); michael@0: GEN_BREAK (SECFailure); michael@0: } michael@0: value->pathLenConstraint = len; michael@0: } else { michael@0: /* here we get an error where the subject is not a CA, but michael@0: the pathLenConstraint is set */ michael@0: PORT_SetError (SEC_ERROR_BAD_DER); michael@0: GEN_BREAK (SECFailure); michael@0: break; michael@0: } michael@0: michael@0: } while (0); michael@0: PORT_FreeArena (our_pool, PR_FALSE); michael@0: return (rv); michael@0: michael@0: }