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

Contents of /trunk/Src/wptGPGUtil.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 73 - (show annotations)
Tue Nov 8 07:15:13 2005 UTC (19 years, 3 months ago) by twoaday
File size: 15953 byte(s)
2005-11-08  Timo Schulz  <ts@g10code.com>
 
        More minor changes to avoid GCC warnings.
         
        * wptGPG.cpp (check_homedir): Free memory in case of errors.
        (multi_gnupg_path): Add strict mode. If non-strict mode return
        the folder even if it does not exist.
        (check_for_gpgwin): New.
        * wptKeyserverDlg.cpp (hkp_recv_key): Make sure import_res is
        initialized.
        * wptRegistry.cpp (get_reg_entry_gpg4win): New.
        (get_reg_entry_mo): Support for gpg4win.
         
For complete changes see ChangeLogs.

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

Properties

Name Value
svn:eol-style native

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26