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

Contents of /trunk/Src/wptPassphraseCB.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 344 - (show annotations)
Sun Nov 27 14:56:52 2011 UTC (13 years, 3 months ago) by twoaday
File size: 14834 byte(s)


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