/src/nss/lib/certhigh/certreq.c
Line | Count | Source (jump to first uncovered line) |
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 | | #include "cert.h" |
6 | | #include "certt.h" |
7 | | #include "secder.h" |
8 | | #include "keyhi.h" |
9 | | #include "secitem.h" |
10 | | #include "secasn1.h" |
11 | | #include "secerr.h" |
12 | | |
13 | | SEC_ASN1_MKSUB(SEC_AnyTemplate) |
14 | | |
15 | | const SEC_ASN1Template CERT_AttributeTemplate[] = { |
16 | | { SEC_ASN1_SEQUENCE, |
17 | | 0, NULL, sizeof(CERTAttribute) }, |
18 | | { SEC_ASN1_OBJECT_ID, offsetof(CERTAttribute, attrType) }, |
19 | | { SEC_ASN1_SET_OF | SEC_ASN1_XTRN, offsetof(CERTAttribute, attrValue), |
20 | | SEC_ASN1_SUB(SEC_AnyTemplate) }, |
21 | | { 0 } |
22 | | }; |
23 | | |
24 | | const SEC_ASN1Template CERT_SetOfAttributeTemplate[] = { |
25 | | { SEC_ASN1_SET_OF, 0, CERT_AttributeTemplate }, |
26 | | }; |
27 | | |
28 | | const SEC_ASN1Template CERT_CertificateRequestTemplate[] = { |
29 | | { SEC_ASN1_SEQUENCE, |
30 | | 0, NULL, sizeof(CERTCertificateRequest) }, |
31 | | { SEC_ASN1_INTEGER, |
32 | | offsetof(CERTCertificateRequest, version) }, |
33 | | { SEC_ASN1_INLINE, |
34 | | offsetof(CERTCertificateRequest, subject), |
35 | | CERT_NameTemplate }, |
36 | | { SEC_ASN1_INLINE, |
37 | | offsetof(CERTCertificateRequest, subjectPublicKeyInfo), |
38 | | CERT_SubjectPublicKeyInfoTemplate }, |
39 | | { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, |
40 | | offsetof(CERTCertificateRequest, attributes), |
41 | | CERT_SetOfAttributeTemplate }, |
42 | | { 0 } |
43 | | }; |
44 | | |
45 | | SEC_ASN1_CHOOSER_IMPLEMENT(CERT_CertificateRequestTemplate) |
46 | | |
47 | | CERTCertificate * |
48 | | CERT_CreateCertificate(unsigned long serialNumber, |
49 | | CERTName *issuer, |
50 | | CERTValidity *validity, |
51 | | CERTCertificateRequest *req) |
52 | 0 | { |
53 | 0 | CERTCertificate *c; |
54 | 0 | int rv; |
55 | 0 | PLArenaPool *arena; |
56 | |
|
57 | 0 | arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
58 | |
|
59 | 0 | if (!arena) { |
60 | 0 | return (0); |
61 | 0 | } |
62 | | |
63 | 0 | c = (CERTCertificate *)PORT_ArenaZAlloc(arena, sizeof(CERTCertificate)); |
64 | |
|
65 | 0 | if (!c) { |
66 | 0 | PORT_FreeArena(arena, PR_FALSE); |
67 | 0 | return 0; |
68 | 0 | } |
69 | | |
70 | 0 | c->referenceCount = 1; |
71 | 0 | c->arena = arena; |
72 | | |
73 | | /* |
74 | | * Default is a plain version 1. |
75 | | * If extensions are added, it will get changed as appropriate. |
76 | | */ |
77 | 0 | rv = DER_SetUInteger(arena, &c->version, SEC_CERTIFICATE_VERSION_1); |
78 | 0 | if (rv) |
79 | 0 | goto loser; |
80 | | |
81 | 0 | rv = DER_SetUInteger(arena, &c->serialNumber, serialNumber); |
82 | 0 | if (rv) |
83 | 0 | goto loser; |
84 | | |
85 | 0 | rv = CERT_CopyName(arena, &c->issuer, issuer); |
86 | 0 | if (rv) |
87 | 0 | goto loser; |
88 | | |
89 | 0 | rv = CERT_CopyValidity(arena, &c->validity, validity); |
90 | 0 | if (rv) |
91 | 0 | goto loser; |
92 | | |
93 | 0 | rv = CERT_CopyName(arena, &c->subject, &req->subject); |
94 | 0 | if (rv) |
95 | 0 | goto loser; |
96 | 0 | rv = SECKEY_CopySubjectPublicKeyInfo(arena, &c->subjectPublicKeyInfo, |
97 | 0 | &req->subjectPublicKeyInfo); |
98 | 0 | if (rv) |
99 | 0 | goto loser; |
100 | | |
101 | 0 | return c; |
102 | | |
103 | 0 | loser: |
104 | 0 | CERT_DestroyCertificate(c); |
105 | 0 | return 0; |
106 | 0 | } |
107 | | |
108 | | /************************************************************************/ |
109 | | /* It's clear from the comments that the original author of this |
110 | | * function expected the template for certificate requests to treat |
111 | | * the attributes as a SET OF ANY. This function expected to be |
112 | | * passed an array of SECItems each of which contained an already encoded |
113 | | * Attribute. But the cert request template does not treat the |
114 | | * Attributes as a SET OF ANY, and AFAIK never has. Instead the template |
115 | | * encodes attributes as a SET OF xxxxxxx. That is, it expects to encode |
116 | | * each of the Attributes, not have them pre-encoded. Consequently an |
117 | | * array of SECItems containing encoded Attributes is of no value to this |
118 | | * function. But we cannot change the signature of this public function. |
119 | | * It must continue to take SECItems. |
120 | | * |
121 | | * I have recoded this function so that each SECItem contains an |
122 | | * encoded cert extension. The encoded cert extensions form the list for the |
123 | | * single attribute of the cert request. In this implementation there is at most |
124 | | * one attribute and it is always of type SEC_OID_PKCS9_EXTENSION_REQUEST. |
125 | | */ |
126 | | |
127 | | CERTCertificateRequest * |
128 | | CERT_CreateCertificateRequest(CERTName *subject, |
129 | | CERTSubjectPublicKeyInfo *spki, |
130 | | SECItem **attributes) |
131 | 0 | { |
132 | 0 | CERTCertificateRequest *certreq; |
133 | 0 | PLArenaPool *arena; |
134 | 0 | CERTAttribute *attribute; |
135 | 0 | SECOidData *oidData; |
136 | 0 | SECStatus rv; |
137 | 0 | int i = 0; |
138 | |
|
139 | 0 | arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
140 | 0 | if (arena == NULL) { |
141 | 0 | return NULL; |
142 | 0 | } |
143 | | |
144 | 0 | certreq = PORT_ArenaZNew(arena, CERTCertificateRequest); |
145 | 0 | if (!certreq) { |
146 | 0 | PORT_FreeArena(arena, PR_FALSE); |
147 | 0 | return NULL; |
148 | 0 | } |
149 | | /* below here it is safe to goto loser */ |
150 | | |
151 | 0 | certreq->arena = arena; |
152 | |
|
153 | 0 | rv = DER_SetUInteger(arena, &certreq->version, |
154 | 0 | SEC_CERTIFICATE_REQUEST_VERSION); |
155 | 0 | if (rv != SECSuccess) |
156 | 0 | goto loser; |
157 | | |
158 | 0 | rv = CERT_CopyName(arena, &certreq->subject, subject); |
159 | 0 | if (rv != SECSuccess) |
160 | 0 | goto loser; |
161 | | |
162 | 0 | rv = SECKEY_CopySubjectPublicKeyInfo(arena, |
163 | 0 | &certreq->subjectPublicKeyInfo, |
164 | 0 | spki); |
165 | 0 | if (rv != SECSuccess) |
166 | 0 | goto loser; |
167 | | |
168 | 0 | certreq->attributes = PORT_ArenaZNewArray(arena, CERTAttribute *, 2); |
169 | 0 | if (!certreq->attributes) |
170 | 0 | goto loser; |
171 | | |
172 | | /* Copy over attribute information */ |
173 | 0 | if (!attributes || !attributes[0]) { |
174 | | /* |
175 | | ** Invent empty attribute information. According to the |
176 | | ** pkcs#10 spec, attributes has this ASN.1 type: |
177 | | ** |
178 | | ** attributes [0] IMPLICIT Attributes |
179 | | ** |
180 | | ** Which means, we should create a NULL terminated list |
181 | | ** with the first entry being NULL; |
182 | | */ |
183 | 0 | certreq->attributes[0] = NULL; |
184 | 0 | return certreq; |
185 | 0 | } |
186 | | |
187 | | /* allocate space for attributes */ |
188 | 0 | attribute = PORT_ArenaZNew(arena, CERTAttribute); |
189 | 0 | if (!attribute) |
190 | 0 | goto loser; |
191 | | |
192 | 0 | oidData = SECOID_FindOIDByTag(SEC_OID_PKCS9_EXTENSION_REQUEST); |
193 | 0 | PORT_Assert(oidData); |
194 | 0 | if (!oidData) |
195 | 0 | goto loser; |
196 | 0 | rv = SECITEM_CopyItem(arena, &attribute->attrType, &oidData->oid); |
197 | 0 | if (rv != SECSuccess) |
198 | 0 | goto loser; |
199 | | |
200 | 0 | for (i = 0; attributes[i] != NULL; i++) |
201 | 0 | ; |
202 | 0 | attribute->attrValue = PORT_ArenaZNewArray(arena, SECItem *, i + 1); |
203 | 0 | if (!attribute->attrValue) |
204 | 0 | goto loser; |
205 | | |
206 | | /* copy attributes */ |
207 | 0 | for (i = 0; attributes[i]; i++) { |
208 | | /* |
209 | | ** Attributes are a SetOf Attribute which implies |
210 | | ** lexigraphical ordering. It is assumes that the |
211 | | ** attributes are passed in sorted. If we need to |
212 | | ** add functionality to sort them, there is an |
213 | | ** example in the PKCS 7 code. |
214 | | */ |
215 | 0 | attribute->attrValue[i] = SECITEM_ArenaDupItem(arena, attributes[i]); |
216 | 0 | if (!attribute->attrValue[i]) |
217 | 0 | goto loser; |
218 | 0 | } |
219 | | |
220 | 0 | certreq->attributes[0] = attribute; |
221 | |
|
222 | 0 | return certreq; |
223 | | |
224 | 0 | loser: |
225 | 0 | CERT_DestroyCertificateRequest(certreq); |
226 | 0 | return NULL; |
227 | 0 | } |
228 | | |
229 | | void |
230 | | CERT_DestroyCertificateRequest(CERTCertificateRequest *req) |
231 | 0 | { |
232 | 0 | if (req && req->arena) { |
233 | 0 | PORT_FreeArena(req->arena, PR_FALSE); |
234 | 0 | } |
235 | 0 | return; |
236 | 0 | } |
237 | | |
238 | | static void |
239 | | setCRExt(void *o, CERTCertExtension **exts) |
240 | 0 | { |
241 | 0 | ((CERTCertificateRequest *)o)->attributes = (struct CERTAttributeStr **)exts; |
242 | 0 | } |
243 | | |
244 | | /* |
245 | | ** Set up to start gathering cert extensions for a cert request. |
246 | | ** The list is created as CertExtensions and converted to an |
247 | | ** attribute list by CERT_FinishCRAttributes(). |
248 | | */ |
249 | | extern void *cert_StartExtensions(void *owner, PLArenaPool *ownerArena, |
250 | | void (*setExts)(void *object, CERTCertExtension **exts)); |
251 | | void * |
252 | | CERT_StartCertificateRequestAttributes(CERTCertificateRequest *req) |
253 | 0 | { |
254 | 0 | return (cert_StartExtensions((void *)req, req->arena, setCRExt)); |
255 | 0 | } |
256 | | |
257 | | /* |
258 | | ** At entry req->attributes actually contains an list of cert extensions-- |
259 | | ** req-attributes is overloaded until the list is DER encoded (the first |
260 | | ** ...EncodeItem() below). |
261 | | ** We turn this into an attribute list by encapsulating it |
262 | | ** in a PKCS 10 Attribute structure |
263 | | */ |
264 | | SECStatus |
265 | | CERT_FinishCertificateRequestAttributes(CERTCertificateRequest *req) |
266 | 0 | { |
267 | 0 | SECItem *extlist; |
268 | 0 | SECOidData *oidrec; |
269 | 0 | CERTAttribute *attribute; |
270 | |
|
271 | 0 | if (!req || !req->arena) { |
272 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
273 | 0 | return SECFailure; |
274 | 0 | } |
275 | 0 | if (req->attributes == NULL || req->attributes[0] == NULL) |
276 | 0 | return SECSuccess; |
277 | | |
278 | 0 | extlist = SEC_ASN1EncodeItem(req->arena, NULL, &req->attributes, |
279 | 0 | SEC_ASN1_GET(CERT_SequenceOfCertExtensionTemplate)); |
280 | 0 | if (extlist == NULL) |
281 | 0 | return (SECFailure); |
282 | | |
283 | 0 | oidrec = SECOID_FindOIDByTag(SEC_OID_PKCS9_EXTENSION_REQUEST); |
284 | 0 | if (oidrec == NULL) |
285 | 0 | return SECFailure; |
286 | | |
287 | | /* now change the list of cert extensions into a list of attributes |
288 | | */ |
289 | 0 | req->attributes = PORT_ArenaZNewArray(req->arena, CERTAttribute *, 2); |
290 | |
|
291 | 0 | attribute = PORT_ArenaZNew(req->arena, CERTAttribute); |
292 | |
|
293 | 0 | if (req->attributes == NULL || attribute == NULL || |
294 | 0 | SECITEM_CopyItem(req->arena, &attribute->attrType, &oidrec->oid) != 0) { |
295 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
296 | 0 | return SECFailure; |
297 | 0 | } |
298 | 0 | attribute->attrValue = PORT_ArenaZNewArray(req->arena, SECItem *, 2); |
299 | |
|
300 | 0 | if (attribute->attrValue == NULL) |
301 | 0 | return SECFailure; |
302 | | |
303 | 0 | attribute->attrValue[0] = extlist; |
304 | 0 | attribute->attrValue[1] = NULL; |
305 | 0 | req->attributes[0] = attribute; |
306 | 0 | req->attributes[1] = NULL; |
307 | |
|
308 | 0 | return SECSuccess; |
309 | 0 | } |
310 | | |
311 | | SECStatus |
312 | | CERT_GetCertificateRequestExtensions(CERTCertificateRequest *req, |
313 | | CERTCertExtension ***exts) |
314 | 0 | { |
315 | 0 | if (req == NULL || exts == NULL) { |
316 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
317 | 0 | return SECFailure; |
318 | 0 | } |
319 | | |
320 | 0 | if (req->attributes == NULL || *req->attributes == NULL) |
321 | 0 | return SECSuccess; |
322 | | |
323 | 0 | if ((*req->attributes)->attrValue == NULL) { |
324 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
325 | 0 | return SECFailure; |
326 | 0 | } |
327 | | |
328 | 0 | return (SEC_ASN1DecodeItem(req->arena, exts, |
329 | 0 | SEC_ASN1_GET(CERT_SequenceOfCertExtensionTemplate), |
330 | 0 | (*req->attributes)->attrValue[0])); |
331 | 0 | } |