~noskcaj/ubuntu/vivid/gnome-keyring/3.15.90

« back to all changes in this revision

Viewing changes to egg/egg-armor.c

  • Committer: Package Import Robot
  • Author(s): Jordi Mallach
  • Date: 2012-05-14 22:13:02 UTC
  • mfrom: (1.3.1)
  • mto: (80.2.8 experimental) (1.1.77)
  • mto: This revision was merged to the branch mainline in revision 148.
  • Revision ID: package-import@ubuntu.com-20120514221302-0l3gjmqpe6xopond
ImportĀ upstreamĀ versionĀ 3.4.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
 
2
/* egg-openssl.c - OpenSSL compatibility functionality
 
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
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
19
   Boston, MA 02111-1307, USA.
 
20
 
 
21
   Author: Stef Walter <stef@memberwebs.com>
 
22
*/
 
23
 
 
24
#include "config.h"
 
25
 
 
26
#include "egg-hex.h"
 
27
#include "egg-armor.h"
 
28
#include "egg-secure-memory.h"
 
29
 
 
30
#include <gcrypt.h>
 
31
 
 
32
#include <glib.h>
 
33
 
 
34
#include <ctype.h>
 
35
#include <string.h>
 
36
 
 
37
/*
 
38
 * Armor looks like:
 
39
 *
 
40
 *      -----BEGIN RSA PRIVATE KEY-----
 
41
 *      Proc-Type: 4,ENCRYPTED
 
42
 *      DEK-Info: DES-EDE3-CBC,704CFFD62FBA03E9
 
43
 *
 
44
 *      4AV/g0BiTeb07hzo4/Ct47HGhHEshMhBPGJ843QzuAinpZBbg3OxwPsQsLgoPhJL
 
45
 *      Bg6Oxyz9M4UN1Xlx6Lyo2lRT908mBP6dl/OItLsVArqAzM+e29KHQVNjV1h7xN9F
 
46
 *      u84tOgZftKun+ZkQUOoRvMLLu4yV4CUraks9tgyXquugGba/tbeyj2MYsC8wwSJX
 
47
 *      ................................................................
 
48
 *      =on29
 
49
 *      -----END RSA PRIVATE KEY-----
 
50
 *
 
51
 * The last line before END is an option OpenPGP armor checksum
 
52
 */
 
53
 
 
54
EGG_SECURE_DECLARE (armor);
 
55
 
 
56
#define ARMOR_SUFF          "-----"
 
57
#define ARMOR_SUFF_L        5
 
58
#define ARMOR_PREF_BEGIN    "-----BEGIN "
 
59
#define ARMOR_PREF_BEGIN_L  11
 
60
#define ARMOR_PREF_END      "-----END "
 
61
#define ARMOR_PREF_END_L    9
 
62
 
 
63
static void
 
64
parse_header_lines (const gchar *hbeg,
 
65
                    const gchar *hend,
 
66
                    GHashTable **result)
 
67
{
 
68
        gchar **lines, **l;
 
69
        gchar *line, *name, *value;
 
70
        gchar *copy;
 
71
 
 
72
        copy = g_strndup (hbeg, hend - hbeg);
 
73
        lines = g_strsplit (copy, "\n", 0);
 
74
        g_free (copy);
 
75
 
 
76
        for (l = lines; l && *l; ++l) {
 
77
                line = *l;
 
78
                g_strstrip (line);
 
79
 
 
80
                /* Look for the break between name: value */
 
81
                value = strchr (line, ':');
 
82
                if (value == NULL)
 
83
                        continue;
 
84
 
 
85
                *value = 0;
 
86
                value = g_strdup (value + 1);
 
87
                g_strstrip (value);
 
88
 
 
89
                name = g_strdup (line);
 
90
                g_strstrip (name);
 
91
 
 
92
                if (!*result)
 
93
                        *result = egg_armor_headers_new ();
 
94
                g_hash_table_replace (*result, name, value);
 
95
        }
 
96
 
 
97
        g_strfreev (lines);
 
98
}
 
99
 
 
100
static const gchar*
 
101
armor_find_begin (const gchar *data,
 
102
                  gsize n_data,
 
103
                  GQuark *type,
 
104
                  const gchar **outer)
 
105
{
 
106
        const gchar *pref, *suff;
 
107
        gchar *stype;
 
108
 
 
109
        /* Look for a prefix */
 
110
        pref = g_strstr_len ((gchar*)data, n_data, ARMOR_PREF_BEGIN);
 
111
        if (!pref)
 
112
                return NULL;
 
113
 
 
114
        n_data -= (pref - data) + ARMOR_PREF_BEGIN_L;
 
115
        data = pref + ARMOR_PREF_BEGIN_L;
 
116
 
 
117
        /* Look for the end of that begin */
 
118
        suff = g_strstr_len ((gchar*)data, n_data, ARMOR_SUFF);
 
119
        if (!suff)
 
120
                return NULL;
 
121
 
 
122
        /* Make sure on the same line */
 
123
        if (memchr (pref, '\n', suff - pref))
 
124
                return NULL;
 
125
 
 
126
        if (outer)
 
127
                *outer = pref;
 
128
 
 
129
        if (type) {
 
130
                *type = 0;
 
131
                pref += ARMOR_PREF_BEGIN_L;
 
132
                g_assert (suff > pref);
 
133
                stype = g_alloca (suff - pref + 1);
 
134
                memcpy (stype, pref, suff - pref);
 
135
                stype[suff - pref] = 0;
 
136
                *type = g_quark_from_string (stype);
 
137
        }
 
138
 
 
139
        /* The byte after this ---BEGIN--- */
 
140
        return suff + ARMOR_SUFF_L;
 
141
}
 
142
 
 
143
static const gchar*
 
144
armor_find_end (const gchar *data,
 
145
                gsize n_data,
 
146
                GQuark type,
 
147
                const gchar **outer)
 
148
{
 
149
        const gchar *stype;
 
150
        const gchar *pref;
 
151
        const gchar *line;
 
152
        gsize n_type;
 
153
 
 
154
        /* Look for a prefix */
 
155
        pref = g_strstr_len (data, n_data, ARMOR_PREF_END);
 
156
        if (!pref)
 
157
                return NULL;
 
158
 
 
159
        n_data -= (pref - data) + ARMOR_PREF_END_L;
 
160
        data = pref + ARMOR_PREF_END_L;
 
161
 
 
162
        /* Next comes the type string */
 
163
        stype = g_quark_to_string (type);
 
164
        n_type = strlen (stype);
 
165
        if (strncmp ((gchar*)data, stype, n_type) != 0)
 
166
                return NULL;
 
167
 
 
168
        n_data -= n_type;
 
169
        data += n_type;
 
170
 
 
171
        /* Next comes the suffix */
 
172
        if (strncmp ((gchar*)data, ARMOR_SUFF, ARMOR_SUFF_L) != 0)
 
173
                return NULL;
 
174
 
 
175
        /*
 
176
         * Check if there's a OpenPGP style armor checksum line. OpenPGP
 
177
         * does not insist that we validate this line, and is more useful
 
178
         * for PGP messages, rather than the keys we usually see.
 
179
         */
 
180
        line = memrchr (data, '\n', (pref - 1) - data);
 
181
        if (line && line[1] == '=')
 
182
                pref = line;
 
183
 
 
184
        if (outer != NULL) {
 
185
                data += ARMOR_SUFF_L;
 
186
                if (isspace (data[0]))
 
187
                        data++;
 
188
                *outer = data;
 
189
        }
 
190
 
 
191
        /* The end of the data */
 
192
        return pref;
 
193
}
 
194
 
 
195
static gboolean
 
196
armor_parse_block (const gchar *data,
 
197
                   gsize n_data,
 
198
                   guchar **decoded,
 
199
                   gsize *n_decoded,
 
200
                   GHashTable **headers)
 
201
{
 
202
        const gchar *x, *hbeg, *hend;
 
203
        const gchar *p, *end;
 
204
        gint state = 0;
 
205
        guint save = 0;
 
206
 
 
207
        g_assert (data);
 
208
        g_assert (n_data);
 
209
 
 
210
        g_assert (decoded);
 
211
        g_assert (n_decoded);
 
212
 
 
213
        p = data;
 
214
        end = p + n_data;
 
215
 
 
216
        hbeg = hend = NULL;
 
217
 
 
218
        /* Try and find a pair of blank lines with only white space between */
 
219
        while (hend == NULL) {
 
220
                x = memchr (p, '\n', end - p);
 
221
                if (!x)
 
222
                        break;
 
223
                ++x;
 
224
                while (isspace (*x)) {
 
225
                        /* Found a second line, with only spaces between */
 
226
                        if (*x == '\n') {
 
227
                                hbeg = data;
 
228
                                hend = x;
 
229
                                break;
 
230
                        /* Found a space between two lines */
 
231
                        } else {
 
232
                                ++x;
 
233
                        }
 
234
                }
 
235
 
 
236
                /* Try next line */
 
237
                p = x;
 
238
        }
 
239
 
 
240
        /* Headers found? */
 
241
        if (hbeg && hend) {
 
242
                data = hend;
 
243
                n_data = end - data;
 
244
        }
 
245
 
 
246
        *n_decoded = (n_data * 3) / 4 + 1;
 
247
        if (egg_secure_check (data))
 
248
                *decoded = egg_secure_alloc (*n_decoded);
 
249
        else
 
250
                *decoded = g_malloc0 (*n_decoded);
 
251
        g_return_val_if_fail (*decoded, FALSE);
 
252
 
 
253
        *n_decoded = g_base64_decode_step (data, n_data, *decoded, &state, &save);
 
254
        if (!*n_decoded) {
 
255
                egg_secure_free (*decoded);
 
256
                return FALSE;
 
257
        }
 
258
 
 
259
        if (headers && hbeg && hend)
 
260
                parse_header_lines (hbeg, hend, headers);
 
261
 
 
262
        return TRUE;
 
263
}
 
264
 
 
265
GHashTable*
 
266
egg_armor_headers_new (void)
 
267
{
 
268
        return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 
269
}
 
270
 
 
271
guint
 
272
egg_armor_parse (gconstpointer data,
 
273
                 gsize n_data,
 
274
                 EggArmorCallback callback,
 
275
                 gpointer user_data)
 
276
{
 
277
        const gchar *beg, *end;
 
278
        const gchar *outer_beg, *outer_end;
 
279
        guint nfound = 0;
 
280
        guchar *decoded = NULL;
 
281
        gsize n_decoded = 0;
 
282
        GHashTable *headers = NULL;
 
283
        GQuark type;
 
284
 
 
285
        g_return_val_if_fail (data, 0);
 
286
        g_return_val_if_fail (n_data, 0);
 
287
 
 
288
        while (n_data > 0) {
 
289
 
 
290
                /* This returns the first character after the PEM BEGIN header */
 
291
                beg = armor_find_begin ((const gchar*)data, n_data, &type, &outer_beg);
 
292
                if (beg == NULL)
 
293
                        break;
 
294
 
 
295
                g_assert (type);
 
296
 
 
297
                /* This returns the character position before the PEM END header */
 
298
                end = armor_find_end ((const gchar*)beg,
 
299
                                      n_data - ((const gchar*)beg - (const gchar *)data),
 
300
                                      type, &outer_end);
 
301
                if (end == NULL)
 
302
                        break;
 
303
 
 
304
                if (beg != end) {
 
305
                        if (armor_parse_block (beg, end - beg, &decoded, &n_decoded, &headers)) {
 
306
                                g_assert (outer_end > outer_beg);
 
307
                                if (callback != NULL)
 
308
                                        (callback) (type,
 
309
                                                    decoded, n_decoded,
 
310
                                                    outer_beg, outer_end - outer_beg,
 
311
                                                    headers, user_data);
 
312
                                ++nfound;
 
313
                                egg_secure_free (decoded);
 
314
                                if (headers)
 
315
                                        g_hash_table_remove_all (headers);
 
316
                        }
 
317
                }
 
318
 
 
319
                /* Try for another block */
 
320
                end += ARMOR_SUFF_L;
 
321
                n_data -= (const gchar*)end - (const gchar*)data;
 
322
                data = end;
 
323
        }
 
324
 
 
325
        if (headers)
 
326
                g_hash_table_destroy (headers);
 
327
 
 
328
        return nfound;
 
329
}
 
