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

Annotation of /trunk/Src/wptPassphraseCB.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 304 - (hide annotations)
Wed Mar 21 10:59:31 2007 UTC (17 years, 11 months ago) by twoaday
File size: 13998 byte(s)


1 werner 36 /* wptPassphraseCB.cpp - GPGME Passphrase Callback
2 twoaday 255 * Copyright (C) 2001, 2002-2006 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 void ListBox_AddString_utf8 (HWND lb, const char *txt);
50    
51 twoaday 255
52 werner 36 /* Overwrite passphrase and free memory. */
53     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     _("Encrypted with the following public key(s)"));
100     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     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 werner 36 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 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
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 werner 36 }
131     else {
132 twoaday 278 inf = _("Unknown key ID");
133     inf = inf + " (" + get_key_pubalgo (r->pubkey_algo);
134     inf = inf + ", 0x" + (r->keyid+8) + ")";
135 werner 36 }
136 twoaday 278 ListBox_AddString_utf8 (GetDlgItem (dlg, IDC_DECRYPT_LIST),
137     inf.getBuffer ());
138 twoaday 217 winpt_release_pubkey (&key);
139 werner 36 }
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 twoaday 278 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 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     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 twoaday 229 char *p = new char[n+2];
197     if (!p)
198 werner 36 BUG (NULL);
199 twoaday 229 /* 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 werner 36 }
205     res = gpgme_op_decrypt_result (c->gpg);
206     if (!res)
207 twoaday 271 res_sig = gpgme_op_sign_result (c->gpg);
208 twoaday 255 if (!c->is_card && reg_prefs.cache_time > 0 &&
209 twoaday 77 (res || res_sig)) {
210 twoaday 255 if (agent_get_cache (c->keyid, &item))
211 werner 36 agent_unlock_cache_entry (&item);
212 twoaday 77 else
213 werner 36 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 twoaday 271 return TRUE;
224 werner 36 }
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 twoaday 77 if (strlen (pass_info) < 16) {
241     log_debug ("parse_gpg_keyid: error '%s'\r\n", pass_info);
242 werner 36 return NULL;
243 twoaday 77 }
244 werner 36 /* 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 twoaday 205 char *p, *uid;
263 werner 36 int n=0;
264    
265 twoaday 69 algo = (gpgme_pubkey_algo_t)0;
266 werner 36 /* 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 twoaday 77 log_debug ("parse_gpg_description: error '%s'\r\n", uid_hint);
270 werner 36 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 twoaday 205 uid = utf8_to_native (uid_hint);
284 werner 36 if (strcmp (usedkey, mainkey))
285     _snprintf (desc, size-1,
286 twoaday 255 _("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 twoaday 205 uid, get_key_pubalgo (algo), usedkey+8, mainkey+8);
290 werner 36 else if (!strcmp (usedkey, mainkey))
291     _snprintf (desc, size-1,
292 twoaday 255 _("You need a passphrase to unlock the secret key for user:\n"
293     "\"%s\"\n"
294     "%s key, ID 0x%s\n"),
295 twoaday 205 uid, get_key_pubalgo (algo), usedkey+8);
296     safe_free (uid);
297 werner 36 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 twoaday 77 if (!p) {
310     log_debug ("extract_serial_no: error '%s'\r\n", id);
311 twoaday 260 return "";
312 twoaday 77 }
313 twoaday 260 memset (buf, 0, sizeof (buf));
314 werner 36 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 twoaday 69 const char *keyid=NULL, *pass;
329 werner 36 DWORD n;
330 twoaday 77 int rc = 0;
331 werner 36
332 twoaday 77 if (!c) {
333 twoaday 271 log_debug ("passphrase_cb: error no valid callback\r\n");
334 werner 36 return gpg_error (GPG_ERR_INV_ARG);
335 twoaday 77 }
336 werner 36 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 twoaday 69 int pos=2;
349 werner 36 c->sym.sym_algo = atoi (passphrase_info);
350     if (c->sym.sym_algo > 9)
351 twoaday 69 pos++;
352 werner 36 /* XXX: be more strict. */
353 twoaday 69 c->sym.s2k_mode = atoi (passphrase_info+pos);
354     c->sym.s2k_hash = atoi (passphrase_info+pos+2);
355 werner 36 }
356    
357     keyid = parse_gpg_keyid (passphrase_info);
358 twoaday 77 pass = agent_get_cache (keyid+8, &item);
359 werner 36 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 twoaday 304 _snprintf (c->info, DIM (c->info)-1,
381 werner 36 _("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 twoaday 181 c->info, sizeof (c->info) - 1);
388 werner 36 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 twoaday 271 log_debug ("passphrase_cb: could not create dialog box\n");
402 werner 36 return 0;
403     }
404     c->pwd_init = 0;
405     }
406 twoaday 214 if (c->cancel || !c->pwd) {
407 werner 36 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 twoaday 77 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 werner 36 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 twoaday 214 /* Release the gpg recipient list. */
442 werner 36 void
443 twoaday 214 release_gpg_recipients (gpgme_recipient_t *recipients)
444 werner 36 {
445     gpgme_recipient_t r, n;
446    
447 twoaday 214 r = *recipients;
448     while (r != NULL) {
449 werner 36 n = r->next;
450     safe_free (r->keyid);
451     safe_free (r);
452     r = n;
453     }
454 twoaday 214 *recipients = NULL;
455 werner 36 }
456    
457    
458 twoaday 214
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 twoaday 271 /* _Simple_ check to measure passphrase (@pass) quality.
472 werner 36 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 twoaday 271 /* A good passphrase should be at least 8 characters. */
479 werner 36 n = strlen (pass);
480     if (n < 8)
481     return -1;
482    
483     for (i=0; i < n; i++) {
484 twoaday 271 if (isdigit (pass[i]))
485 werner 36 nd++;
486 twoaday 271 if (isalpha (pass[i]))
487 werner 36 nc++;
488     }
489    
490 twoaday 271 /* Check that the passphrase contains letters and numbers. */
491 werner 36 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