1 /*
2  * gnome-keyring
3  *
4  * Copyright (C) 2011 Collabora Ltd.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * 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 program; if not, see <http://www.gnu.org/licenses/>.
18  *
19  * Author: Stef Walter <stefw@collabora.co.uk>
20  */
21 #include "config.h"
22 
23 #include "gcr-certificate-extensions.h"
24 
25 #include "gcr/gcr-oids.h"
26 
27 #include "egg/egg-asn1x.h"
28 #include "egg/egg-asn1-defs.h"
29 #include "egg/egg-dn.h"
30 
31 #include <glib/gi18n-lib.h>
32 
33 GBytes *
_gcr_certificate_extension_find(GNode * cert,GQuark oid,gboolean * critical)34 _gcr_certificate_extension_find (GNode *cert,
35                                  GQuark oid,
36                                  gboolean *critical)
37 {
38 	GNode *node;
39 	gint index;
40 
41 	g_return_val_if_fail (cert != NULL, NULL);
42 
43 	/* Extensions */
44 	for (index = 1; TRUE; ++index) {
45 		node = egg_asn1x_node (cert, "tbsCertificate", "extensions", index, NULL);
46 		if (node == NULL)
47 			return NULL;
48 
49 		/* Dig out the OID */
50 		if (egg_asn1x_get_oid_as_quark (egg_asn1x_node (node, "extnID", NULL)) == oid) {
51 
52 			if (critical) {
53 				if (!egg_asn1x_get_boolean (egg_asn1x_node (node, "critical", NULL), critical))
54 					g_return_val_if_reached (NULL);
55 			}
56 
57 			/* Extension value */
58 			return egg_asn1x_get_string_as_bytes (egg_asn1x_node (node, "extnValue", NULL));
59 		}
60 	}
61 
62 	g_assert_not_reached ();
63 }
64 
65 gboolean
_gcr_certificate_extension_basic_constraints(GBytes * data,gboolean * is_ca,gint * path_len)66 _gcr_certificate_extension_basic_constraints (GBytes *data,
67                                               gboolean *is_ca,
68                                               gint *path_len)
69 {
70 	gboolean ret = TRUE;
71 	GNode *asn = NULL;
72 	GNode *node;
73 	gulong value;
74 
75 	g_return_val_if_fail (data != NULL, FALSE);
76 
77 	asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "BasicConstraints", data);
78 	if (asn == NULL)
79 		return FALSE;
80 
81 	if (path_len) {
82 		node = egg_asn1x_node (asn, "pathLenConstraint", NULL);
83 		if (!egg_asn1x_have (node))
84 			*path_len = -1;
85 		else if (!egg_asn1x_get_integer_as_ulong (node, &value))
86 			ret = FALSE;
87 		else
88 			*path_len = value;
89 	}
90 
91 	if (is_ca) {
92 		node = egg_asn1x_node (asn, "cA", NULL);
93 		if (!egg_asn1x_have (node))
94 			*is_ca = FALSE;
95 		else if (!egg_asn1x_get_boolean (node, is_ca))
96 			ret = FALSE;
97 	}
98 
99 	egg_asn1x_destroy (asn);
100 	return ret;
101 }
102 
103 GQuark *
_gcr_certificate_extension_extended_key_usage(GBytes * data)104 _gcr_certificate_extension_extended_key_usage (GBytes *data)
105 {
106 	GNode *asn = NULL;
107 	GNode *node;
108 	GArray *array;
109 	GQuark oid;
110 	int i;
111 
112 	g_return_val_if_fail (data != NULL, NULL);
113 
114 	asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "ExtKeyUsageSyntax", data);
115 	if (asn == NULL)
116 		return NULL;
117 
118 	array = g_array_new (TRUE, TRUE, sizeof (GQuark));
119 	for (i = 0; TRUE; ++i) {
120 		node = egg_asn1x_node (asn, i + 1, NULL);
121 		if (node == NULL)
122 			break;
123 		oid = egg_asn1x_get_oid_as_quark (node);
124 		g_array_append_val (array, oid);
125 	}
126 
127 	egg_asn1x_destroy (asn);
128 	return (GQuark*)g_array_free (array, FALSE);
129 }
130 
131 gpointer
_gcr_certificate_extension_subject_key_identifier(GBytes * data,gsize * n_keyid)132 _gcr_certificate_extension_subject_key_identifier (GBytes *data,
133                                                    gsize *n_keyid)
134 {
135 	GNode *asn = NULL;
136 	gpointer result;
137 
138 	g_return_val_if_fail (data != NULL, NULL);
139 
140 	asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "SubjectKeyIdentifier", data);
141 	if (asn == NULL)
142 		return NULL;
143 
144 	result = egg_asn1x_get_string_as_raw (asn, g_realloc, n_keyid);
145 	egg_asn1x_destroy (asn);
146 
147 	return result;
148 }
149 
150 static gulong
_gcr_reverse_bits(gulong num,guint n_bits)151 _gcr_reverse_bits(gulong num, guint n_bits)
152 {
153     gulong reverse_num = 0;
154     guint i;
155     for (i = 0; i < n_bits; i++) {
156         if ((num & (1 << i)))
157             reverse_num |= 1 << ((n_bits - 1) - i);
158     }
159     return reverse_num;
160 }
161 
162 gboolean
_gcr_certificate_extension_key_usage(GBytes * data,gulong * key_usage)163 _gcr_certificate_extension_key_usage (GBytes *data,
164                                       gulong *key_usage)
165 {
166 	GNode *asn = NULL;
167 	gboolean ret = TRUE;
168 	guint n_bits;
169 
170 	g_return_val_if_fail (data != NULL, FALSE);
171 
172 	asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "KeyUsage", data);
173 	if (asn == NULL)
174 		return FALSE;
175 
176 	ret = egg_asn1x_get_bits_as_ulong (asn, key_usage, &n_bits);
177 	egg_asn1x_destroy (asn);
178 	*key_usage = _gcr_reverse_bits(*key_usage, n_bits);
179 	return ret;
180 }
181 
182 static void
general_name_parse_other(GNode * node,GcrGeneralName * general)183 general_name_parse_other (GNode *node, GcrGeneralName *general)
184 {
185 	GNode *decode = NULL;
186 	GQuark oid;
187 	GNode *any;
188 
189 	general->type = GCR_GENERAL_NAME_OTHER;
190 	general->description = _("Other Name");
191 	general->display = NULL;
192 
193 	oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (node, "type-id", NULL));
194 	any = egg_asn1x_node (node, "value", NULL);
195 
196 	if (any == NULL)
197 		return;
198 
199 	if (oid == GCR_OID_ALT_NAME_XMPP_ADDR) {
200 		general->description = _("XMPP Addr");
201 		decode = egg_asn1x_get_any_as_string (any, EGG_ASN1X_UTF8_STRING);
202 		general->display = egg_asn1x_get_string_as_utf8 (decode, g_realloc);
203 	} else if (oid == GCR_OID_ALT_NAME_DNS_SRV) {
204 		general->description = _("DNS SRV");
205 		decode = egg_asn1x_get_any_as_string (any, EGG_ASN1X_IA5_STRING);
206 		general->display = egg_asn1x_get_string_as_utf8 (decode, g_realloc);
207 	}
208 
209 	egg_asn1x_destroy (decode);
210 }
211 
212 static void
general_name_parse_rfc822(GNode * node,GcrGeneralName * general)213 general_name_parse_rfc822 (GNode *node, GcrGeneralName *general)
214 {
215 	general->type = GCR_GENERAL_NAME_RFC822;
216 	general->description = _("Email");
217 	general->display = egg_asn1x_get_string_as_utf8 (node, g_realloc);
218 }
219 
220 static void
general_name_parse_dns(GNode * node,GcrGeneralName * general)221 general_name_parse_dns (GNode *node, GcrGeneralName *general)
222 {
223 	general->type = GCR_GENERAL_NAME_DNS;
224 	general->description = _("DNS");
225 	general->display = egg_asn1x_get_string_as_utf8 (node, g_realloc);
226 }
227 
228 static void
general_name_parse_x400(GNode * node,GcrGeneralName * general)229 general_name_parse_x400 (GNode *node, GcrGeneralName *general)
230 {
231 	general->type = GCR_GENERAL_NAME_X400;
232 	general->description = _("X400 Address");
233 }
234 
235 static void
general_name_parse_dn(GNode * node,GcrGeneralName * general)236 general_name_parse_dn (GNode *node, GcrGeneralName *general)
237 {
238 	general->type = GCR_GENERAL_NAME_DNS;
239 	general->description = _("Directory Name");
240 	general->display = egg_dn_read (node);
241 }
242 
243 static void
general_name_parse_edi(GNode * node,GcrGeneralName * general)244 general_name_parse_edi (GNode *node, GcrGeneralName *general)
245 {
246 	general->type = GCR_GENERAL_NAME_EDI;
247 	general->description = _("EDI Party Name");
248 }
249 
250 static void
general_name_parse_uri(GNode * node,GcrGeneralName * general)251 general_name_parse_uri (GNode *node, GcrGeneralName *general)
252 {
253 	general->type = GCR_GENERAL_NAME_URI;
254 	general->description = _("URI");
255 	general->display = egg_asn1x_get_string_as_utf8 (node, g_realloc);
256 }
257 
258 static void
general_name_parse_ip(GNode * node,GcrGeneralName * general)259 general_name_parse_ip (GNode *node, GcrGeneralName *general)
260 {
261 	general->type = GCR_GENERAL_NAME_IP;
262 	general->description = _("IP Address");
263 	general->display = egg_asn1x_get_string_as_utf8 (node, g_realloc);
264 }
265 
266 static void
general_name_parse_registered(GNode * node,GcrGeneralName * general)267 general_name_parse_registered (GNode *node, GcrGeneralName *general)
268 {
269 	general->type = GCR_GENERAL_NAME_REGISTERED_ID;
270 	general->description = _("Registered ID");
271 	general->display = egg_asn1x_get_oid_as_string (node);
272 }
273 
274 GArray*
_gcr_certificate_extension_subject_alt_name(GBytes * data)275 _gcr_certificate_extension_subject_alt_name (GBytes *data)
276 {
277 	GNode *asn = NULL;
278 	guint count, i;
279 	const gchar *node_name;
280 	GArray *names;
281 	GcrGeneralName general;
282 	GNode *choice;
283 
284 	asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "SubjectAltName", data);
285 	if (asn == NULL)
286 		return NULL;
287 
288 	names = g_array_new (FALSE, TRUE, sizeof (GcrGeneralName));
289 	count = egg_asn1x_count (asn);
290 
291 	for (i = 0; i < count; i++) {
292 		choice = egg_asn1x_get_choice (egg_asn1x_node (asn, i + 1, NULL));
293 		g_return_val_if_fail (choice, NULL);
294 
295 		node_name = egg_asn1x_name (choice);
296 		g_return_val_if_fail (node_name, NULL);
297 
298 		memset (&general, 0, sizeof (general));
299 
300 		if (g_str_equal (node_name, "otherName"))
301 			general_name_parse_other (choice, &general);
302 
303 		else if (g_str_equal (node_name, "rfc822Name"))
304 			general_name_parse_rfc822 (choice, &general);
305 
306 		else if (g_str_equal (node_name, "dNSName"))
307 			general_name_parse_dns (choice, &general);
308 
309 		else if (g_str_equal (node_name, "x400Address"))
310 			general_name_parse_x400 (choice, &general);
311 
312 		else if (g_str_equal (node_name, "directoryName"))
313 			general_name_parse_dn (choice, &general);
314 
315 		else if (g_str_equal (node_name, "ediPartyName"))
316 			general_name_parse_edi (choice, &general);
317 
318 		else if (g_str_equal (node_name, "uniformResourceIdentifier"))
319 			general_name_parse_uri (choice, &general);
320 
321 		else if (g_str_equal (node_name, "iPAddress"))
322 			general_name_parse_ip (choice, &general);
323 
324 		else if (g_str_equal (node_name, "registeredID"))
325 			general_name_parse_registered (choice, &general);
326 
327 		general.raw = egg_asn1x_get_element_raw (choice);
328 		g_array_append_val (names, general);
329 	}
330 
331 	egg_asn1x_destroy (asn);
332 	return names;
333 }
334 
335 void
_gcr_general_names_free(GArray * names)336 _gcr_general_names_free (GArray *names)
337 {
338 	GcrGeneralName *name;
339 	guint i;
340 
341 	for (i = 0; names && i < names->len; i++) {
342 		name = &g_array_index (names, GcrGeneralName, i);
343 		g_free (name->display);
344 		g_bytes_unref (name->raw);
345 	}
346 	g_array_free (names, TRUE);
347 }
348