/[winpt]/trunk/Src/wptGPGUtil.cpp
ViewVC logotype

Contents of /trunk/Src/wptGPGUtil.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 133 - (show annotations)
Mon Jan 9 09:15:29 2006 UTC (19 years, 1 month ago) by twoaday
File size: 17444 byte(s)
A lot of minor bug fixes.
New icons.

For a complete history, see the ChangeLog entries.


1 /* wptGPGUtil.cpp - GPG util functions
2 * Copyright (C) 2005, 2006 Timo Schulz
3 * Copyright (C) 2005 g10 Code GmbH
4 *
5 * This file is part of WinPT.
6 *
7 * WinPT 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
10 * (at your option) any later version.
11 *
12 * WinPT is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with WinPT; if not, write to the Free Software Foundation,
19 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <windows.h>
27 #include <sys/stat.h>
28
29 #include "gpgme.h"
30 #include "wptTypes.h"
31 #include "wptErrors.h"
32
33 #define NROFHEXDIGITS 2
34 /* Convert two hexadecimal digits from STR to the value they
35 represent. Returns -1 if one of the characters is not a
36 hexadecimal digit. */
37 static int
38 hextobyte (const unsigned char *str)
39 {
40 int val = 0;
41 int i;
42
43 for (i = 0; i < NROFHEXDIGITS; i++) {
44 if (*str >= '0' && *str <= '9')
45 val += *str - '0';
46 else if (*str >= 'A' && *str <= 'F')
47 val += 10 + *str - 'A';
48 else if (*str >= 'a' && *str <= 'f')
49 val += 10 + *str - 'a';
50 else
51 return -1;
52 if (i < NROFHEXDIGITS - 1)
53 val *= 16;
54 str++;
55 }
56 return val;
57 }
58
59 /* Decode the C formatted string @src and store the result in the
60 buffer @destp which is @len bytes long. If @len is zero, then a
61 large enough buffer is allocated with malloc and @destp is set to
62 the result. Currently, @len is only used to specify if allocation
63 is desired or not, the caller is expected to make sure that @destp
64 is large enough if @len is not zero. */
65 gpgme_error_t
66 gpg_decode_c_string (const char *src, char **destp, size_t len)
67 {
68 char *dest;
69
70 /* Set up the destination buffer. */
71 if (len) {
72 if (len < strlen (src) + 1)
73 return gpg_error (GPG_ERR_TOO_SHORT);
74 dest = *destp;
75 }
76 else {
77 /* The converted string will never be larger than the original string. */
78 dest = (char*)malloc (strlen (src) + 1);
79 if (!dest)
80 return gpg_error (GPG_ERR_ENOMEM);
81 *destp = dest;
82 }
83
84 /* Convert the string. */
85 while (*src) {
86 if (*src != '\\') {
87 *(dest++) = *(src++);
88 continue;
89 }
90
91 switch (src[1]) {
92 #define DECODE_ONE(match,result) \
93 case match: \
94 src += 2; \
95 *(dest++) = result; \
96 break;
97
98 DECODE_ONE ('\'', '\'');
99 DECODE_ONE ('\"', '\"');
100 DECODE_ONE ('\?', '\?');
101 DECODE_ONE ('\\', '\\');
102 DECODE_ONE ('a', '\a');
103 DECODE_ONE ('b', '\b');
104 DECODE_ONE ('f', '\f');
105 DECODE_ONE ('n', '\n');
106 DECODE_ONE ('r', '\r');
107 DECODE_ONE ('t', '\t');
108 DECODE_ONE ('v', '\v');
109
110 case 'x': {
111 int val = hextobyte ((unsigned char*)&src[2]);
112 if (val == -1) { /* Should not happen. */
113 *(dest++) = *(src++);
114 *(dest++) = *(src++);
115 if (*src)
116 *(dest++) = *(src++);
117 if (*src)
118 *(dest++) = *(src++);
119 }
120 else {
121 if (!val) {
122 /* A binary zero is not representable in a C string. */
123 *(dest++) = '\\';
124 *(dest++) = '0';
125 }
126 else
127 *((unsigned char *) dest++) = val;
128 src += 4;
129 }
130 }
131
132 default: /* Should not happen. */
133 {
134 *(dest++) = *(src++);
135 *(dest++) = *(src++);
136 }
137 }
138 }
139 *(dest++) = 0;
140 return 0;
141 }
142
143 /* Replace %foo% entries with its real values.
144 Return value: expanded path or NULL on error. */
145 static char *
146 expand_path (const char *path)
147 {
148 DWORD len;
149 char *p;
150
151 len = ExpandEnvironmentStrings (path, NULL, 0);
152 if (!len)
153 return NULL;
154 len += 1;
155 p = (char*)calloc (1, len+1);
156 if (!p)
157 BUG (NULL);
158 len = ExpandEnvironmentStrings (path, p, len);
159 if (!len) {
160 free (p);
161 return NULL;
162 }
163 return p;
164 }
165
166
167 /* Read a string from the W32 registry. The directory is given
168 in @dir and the name of the value in @name, */
169 static char *
170 read_w32_registry (HKEY root_key, const char *dir, const char *name)
171 {
172 HKEY key_handle;
173 DWORD n1, nbytes;
174 DWORD type;
175 char *result = NULL;
176
177 if (RegOpenKeyEx (root_key, dir, 0, KEY_READ, &key_handle))
178 return NULL; /* no need for a RegClose, so return direct */
179
180 nbytes = 1;
181 if (RegQueryValueEx (key_handle, name, 0, NULL, NULL, &nbytes))
182 goto leave;
183 result = (char*)calloc (1, (n1=nbytes+1));
184 if (!result)
185 BUG (NULL);
186 if (RegQueryValueEx (key_handle, name, 0, &type, (BYTE*)result, &n1)) {
187 free (result);
188 result = NULL;
189 goto leave;
190 }
191 if (type == REG_EXPAND_SZ && strchr (result, '%')) {
192 char *p = expand_path (result);
193 free (result);
194 result = p;
195 }
196
197 leave:
198 RegCloseKey (key_handle);
199 return result;
200 }
201
202 static char*
203 read_gpg_program (void)
204 {
205 return read_w32_registry (HKEY_CURRENT_USER,
206 "Software\\GNU\\GnuPG", "gpgProgram");
207 }
208
209
210 /* Create a temp file based on the name of @name.
211 Return value: handle to the file in case of success. */
212 static HANDLE
213 create_tmpfile (const char *name)
214 {
215 HANDLE out;
216 SECURITY_ATTRIBUTES sec_attr;
217 char tmp[300];
218
219 memset (&sec_attr, 0, sizeof sec_attr);
220 sec_attr.bInheritHandle = TRUE;
221 sec_attr.lpSecurityDescriptor = NULL;
222 sec_attr.nLength = sizeof sec_attr;
223
224 GetTempPath (sizeof (tmp)-1 - strlen (name)-1, tmp);
225 strcat (tmp, name);
226 out = CreateFile (tmp, GENERIC_READ|GENERIC_WRITE,
227 FILE_SHARE_WRITE, &sec_attr,
228 OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL);
229 if (out == INVALID_HANDLE_VALUE)
230 log_debug ("create_tmpfile: CreateFile failed ec=%d\r\n",
231 (int)GetLastError ());
232 return out;
233 }
234
235
236 /* Create a pipe with a readable remote end and
237 write the data from @dat to the local end.
238 Return value: read handle on success. */
239 static HANDLE
240 create_in_pipe (const char *dat)
241 {
242 HANDLE r, w;
243 SECURITY_ATTRIBUTES sec_attr;
244 DWORD n;
245
246 memset (&sec_attr, 0, sizeof sec_attr);
247 sec_attr.bInheritHandle = TRUE;
248 sec_attr.nLength = sizeof sec_attr;
249
250 if (!CreatePipe (&r, &w, &sec_attr, 4096)) {
251 log_debug ("create_in_pipe: CreatePipeFailed ec=%d\r\n",
252 (int)GetLastError ());
253 return NULL;
254 }
255
256 WriteFile (w, dat, strlen (dat), &n, NULL);
257 CloseHandle (w);
258
259 return r;
260 }
261
262
263 /* Map the contents of the file handle @out to
264 a buffer and return it. */
265 static char*
266 map_tmpfile (HANDLE out, DWORD *nread)
267 {
268 DWORD n;
269 char *p;
270
271 FlushFileBuffers (out);
272 SetFilePointer (out, 0, NULL, FILE_BEGIN);
273 n = GetFileSize (out, NULL);
274 p = (char*)calloc (1, n+1);
275 if (!p)
276 BUG (NULL);
277 ReadFile (out, p, n, &n, NULL);
278 p[n] = 0;
279 if (nread)
280 *nread = n;
281 return p;
282 }
283
284
285 /* Create a process from the command line in @cmd.
286 If @out is != NULL, the output of the process will
287 be redirected to @out. If @in is != NULL the input
288 will be read from @in.
289 Return value: 0 on success. */
290 static int
291 create_process (const char *cmd, HANDLE in, HANDLE out, HANDLE err)
292 {
293 STARTUPINFO si;
294 PROCESS_INFORMATION pi;
295
296 memset (&si, 0, sizeof (si));
297 si.cb = sizeof si;
298 if (in || out || err)
299 si.dwFlags = STARTF_USESTDHANDLES;
300 if (out)
301 si.hStdOutput = out;
302 if (in)
303 si.hStdInput = in;
304 if (err)
305 si.hStdError = err;
306 si.dwFlags |= STARTF_USESHOWWINDOW;
307 si.wShowWindow = SW_HIDE;
308 if (!CreateProcess (NULL, (char*)cmd, NULL, NULL, TRUE, 0,
309 NULL, NULL, &si, &pi)) {
310 log_debug ("create_process: CreateProcess failed ec=%d\r\n",
311 (int)GetLastError ());
312 return -1;
313 }
314 WaitForSingleObject (pi.hProcess, INFINITE);
315 CloseHandle (pi.hProcess);
316 return 0;
317 }
318
319
320 /* Export a GPG secret key given by @keyid into the file @outfile.
321 Return value: 0 on success. */
322 gpgme_error_t
323 gpg_export_seckey (const char *keyid, const char *outfile)
324 {
325 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
326 struct stat st;
327 char *p;
328 char *cmd;
329
330 p = read_gpg_program ();
331 if (!p)
332 return gpg_error (GPG_ERR_INV_ARG);
333 cmd = (char*)calloc (1, strlen (p) + strlen (keyid)
334 + strlen (outfile) + 64 + 2);
335 if (!cmd)
336 BUG (NULL);
337 sprintf (cmd, "%s --yes --output \"%s\" --export-secret-key %s",
338 p, outfile, keyid);
339 if (create_process (cmd, NULL, NULL, NULL))
340 err = gpg_error (GPG_ERR_INTERNAL);
341
342 if (stat (outfile, &st) == -1 || st.st_size == 0)
343 err = gpg_error (GPG_ERR_NO_DATA);
344
345 free (p);
346 free (cmd);
347 return err;
348 }
349
350
351 /* If @export is 1, export the ownertrust data to the
352 buffer @data. Otherwise import the ownertrust data from @data.
353 Return value: 0 on success. */
354 gpgme_error_t
355 gpg_manage_ownertrust (char **data, int do_export)
356 {
357 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
358 HANDLE out = NULL, in = NULL;
359 char *p;
360 char *cmd;
361
362 p = read_gpg_program ();
363 if (!p)
364 return gpg_error (GPG_ERR_INV_ARG);
365
366 cmd = (char*)calloc (1, strlen (p) + 64 + 1);
367 if (!cmd)
368 BUG (NULL);
369 sprintf (cmd, "%s %s", p,
370 do_export? "--export-ownertrust" : "--import-ownertrust");
371
372 if (do_export)
373 out = create_tmpfile ("gpg_ot_out");
374 else {
375 DWORD nw;
376 in = create_tmpfile ("gpg_ot_in");
377 WriteFile (in, *data, strlen (*data), &nw, NULL);
378 FlushFileBuffers (in);
379 /* XXX: need a rewind? */
380 }
381 if (create_process (cmd, in, out, NULL))
382 err = gpg_error (GPG_ERR_INTERNAL);
383
384 free (p);
385 free (cmd);
386
387 if (in)
388 CloseHandle (in);
389 if (out) {
390 *data = map_tmpfile (out, NULL);
391 CloseHandle (out);
392 }
393
394 return err;
395 }
396
397
398 /* Call gpg --rebuild-keydb-caches to speed up signature listings. */
399 gpgme_error_t
400 gpg_rebuild_cache (char **r_inf)
401 {
402 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
403 HANDLE out = NULL;
404 char *p;
405 char *cmd;
406
407 p = read_gpg_program ();
408 if (!p)
409 return gpg_error (GPG_ERR_INV_ARG);
410 cmd = (char*)calloc (1, strlen (p) + 64);
411 if (!cmd)
412 BUG (NULL);
413 sprintf (cmd, "%s --logger-fd=1 --rebuild-keydb-caches", p);
414
415 if (r_inf)
416 out = create_tmpfile ("gpg_rebuild_cache");
417
418 if (create_process (cmd, NULL, out, NULL))
419 err = gpg_error (GPG_ERR_INTERNAL);
420
421 if (r_inf)
422 *r_inf = map_tmpfile (out, NULL);
423 if (out)
424 CloseHandle (out);
425 free (p);
426 free (cmd);
427 return 0;
428 }
429
430
431 /* Call gpg --version to retrieve the 'about' information. */
432 gpgme_error_t
433 gpg_get_version (char **r_inf)
434 {
435 gpgme_error_t err= gpg_error (GPG_ERR_NO_ERROR);
436 HANDLE out;
437 char *p, *cmd;
438
439 p = read_gpg_program ();
440 if (!p)
441 return gpg_error (GPG_ERR_INV_ARG);
442 cmd = (char*)calloc (1, strlen (p) + 32);
443 if (!cmd)
444 BUG (NULL);
445 sprintf (cmd, "%s --version", p);
446
447 out = create_tmpfile ("gpg_out");
448 if (create_process (cmd, NULL, out, NULL))
449 err = gpg_error (GPG_ERR_INTERNAL);
450
451 free (p);
452 free (cmd);
453
454 *r_inf = map_tmpfile (out, NULL);
455 CloseHandle (out);
456 return err;
457 }
458
459
460 /* Return the colon file output of the given file @fname in @r_out.
461 Return value: 0 on success. */
462 gpgme_error_t
463 gpg_import_key_list (const char *fname, char **r_out)
464 {
465 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
466 char *cmd, *p;
467 HANDLE out;
468
469 p = read_gpg_program ();
470 if (!p)
471 return gpg_error (GPG_ERR_INV_ARG);
472
473 cmd = (char*)calloc (1, strlen (p) + strlen (fname) + 2+2 + 64);
474 if (!cmd)
475 BUG (NULL);
476 sprintf (cmd, "%s --fixed-list-mode --with-colons \"%s\"", p, fname);
477
478 out = create_tmpfile ("gpg_keys");
479 if (create_process (cmd, NULL, out, NULL))
480 err = gpg_error (GPG_ERR_INTERNAL);
481
482 free (p);
483 free (cmd);
484
485 *r_out = map_tmpfile (out, NULL);
486 CloseHandle (out);
487 return err;
488 }
489
490
491 char*
492 generate_revoke_input (int code, const char *cmt, const char *pass)
493 {
494 const char *fmt;
495 char *p;
496 size_t n;
497
498 fmt = "Y\n" /* gen_revoke.okay */
499 "%d\n" /* ask_revocation_reason.code */
500 "%s\n" /* ask_revocation_reason.text */
501 "%s" /* text != NULL '\n' otherwise '' */
502 "Y\n" /* ask_revocation_reason.okay */
503 "%s\n"; /* passphrase.enter. */
504 n = strlen (fmt) + 32;
505 if (pass)
506 n += strlen (pass) + 1;
507 if (cmt)
508 n += strlen (cmt) + 1;
509 p = (char*)calloc (1, n+1);
510 if (!p)
511 BUG (NULL);
512 sprintf (p, fmt, code, cmt? cmt : "", cmt? "\n" : "", pass? pass : "");
513 return p;
514 }
515
516
517 /* Generate a revocation certificate for the key with the keyid @keyid.
518 @inp_data contains all needed data to answer the questions of the
519 command handler. Each separate with a '\n'.
520 @r_revcert contains the revocation cert on success.
521 Return value: 0 on success. */
522 gpgme_error_t
523 gpg_revoke_cert (const char *inp_data, const char *keyid, char **r_revcert)
524 {
525 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
526 char *rcrt;
527 char *cmd, *p;
528 HANDLE in, out;
529
530 p = read_gpg_program ();
531 if (!p)
532 return gpg_error (GPG_ERR_INV_ARG);
533
534 cmd = (char*)calloc (1, strlen (p) + strlen (keyid)+1 + 128);
535 if (!cmd)
536 BUG (NULL);
537 sprintf (cmd, "%s --pgp7 --command-fd=0 --status-fd=2 --gen-revoke %s",
538 p, keyid);
539
540 in = create_in_pipe (inp_data);
541 out = create_tmpfile ("gpg_revcert");
542 if (create_process (cmd, in, out, NULL)) {
543 *r_revcert = NULL;
544 err = gpg_error (GPG_ERR_INTERNAL);
545 }
546 else {
547 rcrt = map_tmpfile (out, NULL);
548 *r_revcert = rcrt;
549 if (rcrt && strlen (rcrt) == 0)
550 err = gpg_error (GPG_ERR_BAD_PASSPHRASE);
551 }
552
553 free (p);
554 free (cmd);
555
556 CloseHandle (in);
557 CloseHandle (out);
558 return err;
559 }
560
561
562 /* Return the raw photo-id data combined with the status-fd
563 entry in @r_data. If @keyid is set, only the data for this
564 key will be returned.
565 Return value: 0 on success. */
566 gpgme_error_t
567 gpg_get_photoid_data (const char *keyid, char **r_status_data,
568 unsigned char **r_data, unsigned long *ndata)
569 {
570 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
571 HANDLE herr, hdat;
572 char *p, *cmd;
573 DWORD n;
574
575 p = read_gpg_program ();
576 if (!p)
577 return gpg_error (GPG_ERR_INV_ARG);
578 n = strlen (p) + 128;
579 if (keyid)
580 n += strlen (keyid) + 1;
581 cmd = (char*)calloc (1, n+1);
582 if (!cmd)
583 BUG (NULL);
584 hdat = create_tmpfile ("gpg_uat_data");
585 herr = create_tmpfile ("gpg_uat_status");
586 /* XXX: add --list-options show-unsuable-uid to display
587 revoked attribute IDs */
588 sprintf (cmd, "%s --attribute-fd=%d --status-fd=2 --list-keys %s",
589 p, (int)hdat, keyid? keyid : "");
590 if (create_process (cmd, NULL, NULL, herr))
591 err = gpg_error (GPG_ERR_INTERNAL);
592
593 free (p);
594 free (cmd);
595
596 *r_data = (BYTE*)map_tmpfile (hdat, ndata);
597 *r_status_data = map_tmpfile (herr, NULL);
598 CloseHandle (hdat);
599 CloseHandle (herr);
600
601 return err;
602 }
603
604
605 /* Return the validity of the user attribute, informerly known
606 as photo-ID. If no uat was found, return 0 for unknown. */
607 gpgme_error_t
608 get_uat_validity (const char *keyid, gpgme_validity_t *r_valid)
609 {
610 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
611 HANDLE out;
612 char *p, *cmd;
613 char *uat;
614
615 *r_valid = GPGME_VALIDITY_UNKNOWN;
616 p = read_gpg_program ();
617 if (!p)
618 return gpg_error (GPG_ERR_INV_ARG);
619
620 cmd = (char*)calloc (1, strlen (p) + strlen (keyid)+1 + 128);
621 if (!cmd)
622 BUG (NULL);
623 sprintf (cmd, "%s --with-colons --fixed-list-mode --list-keys \"%s\"",
624 p, keyid);
625
626 out = create_tmpfile ("gpg_keys");
627 if (create_process (cmd, NULL, out, NULL))
628 err = gpg_error (GPG_ERR_INTERNAL);
629
630 free (p);
631 free (cmd);
632
633 p = map_tmpfile (out, NULL);
634 if ((uat = strstr (p, "uat:"))) {
635 switch (*(uat+4)) {
636 case 'm': *r_valid = GPGME_VALIDITY_MARGINAL; break;
637 case 'f':
638 case 'u': *r_valid = GPGME_VALIDITY_FULL; break;
639 default: *r_valid = GPGME_VALIDITY_UNDEFINED; break;
640 }
641 }
642
643 free (p);
644 CloseHandle (out);
645 return err;
646 }
647
648
649 #if 0
650 /* Extract all recipients from the file @file.
651 Return value: 0 on success. */
652 static int
653 do_get_recipients (const char *file, gpgme_recipient_t *r_list)
654 {
655 gpgme_recipient_t l;
656 PACKET *pkt;
657 gpg_iobuf_t inp = NULL;
658 armor_filter_context_t afx;
659 int rc = 0, quit=0;
660
661 if (!file || !r_list) {
662 log_debug ("do_list_packets: !r_list || !file");
663 return -1;
664 }
665
666 *r_list=NULL;
667 inp = gpg_iobuf_open (file);
668 if (!inp)
669 return WPTERR_FILE_OPEN;
670 gpg_iobuf_ioctl (inp, 3, 1, NULL); /* disable cache */
671 if (gpg_use_armor_filter (inp)) {
672 memset (&afx, 0, sizeof (afx));
673 gpg_iobuf_push_filter (inp, gpg_armor_filter, &afx);
674 }
675 pkt = (PACKET *)calloc(1, sizeof *pkt);
676 gpg_init_packet (pkt);
677 while (!quit && (rc = gpg_parse_packet (inp, pkt)) != -1) {
678 switch (pkt->pkttype) {
679 case PKT_PUBKEY_ENC:
680 {PKT_pubkey_enc *enc = pkt->pkt.pubkey_enc;
681 if (!enc)
682 break;
683 l = calloc (1, sizeof *l);
684 l->keyid = calloc (1, 16+1);
685 _snprintf (l->keyid, 16, "%08lX%08lX", enc->keyid[0], enc->keyid[1]);
686 l->pubkey_algo = enc->pubkey_algo;
687 l->status = 0;
688 l->next = (*r_list);
689 *r_list = l;
690 break;}
691
692 case PKT_ENCRYPTED:
693 case PKT_ENCRYPTED_MDC:
694 case PKT_COMPRESSED:
695 case PKT_PUBLIC_KEY:
696 case PKT_SECRET_KEY:
697 quit = 1;
698 break;
699 }
700 gpg_free_packet (pkt);
701 gpg_init_packet (pkt);
702 }
703 gpg_iobuf_close (inp);
704 safe_free (pkt);
705 return 0;
706 }
707 #endif

Properties

Name Value
svn:eol-style native

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26