Mercurial > ~mikael > mcabber > hg
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... */ |