330
 
 
331
static void
 
332
append_each_header (gpointer key, gpointer value, gpointer user_data)
 
333
{
 
334
        GString *string = (GString*)user_data;
 
335
 
 
336
        g_string_append (string, (gchar*)key);
 
337
        g_string_append (string, ": ");
 
338
        g_string_append (string, (gchar*)value);
 
339
        g_string_append_c (string, '\n');
 
340
}
 
341
 
 
342
guchar*
 
343
egg_armor_write (const guchar *data,
 
344
                 gsize n_data,
 
345
                 GQuark type,
 
346
                 GHashTable *headers,
 
347
                 gsize *n_result)
 
348
{
 
349
        GString *string;
 
350
        gint state, save;
 
351
        gsize i, length;
 
352
        gsize n_prefix, estimate;
 
353
 
 
354
        g_return_val_if_fail (data || !n_data, NULL);
 
355
        g_return_val_if_fail (type, NULL);
 
356
        g_return_val_if_fail (n_result, NULL);
 
357
 
 
358
        string = g_string_sized_new (4096);
 
359
 
 
360
        /* The prefix */
 
361
        g_string_append_len (string, ARMOR_PREF_BEGIN, ARMOR_PREF_BEGIN_L);
 
362
        g_string_append (string, g_quark_to_string (type));
 
363
        g_string_append_len (string, ARMOR_SUFF, ARMOR_SUFF_L);
 
364
        g_string_append_c (string, '\n');
 
365
 
 
366
        /* The headers */
 
367
        if (headers && g_hash_table_size (headers) > 0) {
 
368
                g_hash_table_foreach (headers, append_each_header, string);
 
369
                g_string_append_c (string, '\n');
 
370
        }
 
371
 
 
372
        /* Resize string to fit the base64 data. Algorithm from Glib reference */
 
373
        estimate = n_data * 4 / 3 + n_data * 4 / (3 * 65) + 7;
 
374
        n_prefix = string->len;
 
375
        g_string_set_size (string, n_prefix + estimate);
 
376
 
 
377
        /* The actual base64 data, without line breaks */
 
378
        state = save = 0;
 
379
        length = g_base64_encode_step (data, n_data, FALSE,
 
380
                                       string->str + n_prefix, &state, &save);
 
381
        length += g_base64_encode_close (TRUE, string->str + n_prefix + length,
 
382
                                         &state, &save);
 
383
 
 
384
        g_assert (length <= estimate);
 
385
        g_string_set_size (string, n_prefix + length);
 
386
 
 
387
        /*
 
388
         * OpenSSL is absolutely certain that it wants its PEM base64
 
389
         * lines to be 64 characters in length. So go through and break
 
390
         * those lines up.
 
391
         */
 
392
 
 
393
        for (i = 64; i < length; i += 64) {
 
394
                g_string_insert_c (string, n_prefix + i, '\n');
 
395
                ++length;
 
396
                ++i;
 
397
        }
 
398
 
 
399
        /* The suffix */
 
400
        g_string_append_len (string, ARMOR_PREF_END, ARMOR_PREF_END_L);
 
401
        g_string_append (string, g_quark_to_string (type));
 
402
        g_string_append_len (string, ARMOR_SUFF, ARMOR_SUFF_L);
 
403
        g_string_append_c (string, '\n');
 
404
 
 
405
        *n_result = string->len;
 
406
        return (guchar*)g_string_free (string, FALSE);
 
407
}