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

Contents of /trunk/Src/wptPassphraseCB.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 297 - (show annotations)
Sat Mar 17 22:13:40 2007 UTC (17 years, 11 months ago) by twoaday
File size: 14001 byte(s)


1 /* wptPassphraseCB.cpp - GPGME Passphrase Callback
2 * Copyright (C) 2001, 2002-2006 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 void ListBox_AddString_utf8 (HWND lb, const char *txt);
50
51
52 /* Overwrite passphrase and free memory. */
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)
111 recip = c->recipients; /* recipients were already extracted. */
112 else {
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
125 inf = (u->name? u->name : _("Invalid User ID"));
126 if (u->email != NULL && strlen (u->email) > 1)
127 inf = inf + " <" + u->email + ">";
128 inf = inf + " (" + get_key_pubalgo (r->pubkey_algo);
129 inf = inf + ", 0x" + (r->keyid+8) + ")";
130 }
131 else {
132 inf = _("Unknown key ID");
133 inf = inf + " (" + get_key_pubalgo (r->pubkey_algo);
134 inf = inf + ", 0x" + (r->keyid+8) + ")";
135 }
136 ListBox_AddString_utf8 (GetDlgItem (dlg, IDC_DECRYPT_LIST),
137 inf.getBuffer ());
138 winpt_release_pubkey (&key);
139 }
140 }
141 else if (c->gpg_cmd == GPG_CMD_DECRYPT)
142 EnableWindow (GetDlgItem (dlg, IDC_DECRYPT_LIST), FALSE);
143 SetDlgItemText (dlg, c->gpg_cmd == GPG_CMD_DECRYPT?
144 IDC_DECRYPT_PWDINFO : IDC_DECRYPT_SIGN_PWDINFO,
145 c->bad_pwd? _("Bad passphrase; Enter passphrase again") :
146 _("Please enter your passphrase"));
147 if (c->gpg_cmd == GPG_CMD_DECRYPT) {
148 SetFocus (GetDlgItem (dlg, IDC_DECRYPT_PWD));
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 = IsDlgButtonChecked (dlg, item_ctrl_id2 (c->gpg_cmd));
175 hwnd = GetDlgItem (dlg, item_ctrl_id (c->gpg_cmd));
176 SendMessage (hwnd, EM_SETPASSWORDCHAR, hide? '*' : 0, 0);
177 SetFocus (hwnd);
178 }
179 }
180
181 switch (LOWORD (wparam)) {
182 case IDOK:
183 /* XXX: the item is even cached when the passphrase is not
184 correct, which means that the user needs to delete all
185 cached entries to continue. */
186 if (c->pwd)
187 burn_passphrase (&c->pwd);
188 n = item_get_text_length (dlg, item_ctrl_id (c->gpg_cmd));
189 if (!n) {
190 c->pwd = new char[2];
191 if (!c->pwd)
192 BUG (NULL);
193 strcpy (c->pwd, "");
194 }
195 else {
196 char *p = new char[n+2];
197 if (!p)
198 BUG (NULL);
199 /* Get the passphrase and convert it utf8.
200 This does not just the us-ascii charset. */
201 SafeGetDlgItemText (dlg, item_ctrl_id (c->gpg_cmd), p, n+1);
202 c->pwd = native_to_utf8 (p);
203 sfree_if_alloc (p);
204 }
205 res = gpgme_op_decrypt_result (c->gpg);
206 if (!res)
207 res_sig = gpgme_op_sign_result (c->gpg);
208 if (!c->is_card && reg_prefs.cache_time > 0 &&
209 (res || res_sig)) {
210 if (agent_get_cache (c->keyid, &item))
211 agent_unlock_cache_entry (&item);
212 else
213 agent_put_cache (c->keyid, c->pwd, reg_prefs.cache_time);
214 }
215 c->cancel = 0;
216 EndDialog (dlg, TRUE);
217 return TRUE;
218
219 case IDCANCEL:
220 SetDlgItemText (dlg, item_ctrl_id (c->gpg_cmd), "");
221 c->cancel = 1;
222 EndDialog (dlg, FALSE);
223 return TRUE;
224 }
225 break;
226 }
227
228 return FALSE;
229 }
230
231
232 /* Extract the main keyid from @pass_info.
233 Return value: long main keyid or NULL for an error. */
234 static const char*
235 parse_gpg_keyid (const char *pass_info)
236 {
237 static char keyid[16+1];
238
239 /* XXX: check for leading alpha-chars? */
240 if (strlen (pass_info) < 16) {
241 log_debug ("parse_gpg_keyid: error '%s'\r\n", pass_info);
242 return NULL;
243 }
244 /* the format of the desc buffer looks like this:
245 request_keyid[16] main_keyid[16] keytype[1] keylength[4]
246 we use the main keyid to use only one cache entry. */
247 strncpy (keyid, pass_info+17, 16);
248 keyid[16] = 0;
249 return keyid;
250 }
251
252
253 /* Parse the information in @uid_hint and @pass_info to generate
254 a input message for the user in @desc. */
255 static int
256 parse_gpg_description (const char *uid_hint, const char *pass_info,
257 char *desc, int size)
258 {
259 gpgme_pubkey_algo_t algo;
260 char usedkey[16+1];
261 char mainkey[16+1];
262 char *p, *uid;
263 int n=0;
264
265 algo = (gpgme_pubkey_algo_t)0;
266 /* Each uid_hint contains a long key-ID so it is at least 16 bytes. */
267 if (strlen (uid_hint) < 17) {
268 *desc = 0;
269 log_debug ("parse_gpg_description: error '%s'\r\n", uid_hint);
270 return -1;
271 }
272
273 while (p = strsep ((char**)&pass_info, " ")) {
274 switch (n++) {
275 case 0: strncpy (mainkey, p, 16); mainkey[16] = 0; break;
276 case 1: strncpy (usedkey, p, 16); usedkey[16] = 0; break;
277 case 2: algo = (gpgme_pubkey_algo_t)atol (p); break;
278 }
279 }
280 uid_hint += 16; /* skip keyid */
281 uid_hint += 1; /* space */
282
283 uid = utf8_to_native (uid_hint);
284 if (strcmp (usedkey, mainkey))
285 _snprintf (desc, size-1,
286 _("You need a passphrase to unlock the secret key for user:\n"
287 "\"%s\"\n"
288 "%s key, ID 0x%s (main key ID 0x%s)\n"),
289 uid, get_key_pubalgo (algo), usedkey+8, mainkey+8);
290 else if (!strcmp (usedkey, mainkey))
291 _snprintf (desc, size-1,
292 _("You need a passphrase to unlock the secret key for user:\n"
293 "\"%s\"\n"
294 "%s key, ID 0x%s\n"),
295 uid, get_key_pubalgo (algo), usedkey+8);
296 safe_free (uid);
297 return 0;
298 }
299
300
301 /* Extract the serial number from the card ID @id and return it. */
302 const char*
303 extract_serial_no (const char *id)
304 {
305 static char buf[8];
306 char *p;
307
308 p = strchr (id, '/');
309 if (!p) {
310 log_debug ("extract_serial_no: error '%s'\r\n", id);
311 return "";
312 }
313 memset (buf, 0, sizeof (buf));
314 strncpy (buf, id+(p-id)-6, 6);
315 return buf;
316 }
317
318
319 /* Passphrase callback with the ability to support caching. */
320 gpgme_error_t
321 passphrase_cb (void *hook, const char *uid_hint,
322 const char *passphrase_info,
323 int prev_was_bad, int fd)
324 {
325 passphrase_cb_s *c = (passphrase_cb_s*)hook;
326 HANDLE hd = (HANDLE)fd;
327 void *item;
328 const char *keyid=NULL, *pass;
329 DWORD n;
330 int rc = 0;
331
332 if (!c) {
333 log_debug ("passphrase_cb: error no valid callback\r\n");
334 return gpg_error (GPG_ERR_INV_ARG);
335 }
336 c->bad_pwd = prev_was_bad? 1 : 0;
337 if (prev_was_bad && !c->cancel) {
338 if (c->pwd)
339 burn_passphrase (&c->pwd);
340 agent_del_cache (c->keyid);
341 c->pwd_init = 1;
342 }
343
344 if (passphrase_info) {
345 if (strlen (passphrase_info) < 16 &&
346 !strstr (passphrase_info, "OPENPGP")) {
347 /* assume symetric encryption. */
348 int pos=2;
349 c->sym.sym_algo = atoi (passphrase_info);
350 if (c->sym.sym_algo > 9)
351 pos++;
352 /* XXX: be more strict. */
353 c->sym.s2k_mode = atoi (passphrase_info+pos);
354 c->sym.s2k_hash = atoi (passphrase_info+pos+2);
355 }
356
357 keyid = parse_gpg_keyid (passphrase_info);
358 pass = agent_get_cache (keyid+8, &item);
359 if (pass) {
360 agent_unlock_cache_entry (&item);
361 c->pwd_init = 0;
362 if (!WriteFile (hd, pass, strlen (pass), &n, NULL))
363 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
364 if (!WriteFile (hd, "\n", 1, &n, NULL))
365 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
366 return 0;
367 }
368 }
369
370 if (c->pwd_init) {
371 if (keyid && strlen (keyid) == 16)
372 strcpy (c->keyid, keyid+8);
373
374 /* if @passphrase_info contains 'OPENPGP' we assume a smart card
375 has been used. */
376 if (strstr (passphrase_info, "OPENPGP")) {
377 const char *s=passphrase_info;
378 while (s && *s && *s != 'D')
379 s++;
380 _snprintf (c->info, sizeof (c->info)-1,
381 _("Please enter the PIN to unlock your secret card key\n"
382 "Card: %s"), extract_serial_no (s));
383 c->is_card = 1;
384 }
385 else if (uid_hint)
386 parse_gpg_description (uid_hint, passphrase_info,
387 c->info, sizeof (c->info) - 1);
388 if (c->gpg_cmd == GPG_CMD_DECRYPT) {
389 rc = DialogBoxParam (glob_hinst, (LPCSTR)IDD_WINPT_DECRYPT,
390 (HWND)c->hwnd, passphrase_callback_proc,
391 (LPARAM)c);
392 }
393 else if (c->gpg_cmd == GPG_CMD_SIGN) {
394 rc = DialogBoxParam (glob_hinst, (LPCSTR)IDD_WINPT_DECRYPT_SIGN,
395 (HWND)c->hwnd, passphrase_callback_proc,
396 (LPARAM)c);
397 }
398 if (rc == -1) {
399 if (!WriteFile (hd, "\n", 1, &n, NULL))
400 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
401 log_debug ("passphrase_cb: could not create dialog box\n");
402 return 0;
403 }
404 c->pwd_init = 0;
405 }
406 if (c->cancel || !c->pwd) {
407 if (!WriteFile (hd, "\n", 1, &n, NULL))
408 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
409 return 0;
410 }
411
412 if (!WriteFile (hd, c->pwd, strlen (c->pwd), &n, NULL))
413 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
414 if (!WriteFile (hd, "\n", 1, &n, NULL))
415 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
416 return 0;
417 }
418
419
420 /* Initialize the given passphrase callback @cb with the
421 used gpgme context @ctx, the command @cmd and a title
422 @title for the dialog. */
423 void
424 set_gpg_passphrase_cb (passphrase_cb_s *cb, gpgme_ctx_t ctx,
425 int cmd, HWND hwnd, const char *title)
426 {
427 memset (cb, 0, sizeof *cb);
428 cb->gpg_cmd = cmd;
429 cb->bad_pwd = 0;
430 cb->is_card = 0;
431 cb->cancel = 0;
432 cb->hwnd = hwnd;
433 cb->pwd_init = 1;
434 free_if_alloc (cb->title);
435 cb->title = m_strdup (title);
436 gpgme_set_passphrase_cb (ctx, passphrase_cb, cb);
437 cb->gpg = ctx;
438 }
439
440
441 /* Release the gpg recipient list. */
442 void
443 release_gpg_recipients (gpgme_recipient_t *recipients)
444 {
445 gpgme_recipient_t r, n;
446
447 r = *recipients;
448 while (r != NULL) {
449 n = r->next;
450 safe_free (r->keyid);
451 safe_free (r);
452 r = n;
453 }
454 *recipients = NULL;
455 }
456
457
458
459 /* Release a passphrase callback @ctx. */
460 void
461 release_gpg_passphrase_cb (passphrase_cb_s *ctx)
462 {
463 if (!ctx)
464 return;
465 sfree_if_alloc (ctx->pwd);
466 free_if_alloc (ctx->title);
467 release_gpg_recipients (&ctx->recipients);
468 }
469
470
471 /* _Simple_ check to measure passphrase (@pass) quality.
472 Return value: 0 on success. */
473 int
474 check_passwd_quality (const char *pass, int strict)
475 {
476 int i, nd=0, nc=0, n;
477
478 /* A good passphrase should be at least 8 characters. */
479 n = strlen (pass);
480 if (n < 8)
481 return -1;
482
483 for (i=0; i < n; i++) {
484 if (isdigit (pass[i]))
485 nd++;
486 if (isalpha (pass[i]))
487 nc++;
488 }
489
490 /* Check that the passphrase contains letters and numbers. */
491 if (nd == n || nc == n)
492 return -1;
493
494 return 0;
495 }

Properties

Name Value
svn:eol-style native

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26