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  * X.509 v3 Basic Constraints Extension
7  */
8 
9 #include "prtypes.h"
10 #include <limits.h> /* for LONG_MAX */
11 #include "seccomon.h"
12 #include "secdert.h"
13 #include "secoidt.h"
14 #include "secasn1t.h"
15 #include "secasn1.h"
16 #include "certt.h"
17 #include "secder.h"
18 #include "prprf.h"
19 #include "secerr.h"
20 
21 typedef struct EncodedContext {
22     SECItem isCA;
23     SECItem pathLenConstraint;
24     SECItem encodedValue;
25     PLArenaPool *arena;
26 } EncodedContext;
27 
28 static const SEC_ASN1Template CERTBasicConstraintsTemplate[] = {
29     { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(EncodedContext) },
30     { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN, /* XXX DER_DEFAULT */
31       offsetof(EncodedContext, isCA) },
32     { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER,
33       offsetof(EncodedContext, pathLenConstraint) },
34     { 0 }
35 };
36 
37 static unsigned char hexTrue = 0xff;
38 static unsigned char hexFalse = 0x00;
39 
40 #define GEN_BREAK(status) \
41     rv = status;          \
42     break;
43 
44 SECStatus
CERT_EncodeBasicConstraintValue(PLArenaPool * arena,CERTBasicConstraints * value,SECItem * encodedValue)45 CERT_EncodeBasicConstraintValue(PLArenaPool *arena, CERTBasicConstraints *value,
46                                 SECItem *encodedValue)
47 {
48     EncodedContext encodeContext;
49     PLArenaPool *our_pool = NULL;
50     SECStatus rv = SECSuccess;
51 
52     do {
53         PORT_Memset(&encodeContext, 0, sizeof(encodeContext));
54         if (!value->isCA && value->pathLenConstraint >= 0) {
55             PORT_SetError(SEC_ERROR_EXTENSION_VALUE_INVALID);
56             GEN_BREAK(SECFailure);
57         }
58 
59         encodeContext.arena = arena;
60         if (value->isCA == PR_TRUE) {
61             encodeContext.isCA.data = &hexTrue;
62             encodeContext.isCA.len = 1;
63         }
64 
65         /* If the pathLenConstraint is less than 0, then it should be
66          * omitted from the encoding.
67          */
68         if (value->isCA && value->pathLenConstraint >= 0) {
69             our_pool = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
70             if (our_pool == NULL) {
71                 PORT_SetError(SEC_ERROR_NO_MEMORY);
72                 GEN_BREAK(SECFailure);
73             }
74             if (SEC_ASN1EncodeUnsignedInteger(
75                     our_pool, &encodeContext.pathLenConstraint,
76                     (unsigned long)value->pathLenConstraint) == NULL) {
77                 PORT_SetError(SEC_ERROR_NO_MEMORY);
78                 GEN_BREAK(SECFailure);
79             }
80         }
81         if (SEC_ASN1EncodeItem(arena, encodedValue, &encodeContext,
82                                CERTBasicConstraintsTemplate) == NULL) {
83             GEN_BREAK(SECFailure);
84         }
85     } while (0);
86     if (our_pool)
87         PORT_FreeArena(our_pool, PR_FALSE);
88     return (rv);
89 }
90 
91 SECStatus
CERT_DecodeBasicConstraintValue(CERTBasicConstraints * value,const SECItem * encodedValue)92 CERT_DecodeBasicConstraintValue(CERTBasicConstraints *value,
93                                 const SECItem *encodedValue)
94 {
95     EncodedContext decodeContext;
96     PORTCheapArenaPool tmpArena;
97     SECStatus rv = SECSuccess;
98 
99     do {
100         PORT_Memset(&decodeContext, 0, sizeof(decodeContext));
101         /* initialize the value just in case we got "0x30 00", or when the
102            pathLenConstraint is omitted.
103          */
104         decodeContext.isCA.data = &hexFalse;
105         decodeContext.isCA.len = 1;
106 
107         PORT_InitCheapArena(&tmpArena, SEC_ASN1_DEFAULT_ARENA_SIZE);
108 
109         rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &decodeContext,
110                                     CERTBasicConstraintsTemplate, encodedValue);
111         if (rv == SECFailure)
112             break;
113 
114         value->isCA = decodeContext.isCA.data
115                           ? (PRBool)(decodeContext.isCA.data[0] != 0)
116                           : PR_FALSE;
117         if (decodeContext.pathLenConstraint.data == NULL) {
118             /* if the pathLenConstraint is not encoded, and the current setting
119               is CA, then the pathLenConstraint should be set to a negative
120               number
121               for unlimited certificate path.
122              */
123             if (value->isCA)
124                 value->pathLenConstraint = CERT_UNLIMITED_PATH_CONSTRAINT;
125         } else if (value->isCA) {
126             long len = DER_GetInteger(&decodeContext.pathLenConstraint);
127             if (len < 0 || len == LONG_MAX) {
128                 PORT_SetError(SEC_ERROR_BAD_DER);
129                 GEN_BREAK(SECFailure);
130             }
131             value->pathLenConstraint = len;
132         } else {
133             /* here we get an error where the subject is not a CA, but
134                the pathLenConstraint is set */
135             PORT_SetError(SEC_ERROR_BAD_DER);
136             GEN_BREAK(SECFailure);
137             break;
138         }
139     } while (0);
140 
141     PORT_DestroyCheapArena(&tmpArena);
142     return (rv);
143 }
144