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

Annotation of /trunk/Src/wptPassphraseCB.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 328 - (hide annotations)
Fri Sep 25 16:07:38 2009 UTC (15 years, 5 months ago) by twoaday
File size: 14498 byte(s)


1 werner 36 /* wptPassphraseCB.cpp - GPGME Passphrase Callback
2 twoaday 328 * Copyright (C) 2001, 2002-2006, 2009 Timo Schulz
3 werner 36 * 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 werner 47 #include "resource.h"
25 werner 36 #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 werner 48 #include "wptKeylist.h"
35 werner 36 #include "wptAgent.h"
36     #include "wptRegistry.h"
37 twoaday 205 #include "wptUTF8.h"
38 twoaday 278 #include "StringBuffer.h"
39 werner 36
40    
41 twoaday 255 /* Return the control ID dependent on the mode (sign or decrypt). */
42 twoaday 181 #define item_ctrl_id(cmd) \
43 werner 36 ((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 twoaday 255 const char* get_symkey_algo (int algo);
49 twoaday 204
50 twoaday 255
51 twoaday 328 /* Overwrites the passphrase and free the memory.
52     Pointer is also reset to NULL. */
53 werner 36 static void
54     burn_passphrase (char **pwd)
55     {
56     char *pass = *pwd;
57 twoaday 271
58 werner 36 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 twoaday 77 static passphrase_cb_s *c;
69     gpgme_decrypt_result_t res=NULL;
70     gpgme_sign_result_t res_sig=NULL;
71 twoaday 69 gpgme_recipient_t recip=NULL, r;
72 twoaday 217 winpt_key_s key;
73 twoaday 69 void *item;
74 werner 36 int n;
75    
76     switch (msg) {
77 twoaday 181 case WM_ACTIVATE:
78 twoaday 297 /* 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 twoaday 181 break;
84    
85     case WM_DESTROY:
86 twoaday 297 if (!reg_prefs.no_safe_pwd_ctrl)
87     safe_edit_control_free (dlg, item_ctrl_id (c->gpg_cmd));
88 twoaday 181 break;
89    
90 werner 36 case WM_INITDIALOG:
91     c = (passphrase_cb_s *)lparam;
92     if (!c)
93     BUG (0);
94 twoaday 105 SetDlgItemText (dlg, IDCANCEL, _("&Cancel"));
95 werner 36 SetWindowText (dlg, c->title);
96     if (c->gpg_cmd == GPG_CMD_DECRYPT) {
97 twoaday 214 SetDlgItemText (dlg, IDC_DECRYPT_HIDE, _("&Hide Typing"));
98 werner 36 SetDlgItemText (dlg, IDC_DECRYPT_LISTINF,
99 twoaday 328 _("Encrypted with the following public key(s):"));
100 werner 36 CheckDlgButton (dlg, IDC_DECRYPT_HIDE, BST_CHECKED);
101     }
102 twoaday 95 else if (c->gpg_cmd == GPG_CMD_SIGN) {
103     SetDlgItemText (dlg, IDC_DECRYPT_SIGN_HIDE, _("&Hide Typing"));
104 werner 36 CheckDlgButton (dlg, IDC_DECRYPT_SIGN_HIDE, BST_CHECKED);
105 twoaday 95 }
106 twoaday 179 /* Because it depends on the order the keys are stored in the
107 twoaday 328 keyring, whether res->recipients is complete or not, we also
108 twoaday 179 support that the recipients were externally extracted and then
109     we use this list. */
110 twoaday 328 if (c->recipients) /* recipients were already extracted. */
111     recip = c->recipients;
112     else if (c->gpg) {
113 werner 36 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 twoaday 278 StringBuffer inf;
119    
120 twoaday 179 for (r = recip; r; r = r->next) {
121 twoaday 217 memset (&key, 0, sizeof (key));
122     if (!winpt_get_pubkey (r->keyid, &key)) {
123     gpgme_user_id_t u = key.ctx->uids;
124 twoaday 278 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 werner 36 }
130     else {
131 twoaday 278 inf = _("Unknown key ID");
132     inf = inf + " (" + get_key_pubalgo (r->pubkey_algo);
133     inf = inf + ", 0x" + (r->keyid+8) + ")";
134 werner 36 }
135 twoaday 278 ListBox_AddString_utf8 (GetDlgItem (dlg, IDC_DECRYPT_LIST),
136     inf.getBuffer ());
137 twoaday 217 winpt_release_pubkey (&key);
138 werner 36 }
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 twoaday 328 c->bad_pwd? _("Invalid passphrase; Please enter your passphrase again") :
145 werner 36 _("Please enter your passphrase"));
146     if (c->gpg_cmd == GPG_CMD_DECRYPT) {
147     SetFocus (GetDlgItem (dlg, IDC_DECRYPT_PWD));
148 twoaday 328 /* no recipients means symmetric encryption */
149     if (res && !res->recipients) {
150 twoaday 278 StringBuffer sinf;
151    
152     sinf = _("Symmetric encryption.");
153     sinf = sinf + "\n" + get_symkey_algo (c->sym.sym_algo);
154 twoaday 328 sinf = sinf + " " + _("encrypted data") + ".";
155 twoaday 278 SetDlgItemText (dlg, IDC_DECRYPT_MSG, sinf.getBuffer ());
156 werner 36 }
157     else
158 twoaday 205 SetDlgItemText (dlg, IDC_DECRYPT_MSG, c->info);
159 werner 36 }
160     else {
161 twoaday 204 SetFocus (GetDlgItem (dlg, IDC_DECRYPT_SIGN_PWD));
162 twoaday 205 SetDlgItemText (dlg, IDC_DECRYPT_SIGN_MSG, c->info);
163 werner 36 }
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 twoaday 328 if (LOWORD (wparam) == IDC_DECRYPT_HIDE ||
172     LOWORD (wparam) == IDC_DECRYPT_SIGN_HIDE) {
173 werner 36 HWND hwnd;
174 twoaday 328 int hide;
175     hide = IsDlgButtonChecked (dlg, item_ctrl_id2 (c->gpg_cmd));
176 werner 36 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 twoaday 328 case IDOK:
184 werner 36 /* 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 twoaday 229 char *p = new char[n+2];
196     SafeGetDlgItemText (dlg, item_ctrl_id (c->gpg_cmd), p, n+1);
197 twoaday 328 if (emulate_utf8_bug)
198     c->pwd = native_to_utf8 (p);
199     else
200     c->pwd = m_strdup (p);
201 twoaday 229 sfree_if_alloc (p);
202 werner 36 }
203 twoaday 328 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 werner 36 }
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 twoaday 271 return TRUE;
225 werner 36 }
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 twoaday 77 if (strlen (pass_info) < 16) {
242     log_debug ("parse_gpg_keyid: error '%s'\r\n", pass_info);
243 werner 36 return NULL;
244 twoaday 77 }
245 werner 36 /* 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 twoaday 328 char *desc, size_t desc_size)
259 werner 36 {
260     gpgme_pubkey_algo_t algo;
261     char usedkey[16+1];
262     char mainkey[16+1];
263 twoaday 205 char *p, *uid;
264 werner 36 int n=0;
265    
266 twoaday 69 algo = (gpgme_pubkey_algo_t)0;
267 werner 36 /* 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 twoaday 77 log_debug ("parse_gpg_description: error '%s'\r\n", uid_hint);
271 werner 36 return -1;
272     }
273    
274 twoaday 328 while ((p = strsep ((char**)&pass_info, " "))) {
275 werner 36 switch (n++) {
276 twoaday 328 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 werner 36 }
290     }
291     uid_hint += 16; /* skip keyid */
292     uid_hint += 1; /* space */
293    
294 twoaday 205 uid = utf8_to_native (uid_hint);
295 werner 36 if (strcmp (usedkey, mainkey))
296 twoaday 328 _snprintf (desc, desc_size-1,
297 twoaday 255 _("You need a passphrase to unlock the secret key for user:\n"
298     "\"%s\"\n"
299     "%s key, ID 0x%s (main key ID 0x%s)\n"),
300 twoaday 205 uid, get_key_pubalgo (algo), usedkey+8, mainkey+8);
301 werner 36 else if (!strcmp (usedkey, mainkey))
302 twoaday 328 _snprintf (desc, desc_size-1,
303 twoaday 255 _("You need a passphrase to unlock the secret key for user:\n"
304     "\"%s\"\n"
305     "%s key, ID 0x%s\n"),
306 twoaday 205 uid, get_key_pubalgo (algo), usedkey+8);
307     safe_free (uid);
308 werner 36 return 0;
309     }
310    
311    
312     /* Extract the serial number from the card ID @id and return it. */
313     const char*
314     extract_serial_no (const char *id)
315     {
316     static char buf[8];
317     char *p;
318    
319     p = strchr (id, '/');
320 twoaday 77 if (!p) {
321     log_debug ("extract_serial_no: error '%s'\r\n", id);
322 twoaday 260 return "";
323 twoaday 77 }
324 twoaday 260 memset (buf, 0, sizeof (buf));
325 werner 36 strncpy (buf, id+(p-id)-6, 6);
326     return buf;
327     }
328    
329    
330     /* Passphrase callback with the ability to support caching. */
331     gpgme_error_t
332     passphrase_cb (void *hook, const char *uid_hint,
333     const char *passphrase_info,
334     int prev_was_bad, int fd)
335     {
336     passphrase_cb_s *c = (passphrase_cb_s*)hook;
337     HANDLE hd = (HANDLE)fd;
338     void *item;
339 twoaday 69 const char *keyid=NULL, *pass;
340 werner 36 DWORD n;
341    
342 twoaday 77 if (!c) {
343 twoaday 271 log_debug ("passphrase_cb: error no valid callback\r\n");
344 werner 36 return gpg_error (GPG_ERR_INV_ARG);
345 twoaday 77 }
346 twoaday 328
347     /* If the last entered passphrase was wrong, we delete a
348     possible cached entry, we reset the passphrase buffer
349     and switch to the initial state. */
350 werner 36 c->bad_pwd = prev_was_bad? 1 : 0;
351     if (prev_was_bad && !c->cancel) {
352     if (c->pwd)
353     burn_passphrase (&c->pwd);
354     agent_del_cache (c->keyid);
355     c->pwd_init = 1;
356     }
357    
358     if (passphrase_info) {
359     if (strlen (passphrase_info) < 16 &&
360     !strstr (passphrase_info, "OPENPGP")) {
361     /* assume symetric encryption. */
362 twoaday 69 int pos=2;
363 werner 36 c->sym.sym_algo = atoi (passphrase_info);
364     if (c->sym.sym_algo > 9)
365 twoaday 69 pos++;
366     c->sym.s2k_mode = atoi (passphrase_info+pos);
367     c->sym.s2k_hash = atoi (passphrase_info+pos+2);
368 werner 36 }
369    
370     keyid = parse_gpg_keyid (passphrase_info);
371 twoaday 77 pass = agent_get_cache (keyid+8, &item);
372 werner 36 if (pass) {
373     agent_unlock_cache_entry (&item);
374     c->pwd_init = 0;
375     if (!WriteFile (hd, pass, strlen (pass), &n, NULL))
376     log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
377     if (!WriteFile (hd, "\n", 1, &n, NULL))
378     log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
379     return 0;
380     }
381     }
382    
383     if (c->pwd_init) {
384     if (keyid && strlen (keyid) == 16)
385     strcpy (c->keyid, keyid+8);
386    
387     /* if @passphrase_info contains 'OPENPGP' we assume a smart card
388     has been used. */
389     if (strstr (passphrase_info, "OPENPGP")) {
390     const char *s=passphrase_info;
391     while (s && *s && *s != 'D')
392     s++;
393 twoaday 304 _snprintf (c->info, DIM (c->info)-1,
394 werner 36 _("Please enter the PIN to unlock your secret card key\n"
395     "Card: %s"), extract_serial_no (s));
396     c->is_card = 1;
397     }
398     else if (uid_hint)
399     parse_gpg_description (uid_hint, passphrase_info,
400 twoaday 328 c->info, DIM (c->info) - 1);
401     int rc = 0;
402 werner 36 if (c->gpg_cmd == GPG_CMD_DECRYPT) {
403     rc = DialogBoxParam (glob_hinst, (LPCSTR)IDD_WINPT_DECRYPT,
404     (HWND)c->hwnd, passphrase_callback_proc,
405     (LPARAM)c);
406     }
407     else if (c->gpg_cmd == GPG_CMD_SIGN) {
408     rc = DialogBoxParam (glob_hinst, (LPCSTR)IDD_WINPT_DECRYPT_SIGN,
409     (HWND)c->hwnd, passphrase_callback_proc,
410     (LPARAM)c);
411     }
412     if (rc == -1) {
413     if (!WriteFile (hd, "\n", 1, &n, NULL))
414     log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
415 twoaday 271 log_debug ("passphrase_cb: could not create dialog box\n");
416 werner 36 return 0;
417     }
418     c->pwd_init = 0;
419     }
420 twoaday 214 if (c->cancel || !c->pwd) {
421 werner 36 if (!WriteFile (hd, "\n", 1, &n, NULL))
422     log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
423     return 0;
424     }
425    
426 twoaday 77 if (!WriteFile (hd, c->pwd, strlen (c->pwd), &n, NULL))
427     log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
428     if (!WriteFile (hd, "\n", 1, &n, NULL))
429     log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
430 werner 36 return 0;
431     }
432    
433    
434     /* Initialize the given passphrase callback @cb with the
435     used gpgme context @ctx, the command @cmd and a title
436     @title for the dialog. */
437     void
438     set_gpg_passphrase_cb (passphrase_cb_s *cb, gpgme_ctx_t ctx,
439     int cmd, HWND hwnd, const char *title)
440 twoaday 328 {
441 werner 36 memset (cb, 0, sizeof *cb);
442     cb->gpg_cmd = cmd;
443     cb->bad_pwd = 0;
444     cb->is_card = 0;
445     cb->cancel = 0;
446     cb->hwnd = hwnd;
447     cb->pwd_init = 1;
448     cb->title = m_strdup (title);
449     gpgme_set_passphrase_cb (ctx, passphrase_cb, cb);
450     cb->gpg = ctx;
451     }
452    
453 twoaday 328 void
454     set_gpg_auto_passphrase_cb (passphrase_cb_s *cb, const char *title)
455     {
456     memset (cb, 0, sizeof *cb);
457     cb->gpg_cmd = GPG_CMD_SIGN;
458     cb->bad_pwd = 0;
459     cb->is_card = 0;
460     cb->cancel = 0;
461     cb->hwnd = GetActiveWindow ();
462     cb->pwd_init = 1;
463     cb->title = m_strdup (title);
464     }
465 werner 36
466 twoaday 328
467     /* Release a passphrase callback @ctx. */
468     void
469     release_gpg_passphrase_cb (passphrase_cb_s *ctx)
470     {
471     if (!ctx)
472     return;
473     sfree_if_alloc (ctx->pwd);
474     free_if_alloc (ctx->title);
475     release_gpg_recipients (&ctx->recipients);
476     }
477    
478    
479 twoaday 214 /* Release the gpg recipient list. */
480 werner 36 void
481 twoaday 214 release_gpg_recipients (gpgme_recipient_t *recipients)
482 werner 36 {
483     gpgme_recipient_t r, n;
484    
485 twoaday 214 r = *recipients;
486     while (r != NULL) {
487 werner 36 n = r->next;
488     safe_free (r->keyid);
489     safe_free (r);
490     r = n;
491     }
492 twoaday 214 *recipients = NULL;
493 werner 36 }
494    
495    
496 twoaday 271 /* _Simple_ check to measure passphrase (@pass) quality.
497 werner 36 Return value: 0 on success. */
498     int
499     check_passwd_quality (const char *pass, int strict)
500     {
501     int i, nd=0, nc=0, n;
502    
503 twoaday 271 /* A good passphrase should be at least 8 characters. */
504 werner 36 n = strlen (pass);
505     if (n < 8)
506     return -1;
507    
508     for (i=0; i < n; i++) {
509 twoaday 271 if (isdigit (pass[i]))
510 werner 36 nd++;
511 twoaday 271 if (isalpha (pass[i]))
512 werner 36 nc++;
513     }
514    
515 twoaday 271 /* Check that the passphrase contains letters and numbers. */
516 werner 36 if (nd == n || nc == n)
517     return -1;
518    
519     return 0;
520     }

Properties

Name Value
svn:eol-style native

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26