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

Contents of /trunk/Src/wptGPGUtil.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 25 - (show 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 /* 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