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

Contents of /trunk/Src/wptGPGUtil.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 33 - (show annotations)
Tue Oct 25 07:46:20 2005 UTC (19 years, 4 months ago) by twoaday
File size: 16355 byte(s)
More bug fixes and cleanups.
See ChangeLog for details.

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;
278 if (out)
279 si.hStdOutput = out;
280 if (in)
281 si.hStdInput = in;
282 if (!CreateProcess (NULL, (char*)cmd, NULL, NULL, TRUE, 0,
283 NULL, NULL, &si, &pi))
284 return -1;
285 WaitForSingleObject (pi.hProcess, INFINITE);
286 CloseHandle (pi.hProcess);
287 return 0;
288 }
289
290
291 /* Export a GPG secret key given by @keyid into the file @outfile.
292 Return value: 0 on success. */
293 gpgme_error_t
294 gpg_export_seckey (const char *keyid, const char *outfile)
295 {
296 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
297 struct stat st;
298 char *p;
299 char *cmd;
300
301 p = read_w32_registry (HKEY_CURRENT_USER,
302 "Software\\GNU\\GnuPG", "gpgProgram");
303 if (!p)
304 return gpg_error (GPG_ERR_INV_ARG);
305 cmd = (char*)calloc (1, strlen (p) + strlen (keyid)
306 + strlen (outfile) + 64 + 2);
307 if (!cmd)
308 abort ();
309 sprintf (cmd, "%s --yes --output \"%s\" --export-secret-key %s",
310 p, outfile, keyid);
311 if (create_process (cmd, NULL, NULL))
312 err = gpg_error (GPG_ERR_INTERNAL);
313
314 if (stat (outfile, &st) == -1 || st.st_size == 0)
315 err = gpg_error (GPG_ERR_NO_DATA);
316
317 free (p);
318 free (cmd);
319 return err;
320 }
321
322
323 /* If @export is 1, export the ownertrust data to the
324 buffer @data. Otherwise import the ownertrust data from @data.
325 Return value: 0 on success. */
326 gpgme_error_t
327 gpg_manage_ownertrust (char **data, int export)
328 {
329 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
330 HANDLE out = NULL, in = NULL;
331 char *p;
332 char *cmd;
333
334 p = read_w32_registry (HKEY_CURRENT_USER,
335 "Software\\GNU\\GnuPG", "gpgProgram");
336 if (!p)
337 return gpg_error (GPG_ERR_INV_ARG);
338
339 cmd = (char*)calloc (1, strlen (p) + 64 + 1);
340 if (!cmd)
341 abort ();
342 sprintf (cmd, "%s %s", p,
343 export? "--export-ownertrust" : "--import-ownertrust");
344
345 if (export)
346 out = create_tmpfile ("gpg_ot_out");
347 else {
348 DWORD nw;
349 in = create_tmpfile ("gpg_ot_in");
350 WriteFile (in, *data, strlen (*data), &nw, NULL);
351 FlushFileBuffers (in);
352 /* XXX: need a rewind? */
353 }
354 if (create_process (cmd, in, out))
355 err = gpg_error (GPG_ERR_INTERNAL);
356
357 free (p);
358 free (cmd);
359
360 if (in)
361 CloseHandle (in);
362 if (out) {
363 *data = map_tmpfile (out);
364 CloseHandle (out);
365 }
366 return err;
367 }
368
369
370 /* Call gpg --rebuild-keydb-caches to speed up signature listings. */
371 gpgme_error_t
372 gpg_rebuild_cache (char **r_inf)
373 {
374 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
375 HANDLE out = NULL;
376 char *p;
377 char *cmd;
378
379 p = read_w32_registry (HKEY_CURRENT_USER,
380 "Software\\GNU\\GnuPG", "gpgProgram");
381 if (!p)
382 return gpg_error (GPG_ERR_INV_ARG);
383 cmd = (char*)calloc (1, strlen (p) + 64);
384 if (!cmd)
385 abort ();
386 sprintf (cmd, "%s --logger-fd=1 --rebuild-keydb-caches", p);
387
388 if (r_inf)
389 out = create_tmpfile ("gpg_rebuild_cache");
390
391 if (create_process (cmd, NULL, out))
392 err = gpg_error (GPG_ERR_INTERNAL);
393
394 if (r_inf)
395 *r_inf = map_tmpfile (out);
396 if (out)
397 CloseHandle (out);
398 free (p);
399 free (cmd);
400 return 0;
401 }
402
403
404 /* Call gpg --version to retrieve the 'about' information. */
405 gpgme_error_t
406 gpg_get_version (char **r_inf)
407 {
408 gpgme_error_t err= gpg_error (GPG_ERR_NO_ERROR);
409 HANDLE out;
410 char *p, *cmd;
411
412 p =read_w32_registry (HKEY_CURRENT_USER,
413 "Software\\GNU\\GnuPG", "gpgProgram");
414 if (!p)
415 return gpg_error (GPG_ERR_INV_ARG);
416 cmd = (char*)calloc (1, strlen (p) + 32);
417 if (!cmd)
418 abort ();
419 sprintf (cmd, "%s --version", p);
420
421 out = create_tmpfile ("gpg_out");
422 if (create_process (cmd, NULL, out))
423 err = gpg_error (GPG_ERR_INTERNAL);
424
425 free (p);
426 free (cmd);
427
428 *r_inf = map_tmpfile (out);
429 CloseHandle (out);
430 return err;
431 }
432
433
434 /* Return the colon file output of the given file @fname in @r_out.
435 Return value: 0 on success. */
436 gpgme_error_t
437 gpg_import_key_list (const char *fname, char **r_out)
438 {
439 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
440 char *cmd, *p;
441 HANDLE out;
442
443 p = read_w32_registry (HKEY_CURRENT_USER,
444 "Software\\GNU\\GnuPG", "gpgProgram");
445 if (!p)
446 return gpg_error (GPG_ERR_INV_ARG);
447
448 cmd = (char*)calloc (1, strlen (p) + strlen (fname) + 2+2 + 64);
449 if (!cmd)
450 abort ();
451 sprintf (cmd, "%s --fixed-list-mode --with-colons \"%s\"", p, fname);
452
453 out = create_tmpfile ("gpg_keys");
454 if (create_process (cmd, NULL, out))
455 err = gpg_error (GPG_ERR_INTERNAL);
456
457 free (p);
458 free (cmd);
459
460 *r_out = map_tmpfile (out);
461 CloseHandle (out);
462 return err;
463 }
464
465 char*
466 generate_revoc_input (int code, const char *cmt, const char *pass)
467 {
468 const char *fmt;
469 char *p;
470 size_t n;
471
472 fmt = "Y\n" /* gen_revoke.okay */
473 "%d\n" /* ask_revocation_reason.code */
474 "%s\n" /* ask_revocation_reason.text */
475 "%s" /* text != NULL '\n' otherwise '' */
476 "Y\n" /* ask_revocation_reason.okay */
477 "%s\n"; /* passphrase.enter. */
478 n = strlen (fmt) + 32;
479 if (pass)
480 n += strlen (pass) + 1;
481 if (cmt)
482 n += strlen (cmt) + 1;
483 p = (char*)calloc (1, n+1);
484 if (!p)
485 abort ();
486 sprintf (p, fmt, code, cmt? cmt : "", cmt? "\n" : "", pass? pass : "");
487 return p;
488 }
489
490
491 /* Generate a revocation certificate for the key with the keyid @keyid.
492 @inp_data contains all needed data to answer the questions of the
493 command handler. Each separate with a '\n'.
494 @r_revcert contains the revocation cert on success.
495 Return value: 0 on success. */
496 gpgme_error_t
497 gpg_revoke_key (const char *inp_data, const char *keyid, char **r_revcert)
498 {
499 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
500 char *rcrt;
501 char *cmd, *p;
502 HANDLE in, out;
503
504 p = read_w32_registry (HKEY_CURRENT_USER,
505 "Software\\GNU\\GnuPG", "gpgProgram");
506 if (!p)
507 return gpg_error (GPG_ERR_INV_ARG);
508
509 cmd = (char*)calloc (1, strlen (p) + strlen (keyid)+1 + 128);
510 if (!cmd)
511 abort ();
512 sprintf (cmd, "%s --pgp7 --command-fd=0 --status-fd=2 --gen-revoke %s",
513 p, keyid);
514
515 in = create_in_pipe (inp_data);
516 out = create_tmpfile ("gpg_revcert");
517 if (create_process (cmd, in, out)) {
518 *r_revcert = NULL;
519 err = gpg_error (GPG_ERR_INTERNAL);
520 }
521 else {
522 rcrt = map_tmpfile (out);
523 *r_revcert = rcrt;
524 }
525
526 free (p);
527 free (cmd);
528
529 CloseHandle (in);
530 CloseHandle (out);
531 return err;
532 }
533
534
535 /* Return the validity of the user attribute, informerly known
536 as photo-ID. If no uat was found, return 0 for unknown. */
537 gpgme_error_t
538 get_uat_validity (const char *keyid, gpgme_validity_t *r_valid)
539 {
540 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
541 HANDLE out;
542 char *p, *cmd;
543 char *uat;
544
545 *r_valid = GPGME_VALIDITY_UNKNOWN;
546 p = read_w32_registry (HKEY_CURRENT_USER,
547 "Software\\GNU\\GnuPG", "gpgProgram");
548 if (!p)
549 return gpg_error (GPG_ERR_INV_ARG);
550
551 cmd = (char*)calloc (1, strlen (p) + strlen (keyid)+1 + 128);
552 if (!cmd)
553 abort ();
554 sprintf (cmd, "%s --with-colons --fixed-list-mode --list-keys \"%s\"",
555 p, keyid);
556
557 out = create_tmpfile ("gpg_keys");
558 if (create_process (cmd, NULL, out))
559 err = gpg_error (GPG_ERR_INTERNAL);
560
561 free (p);
562 free (cmd);
563
564 p = map_tmpfile (out);
565 if ((uat = strstr (p, "uat:"))) {
566 switch (*(uat+4)) {
567 case 'm': *r_valid = GPGME_VALIDITY_MARGINAL; break;
568 case 'f':
569 case 'u': *r_valid = GPGME_VALIDITY_FULL; break;
570 default: *r_valid = GPGME_VALIDITY_UNDEFINED; break;
571 }
572 }
573
574 free (p);
575 CloseHandle (out);
576 return err;
577 }
578
579
580 #if 0
581 /* Extract all recipients from the file @file.
582 Return value: 0 on success. */
583 static int
584 do_get_recipients (const char *file, gpgme_recipient_t *r_list)
585 {
586 gpgme_recipient_t l;
587 PACKET *pkt;
588 gpg_iobuf_t inp = NULL;
589 armor_filter_context_t afx;
590 int rc = 0, quit=0;
591
592 if (!file || !r_list) {
593 log_debug ("do_list_packets: !r_list || !file");
594 return -1;
595 }
596
597 *r_list=NULL;
598 inp = gpg_iobuf_open (file);
599 if (!inp)
600 return WPTERR_FILE_OPEN;
601 gpg_iobuf_ioctl (inp, 3, 1, NULL); /* disable cache */
602 if (gpg_use_armor_filter (inp)) {
603 memset (&afx, 0, sizeof (afx));
604 gpg_iobuf_push_filter (inp, gpg_armor_filter, &afx);
605 }
606 pkt = (PACKET *)calloc(1, sizeof *pkt);
607 gpg_init_packet (pkt);
608 while (!quit && (rc = gpg_parse_packet (inp, pkt)) != -1) {
609 switch (pkt->pkttype) {
610 case PKT_PUBKEY_ENC:
611 {PKT_pubkey_enc *enc = pkt->pkt.pubkey_enc;
612 if (!enc)
613 break;
614 l = calloc (1, sizeof *l);
615 l->keyid = calloc (1, 16+1);
616 _snprintf (l->keyid, 16, "%08lX%08lX", enc->keyid[0], enc->keyid[1]);
617 l->pubkey_algo = enc->pubkey_algo;
618 l->status = 0;
619 l->next = (*r_list);
620 *r_list = l;
621 break;}
622
623 case PKT_ENCRYPTED:
624 case PKT_ENCRYPTED_MDC:
625 case PKT_COMPRESSED:
626 case PKT_PUBLIC_KEY:
627 case PKT_SECRET_KEY:
628 quit = 1;
629 break;
630 }
631 gpg_free_packet (pkt);
632 gpg_init_packet (pkt);
633 }
634 gpg_iobuf_close (inp);
635 safe_free (pkt);
636 return 0;
637 }
638 #endif

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26