Improved the certificate generation stuff.
[gnutls:gnutls.git] / lib / x509 / extensions.c
1 /*
2  *  Copyright (C) 2003 Nikos Mavroyanopoulos
3  *
4  *  This file is part of GNUTLS.
5  *
6  *  The GNUTLS library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public   
8  *  License as published by the Free Software Foundation; either 
9  *  version 2.1 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of 
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
19  *
20  */
21
22 /* Functions that relate to the X.509 extension parsing.
23  */
24
25 #include <gnutls_int.h>
26 #include <gnutls_errors.h>
27 #include <gnutls_global.h>
28 #include <libtasn1.h>
29 #include <common.h>
30 #include <x509.h>
31
32 /* This function will attempt to return the requested extension found in
33  * the given X509v3 certificate. The return value is allocated and stored into
34  * ret.
35  *
36  * Critical will be either 0 or 1.
37  *
38  * If the extension does not exist, GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will
39  * be returned.
40  */
41 int _gnutls_x509_crt_get_extension( gnutls_x509_crt cert, const char* extension_id, 
42         int indx, gnutls_datum* ret, unsigned int * _critical)
43 {
44         int k, result, len;
45         char name[128], name2[128], counter[MAX_INT_DIGITS];
46         char str[1024];
47         char str_critical[10];
48         int critical = 0;
49         char extnID[128];
50         char extnValue[256];
51         int indx_counter = 0;
52
53         ret->data = NULL;
54         ret->size = 0;
55         
56         k = 0;
57         do {
58                 k++;
59
60                 _gnutls_str_cpy(name, sizeof(name), "tbsCertificate.extensions.?"); 
61                 _gnutls_int2str(k, counter); 
62                 _gnutls_str_cat(name, sizeof(name), counter); 
63
64                 len = sizeof(str) - 1;
65                 result = asn1_read_value(cert->cert, name, str, &len);
66
67                 /* move to next
68                  */
69
70                 if (result == ASN1_ELEMENT_NOT_FOUND) {
71                         break;
72                 }
73
74                 do {
75
76                         _gnutls_str_cpy(name2, sizeof(name2), name);
77                         _gnutls_str_cat(name2, sizeof(name2), ".extnID"); 
78
79                         len = sizeof(extnID) - 1;
80                         result =
81                             asn1_read_value(cert->cert, name2, extnID, &len);
82
83                         if (result == ASN1_ELEMENT_NOT_FOUND) {
84                                 gnutls_assert();
85                                 break;
86                         } else if (result != ASN1_SUCCESS) {
87                                 gnutls_assert();
88                                 return _gnutls_asn2err(result);
89                         }
90
91                         _gnutls_str_cpy(name2, sizeof(name2), name);
92                         _gnutls_str_cat(name2, sizeof(name2), ".critical"); 
93
94                         len = sizeof(str_critical);
95                         result =
96                             asn1_read_value(cert->cert, name2, str_critical, &len);
97
98                         if (result == ASN1_ELEMENT_NOT_FOUND) {
99                                 gnutls_assert();
100                                 break;
101                         } else if (result != ASN1_SUCCESS) {
102                                 gnutls_assert();
103                                 return _gnutls_asn2err(result);
104                         }
105
106                         if (strcmp( str_critical, "TRUE")==0)
107                                 critical = 1;
108                         else critical = 0;
109
110                         _gnutls_str_cpy(name2, sizeof(name2), name);
111                         _gnutls_str_cat(name2, sizeof(name2), ".extnValue"); 
112
113                         len = sizeof(extnValue) - 1;
114                         result =
115                             asn1_read_value(cert->cert, name2, extnValue, &len);
116
117                         if (result == ASN1_ELEMENT_NOT_FOUND)
118                                 break;
119                         else {
120                                 if (result == ASN1_MEM_ERROR
121                                     && critical == 0) {
122
123                                         _gnutls_x509_log
124                                             ("X509_EXT: Cannot parse extension: %s. Too small buffer.",
125                                              extnID);
126
127                                         continue;
128                                 }
129                                 if (result != ASN1_SUCCESS) {
130                                         gnutls_assert();
131                                         return _gnutls_asn2err(result);
132                                 }
133                         }
134
135                         /* Handle Extension */
136                         if ( strcmp(extnID, extension_id)==0 && indx == indx_counter++) { 
137                                 /* extension was found */
138
139                                 ret->data = gnutls_malloc( len);
140                                 if (ret->data==NULL)
141                                         return GNUTLS_E_MEMORY_ERROR;   
142
143                                 ret->size = len;
144                                 memcpy( ret->data, extnValue, len);
145                                 
146                                 if (_critical)
147                                         *_critical = critical;
148                                 
149                                 return 0;
150                         }
151
152
153                 } while (0);
154         } while (1);
155
156         if (result == ASN1_ELEMENT_NOT_FOUND) {
157                 return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
158         } else {
159                 gnutls_assert();
160                 return _gnutls_asn2err(result);
161         }
162 }
163
164 /* This function will attempt to set the requested extension in
165  * the given X509v3 certificate. 
166  *
167  * Critical will be either 0 or 1.
168  */
169 int _gnutls_x509_crt_set_extension( gnutls_x509_crt cert, const char* extension_id, 
170         const gnutls_datum* ext_data, unsigned int critical)
171 {
172         int result;
173         const char *str;
174
175         /* Add a new extension in the list.
176          */
177         result = asn1_write_value(cert->cert, "tbsCertificate.extensions", "NEW", 1);
178         if (result != ASN1_SUCCESS) {
179                 gnutls_assert();
180                 return _gnutls_asn2err(result);
181         }
182
183         result = asn1_write_value(cert->cert, "tbsCertificate.extensions.?LAST.extnID", extension_id, 1);
184         if (result != ASN1_SUCCESS) {
185                 gnutls_assert();
186                 return _gnutls_asn2err(result);
187         }
188
189         if (critical==0) str = "FALSE";
190         else str = "TRUE";
191         
192
193         result = asn1_write_value(cert->cert, "tbsCertificate.extensions.?LAST.critical", str, 1);
194         if (result != ASN1_SUCCESS) {
195                 gnutls_assert();
196                 return _gnutls_asn2err(result);
197         }
198
199         result = _gnutls_x509_write_value( cert->cert, "tbsCertificate.extensions.?LAST.extnValue",
200                 ext_data, 0);
201         if (result < 0) {
202                 gnutls_assert();
203                 return result;
204         }
205         
206         return 0;
207 }
208
209
210 /* Here we only extract the KeyUsage field, from the DER encoded
211  * extension.
212  */
213 int _gnutls_x509_ext_extract_keyUsage(uint16 *keyUsage, opaque * extnValue,
214                              int extnValueLen)
215 {
216         ASN1_TYPE ext = ASN1_TYPE_EMPTY;
217         char str[10];
218         int len, result;
219
220         keyUsage[0] = 0;
221
222         if ((result=asn1_create_element
223             (_gnutls_get_pkix(), "PKIX1.KeyUsage", &ext
224              )) != ASN1_SUCCESS) {
225                 gnutls_assert();
226                 return _gnutls_asn2err(result);
227         }
228
229         result = asn1_der_decoding(&ext, extnValue, extnValueLen, NULL);
230
231         if (result != ASN1_SUCCESS) {
232                 gnutls_assert();
233                 asn1_delete_structure(&ext);
234                 return 0;
235         }
236
237         len = sizeof(str) - 1;
238         result = asn1_read_value(ext, "", str, &len);
239         if (result != ASN1_SUCCESS) {
240                 gnutls_assert();
241                 asn1_delete_structure(&ext);
242                 return 0;
243         }
244
245         keyUsage[0] = str[0];
246
247         asn1_delete_structure(&ext);
248
249         return 0;
250 }
251
252 /* extract the basicConstraints from the DER encoded extension
253  */
254 int _gnutls_x509_ext_extract_basicConstraints(int *CA, opaque * extnValue,
255                                      int extnValueLen)
256 {
257         ASN1_TYPE ext = ASN1_TYPE_EMPTY;
258         char str[128];
259         int len, result;
260
261         *CA = 0;
262
263         if ((result=asn1_create_element
264             (_gnutls_get_pkix(), "PKIX1.BasicConstraints", &ext
265              )) != ASN1_SUCCESS) {
266                 gnutls_assert();
267                 return _gnutls_asn2err(result);
268         }
269
270         result = asn1_der_decoding(&ext, extnValue, extnValueLen, NULL);
271
272         if (result != ASN1_SUCCESS) {
273                 gnutls_assert();
274                 asn1_delete_structure(&ext);
275                 return 0;
276         }
277
278         len = sizeof(str) - 1;
279         result = asn1_read_value(ext, "cA", str, &len);
280         if (result != ASN1_SUCCESS) {
281                 gnutls_assert();
282                 asn1_delete_structure(&ext);
283                 return 0;
284         }
285
286         asn1_delete_structure(&ext);
287
288         if (strcmp(str, "TRUE") == 0)
289                 *CA = 1;
290         else
291                 *CA = 0;
292
293
294         return 0;
295 }
296
297 /* generate the basicConstraints in a DER encoded extension
298  * Use 0 or 1 (TRUE) for CA.
299  */
300 int _gnutls_x509_ext_gen_basicConstraints(int CA, gnutls_datum* der_ext)
301 {
302         ASN1_TYPE ext = ASN1_TYPE_EMPTY;
303         const char *str;
304         int result;
305
306         if (CA == 0) str = "FALSE";
307         else str = "TRUE";
308
309         result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.BasicConstraints", &ext);
310         if (result != ASN1_SUCCESS) {
311                 gnutls_assert();
312                 return _gnutls_asn2err(result);
313         }
314
315         result = asn1_write_value(ext, "cA", str, 1);
316         if (result != ASN1_SUCCESS) {
317                 gnutls_assert();
318                 asn1_delete_structure(&ext);
319                 return _gnutls_asn2err(result);
320         }
321
322         asn1_write_value(ext, "pathLenConstraint", NULL, 0);
323         
324         result = _gnutls_x509_der_encode( ext, "", der_ext, 0);
325
326         asn1_delete_structure(&ext);
327
328         if (result < 0) {
329                 gnutls_assert();
330                 return result;
331         }
332
333         return 0;
334 }
335
336 /* generate the subject alternative name in a DER encoded extension
337  */
338 int _gnutls_x509_ext_gen_subject_alt_name(gnutls_x509_subject_alt_name type, 
339         const char* data_string, gnutls_datum* der_ext)
340 {
341         ASN1_TYPE ext = ASN1_TYPE_EMPTY;
342         const char *str;
343         char name[128];
344         int result;
345
346         result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.SubjectAltName", &ext);
347         if (result != ASN1_SUCCESS) {
348                 gnutls_assert();
349                 return _gnutls_asn2err(result);
350         }
351
352         result = asn1_write_value( ext, "", "NEW", 1);
353         if (result != ASN1_SUCCESS) {
354                 gnutls_assert();
355                 return _gnutls_asn2err(result);
356         }
357
358         switch(type) {
359                 case GNUTLS_SAN_DNSNAME:
360                         str = "dNSName";
361                         break;
362                 case GNUTLS_SAN_RFC822NAME:
363                         str = "rfc822Name";
364                         break;
365                 case GNUTLS_SAN_URI:
366                         str = "uniformResourceIdentifier";
367                         break;
368                 case GNUTLS_SAN_IPADDRESS:
369                         str = "iPAddress";
370                         break;
371                 default:
372                         gnutls_assert();
373                         return GNUTLS_E_INTERNAL_ERROR;
374         }
375
376         result = asn1_write_value( ext, "?LAST", str, 1);
377         if (result != ASN1_SUCCESS) {
378                 gnutls_assert();
379                 return _gnutls_asn2err(result);
380         }
381
382         _gnutls_str_cpy( name, sizeof(name), "?LAST.");
383         _gnutls_str_cat( name, sizeof(name), str);
384
385         result = asn1_write_value(ext, name, data_string, strlen(data_string));
386         if (result != ASN1_SUCCESS) {
387                 gnutls_assert();
388                 asn1_delete_structure(&ext);
389                 return _gnutls_asn2err(result);
390         }
391
392         result = _gnutls_x509_der_encode( ext, "", der_ext, 0);
393
394         asn1_delete_structure(&ext);
395
396         if (result < 0) {
397                 gnutls_assert();
398                 return result;
399         }
400
401         return 0;
402 }