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

Annotation of /trunk/Src/wptGPGUtil.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 25 - (hide annotations)
Wed Oct 12 10:04:26 2005 UTC (19 years, 4 months ago) by twoaday
File size: 13520 byte(s)
First testing phase finished.
Provide bug fixes for a lot of (minor) problems.

1 twoaday 25 /* 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) {
106     /* Should not happen. */
107     *(dest++) = *(src++);
108     *(dest++) = *(src++);
109     if (*src)
110     *(dest++) = *(src++);
111     if (*src)
112     *(dest++) = *(src++);
113     }
114     else {
115     if (!val) {
116     /* A binary zero is not representable in a C
117     string. */
118     *(dest++) = '\\';
119     *(dest++) = '0';
120     }
121     else
122     *((unsigned char *) dest++) = val;
123     src += 4;
124     }
125     }
126    
127     default:
128     {
129     /* Should not happen. */
130     *(dest++) = *(src++);
131     *(dest++) = *(src++);
132     }
133     }
134     }
135     *(dest++) = 0;
136     return 0;
137     }
138    
139     /* Replace %foo% entries with its real values.
140     Return value: expanded path or NULL on error. */
141     static char *
142     expand_path (const char *path)
143     {
144     DWORD len;
145     char *p;
146    
147     len = ExpandEnvironmentStrings (path, NULL, 0);
148     if (!len)
149     return NULL;
150     len += 1;
151     p = (char*)calloc (1, len+1);
152     if (!p)
153     abort ();
154     len = ExpandEnvironmentStrings (path, p, len);
155     if (!len) {
156     free (p);
157     return NULL;
158     }
159     return p;
160     }
161    
162    
163     /* Read a string from the W32 registry. The directory is given
164     in @dir and the name of the value in @name, */
165     static char *
166     read_w32_registry (HKEY root_key, const char *dir, const char *name )
167     {
168     HKEY key_handle;
169     DWORD n1, nbytes;
170     DWORD type;
171     char *result = NULL;
172    
173     if (RegOpenKeyEx (root_key, dir, 0, KEY_READ, &key_handle))
174     return NULL; /* no need for a RegClose, so return direct */
175    
176     nbytes = 1;
177     if (RegQueryValueEx (key_handle, name, 0, NULL, NULL, &nbytes))
178     goto leave;
179     result = (char*)calloc (1, (n1=nbytes+1));
180     if (!result)
181     abort ();
182     if (RegQueryValueEx (key_handle, name, 0, &type, (BYTE*)result, &n1)) {
183     free (result);
184     result = NULL;
185     goto leave;
186     }
187     if (type == REG_EXPAND_SZ && strchr (result, '%')) {
188     char *p = expand_path (result);
189     free (result);
190     result = p;
191     }
192    
193     leave:
194     RegCloseKey (key_handle);
195     return result;
196     }
197    
198    
199     /* Create a temp file based on the name of @name.
200     Return value: handle to the file in case of success. */
201     static HANDLE
202     create_tmpfile (const char *name)
203     {
204     HANDLE out;
205     SECURITY_ATTRIBUTES sattr;
206     char tmp[300];
207    
208     memset (&sattr, 0, sizeof sattr);
209     sattr.bInheritHandle = TRUE;
210     sattr.lpSecurityDescriptor = NULL;
211     sattr.nLength = sizeof sattr;
212    
213     GetTempPath (300 - 64, tmp);
214     strcat (tmp, name);
215     out = CreateFile (tmp, GENERIC_READ|GENERIC_WRITE,
216     FILE_SHARE_WRITE, &sattr,
217     OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL);
218     return out;
219     }
220    
221    
222     /* Create a pipe with a readable remote end and
223     write the data from @dat to the local end.
224     Return value: read handle on success. */
225     static HANDLE
226     create_in_pipe (const char *dat)
227     {
228     HANDLE r, w;
229     SECURITY_ATTRIBUTES sec_attr;
230     DWORD n;
231    
232     memset (&sec_attr, 0, sizeof sec_attr);
233     sec_attr.bInheritHandle = TRUE;
234     sec_attr.nLength = sizeof sec_attr;
235    
236     if (!CreatePipe (&r, &w, &sec_attr, 4096))
237     return NULL;
238    
239     WriteFile (w, dat, strlen (dat), &n, NULL);
240     CloseHandle (w);
241    
242     return r;
243     }
244    
245    
246     /* Map the contents of the file handle @out to
247     a buffer and return it. */
248     static char*
249     map_tmpfile (HANDLE out)
250     {
251     DWORD n;
252     char *p;
253    
254     FlushFileBuffers (out);
255     SetFilePointer (out, 0, NULL, FILE_BEGIN);
256     n = GetFileSize (out, NULL);
257     p = (char*)calloc (1, n+1);
258     if (!p)
259     abort ();
260     ReadFile (out, p, n, &n, NULL);
261     p[n] = 0;
262     return p;
263     }
264    
265    
266     /* Create a process from the command line in @cmd.
267     If @out is != NULL, the output of the process will
268     be redirected to @out. If @in is != NULL the input
269     will be read from @in.
270     Return value: 0 on success. */
271     static int
272     create_process (const char *cmd, HANDLE in, HANDLE out)
273     {
274     STARTUPINFO si;
275     PROCESS_INFORMATION pi;
276    
277     memset (&si, 0, sizeof (si));
278     si.cb = sizeof si;
279     if (in || out)
280     si.dwFlags = STARTF_USESTDHANDLES;
281     if (out)
282     si.hStdOutput = out;
283     if (in)
284     si.hStdInput = in;
285     if (!CreateProcess (NULL, (char*)cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
286     return -1;
287     WaitForSingleObject (pi.hProcess, INFINITE);
288     CloseHandle (pi.hProcess);
289     return 0;
290     }
291    
292    
293     /* Export a GPG secret key given by @keyid into the file @outfile.
294     Return value: 0 on success. */
295     gpgme_error_t
296     gpg_export_seckey (const char *keyid, const char *outfile)
297     {
298     gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
299     struct stat st;
300     char *p;
301     char *cmd;
302    
303     p = read_w32_registry (HKEY_CURRENT_USER, "Software\\GNU\\GnuPG", "gpgProgram");
304     if (!p)
305     return gpg_error (GPG_ERR_INV_ARG);
306     cmd = (char*)calloc (1, strlen (p) + strlen (keyid) + strlen (outfile ) + 64 + 2);
307     if (!cmd)
308     abort ();
309     sprintf (cmd, "%s --yes --output \"%s\" --export-secret-key %s", p, outfile, keyid);
310     if (create_process (cmd, NULL, NULL))
311     err = gpg_error (GPG_ERR_INTERNAL);
312    
313     if (stat (outfile, &st) == -1 || st.st_size == 0)
314     err = gpg_error (GPG_ERR_NO_DATA);
315    
316     free (p);
317     free (cmd);
318     return err;
319     }
320    
321    
322     /* If @export is 1, export the ownertrust data to the
323     buffer @data. Otherwise import the ownertrust data from @data.
324     Return value: 0 on success. */
325     gpgme_error_t
326     gpg_manage_ownertrust (char **data, int export)
327     {
328     gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
329     HANDLE out = NULL, in = NULL;
330     char *p;
331     char *cmd;
332    
333     p = read_w32_registry (HKEY_CURRENT_USER, "Software\\GNU\\GnuPG", "gpgProgram");
334     if (!p)
335     return gpg_error (GPG_ERR_INV_ARG);
336    
337     cmd = (char*)calloc (1, strlen (p) + 64 + 1);
338     if (!cmd)
339     abort ();
340     sprintf (cmd, "%s %s", p, export? "--export-ownertrust" : "--import-ownertrust");
341    
342     if (export)
343     out = create_tmpfile ("gpg_ot_out");
344     else {
345     DWORD nw;
346     in = create_tmpfile ("gpg_ot_in");
347     WriteFile (in, *data, strlen (*data), &nw, NULL);
348     FlushFileBuffers (in);
349     /* XXX: need a rewind? */
350     }
351     if (create_process (cmd, in, out))
352     err = gpg_error (GPG_ERR_INTERNAL);
353    
354     free (p);
355     free (cmd);
356    
357     if (in)
358     CloseHandle (in);
359     if (out) {
360     *data = map_tmpfile (out);
361     CloseHandle (out);
362     }
363     return err;
364     }
365    
366     /* Call gpg --rebuild-keydb-caches to speed up signature listings. */
367     gpgme_error_t
368     gpg_rebuild_cache (char **r_inf)
369     {
370     gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
371     HANDLE out = NULL;
372     char *p;
373     char *cmd;
374    
375     p = read_w32_registry (HKEY_CURRENT_USER, "Software\\GNU\\GnuPG", "gpgProgram");
376     if (!p)
377     return gpg_error (GPG_ERR_INV_ARG);
378     cmd = (char*)calloc (1, strlen (p) + 64);
379     if (!cmd)
380     abort ();
381     sprintf (cmd, "%s --logger-fd=1 --rebuild-keydb-caches", p);
382    
383     if (r_inf)
384     out = create_tmpfile ("gpg_rebuild_cache");
385    
386     if (create_process (cmd, NULL, out))
387     err = gpg_error (GPG_ERR_INTERNAL);
388    
389     if (r_inf)
390     *r_inf = map_tmpfile (out);
391     if (out)
392     CloseHandle (out);
393     free (p);
394     free (cmd);
395     return 0;
396     }
397    
398     /* Call gpg --version to retrieve the 'about' information. */
399     gpgme_error_t
400     gpg_get_version (char **r_inf)
401     {
402     gpgme_error_t err= gpg_error (GPG_ERR_NO_ERROR);
403     HANDLE out;
404     char *p, *cmd;
405    
406     p =read_w32_registry (HKEY_CURRENT_USER, "Software\\GNU\\GnuPG", "gpgProgram");
407     if (!p)
408     return gpg_error (GPG_ERR_INV_ARG);
409     cmd = (char*)calloc (1, strlen (p) + 32);
410     if (!cmd)
411     abort ();
412     sprintf (cmd, "%s --version", p);
413    
414     out = create_tmpfile ("gpg_out");
415     if (create_process (cmd, NULL, out))
416     err = gpg_error (GPG_ERR_INTERNAL);
417    
418     free (p);
419     free (cmd);
420    
421     *r_inf = map_tmpfile (out);
422     CloseHandle (out);
423     return err;
424     }
425    
426     /* Return the colon file output of the given file @fname in @r_out.
427     Return value: 0 on success. */
428     gpgme_error_t
429     gpg_import_key_list (const char *fname, char **r_out)
430     {
431     gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
432     char *cmd, *p;
433     HANDLE out;
434    
435     p = read_w32_registry (HKEY_CURRENT_USER, "Software\\GNU\\GnuPG", "gpgProgram");
436     if (!p)
437     return gpg_error (GPG_ERR_INV_ARG);
438    
439     cmd = (char*)calloc (1, strlen (p) + strlen (fname) + 2+2 + 64);
440     if (!cmd)
441     abort ();
442     sprintf (cmd, "%s --fixed-list-mode --with-colons \"%s\"", p, fname);
443    
444     out = create_tmpfile ("gpg_keys");
445     if (create_process (cmd, NULL, out))
446     err = gpg_error (GPG_ERR_INTERNAL);
447    
448     free (p);
449     free (cmd);
450    
451     *r_out = map_tmpfile (out);
452     CloseHandle (out);
453     return err;
454     }
455    
456     char*
457     generate_revoc_input (int code, const char *cmt, const char *pass)
458     {
459     const char *fmt;
460     char *p;
461     size_t n;
462    
463     fmt = "Y\n" /* gen_revoke.okay */
464     "%d\n" /* ask_revocation_reason.code */
465     "%s\n" /* ask_revocation_reason.text */
466     "%s" /* text != NULL '\n' otherwise '' */
467     "Y\n" /* ask_revocation_reason.okay */
468     "%s\n"; /* passphrase.enter. */
469     n = strlen (fmt) + 32;
470     if (pass)
471     n += strlen (pass) + 1;
472     if (cmt)
473     n += strlen (cmt) + 1;
474     p = (char*)calloc (1, n+1);
475     if (!p)
476     abort ();
477     sprintf (p, fmt, code, cmt? cmt : "", cmt? "\n" : "", pass? pass : "");
478     return p;
479     }
480    
481    
482     /* Generate a revocation certificate for the key with the keyid @keyid.
483     @inp_data contains all needed data to answer the questions of the
484     command handler. Each separate with a '\n'.
485     @r_revcert contains the revocation cert on success.
486     Return value: 0 on success. */
487     gpgme_error_t
488     gpg_revoke_key (const char *inp_data, const char *keyid, char **r_revcert)
489     {
490     gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
491     char *rcrt;
492     char *cmd, *p;
493     HANDLE in, out;
494    
495     p = read_w32_registry (HKEY_CURRENT_USER, "Software\\GNU\\GnuPG", "gpgProgram");
496     if (!p)
497     return gpg_error (GPG_ERR_INV_ARG);
498    
499     cmd = (char*)calloc (1, strlen (p) + 128);
500     if (!cmd)
501     abort ();
502     sprintf (cmd, "%s --pgp7 --command-fd=0 --status-fd=2 --gen-revoke %s", p, keyid);
503    
504     in = create_in_pipe (inp_data);
505     out = create_tmpfile ("gpg_revcert");
506     if (create_process (cmd, in, out)) {
507     *r_revcert = NULL;
508     err = gpg_error (GPG_ERR_INTERNAL);
509     }
510     else {
511     rcrt = map_tmpfile (out);
512     *r_revcert = rcrt;
513     }
514    
515     free (p);
516     free (cmd);
517    
518     CloseHandle (in);
519     CloseHandle (out);
520     return err;
521     }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26