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

Contents of /trunk/Src/wptGPGUtil.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 34 - (show annotations)
Wed Oct 26 11:20:09 2005 UTC (19 years, 4 months ago) by twoaday
File size: 16407 byte(s)
2005-10-25  Timo Schulz  <twoaday@g10code.com>
                                                                                
        * wptGPGUtil.cpp (create_process): Hide window.
        * wptKeyPropsDlg.cpp (get_photo_tmpname): New.
        * wptClipSignEncDlg.cpp (clip_signenc_dlg_proc): Remove
        static var 'enable'.
        * wptKeygenDlg.cpp (keygen_dlg_proc): Likewise.
        (gpg_genkey_params): Make sure all primary keys are capable
        for signing and certification.
        * wptKeySigDlg.cpp (is_sig): If no item is selected, return 0.
        * wptGPG.cpp (gnupg_access_keyring): Check return value for
        NULL. Noted by Ralf.
        (get_gnupg_prog): Simplified.
        (check_homedir): Fixed. Return 0 when the dir is successfully created.
        * wptKeyManagerDlg.cpp (km_file_import): Use the hourglass to
        indicate a pending GPG process.
        * wptFileManager.cpp (op_begin, op_end): New. Indicate an start
        and and of an operation. For now just the cursor changes.
        (fm_parse_command_line): Remove debug output. Thanks to Ralf again.
        * WinPT.cpp (WinMain): Check if there is already an instance and
        set a variable early as possible.
        (load_gettext): If a previous instance was found, do not output
        any errors. Kudos to Ralf.


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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26