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

Contents of /trunk/Src/wptPassphraseCB.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 333 - (show annotations)
Tue Oct 13 10:51:21 2009 UTC (15 years, 4 months ago) by twoaday
File size: 14858 byte(s)


1 /* wptPassphraseCB.cpp - GPGME Passphrase Callback
2 * Copyright (C) 2001, 2002-2006, 2009 Timo Schulz
3 * Copyright (C) 2005 g10 Code GmbH
4 *
5 * This file is part of WinPT.
6 *
7 * WinPT is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (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 GNU
15 * General Public License for more details.
16 */
17 #ifdef HAVE_CONFIG_H
18 #include <config.h>
19 #endif
20
21 #include <windows.h>
22 #include <ctype.h>
23
24 #include "resource.h"
25 #include "wptNLS.h"
26 #include "wptW32API.h"
27 #include "wptVersion.h"
28 #include "wptGPG.h"
29 #include "wptCommonCtl.h"
30 #include "wptContext.h"
31 #include "wptDlgs.h"
32 #include "wptErrors.h"
33 #include "wptTypes.h"
34 #include "wptKeylist.h"
35 #include "wptAgent.h"
36 #include "wptRegistry.h"
37 #include "wptUTF8.h"
38 #include "StringBuffer.h"
39
40
41 /* Return the control ID dependent on the mode (sign or decrypt). */
42 #define item_ctrl_id(cmd) \
43 ((cmd) == GPG_CMD_DECRYPT? IDC_DECRYPT_PWD : IDC_DECRYPT_SIGN_PWD)
44
45 #define item_ctrl_id2(cmd) \
46 ((cmd) == GPG_CMD_DECRYPT? IDC_DECRYPT_HIDE : IDC_DECRYPT_SIGN_HIDE)
47
48 const char* get_symkey_algo (int algo);
49
50
51 /* Overwrites the passphrase and free the memory.
52 Pointer is also reset to NULL. */
53 static void
54 burn_passphrase (char **pwd)
55 {
56 char *pass = *pwd;
57
58 wipememory (pass, strlen (pass));
59 delete []pass;
60 *pwd = NULL;
61 }
62
63
64 /* Dialog procedure for the passphrase callback. */
65 static BOOL CALLBACK
66 passphrase_callback_proc (HWND dlg, UINT msg, WPARAM wparam, LPARAM lparam)
67 {
68 static passphrase_cb_s *c;
69 gpgme_decrypt_result_t res=NULL;
70 gpgme_sign_result_t res_sig=NULL;
71 gpgme_recipient_t recip=NULL, r;
72 winpt_key_s key;
73 void *item;
74 int n;
75
76 switch (msg) {
77 case WM_ACTIVATE:
78 /* Some people complained that it is no longer possible to
79 paste in the passphrase in this dialog. When this option
80 is enabled, the ordinary passphrase control will be used. */
81 if (!reg_prefs.no_safe_pwd_ctrl)
82 safe_edit_control_init (dlg, item_ctrl_id (c->gpg_cmd));
83 break;
84
85 case WM_DESTROY:
86 if (!reg_prefs.no_safe_pwd_ctrl)
87 safe_edit_control_free (dlg, item_ctrl_id (c->gpg_cmd));
88 break;
89
90 case WM_INITDIALOG:
91 c = (passphrase_cb_s *)lparam;
92 if (!c)
93 BUG (0);
94 SetDlgItemText (dlg, IDCANCEL, _("&Cancel"));
95 SetWindowText (dlg, c->title);
96 if (c->gpg_cmd == GPG_CMD_DECRYPT) {
97 SetDlgItemText (dlg, IDC_DECRYPT_HIDE, _("&Hide Typing"));
98 SetDlgItemText (dlg, IDC_DECRYPT_LISTINF,
99 _("Encrypted with the following public key(s):"));
100 CheckDlgButton (dlg, IDC_DECRYPT_HIDE, BST_CHECKED);
101 }
102 else if (c->gpg_cmd == GPG_CMD_SIGN) {
103 SetDlgItemText (dlg, IDC_DECRYPT_SIGN_HIDE, _("&Hide Typing"));
104 CheckDlgButton (dlg, IDC_DECRYPT_SIGN_HIDE, BST_CHECKED);
105 }
106 /* Because it depends on the order the keys are stored in the
107 keyring, whether res->recipients is complete or not, we also
108 support that the recipients were externally extracted and then
109 we use this list. */
110 if (c->recipients) /* recipients were already extracted. */
111 recip = c->recipients;
112 else if (c->gpg) {
113 res = gpgme_op_decrypt_result (c->gpg);
114 if (res && res->recipients)
115 recip = res->recipients;
116 }
117 if (recip != NULL && c->gpg_cmd == GPG_CMD_DECRYPT) {
118 StringBuffer inf;
119
120 for (r = recip; r; r = r->next) {
121 memset (&key, 0, sizeof (key));
122 if (!winpt_get_pubkey (r->keyid, &key)) {
123 gpgme_user_id_t u = key.ctx->uids;
124 inf = (u->name? u->name : _("Invalid User ID"));
125 if (u->email != NULL && strlen (u->email) > 1)
126 inf = inf + " <" + u->email + ">";
127 inf = inf + " (" + get_key_pubalgo (r->pubkey_algo);
128 inf = inf + ", 0x" + (r->keyid+8) + ")";
129 }
130 else {
131 inf = _("Unknown key ID");
132 inf = inf + " (" + get_key_pubalgo (r->pubkey_algo);
133 inf = inf + ", 0x" + (r->keyid+8) + ")";
134 }
135 ListBox_AddString_utf8 (GetDlgItem (dlg, IDC_DECRYPT_LIST),
136 inf.getBuffer ());
137 winpt_release_pubkey (&key);
138 }
139 }
140 else if (c->gpg_cmd == GPG_CMD_DECRYPT)
141 EnableWindow (GetDlgItem (dlg, IDC_DECRYPT_LIST), FALSE);
142 SetDlgItemText (dlg, c->gpg_cmd == GPG_CMD_DECRYPT?
143 IDC_DECRYPT_PWDINFO : IDC_DECRYPT_SIGN_PWDINFO,
144 c->bad_pwd? _("Invalid passphrase; Please enter your passphrase again") :
145 _("Please enter your passphrase"));
146 if (c->gpg_cmd == GPG_CMD_DECRYPT) {
147 SetFocus (GetDlgItem (dlg, IDC_DECRYPT_PWD));
148 /* no recipients means symmetric encryption */
149 if (res && !res->recipients) {
150 StringBuffer sinf;
151
152 sinf = _("Symmetric encryption.");
153 sinf = sinf + "\n" + get_symkey_algo (c->sym.sym_algo);
154 sinf = sinf + " " + _("encrypted data") + ".";
155 SetDlgItemText (dlg, IDC_DECRYPT_MSG, sinf.getBuffer ());
156 }
157 else
158 SetDlgItemText (dlg, IDC_DECRYPT_MSG, c->info);
159 }
160 else {
161 SetFocus (GetDlgItem (dlg, IDC_DECRYPT_SIGN_PWD));
162 SetDlgItemText (dlg, IDC_DECRYPT_SIGN_MSG, c->info);
163 }
164 center_window (dlg, NULL);
165 SetForegroundWindow (dlg);
166 return FALSE;
167
168 case WM_COMMAND:
169 switch (HIWORD (wparam)) {
170 case BN_CLICKED:
171 if (LOWORD (wparam) == IDC_DECRYPT_HIDE ||
172 LOWORD (wparam) == IDC_DECRYPT_SIGN_HIDE) {
173 HWND hwnd;
174 int hide;
175 hide = IsDlgButtonChecked (dlg, item_ctrl_id2 (c->gpg_cmd));
176 hwnd = GetDlgItem (dlg, item_ctrl_id (c->gpg_cmd));
177 SendMessage (hwnd, EM_SETPASSWORDCHAR, hide? '*' : 0, 0);
178 SetFocus (hwnd);
179 }
180 }
181
182 switch (LOWORD (wparam)) {
183 case IDOK:
184 /* XXX: the item is even cached when the passphrase is not
185 correct, which means that the user needs to delete all
186 cached entries to continue. */
187 if (c->pwd)
188 burn_passphrase (&c->pwd);
189 n = item_get_text_length (dlg, item_ctrl_id (c->gpg_cmd));
190 if (!n) {
191 c->pwd = new char[2];
192 strcpy (c->pwd, "");
193 }
194 else {
195 char *p = new char[n+2];
196 SafeGetDlgItemText (dlg, item_ctrl_id (c->gpg_cmd), p, n+1);
197 if (emulate_utf8_bug)
198 c->pwd = native_to_utf8 (p);
199 else
200 c->pwd = m_strdup (p);
201 sfree_if_alloc (p);
202 }
203 if (c->gpg != NULL) {
204 res = gpgme_op_decrypt_result (c->gpg);
205 if (!res)
206 res_sig = gpgme_op_sign_result (c->gpg);
207 if (!c->is_card && reg_prefs.cache_time > 0 &&
208 (res || res_sig)) {
209 if (agent_get_cache (c->keyid, &item))
210 agent_unlock_cache_entry (&item);
211 else
212 agent_put_cache (c->keyid, c->pwd,
213 reg_prefs.cache_time);
214 }
215 }
216 c->cancel = 0;
217 EndDialog (dlg, TRUE);
218 return TRUE;
219
220 case IDCANCEL:
221 SetDlgItemText (dlg, item_ctrl_id (c->gpg_cmd), "");
222 c->cancel = 1;
223 EndDialog (dlg, FALSE);
224 return TRUE;
225 }
226 break;
227 }
228
229 return FALSE;
230 }
231
232
233 /* Extract the main keyid from @pass_info.
234 Return value: long main keyid or NULL for an error. */
235 static const char*
236 parse_gpg_keyid (const char *pass_info)
237 {
238 static char keyid[16+1];
239
240 /* XXX: check for leading alpha-chars? */
241 if (strlen (pass_info) < 16) {
242 log_debug ("parse_gpg_keyid: error '%s'\r\n", pass_info);
243 return NULL;
244 }
245 /* the format of the desc buffer looks like this:
246 request_keyid[16] main_keyid[16] keytype[1] keylength[4]
247 we use the main keyid to use only one cache entry. */
248 strncpy (keyid, pass_info+17, 16);
249 keyid[16] = 0;
250 return keyid;
251 }
252
253
254 /* Parse the information in @uid_hint and @pass_info to generate
255 a input message for the user in @desc. */
256 static int
257 parse_gpg_description (const char *uid_hint, const char *pass_info,
258 char *desc, size_t desc_size)
259 {
260 gpgme_pubkey_algo_t algo;
261 char usedkey[16+1];
262 char mainkey[16+1];
263 char *p, *uid;
264 int n=0;
265
266 algo = (gpgme_pubkey_algo_t)0;
267 /* Each uid_hint contains a long key-ID so it is at least 16 bytes. */
268 if (strlen (uid_hint) < 17) {
269 *desc = 0;
270 log_debug ("parse_gpg_description: error '%s'\r\n", uid_hint);
271 return -1;
272 }
273
274 while ((p = strsep ((char**)&pass_info, " "))) {
275 switch (n++) {
276 case 0:
277 strncpy (mainkey, p, 16);
278 mainkey[16] = 0;
279 break;
280
281 case 1:
282 strncpy (usedkey, p, 16);
283 usedkey[16] = 0;
284 break;
285
286 case 2:
287 algo = (gpgme_pubkey_algo_t)atol (p);
288 break;
289 }
290 }
291 uid_hint += 16; /* skip keyid */
292 uid_hint += 1; /* space */
293
294 struct winpt_key_s skey;
295 gpgme_subkey_t sk;
296 if (winpt_get_seckey (mainkey, &skey))
297 BUG (0);
298 for (sk = skey.ctx->subkeys; sk; sk = sk->next) {
299 if (memcmp (sk->keyid, usedkey, 8) == 0)
300 break;
301 }
302 uid = utf8_to_native (uid_hint);
303 if (strcmp (usedkey, mainkey))
304 _snprintf (desc, desc_size-1,
305 _("You need a passphrase to unlock the secret key for user:\n"
306 "\"%s\"\n"
307 "%d-bit %s key, ID 0x%s, created %s (main key ID 0x%s)\n"),
308 uid, sk->length, get_key_pubalgo (algo),
309 usedkey+8, get_key_created (sk->timestamp), mainkey+8);
310 else if (!strcmp (usedkey, mainkey))
311 _snprintf (desc, desc_size-1,
312 _("You need a passphrase to unlock the secret key for user:\n"
313 "\"%s\"\n"
314 "%d-bit %s key, created %s, ID 0x%s\n"),
315 uid, sk->length, get_key_pubalgo (algo),
316 get_key_created (sk->timestamp), usedkey+8);
317 safe_free (uid);
318 return 0;
319 }
320
321
322 /* Extract the serial number from the card ID @id and return it. */
323 const char*
324 extract_serial_no (const char *id)
325 {
326 static char buf[8];
327 char *p;
328
329 p = strchr (id, '/');
330 if (!p) {
331 log_debug ("extract_serial_no: error '%s'\r\n", id);
332 return "";
333 }
334 memset (buf, 0, sizeof (buf));
335 strncpy (buf, id+(p-id)-6, 6);
336 return buf;
337 }
338
339
340 /* Passphrase callback with the ability to support caching. */
341 gpgme_error_t
342 passphrase_cb (void *hook, const char *uid_hint,
343 const char *passphrase_info,
344 int prev_was_bad, int fd)
345 {
346 passphrase_cb_s *c = (passphrase_cb_s*)hook;
347 HANDLE hd = (HANDLE)fd;
348 void *item;
349 const char *keyid=NULL, *pass;
350 DWORD n;
351
352 if (!c) {
353 log_debug ("passphrase_cb: error no valid callback\r\n");
354 return gpg_error (GPG_ERR_INV_ARG);
355 }
356
357 /* If the last entered passphrase was wrong, we delete a
358 possible cached entry, we reset the passphrase buffer
359 and switch to the initial state. */
360 c->bad_pwd = prev_was_bad? 1 : 0;
361 if (prev_was_bad && !c->cancel) {
362 if (c->pwd)
363 burn_passphrase (&c->pwd);
364 agent_del_cache (c->keyid);
365 c->pwd_init = 1;
366 }
367
368 if (passphrase_info) {
369 if (strlen (passphrase_info) < 16 &&
370 !strstr (passphrase_info, "OPENPGP")) {
371 /* assume symetric encryption. */
372 int pos=2;
373 c->sym.sym_algo = atoi (passphrase_info);
374 if (c->sym.sym_algo > 9)
375 pos++;
376 c->sym.s2k_mode = atoi (passphrase_info+pos);
377 c->sym.s2k_hash = atoi (passphrase_info+pos+2);
378 }
379
380 keyid = parse_gpg_keyid (passphrase_info);
381 pass = agent_get_cache (keyid+8, &item);
382 if (pass) {
383 agent_unlock_cache_entry (&item);
384 c->pwd_init = 0;
385 if (!WriteFile (hd, pass, strlen (pass), &n, NULL))
386 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
387 if (!WriteFile (hd, "\n", 1, &n, NULL))
388 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
389 return 0;
390 }
391 }
392
393 if (c->pwd_init) {
394 if (keyid && strlen (keyid) == 16)
395 strcpy (c->keyid, keyid+8);
396
397 /* if @passphrase_info contains 'OPENPGP' we assume a smart card
398 has been used. */
399 if (strstr (passphrase_info, "OPENPGP")) {
400 const char *s=passphrase_info;
401 while (s && *s && *s != 'D')
402 s++;
403 _snprintf (c->info, DIM (c->info)-1,
404 _("Please enter the PIN to unlock your secret card key\n"
405 "Card: %s"), extract_serial_no (s));
406 c->is_card = 1;
407 }
408 else if (uid_hint)
409 parse_gpg_description (uid_hint, passphrase_info,
410 c->info, DIM (c->info) - 1);
411 int rc = 0;
412 if (c->gpg_cmd == GPG_CMD_DECRYPT) {
413 rc = DialogBoxParam (glob_hinst, (LPCSTR)IDD_WINPT_DECRYPT,
414 (HWND)c->hwnd, passphrase_callback_proc,
415 (LPARAM)c);
416 }
417 else if (c->gpg_cmd == GPG_CMD_SIGN) {
418 rc = DialogBoxParam (glob_hinst, (LPCSTR)IDD_WINPT_DECRYPT_SIGN,
419 (HWND)c->hwnd, passphrase_callback_proc,
420 (LPARAM)c);
421 }
422 if (rc == -1) {
423 if (!WriteFile (hd, "\n", 1, &n, NULL))
424 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
425 log_debug ("passphrase_cb: could not create dialog box\n");
426 return 0;
427 }
428 c->pwd_init = 0;
429 }
430 if (c->cancel || !c->pwd) {
431 if (!WriteFile (hd, "\n", 1, &n, NULL))
432 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
433 return 0;
434 }
435
436 if (!WriteFile (hd, c->pwd, strlen (c->pwd), &n, NULL))
437 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
438 if (!WriteFile (hd, "\n", 1, &n, NULL))
439 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
440 return 0;
441 }
442
443
444 /* Initialize the given passphrase callback @cb with the
445 used gpgme context @ctx, the command @cmd and a title
446 @title for the dialog. */
447 void
448 set_gpg_passphrase_cb (passphrase_cb_s *cb, gpgme_ctx_t ctx,
449 int cmd, HWND hwnd, const char *title)
450 {
451 memset (cb, 0, sizeof *cb);
452 cb->gpg_cmd = cmd;
453 cb->bad_pwd = 0;
454 cb->is_card = 0;
455 cb->cancel = 0;
456 cb->hwnd = hwnd;
457 cb->pwd_init = 1;
458 cb->title = m_strdup (title);
459 gpgme_set_passphrase_cb (ctx, passphrase_cb, cb);
460 cb->gpg = ctx;
461 }
462
463 void
464 set_gpg_auto_passphrase_cb (passphrase_cb_s *cb, const char *title)
465 {
466 memset (cb, 0, sizeof *cb);
467 cb->gpg_cmd = GPG_CMD_SIGN;
468 cb->bad_pwd = 0;
469 cb->is_card = 0;
470 cb->cancel = 0;
471 cb->hwnd = GetActiveWindow ();
472 cb->pwd_init = 1;
473 cb->title = m_strdup (title);
474 }
475
476
477 /* Release a passphrase callback @ctx. */
478 void
479 release_gpg_passphrase_cb (passphrase_cb_s *ctx)
480 {
481 if (!ctx)
482 return;
483 sfree_if_alloc (ctx->pwd);
484 free_if_alloc (ctx->title);
485 release_gpg_recipients (&ctx->recipients);
486 }
487
488
489 /* Release the gpg recipient list. */
490 void
491 release_gpg_recipients (gpgme_recipient_t *recipients)
492 {
493 gpgme_recipient_t r, n;
494
495 r = *recipients;
496 while (r != NULL) {
497 n = r->next;
498 safe_free (r->keyid);
499 safe_free (r);
500 r = n;
501 }
502 *recipients = NULL;
503 }
504
505
506 /* _Simple_ check to measure passphrase (@pass) quality.
507 Return value: 0 on success. */
508 int
509 check_passwd_quality (const char *pass, int strict)
510 {
511 int i, nd=0, nc=0, n;
512
513 /* A good passphrase should be at least 8 characters. */
514 n = strlen (pass);
515 if (n < 8)
516 return -1;
517
518 for (i=0; i < n; i++) {
519 if (isdigit (pass[i]))
520 nd++;
521 if (isalpha (pass[i]))
522 nc++;
523 }
524
525 /* Check that the passphrase contains letters and numbers. */
526 if (nd == n || nc == n)
527 return -1;
528
529 return 0;
530 }

Properties

Name Value
svn:eol-style native

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26