Line data Source code
1 : /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 : /* egg-asn1.c - ASN.1 helper routines
3 :
4 : Copyright (C) 2007 Stefan Walter
5 :
6 : The Gnome Keyring Library is free software; you can redistribute it and/or
7 : modify it under the terms of the GNU Library General Public License as
8 : published by the Free Software Foundation; either version 2 of the
9 : License, or (at your option) any later version.
10 :
11 : The Gnome Keyring 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 : Library General Public License for more details.
15 :
16 : You should have received a copy of the GNU Library General Public
17 : License along with the Gnome Library; see the file COPYING.LIB. If not,
18 : <http://www.gnu.org/licenses/>.
19 :
20 : Author: Stef Walter <stef@memberwebs.com>
21 : */
22 :
23 : #include "config.h"
24 :
25 : #include "egg-asn1-defs.h"
26 : #include "egg-asn1x.h"
27 : #include "egg-dn.h"
28 : #include "egg-oid.h"
29 :
30 : #include <string.h>
31 :
32 : static const char HEXC[] = "0123456789ABCDEF";
33 :
34 : static gchar*
35 1 : dn_print_hex_value (GBytes *val)
36 : {
37 1 : const gchar *data = g_bytes_get_data (val, NULL);
38 1 : gsize size = g_bytes_get_size (val);
39 1 : GString *result = g_string_sized_new (size * 2 + 1);
40 : gsize i;
41 :
42 : g_string_append_c (result, '#');
43 29 : for (i = 0; i < size; ++i) {
44 28 : g_string_append_c (result, HEXC[data[i] >> 4 & 0xf]);
45 28 : g_string_append_c (result, HEXC[data[i] & 0xf]);
46 : }
47 :
48 1 : return g_string_free (result, FALSE);
49 : }
50 :
51 : static gchar*
52 24 : dn_print_oid_value_parsed (GQuark oid,
53 : guint flags,
54 : GNode *val)
55 : {
56 : GNode *asn1, *node;
57 : GBytes *value;
58 : const gchar *data;
59 : gsize size;
60 : gchar *result;
61 :
62 24 : g_assert (val != NULL);
63 :
64 24 : asn1 = egg_asn1x_create_quark (pkix_asn1_tab, oid);
65 24 : g_return_val_if_fail (asn1, NULL);
66 :
67 24 : if (!egg_asn1x_get_any_into (val, asn1)) {
68 0 : g_message ("couldn't decode value for OID: %s: %s",
69 : g_quark_to_string (oid), egg_asn1x_message (asn1));
70 0 : egg_asn1x_destroy (asn1);
71 0 : return NULL;
72 : }
73 :
74 : /*
75 : * If it's a choice element, then we have to read depending
76 : * on what's there.
77 : */
78 24 : if (flags & EGG_OID_IS_CHOICE)
79 20 : node = egg_asn1x_get_choice (asn1);
80 : else
81 4 : node = asn1;
82 :
83 24 : value = egg_asn1x_get_value_raw (node);
84 24 : data = g_bytes_get_data (value, &size);
85 :
86 : /*
87 : * Now we make sure it's UTF-8.
88 : */
89 :
90 24 : if (!value) {
91 0 : g_message ("couldn't read value for OID: %s", g_quark_to_string (oid));
92 0 : result = NULL;
93 :
94 24 : } else if (!g_utf8_validate (data, size, NULL)) {
95 0 : result = dn_print_hex_value (value);
96 :
97 : } else {
98 24 : result = g_strndup (data, size);
99 : }
100 :
101 24 : g_bytes_unref (value);
102 24 : egg_asn1x_destroy (asn1);
103 :
104 24 : return result;
105 : }
106 :
107 : static gchar*
108 25 : dn_print_oid_value (GQuark oid,
109 : guint flags,
110 : GNode *val)
111 : {
112 : GBytes *der;
113 : gchar *value;
114 :
115 25 : g_assert (val != NULL);
116 :
117 25 : if (flags & EGG_OID_PRINTABLE) {
118 24 : value = dn_print_oid_value_parsed (oid, flags, val);
119 24 : if (value != NULL)
120 24 : return value;
121 : }
122 :
123 1 : der = egg_asn1x_get_element_raw (val);
124 1 : value = dn_print_hex_value (der);
125 1 : g_bytes_unref (der);
126 :
127 1 : return value;
128 : }
129 :
130 : static gchar*
131 7 : dn_parse_rdn (GNode *asn)
132 : {
133 : const gchar *name;
134 : guint flags;
135 : GQuark oid;
136 : GNode *value;
137 : gchar *display;
138 : gchar *result;
139 :
140 7 : g_assert (asn);
141 :
142 7 : oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, "type", NULL));
143 7 : g_return_val_if_fail (oid, NULL);
144 :
145 7 : flags = egg_oid_get_flags (oid);
146 7 : name = egg_oid_get_name (oid);
147 :
148 7 : value = egg_asn1x_node (asn, "value", NULL);
149 7 : g_return_val_if_fail (value, NULL);
150 :
151 7 : display = dn_print_oid_value (oid, flags, value);
152 7 : result = g_strconcat ((flags & EGG_OID_PRINTABLE) ? name : g_quark_to_string (oid),
153 : "=", display, NULL);
154 7 : g_free (display);
155 :
156 7 : return result;
157 : }
158 :
159 : gchar*
160 1 : egg_dn_read (GNode* asn)
161 : {
162 1 : gboolean done = FALSE;
163 : GString *result;
164 : GNode *node;
165 : gchar *rdn;
166 : gint i, j;
167 :
168 1 : g_return_val_if_fail (asn, NULL);
169 :
170 1 : result = g_string_sized_new (64);
171 :
172 : /* Each (possibly multi valued) RDN */
173 9 : for (i = 1; !done; ++i) {
174 :
175 : /* Each type=value pair of an RDN */
176 8 : for (j = 1; TRUE; ++j) {
177 15 : node = egg_asn1x_node (asn, i, j, NULL);
178 15 : if (!node) {
179 8 : done = j == 1;
180 8 : break;
181 : }
182 :
183 7 : rdn = dn_parse_rdn (node);
184 7 : g_return_val_if_fail (rdn, NULL);
185 :
186 : /* Account for multi valued RDNs */
187 7 : if (j > 1)
188 0 : g_string_append (result, "+");
189 7 : else if (i > 1)
190 12 : g_string_append (result, ", ");
191 :
192 : g_string_append (result, rdn);
193 7 : g_free (rdn);
194 : }
195 : }
196 :
197 : /* Returns null when string is empty */
198 1 : return g_string_free (result, (result->len == 0));
199 : }
200 :
201 : gchar*
202 12 : egg_dn_read_part (GNode *asn, const gchar *match)
203 : {
204 12 : gboolean done = FALSE;
205 : const gchar *name;
206 : GNode *node;
207 : GQuark oid;
208 : gint i, j;
209 :
210 12 : g_return_val_if_fail (asn, NULL);
211 12 : g_return_val_if_fail (match, NULL);
212 :
213 : /* Each (possibly multi valued) RDN */
214 57 : for (i = 1; !done; ++i) {
215 :
216 : /* Each type=value pair of an RDN */
217 54 : for (j = 1; TRUE; ++j) {
218 96 : node = egg_asn1x_node (asn, i, j, "type", NULL);
219 96 : if (!node) {
220 45 : done = j == 1;
221 45 : break;
222 : }
223 :
224 51 : oid = egg_asn1x_get_oid_as_quark (node);
225 51 : g_return_val_if_fail (oid, NULL);
226 :
227 : /* Does it match either the OID or the displayable? */
228 51 : if (g_ascii_strcasecmp (g_quark_to_string (oid), match) != 0) {
229 50 : name = egg_oid_get_name (oid);
230 50 : if (g_ascii_strcasecmp (name, match) != 0)
231 42 : continue;
232 : }
233 :
234 9 : node = egg_asn1x_node (asn, i, j, "value", NULL);
235 9 : g_return_val_if_fail (node, NULL);
236 :
237 9 : return dn_print_oid_value (oid, egg_oid_get_flags (oid), node);
238 : }
239 : }
240 :
241 3 : return NULL;
242 : }
243 :
244 : gboolean
245 1 : egg_dn_parse (GNode *asn, EggDnCallback callback, gpointer user_data)
246 : {
247 1 : gboolean done = FALSE;
248 : GNode *node;
249 : GQuark oid;
250 : guint i, j;
251 :
252 1 : g_return_val_if_fail (asn, FALSE);
253 :
254 : /* Each (possibly multi valued) RDN */
255 9 : for (i = 1; !done; ++i) {
256 :
257 : /* Each type=value pair of an RDN */
258 8 : for (j = 1; TRUE; ++j) {
259 :
260 : /* Dig out the type */
261 15 : node = egg_asn1x_node (asn, i, j, "type", NULL);
262 15 : if (!node) {
263 8 : done = j == 1;
264 8 : break;
265 : }
266 :
267 7 : oid = egg_asn1x_get_oid_as_quark (node);
268 7 : g_return_val_if_fail (oid, FALSE);
269 :
270 : /* Dig out the value */
271 7 : node = egg_asn1x_node (asn, i, j, "value", NULL);
272 7 : if (!node) {
273 0 : done = j == 1;
274 0 : break;
275 : }
276 :
277 7 : if (callback)
278 7 : (callback) (i, oid, node, user_data);
279 : }
280 : }
281 :
282 1 : return i > 1;
283 : }
284 :
285 : gchar *
286 9 : egg_dn_print_value (GQuark oid,
287 : GNode *value)
288 : {
289 9 : g_return_val_if_fail (oid != 0, NULL);
290 9 : g_return_val_if_fail (value != NULL, NULL);
291 :
292 9 : return dn_print_oid_value (oid, egg_oid_get_flags (oid), value);
293 : }
294 :
295 : static gboolean
296 0 : is_ascii_string (const gchar *string)
297 : {
298 0 : const gchar *p = string;
299 :
300 0 : g_return_val_if_fail (string != NULL, FALSE);
301 :
302 0 : for (p = string; *p != '\0'; p++) {
303 0 : if (!g_ascii_isspace (*p) && *p < ' ')
304 0 : return FALSE;
305 : }
306 :
307 0 : return TRUE;
308 : }
309 :
310 : static gboolean
311 5 : is_printable_string (const gchar *string)
312 : {
313 5 : const gchar *p = string;
314 :
315 5 : g_return_val_if_fail (string != NULL, FALSE);
316 :
317 100 : for (p = string; *p != '\0'; p++) {
318 95 : if (!g_ascii_isalnum (*p) && !strchr (" '()+,-./:=?", *p))
319 0 : return FALSE;
320 : }
321 :
322 5 : return TRUE;
323 : }
324 :
325 : void
326 7 : egg_dn_add_string_part (GNode *asn,
327 : GQuark oid,
328 : const gchar *string)
329 : {
330 : GNode *node;
331 : GNode *value;
332 : GNode *val;
333 : guint flags;
334 :
335 7 : g_return_if_fail (asn != NULL);
336 7 : g_return_if_fail (oid != 0);
337 7 : g_return_if_fail (string != NULL);
338 :
339 7 : flags = egg_oid_get_flags (oid);
340 7 : g_return_if_fail (flags & EGG_OID_PRINTABLE);
341 :
342 : /* Add the RelativeDistinguishedName */
343 7 : node = egg_asn1x_append (asn);
344 :
345 : /* Add the AttributeTypeAndValue */
346 7 : node = egg_asn1x_append (node);
347 :
348 7 : egg_asn1x_set_oid_as_quark (egg_asn1x_node (node, "type", NULL), oid);
349 :
350 7 : value = egg_asn1x_create_quark (pkix_asn1_tab, oid);
351 :
352 7 : if (egg_asn1x_type (value) == EGG_ASN1X_CHOICE) {
353 5 : if (is_printable_string (string))
354 5 : val = egg_asn1x_node (value, "printableString", NULL);
355 0 : else if (is_ascii_string (string))
356 0 : val = egg_asn1x_node (value, "ia5String", NULL);
357 : else
358 0 : val = egg_asn1x_node (value, "utf8String", NULL);
359 5 : egg_asn1x_set_choice (value, val);
360 : } else {
361 2 : val = value;
362 : }
363 :
364 7 : egg_asn1x_set_string_as_utf8 (val, g_strdup (string), g_free);
365 :
366 7 : egg_asn1x_set_any_from (egg_asn1x_node (node, "value", NULL), value);
367 7 : egg_asn1x_destroy (value);
368 : }
|