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

Contents of /trunk/Src/wptPassphraseCB.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 340 - (show annotations)
Sun Nov 27 13:15:07 2011 UTC (13 years, 3 months ago) by twoaday
File size: 14790 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 c->pwd = m_strdup (p);
198 sfree_if_alloc (p);
199 }
200 if (c->gpg != NULL) {
201 res = gpgme_op_decrypt_result (c->gpg);
202 if (!res)
203 res_sig = gpgme_op_sign_result (c->gpg);
204 if (!c->is_card && reg_prefs.cache_time > 0 &&
205 (res || res_sig)) {
206 if (agent_get_cache (c->keyid, &item))
207 agent_unlock_cache_entry (&item);
208 else
209 agent_put_cache (c->keyid, c->pwd,
210 reg_prefs.cache_time);
211 }
212 }
213 c->cancel = 0;
214 EndDialog (dlg, TRUE);
215 return TRUE;
216
217 case IDCANCEL:
218 SetDlgItemText (dlg, item_ctrl_id (c->gpg_cmd), "");
219 c->cancel = 1;
220 EndDialog (dlg, FALSE);
221 return TRUE;
222 }
223 break;
224 }
225
226 return FALSE;
227 }
228
229
230 /* Extract the main keyid from @pass_info.
231 Return value: long main keyid or NULL for an error. */
232 static const char*
233 parse_gpg_keyid (const char *pass_info)
234 {
235 static char keyid[16+1];
236
237 /* XXX: check for leading alpha-chars? */
238 if (strlen (pass_info) < 16) {
239 log_debug ("parse_gpg_keyid: error '%s'\r\n", pass_info);
240 return NULL;
241 }
242 /* the format of the desc buffer looks like this:
243 request_keyid[16] main_keyid[16] keytype[1] keylength[4]
244 we use the main keyid to use only one cache entry. */
245 strncpy (keyid, pass_info+17, 16);
246 keyid[16] = 0;
247 return keyid;
248 }
249
250
251 /* Parse the information in @uid_hint and @pass_info to generate
252 a input message for the user in @desc. */
253 static int
254 parse_gpg_description (const char *uid_hint, const char *pass_info,
255 char *desc, size_t desc_size)
256 {
257 gpgme_pubkey_algo_t algo;
258 char usedkey[16+1];
259 char mainkey[16+1];
260 char *p, *uid;
261 int n=0;
262
263 algo = (gpgme_pubkey_algo_t)0;
264 /* Each uid_hint contains a long key-ID so it is at least 16 bytes. */
265 if (strlen (uid_hint) < 17) {
266 *desc = 0;
267 log_debug ("parse_gpg_description: error '%s'\r\n", uid_hint);
268 return -1;
269 }
270
271 while ((p = strsep ((char**)&pass_info, " "))) {
272 switch (n++) {
273 case 0:
274 strncpy (mainkey, p, 16);
275 mainkey[16] = 0;
276 break;
277
278 case 1:
279 strncpy (usedkey, p, 16);
280 usedkey[16] = 0;
281 break;
282
283 case 2:
284 algo = (gpgme_pubkey_algo_t)atol (p);
285 break;
286 }
287 }
288 uid_hint += 16; /* skip keyid */
289 uid_hint += 1; /* space */
290
291 struct winpt_key_s skey;
292 gpgme_subkey_t sk;
293 if (winpt_get_seckey (mainkey, &skey))
294 BUG (0);
295 for (sk = skey.ctx->subkeys; sk; sk = sk->next) {
296 if (memcmp (sk->keyid, usedkey, 8) == 0)
297 break;
298 }
299 uid = utf8_to_native (uid_hint);
300 if (strcmp (usedkey, mainkey))
301 _snprintf (desc, desc_size-1,
302 _("You need a passphrase to unlock the secret key for user:\n"
303 "\"%s\"\n"
304 "%d-bit %s key, ID 0x%s, created %s (main key ID 0x%s)\n"),
305 uid, sk->length, get_key_pubalgo (algo),
306 usedkey+8, get_key_created (sk->timestamp), mainkey+8);
307 else if (!strcmp (usedkey, mainkey))
308 _snprintf (desc, desc_size-1,
309 _("You need a passphrase to unlock the secret key for user:\n"
310 "\"%s\"\n"
311 "%d-bit %s key, created %s, ID 0x%s\n"),
312 uid, sk->length, get_key_pubalgo (algo),
313 get_key_created (sk->timestamp), usedkey+8);
314 safe_free (uid);
315 return 0;
316 }
317
318
319 /* Extract the serial number from the card ID @id and return it. */
320 const char*
321 extract_serial_no (const char *id)
322 {
323 static char buf[8];
324 char *p;
325
326 p = strchr (id, '/');
327 if (!p) {
328 log_debug ("extract_serial_no: error '%s'\r\n", id);
329 return "";
330 }
331 memset (buf, 0, sizeof (buf));
332 strncpy (buf, id+(p-id)-6, 6);
333 return buf;
334 }
335
336
337 /* Passphrase callback with the ability to support caching. */
338 gpgme_error_t
339 passphrase_cb (void *hook, const char *uid_hint,
340 const char *passphrase_info,
341 int prev_was_bad, int fd)
342 {
343 passphrase_cb_s *c = (passphrase_cb_s*)hook;
344 HANDLE hd = (HANDLE)fd;
345 void *item;
346 const char *keyid=NULL, *pass;
347 DWORD n;
348
349 if (!c) {
350 log_debug ("passphrase_cb: error no valid callback\r\n");
351 return gpg_error (GPG_ERR_INV_ARG);
352 }
353
354 /* If the last entered passphrase was wrong, we delete a
355 possible cached entry, we reset the passphrase buffer
356 and switch to the initial state. */
357 c->bad_pwd = prev_was_bad? 1 : 0;
358 if (prev_was_bad && !c->cancel) {
359 if (c->pwd)
360 burn_passphrase (&c->pwd);
361 agent_del_cache (c->keyid);
362 c->pwd_init = 1;
363 }
364
365 if (passphrase_info) {
366 if (strlen (passphrase_info) < 16 &&
367 !strstr (passphrase_info, "OPENPGP")) {
368 /* assume symetric encryption. */
369 int pos=2;
370 c->sym.sym_algo = atoi (passphrase_info);
371 if (c->sym.sym_algo > 9)
372 pos++;
373 c->sym.s2k_mode = atoi (passphrase_info+pos);
374 c->sym.s2k_hash = atoi (passphrase_info+pos+2);
375 }
376
377 keyid = parse_gpg_keyid (passphrase_info);
378 pass = agent_get_cache (keyid+8, &item);
379 if (pass) {
380 agent_unlock_cache_entry (&item);
381 c->pwd_init = 0;
382 if (!WriteFile (hd, pass, strlen (pass), &n, NULL))
383 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
384 if (!WriteFile (hd, "\n", 1, &n, NULL))
385 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
386 return 0;
387 }
388 }
389
390 if (c->pwd_init) {
391 if (keyid && strlen (keyid) == 16)
392 strcpy (c->keyid, keyid+8);
393
394 /* if @passphrase_info contains 'OPENPGP' we assume a smart card
395 has been used. */
396 if (strstr (passphrase_info, "OPENPGP")) {
397 const char *s=passphrase_info;
398 while (s && *s && *s != 'D')
399 s++;
400 _snprintf (c->info, DIM (c->info)-1,
401 _("Please enter the PIN to unlock your secret card key\n"
402 "Card: %s"), extract_serial_no (s));
403 c->is_card = 1;
404 }
405 else if (uid_hint)
406 parse_gpg_description (uid_hint, passphrase_info,
407 c->info, DIM (c->info) - 1);
408 int rc = 0;
409 if (c->gpg_cmd == GPG_CMD_DECRYPT) {
410 rc = DialogBoxParam (glob_hinst, (LPCSTR)IDD_WINPT_DECRYPT,
411 (HWND)c->hwnd, passphrase_callback_proc,
412 (LPARAM)c);
413 }
414 else if (c->gpg_cmd == GPG_CMD_SIGN) {
415 rc = DialogBoxParam (glob_hinst, (LPCSTR)IDD_WINPT_DECRYPT_SIGN,
416 (HWND)c->hwnd, passphrase_callback_proc,
417 (LPARAM)c);
418 }
419 if (rc == -1) {
420 if (!WriteFile (hd, "\n", 1, &n, NULL))
421 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
422 log_debug ("passphrase_cb: could not create dialog box\n");
423 return 0;
424 }
425 c->pwd_init = 0;
426 }
427 if (c->cancel || !c->pwd) {
428 if (!WriteFile (hd, "\n", 1, &n, NULL))
429 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
430 return 0;
431 }
432
433 if (!WriteFile (hd, c->pwd, strlen (c->pwd), &n, NULL))
434 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
435 if (!WriteFile (hd, "\n", 1, &n, NULL))
436 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
437 return 0;
438 }
439
440
441 /* Initialize the given passphrase callback @cb with the
442 used gpgme context @ctx, the command @cmd and a title
443 @title for the dialog. */
444 void
445 set_gpg_passphrase_cb (passphrase_cb_s *cb, gpgme_ctx_t ctx,
446 int cmd, HWND hwnd, const char *title)
447 {
448 memset (cb, 0, sizeof *cb);
449 cb->gpg_cmd = cmd;
450 cb->bad_pwd = 0;
451 cb->is_card = 0;
452 cb->cancel = 0;
453 cb->hwnd = hwnd;
454 cb->pwd_init = 1;
455 cb->title = m_strdup (title);
456 gpgme_set_passphrase_cb (ctx, passphrase_cb, cb);
457 cb->gpg = ctx;
458 }
459
460 void
461 set_gpg_auto_passphrase_cb (passphrase_cb_s *cb, const char *title)
462 {
463 memset (cb, 0, sizeof *cb);
464 cb->gpg_cmd = GPG_CMD_SIGN;
465 cb->bad_pwd = 0;
466 cb->is_card = 0;
467 cb->cancel = 0;
468 cb->hwnd = GetActiveWindow ();
469 cb->pwd_init = 1;
470 cb->title = m_strdup (title);
471 }
472
473
474 /* Release a passphrase callback @ctx. */
475 void
476 release_gpg_passphrase_cb (passphrase_cb_s *ctx)
477 {
478 if (!ctx)
479 return;
480 sfree_if_alloc (ctx->pwd);
481 free_if_alloc (ctx->title);
482 release_gpg_recipients (&ctx->recipients);
483 }
484
485
486 /* Release the gpg recipient list. */
487 void
488 release_gpg_recipients (gpgme_recipient_t *recipients)
489 {
490 gpgme_recipient_t r, n;
491
492 r = *recipients;
493 while (r != NULL) {
494 n = r->next;
495 safe_free (r->keyid);
496 safe_free (r);
497 r = n;
498 }
499 *recipients = NULL;
500 }
501
502
503 /* _Simple_ check to measure passphrase (@pass) quality.
504 Return value: 0 on success. */
505 int
506 check_passwd_quality (const char *pass, int strict)
507 {
508 int i, nd=0, nc=0, n;
509
510 /* A good passphrase should be at least 8 characters. */
511 n = strlen (pass);
512 if (n < 8)
513 return -1;
514
515 for (i=0; i < n; i++) {
516 if (isdigit (pass[i]))
517 nd++;
518 if (isalpha (pass[i]))
519 nc++;
520 }
521
522 /* Check that the passphrase contains letters and numbers. */
523 if (nd == n || nc == n)
524 return -1;
525
526 return 0;
527 }

Properties

Name Value
svn:eol-style native

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26