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

Contents of /trunk/Src/wptGPGUtil.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 121 - (show annotations)
Mon Dec 12 11:19:56 2005 UTC (19 years, 2 months ago) by twoaday
File size: 16277 byte(s)
2005-12-11  Timo Schulz  <ts@g10code.com>
 
        * wptW32API.cpp (get_file_version): New.
        * wptGPGUtil.cpp (create_process): Always hide window.
        * wptClipEditDlg.cpp (clipedit_dlg_proc): Use 'Close'
        instead of 'Exit'.
        * wptKeyManager.cpp (km_http_import): New filename
        generation code.
        (km_send_to_mail_recipient): Cleanups.
        * wptKeyEditDlg.cpp (showpref_dlg_proc): Localize dialog.
        * wptKeyManagerDlg.cpp (update_ui_items): Handle the case
        when multiple keys are selected.
        (popup_multiple): New.
        * WinPT.cpp (WinMain): Check that the PTD.dll and WinPT.exe
        file versions are equal. Rewrote --keymanager code.
         
Removed temporary w32gpgme dirctory, all code is now in Src.
Changed configure files.


1 /* wptGPGUtil.cpp - GPG util functions
2 * Copyright (C) 2005 g10 Code GmbH
3 * Copyright (C) 2005 Timo Schulz
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
203 /* Create a temp file based on the name of @name.
204 Return value: handle to the file in case of success. */
205 static HANDLE
206 create_tmpfile (const char *name)
207 {
208 HANDLE out;
209 SECURITY_ATTRIBUTES sec_attr;
210 char tmp[300];
211
212 memset (&sec_attr, 0, sizeof sec_attr);
213 sec_attr.bInheritHandle = TRUE;
214 sec_attr.lpSecurityDescriptor = NULL;
215 sec_attr.nLength = sizeof sec_attr;
216
217 GetTempPath (sizeof (tmp)-1 - strlen (name)-1, tmp);
218 strcat (tmp, name);
219 out = CreateFile (tmp, GENERIC_READ|GENERIC_WRITE,
220 FILE_SHARE_WRITE, &sec_attr,
221 OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL);
222 if (out == INVALID_HANDLE_VALUE)
223 log_debug ("create_tmpfile: CreateFile failed ec=%d\r\n",
224 (int)GetLastError ());
225 return out;
226 }
227
228
229 /* Create a pipe with a readable remote end and
230 write the data from @dat to the local end.
231 Return value: read handle on success. */
232 static HANDLE
233 create_in_pipe (const char *dat)
234 {
235 HANDLE r, w;
236 SECURITY_ATTRIBUTES sec_attr;
237 DWORD n;
238
239 memset (&sec_attr, 0, sizeof sec_attr);
240 sec_attr.bInheritHandle = TRUE;
241 sec_attr.nLength = sizeof sec_attr;
242
243 if (!CreatePipe (&r, &w, &sec_attr, 4096)) {
244 log_debug ("create_in_pipe: CreatePipeFailed ec=%d\r\n",
245 (int)GetLastError ());
246 return NULL;
247 }
248
249 WriteFile (w, dat, strlen (dat), &n, NULL);
250 CloseHandle (w);
251
252 return r;
253 }
254
255
256 /* Map the contents of the file handle @out to
257 a buffer and return it. */
258 static char*
259 map_tmpfile (HANDLE out)
260 {
261 DWORD n;
262 char *p;
263
264 FlushFileBuffers (out);
265 SetFilePointer (out, 0, NULL, FILE_BEGIN);
266 n = GetFileSize (out, NULL);
267 p = (char*)calloc (1, n+1);
268 if (!p)
269 BUG (NULL);
270 ReadFile (out, p, n, &n, NULL);
271 p[n] = 0;
272 return p;
273 }
274
275
276 /* Create a process from the command line in @cmd.
277 If @out is != NULL, the output of the process will
278 be redirected to @out. If @in is != NULL the input
279 will be read from @in.
280 Return value: 0 on success. */
281 static int
282 create_process (const char *cmd, HANDLE in, HANDLE out)
283 {
284 STARTUPINFO si;
285 PROCESS_INFORMATION pi;
286
287 memset (&si, 0, sizeof (si));
288 si.cb = sizeof si;
289 if (in || out)
290 si.dwFlags = STARTF_USESTDHANDLES;
291 if (out)
292 si.hStdOutput = out;
293 if (in)
294 si.hStdInput = in;
295 si.dwFlags |= STARTF_USESHOWWINDOW;
296 si.wShowWindow = SW_HIDE;
297 if (!CreateProcess (NULL, (char*)cmd, NULL, NULL, TRUE, 0,
298 NULL, NULL, &si, &pi)) {
299 log_debug ("create_process: CreateProcess failed ec=%d\r\n",
300 (int)GetLastError ());
301 return -1;
302 }
303 WaitForSingleObject (pi.hProcess, INFINITE);
304 CloseHandle (pi.hProcess);
305 return 0;
306 }
307
308
309 /* Export a GPG secret key given by @keyid into the file @outfile.
310 Return value: 0 on success. */
311 gpgme_error_t
312 gpg_export_seckey (const char *keyid, const char *outfile)
313 {
314 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
315 struct stat st;
316 char *p;
317 char *cmd;
318
319 p = read_w32_registry (HKEY_CURRENT_USER,
320 "Software\\GNU\\GnuPG", "gpgProgram");
321 if (!p)
322 return gpg_error (GPG_ERR_INV_ARG);
323 cmd = (char*)calloc (1, strlen (p) + strlen (keyid)
324 + strlen (outfile) + 64 + 2);
325 if (!cmd)
326 BUG (NULL);
327 sprintf (cmd, "%s --yes --output \"%s\" --export-secret-key %s",
328 p, outfile, keyid);
329 if (create_process (cmd, NULL, NULL))
330 err = gpg_error (GPG_ERR_INTERNAL);
331
332 if (stat (outfile, &st) == -1 || st.st_size == 0)
333 err = gpg_error (GPG_ERR_NO_DATA);
334
335 free (p);
336 free (cmd);
337 return err;
338 }
339
340
341 /* If @export is 1, export the ownertrust data to the
342 buffer @data. Otherwise import the ownertrust data from @data.
343 Return value: 0 on success. */
344 gpgme_error_t
345 gpg_manage_ownertrust (char **data, int do_export)
346 {
347 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
348 HANDLE out = NULL, in = NULL;
349 char *p;
350 char *cmd;
351
352 p = read_w32_registry (HKEY_CURRENT_USER,
353 "Software\\GNU\\GnuPG", "gpgProgram");
354 if (!p)
355 return gpg_error (GPG_ERR_INV_ARG);
356
357 cmd = (char*)calloc (1, strlen (p) + 64 + 1);
358 if (!cmd)
359 BUG (NULL);
360 sprintf (cmd, "%s %s", p,
361 do_export? "--export-ownertrust" : "--import-ownertrust");
362
363 if (do_export)
364 out = create_tmpfile ("gpg_ot_out");
365 else {
366 DWORD nw;
367 in = create_tmpfile ("gpg_ot_in");
368 WriteFile (in, *data, strlen (*data), &nw, NULL);
369 FlushFileBuffers (in);
370 /* XXX: need a rewind? */
371 }
372 if (create_process (cmd, in, out))
373 err = gpg_error (GPG_ERR_INTERNAL);
374
375 free (p);
376 free (cmd);
377
378 if (in)
379 CloseHandle (in);
380 if (out) {
381 *data = map_tmpfile (out);
382 CloseHandle (out);
383 }
384 return err;
385 }
386
387
388 /* Call gpg --rebuild-keydb-caches to speed up signature listings. */
389 gpgme_error_t
390 gpg_rebuild_cache (char **r_inf)
391 {
392 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
393 HANDLE out = NULL;
394 char *p;
395 char *cmd;
396
397 p = read_w32_registry (HKEY_CURRENT_USER,
398 "Software\\GNU\\GnuPG", "gpgProgram");
399 if (!p)
400 return gpg_error (GPG_ERR_INV_ARG);
401 cmd = (char*)calloc (1, strlen (p) + 64);
402 if (!cmd)
403 BUG (NULL);
404 sprintf (cmd, "%s --logger-fd=1 --rebuild-keydb-caches", p);
405
406 if (r_inf)
407 out = create_tmpfile ("gpg_rebuild_cache");
408
409 if (create_process (cmd, NULL, out))
410 err = gpg_error (GPG_ERR_INTERNAL);
411
412 if (r_inf)
413 *r_inf = map_tmpfile (out);
414 if (out)
415 CloseHandle (out);
416 free (p);
417 free (cmd);
418 return 0;
419 }
420
421
422 /* Call gpg --version to retrieve the 'about' information. */
423 gpgme_error_t
424 gpg_get_version (char **r_inf)
425 {
426 gpgme_error_t err= gpg_error (GPG_ERR_NO_ERROR);
427 HANDLE out;
428 char *p, *cmd;
429
430 p =read_w32_registry (HKEY_CURRENT_USER,
431 "Software\\GNU\\GnuPG", "gpgProgram");
432 if (!p)
433 return gpg_error (GPG_ERR_INV_ARG);
434 cmd = (char*)calloc (1, strlen (p) + 32);
435 if (!cmd)
436 BUG (NULL);
437 sprintf (cmd, "%s --version", p);
438
439 out = create_tmpfile ("gpg_out");
440 if (create_process (cmd, NULL, out))
441 err = gpg_error (GPG_ERR_INTERNAL);
442
443 free (p);
444 free (cmd);
445
446 *r_inf = map_tmpfile (out);
447 CloseHandle (out);
448 return err;
449 }
450
451
452 /* Return the colon file output of the given file @fname in @r_out.
453 Return value: 0 on success. */
454 gpgme_error_t
455 gpg_import_key_list (const char *fname, char **r_out)
456 {
457 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
458 char *cmd, *p;
459 HANDLE out;
460
461 p = read_w32_registry (HKEY_CURRENT_USER,
462 "Software\\GNU\\GnuPG", "gpgProgram");
463 if (!p)
464 return gpg_error (GPG_ERR_INV_ARG);
465
466 cmd = (char*)calloc (1, strlen (p) + strlen (fname) + 2+2 + 64);
467 if (!cmd)
468 BUG (NULL);
469 sprintf (cmd, "%s --fixed-list-mode --with-colons \"%s\"", p, fname);
470
471 out = create_tmpfile ("gpg_keys");
472 if (create_process (cmd, NULL, out))
473 err = gpg_error (GPG_ERR_INTERNAL);
474
475 free (p);
476 free (cmd);
477
478 *r_out = map_tmpfile (out);
479 CloseHandle (out);
480 return err;
481 }
482
483 char*
484 generate_revoc_input (int code, const char *cmt, const char *pass)
485 {
486 const char *fmt;
487 char *p;
488 size_t n;
489
490 fmt = "Y\n" /* gen_revoke.okay */
491 "%d\n" /* ask_revocation_reason.code */
492 "%s\n" /* ask_revocation_reason.text */
493 "%s" /* text != NULL '\n' otherwise '' */
494 "Y\n" /* ask_revocation_reason.okay */
495 "%s\n"; /* passphrase.enter. */
496 n = strlen (fmt) + 32;
497 if (pass)
498 n += strlen (pass) + 1;
499 if (cmt)
500 n += strlen (cmt) + 1;
501 p = (char*)calloc (1, n+1);
502 if (!p)
503 BUG (NULL);
504 sprintf (p, fmt, code, cmt? cmt : "", cmt? "\n" : "", pass? pass : "");
505 return p;
506 }
507
508
509 /* Generate a revocation certificate for the key with the keyid @keyid.
510 @inp_data contains all needed data to answer the questions of the
511 command handler. Each separate with a '\n'.
512 @r_revcert contains the revocation cert on success.
513 Return value: 0 on success. */
514 gpgme_error_t
515 gpg_revoke_key (const char *inp_data, const char *keyid, char **r_revcert)
516 {
517 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
518 char *rcrt;
519 char *cmd, *p;
520 HANDLE in, out;
521
522 p = read_w32_registry (HKEY_CURRENT_USER,
523 "Software\\GNU\\GnuPG", "gpgProgram");
524 if (!p)
525 return gpg_error (GPG_ERR_INV_ARG);
526
527 cmd = (char*)calloc (1, strlen (p) + strlen (keyid)+1 + 128);
528 if (!cmd)
529 BUG (NULL);
530 sprintf (cmd, "%s --pgp7 --command-fd=0 --status-fd=2 --gen-revoke %s",
531 p, keyid);
532
533 in = create_in_pipe (inp_data);
534 out = create_tmpfile ("gpg_revcert");
535 if (create_process (cmd, in, out)) {
536 *r_revcert = NULL;
537 err = gpg_error (GPG_ERR_INTERNAL);
538 }
539 else {
540 rcrt = map_tmpfile (out);
541 *r_revcert = rcrt;
542 }
543
544 free (p);
545 free (cmd);
546
547 CloseHandle (in);
548 CloseHandle (out);
549 return err;
550 }
551
552
553 /* Return the validity of the user attribute, informerly known
554 as photo-ID. If no uat was found, return 0 for unknown. */
555 gpgme_error_t
556 get_uat_validity (const char *keyid, gpgme_validity_t *r_valid)
557 {
558 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
559 HANDLE out;
560 char *p, *cmd;
561 char *uat;
562
563 *r_valid = GPGME_VALIDITY_UNKNOWN;
564 p = read_w32_registry (HKEY_CURRENT_USER,
565 "Software\\GNU\\GnuPG", "gpgProgram");
566 if (!p)
567 return gpg_error (GPG_ERR_INV_ARG);
568
569 cmd = (char*)calloc (1, strlen (p) + strlen (keyid)+1 + 128);
570 if (!cmd)
571 BUG (NULL);
572 sprintf (cmd, "%s --with-colons --fixed-list-mode --list-keys \"%s\"",
573 p, keyid);
574
575 out = create_tmpfile ("gpg_keys");
576 if (create_process (cmd, NULL, out))
577 err = gpg_error (GPG_ERR_INTERNAL);
578
579 free (p);
580 free (cmd);
581
582 p = map_tmpfile (out);
583 if ((uat = strstr (p, "uat:"))) {
584 switch (*(uat+4)) {
585 case 'm': *r_valid = GPGME_VALIDITY_MARGINAL; break;
586 case 'f':
587 case 'u': *r_valid = GPGME_VALIDITY_FULL; break;
588 default: *r_valid = GPGME_VALIDITY_UNDEFINED; break;
589 }
590 }
591
592 free (p);
593 CloseHandle (out);
594 return err;
595 }
596
597
598 #if 0
599 /* Extract all recipients from the file @file.
600 Return value: 0 on success. */
601 static int
602 do_get_recipients (const char *file, gpgme_recipient_t *r_list)
603 {
604 gpgme_recipient_t l;
605 PACKET *pkt;
606 gpg_iobuf_t inp = NULL;
607 armor_filter_context_t afx;
608 int rc = 0, quit=0;
609
610 if (!file || !r_list) {
611 log_debug ("do_list_packets: !r_list || !file");
612 return -1;
613 }
614
615 *r_list=NULL;
616 inp = gpg_iobuf_open (file);
617 if (!inp)
618 return WPTERR_FILE_OPEN;
619 gpg_iobuf_ioctl (inp, 3, 1, NULL); /* disable cache */
620 if (gpg_use_armor_filter (inp)) {
621 memset (&afx, 0, sizeof (afx));
622 gpg_iobuf_push_filter (inp, gpg_armor_filter, &afx);
623 }
624 pkt = (PACKET *)calloc(1, sizeof *pkt);
625 gpg_init_packet (pkt);
626 while (!quit && (rc = gpg_parse_packet (inp, pkt)) != -1) {
627 switch (pkt->pkttype) {
628 case PKT_PUBKEY_ENC:
629 {PKT_pubkey_enc *enc = pkt->pkt.pubkey_enc;
630 if (!enc)
631 break;
632 l = calloc (1, sizeof *l);
633 l->keyid = calloc (1, 16+1);
634 _snprintf (l->keyid, 16, "%08lX%08lX", enc->keyid[0], enc->keyid[1]);
635 l->pubkey_algo = enc->pubkey_algo;
636 l->status = 0;
637 l->next = (*r_list);
638 *r_list = l;
639 break;}
640
641 case PKT_ENCRYPTED:
642 case PKT_ENCRYPTED_MDC:
643 case PKT_COMPRESSED:
644 case PKT_PUBLIC_KEY:
645 case PKT_SECRET_KEY:
646 quit = 1;
647 break;
648 }
649 gpg_free_packet (pkt);
650 gpg_init_packet (pkt);
651 }
652 gpg_iobuf_close (inp);
653 safe_free (pkt);
654 return 0;
655 }
656 #endif

Properties

Name Value
svn:eol-style native

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26