comparison mcabber/mcabber/pgp.c @ 1668:41c26b7d2890

Install mcabber headers * Change mcabber headers naming scheme * Move 'src/' -> 'mcabber/' * Add missing include <mcabber/config.h>'s * Create and install clean config.h version in 'include/' * Move "dirty" config.h version to 'mcabber/' * Add $(top_srcdir) to compiler include path * Update modules HOWTO
author Myhailo Danylenko <isbear@ukrpost.net>
date Mon, 18 Jan 2010 15:36:19 +0200
parents mcabber/src/pgp.c@dcd5d4c75199
children e6d355e50d7a
comparison
equal deleted inserted replaced
1667:8af0e0ad20ad 1668:41c26b7d2890
1 /*
2 * pgp.c -- PGP utility functions
3 *
4 * Copyright (C) 2006-2009 Mikael Berthe <mikael@lilotux.net>
5 * Some parts inspired by centericq (impgp.cc)
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or (at
10 * your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * USA
21 */
22
23 #include <config.h>
24
25 #ifdef HAVE_GPGME
26
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <locale.h>
31 #include <sys/mman.h>
32 #include <glib.h>
33
34 #include "pgp.h"
35 #include "logprint.h"
36
37 #define MIN_GPGME_VERSION "1.0.0"
38
39 static struct gpg_struct
40 {
41 int enabled;
42 char *private_key;
43 char *passphrase;
44 } gpg;
45
46
47 // gpg_init(priv_key, passphrase)
48 // Initialize the GPG sub-systems. This function must be invoked early.
49 // Note: priv_key & passphrase are optional, they can be set later.
50 // This function returns 0 if gpgme is available and initialized;
51 // if not it returns the gpgme error code.
52 int gpg_init(const char *priv_key, const char *passphrase)
53 {
54 gpgme_error_t err;
55
56 // Check for version and OpenPGP protocol support.
57 if (!gpgme_check_version(MIN_GPGME_VERSION)) {
58 scr_LogPrint(LPRINT_LOGNORM,
59 "GPGME initialization error: Bad library version");
60 return -1;
61 }
62
63 err = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
64 if (err) {
65 scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
66 "GPGME initialization error: %s", gpgme_strerror(err));
67 return err;
68 }
69
70 // Set the locale information.
71 gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
72 gpgme_set_locale(NULL, LC_MESSAGES, setlocale(LC_MESSAGES, NULL));
73
74 // Store private data.
75 gpg_set_private_key(priv_key);
76 gpg_set_passphrase(passphrase);
77
78 gpg.enabled = 1;
79 return 0;
80 }
81
82 // gpg_terminate()
83 // Destroy data and free memory.
84 void gpg_terminate(void)
85 {
86 gpg.enabled = 0;
87 gpg_set_passphrase(NULL);
88 gpg_set_private_key(NULL);
89 }
90
91 // gpg_set_passphrase(passphrase)
92 // Set the current passphrase (use NULL to erase it).
93 void gpg_set_passphrase(const char *passphrase)
94 {
95 // Remove current passphrase
96 if (gpg.passphrase) {
97 ssize_t len = strlen(gpg.passphrase);
98 memset(gpg.passphrase, 0, len);
99 munlock(gpg.passphrase, len);
100 g_free(gpg.passphrase);
101 }
102 if (passphrase) {
103 gpg.passphrase = g_strdup(passphrase);
104 mlock(gpg.passphrase, strlen(gpg.passphrase));
105 } else {
106 gpg.passphrase = NULL;
107 }
108 }
109
110 // gpg_set_private_key(keyid)
111 // Set the current private key id (use NULL to unset it).
112 void gpg_set_private_key(const char *priv_keyid)
113 {
114 g_free(gpg.private_key);
115 if (priv_keyid)
116 gpg.private_key = g_strdup(priv_keyid);
117 else
118 gpg.private_key = NULL;
119 }
120
121 // strip_header_footer(data)
122 // Remove PGP header & footer from data.
123 // Return a new string, or NULL.
124 // The string must be freed by the caller with g_free() when no longer needed.
125 static char *strip_header_footer(const char *data)
126 {
127 char *p, *q;
128
129 if (!data)
130 return NULL;
131
132 // p: beginning of real data
133 // q: end of real data
134
135 // Strip header (to the first empty line)
136 p = strstr(data, "\n\n");
137 if (!p)
138 return g_strdup(data);
139
140 // Strip footer
141 // We want to remove the last lines, until the line beginning with a '-'
142 p += 2;
143 for (q = p ; *q; q++) ;
144 // (q is at the end of data now)
145 for (q--; q > p && (*q != '\n' || *(q+1) != '-'); q--) ;
146
147 if (q <= p)
148 return NULL; // Shouldn't happen...
149
150 return g_strndup(p, q-p);
151 }
152
153 // GCC ignores casts to void, thus we need to hack around that
154 static inline void ignore(void*x) {}
155
156 // passphrase_cb()
157 // GPGME passphrase callback function.
158 static gpgme_error_t passphrase_cb(void *hook, const char *uid_hint,
159 const char *passphrase_info, int prev_was_bad, int fd)
160 {
161 ssize_t len;
162
163 // Abort if we do not have the password.
164 if (!gpg.passphrase) {
165 ignore((void*)write(fd, "\n", 1)); // We have an error anyway, thus it does
166 // not matter if we fail again.
167 return gpg_error(GPG_ERR_CANCELED);
168 }
169
170 // Write the passphrase to the file descriptor.
171 len = strlen(gpg.passphrase);
172 if (write(fd, gpg.passphrase, len) != len)
173 return gpg_error(GPG_ERR_CANCELED);
174 if (write(fd, "\n", 1) != 1)
175 return gpg_error(GPG_ERR_CANCELED);
176
177 return 0; // Success
178 }
179
180 // gpg_verify(gpg_data, text, *sigsum)
181 // Verify that gpg_data is a correct signature for text.
182 // Return the key id (or fingerprint), and set *sigsum to
183 // the gpgme signature summary value.
184 // The returned string must be freed with g_free() after use.
185 char *gpg_verify(const char *gpg_data, const char *text,
186 gpgme_sigsum_t *sigsum)
187 {
188 gpgme_ctx_t ctx;
189 gpgme_data_t data_sign, data_text;
190 char *data;
191 char *verified_key = NULL;
192 gpgme_key_t key;
193 gpgme_error_t err;
194 const char prefix[] = "-----BEGIN PGP SIGNATURE-----\n\n";
195 const char suffix[] = "\n-----END PGP SIGNATURE-----\n";
196
197 // Reset the summary.
198 *sigsum = 0;
199
200 if (!gpg.enabled)
201 return NULL;
202
203 err = gpgme_new(&ctx);
204 if (err) {
205 scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
206 "GPGME error: %s", gpgme_strerror(err));
207 return NULL;
208 }
209
210 gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
211
212 // Surround the given data with the prefix & suffix
213 data = g_new(char, sizeof(prefix) + sizeof(suffix) + strlen(gpg_data));
214 strcpy(data, prefix);
215 strcat(data, gpg_data);
216 strcat(data, suffix);
217
218 err = gpgme_data_new_from_mem(&data_sign, data, strlen(data), 0);
219 if (!err) {
220 err = gpgme_data_new_from_mem(&data_text, text, strlen(text), 0);
221 if (!err) {
222 err = gpgme_op_verify(ctx, data_sign, data_text, 0);
223 if (!err) {
224 gpgme_verify_result_t vr = gpgme_op_verify_result(ctx);
225 if (vr && vr->signatures) {
226 char *r = vr->signatures->fpr;
227 // Found the fingerprint. Let's try to get the key id.
228 if (!gpgme_get_key(ctx, r, &key, 0) && key) {
229 r = key->subkeys->keyid;
230 gpgme_key_release(key);
231 }
232 // r is a static variable, let's copy it.
233 verified_key = g_strdup(r);
234 *sigsum = vr->signatures->summary;
235 // For some reason summary could be 0 when status is 0 too,
236 // which means the signature is valid...
237 if (!*sigsum && !vr->signatures->status)
238 *sigsum = GPGME_SIGSUM_GREEN;
239 }
240 }
241 gpgme_data_release(data_text);
242 }
243 gpgme_data_release(data_sign);
244 }
245 if (err)
246 scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
247 "GPGME verification error: %s", gpgme_strerror(err));
248 gpgme_release(ctx);
249 g_free(data);
250 return verified_key;
251 }
252
253 // gpg_sign(gpg_data)
254 // Return a signature of gpg_data (or NULL).
255 // The returned string must be freed with g_free() after use.
256 char *gpg_sign(const char *gpg_data)
257 {
258 gpgme_ctx_t ctx;
259 gpgme_data_t in, out;
260 char *p;
261 char *signed_data = NULL;
262 size_t nread;
263 gpgme_key_t key;
264 gpgme_error_t err;
265
266 if (!gpg.enabled || !gpg.private_key)
267 return NULL;
268
269 err = gpgme_new(&ctx);
270 if (err) {
271 scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
272 "GPGME error: %s", gpgme_strerror(err));
273 return NULL;
274 }
275
276 gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
277 gpgme_set_textmode(ctx, 0);
278 gpgme_set_armor(ctx, 1);
279
280 p = getenv("GPG_AGENT_INFO");
281 if (!(p && strchr(p, ':')))
282 gpgme_set_passphrase_cb(ctx, passphrase_cb, 0);
283
284 err = gpgme_get_key(ctx, gpg.private_key, &key, 1);
285 if (err || !key) {
286 scr_LogPrint(LPRINT_LOGNORM, "GPGME error: private key not found");
287 gpgme_release(ctx);
288 return NULL;
289 }
290
291 gpgme_signers_clear(ctx);
292 gpgme_signers_add(ctx, key);
293 gpgme_key_release(key);
294 err = gpgme_data_new_from_mem(&in, gpg_data, strlen(gpg_data), 0);
295 if (!err) {
296 err = gpgme_data_new(&out);
297 if (!err) {
298 err = gpgme_op_sign(ctx, in, out, GPGME_SIG_MODE_DETACH);
299 if (!err) {
300 signed_data = gpgme_data_release_and_get_mem(out, &nread);
301 if (signed_data) {
302 // We need to add a trailing NULL
303 char *dd = g_strndup(signed_data, nread);
304 free(signed_data);
305 signed_data = strip_header_footer(dd);
306 g_free(dd);
307 }
308 } else {
309 gpgme_data_release(out);
310 }
311 }
312 gpgme_data_release(in);
313 }
314 if (err && err != GPG_ERR_CANCELED)
315 scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
316 "GPGME signature error: %s", gpgme_strerror(err));
317 gpgme_release(ctx);
318 return signed_data;
319 }
320
321 // gpg_decrypt(gpg_data)
322 // Return decrypted gpg_data (or NULL).
323 // The returned string must be freed with g_free() after use.
324 char *gpg_decrypt(const char *gpg_data)
325 {
326 gpgme_ctx_t ctx;
327 gpgme_data_t in, out;
328 char *p, *data;
329 char *decrypted_data = NULL;
330 size_t nread;
331 gpgme_error_t err;
332 const char prefix[] = "-----BEGIN PGP MESSAGE-----\n\n";
333 const char suffix[] = "\n-----END PGP MESSAGE-----\n";
334
335 if (!gpg.enabled)
336 return NULL;
337
338 err = gpgme_new(&ctx);
339 if (err) {
340 scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
341 "GPGME error: %s", gpgme_strerror(err));
342 return NULL;
343 }
344
345 gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
346
347 p = getenv("GPG_AGENT_INFO");
348 if (!(p && strchr(p, ':')))
349 gpgme_set_passphrase_cb(ctx, passphrase_cb, 0);
350
351 // Surround the given data with the prefix & suffix
352 data = g_new(char, sizeof(prefix) + sizeof(suffix) + strlen(gpg_data));
353 strcpy(data, prefix);
354 strcat(data, gpg_data);
355 strcat(data, suffix);
356
357 err = gpgme_data_new_from_mem(&in, data, strlen(data), 0);
358 if (!err) {
359 err = gpgme_data_new(&out);
360 if (!err) {
361 err = gpgme_op_decrypt(ctx, in, out);
362 if (!err) {
363 decrypted_data = gpgme_data_release_and_get_mem(out, &nread);
364 if (decrypted_data) {
365 // We need to add a trailing NULL
366 char *dd = g_strndup(decrypted_data, nread);
367 free(decrypted_data);
368 decrypted_data = dd;
369 }
370 } else {
371 gpgme_data_release(out);
372 }
373 }
374 gpgme_data_release(in);
375 }
376 if (err && err != GPG_ERR_CANCELED)
377 scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
378 "GPGME decryption error: %s", gpgme_strerror(err));
379 gpgme_release(ctx);
380 g_free(data);
381 return decrypted_data;
382 }
383
384 // gpg_encrypt(gpg_data, keyid)
385 // Return encrypted gpg_data with the key keyid (or NULL).
386 // The returned string must be freed with g_free() after use.
387 char *gpg_encrypt(const char *gpg_data, const char *keyid)
388 {
389 gpgme_ctx_t ctx;
390 gpgme_data_t in, out;
391 char *encrypted_data = NULL, *edata;
392 size_t nread;
393 gpgme_key_t key;
394 gpgme_error_t err;
395
396 if (!gpg.enabled)
397 return NULL;
398
399 err = gpgme_new(&ctx);
400 if (err) {
401 scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
402 "GPGME error: %s", gpgme_strerror(err));
403 return NULL;
404 }
405
406 gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
407 gpgme_set_textmode(ctx, 0);
408 gpgme_set_armor(ctx, 1);
409
410 err = gpgme_get_key(ctx, keyid, &key, 0);
411 if (!err && key) {
412 gpgme_key_t keys[] = { key, 0 };
413 err = gpgme_data_new_from_mem(&in, gpg_data, strlen(gpg_data), 0);
414 if (!err) {
415 err = gpgme_data_new(&out);
416 if (!err) {
417 err = gpgme_op_encrypt(ctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST, in, out);
418 if (!err)
419 encrypted_data = gpgme_data_release_and_get_mem(out, &nread);
420 else
421 gpgme_data_release(out);
422 }
423 gpgme_data_release(in);
424 }
425 gpgme_key_release(key);
426 } else {
427 scr_LogPrint(LPRINT_LOGNORM, "GPGME encryption error: key not found");
428 err = 0;
429 }
430 if (err && err != GPG_ERR_CANCELED)
431 scr_LogPrint(LPRINT_LOGNORM|LPRINT_NOTUTF8,
432 "GPGME encryption error: %s", gpgme_strerror(err));
433 gpgme_release(ctx);
434 edata = strip_header_footer(encrypted_data);
435 if (encrypted_data)
436 free(encrypted_data);
437 return edata;
438 }
439
440 // gpg_test_passphrase()
441 // Test the current gpg.passphrase with gpg.private_key.
442 // If the test doesn't succeed, the passphrase is cleared and a non-null
443 // value is returned.
444 int gpg_test_passphrase(void)
445 {
446 char *s;
447
448 if (!gpg.private_key)
449 return -1; // No private key...
450
451 s = gpg_sign("test");
452 if (s) {
453 free(s);
454 return 0; // Ok, test successful
455 }
456 // The passphrase is wrong (if provided)
457 gpg_set_passphrase(NULL);
458 return -1;
459 }
460
461 int gpg_enabled(void)
462 {
463 return gpg.enabled;
464 }
465
466 #else /* not HAVE_GPGME */
467
468 int gpg_enabled(void)
469 {
470 return 0;
471 }
472
473 #endif /* HAVE_GPGME */
474
475 /* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */