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

Contents of /trunk/Src/wptPassphraseCB.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 26 - (show annotations)
Mon Oct 17 08:49:30 2005 UTC (19 years, 4 months ago) by twoaday
File size: 12503 byte(s)
More bug fixes all over the place.
See ChangeLog for details.

1 /* wptPassphraseCB.cpp - GPGME Passphrase Callback
2 * Copyright (C) 2001, 2002, 2003, 2005 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 * You should have received a copy of the GNU General Public License
18 * along with WinPT; if not, write to the Free Software Foundation,
19 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #include <windows.h>
23 #include <ctype.h>
24
25 #include "../resource.h"
26 #include "wptNLS.h"
27 #include "wptW32API.h"
28 #include "wptVersion.h"
29 #include "wptGPG.h"
30 #include "wptCommonCtl.h"
31 #include "wptContext.h"
32 #include "wptDlgs.h"
33 #include "wptUTF8.h"
34 #include "wptErrors.h"
35 #include "wptTypes.h"
36 #include "wptKeyList.h"
37 #include "wptAgent.h"
38 #include "wptRegistry.h"
39
40 const char* get_symkey_algo (int algo);
41
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
49 /* Overwrite passphrase and free memory. */
50 static void
51 burn_passphrase (char **pwd)
52 {
53 char *pass = *pwd;
54 memset (pass, 0, strlen (pass));
55 delete []pass;
56 *pwd = NULL;
57 }
58
59
60 /* Dialog procedure for the passphrase callback. */
61 static BOOL CALLBACK
62 passphrase_callback_proc (HWND dlg, UINT msg, WPARAM wparam, LPARAM lparam)
63 {
64 static passphrase_cb_s * c;
65 gpgme_decrypt_result_t res;
66 gpgme_sign_result_t res_sig;
67 gpgme_key_t key;
68 void *ctx = NULL, *item;
69 const char *id;
70 char *info;
71 int n;
72
73 switch (msg) {
74 case WM_INITDIALOG:
75 c = (passphrase_cb_s *)lparam;
76 if (!c)
77 BUG (0);
78 SetWindowText (dlg, c->title);
79 if (c->gpg_cmd == GPG_CMD_DECRYPT) {
80 SetDlgItemText( dlg, IDC_DECRYPT_LISTINF,
81 _("Encrypted with the following public key(s)") );
82 CheckDlgButton( dlg, IDC_DECRYPT_HIDE, BST_CHECKED );
83 }
84 else if (c->gpg_cmd == GPG_CMD_SIGN)
85 CheckDlgButton (dlg, IDC_DECRYPT_SIGN_HIDE, BST_CHECKED);
86 res = gpgme_op_decrypt_result (c->gpg);
87 if (res != NULL && c->gpg_cmd == GPG_CMD_DECRYPT) {
88 gpgme_recipient_t r;
89
90 /* XXX: not all ENCRYPT_TO entries are listed here. */
91 for (r = res->recipients; r; r = r->next) {
92 get_pubkey (r->keyid, &key);
93 if (key) {
94 char *uid;
95 id = key->uids->name;
96 if (!id)
97 id = _("Invalid User ID");
98 uid = utf8_to_wincp (id, strlen (id));
99 info = new char [32+strlen (uid)+1 + 4 + strlen (r->keyid)+1
100 + strlen (key->uids->email)+1];
101 if (!info)
102 BUG (NULL);
103 sprintf (info, "%s <%s> (%s, 0x%s)", uid, key->uids->email,
104 get_key_pubalgo (r->pubkey_algo), r->keyid+8);
105 free (uid);
106
107 }
108 else {
109 info = new char [32 + strlen (r->keyid)+1 + 4];
110 if (!info)
111 BUG (NULL);
112 sprintf (info, _("Unknown key ID (%s, 0x%s)"),
113 get_key_pubalgo (r->pubkey_algo), r->keyid+8);
114 }
115 listbox_add_string (GetDlgItem (dlg, IDC_DECRYPT_LIST), info);
116 free_if_alloc (info);
117 }
118 }
119 else if (c->gpg_cmd == GPG_CMD_DECRYPT)
120 EnableWindow (GetDlgItem (dlg, IDC_DECRYPT_LIST), FALSE);
121 SetDlgItemText (dlg, c->gpg_cmd == GPG_CMD_DECRYPT?
122 IDC_DECRYPT_PWDINFO : IDC_DECRYPT_SIGN_PWDINFO,
123 c->bad_pwd? _("Bad passphrase; Enter passphrase again") :
124 _("Please enter your passphrase"));
125 if (c->gpg_cmd == GPG_CMD_DECRYPT) {
126 SetFocus (GetDlgItem (dlg, IDC_DECRYPT_PWD));
127 if (res && !res->recipients) {
128 const char *s = _("Symmetric encryption.\n"
129 "%s encrypted data.");
130 const char *alg = get_symkey_algo (c->sym.sym_algo);
131 info = new char[strlen (s) + strlen (alg) + 2];
132 if (!info)
133 BUG (NULL);
134 sprintf (info, s, alg);
135 SetDlgItemText (dlg, IDC_DECRYPT_MSG, info);
136 free_if_alloc (info);
137 }
138 else
139 SetDlgItemText (dlg, IDC_DECRYPT_MSG, c->info);
140 }
141 else {
142 SetFocus( GetDlgItem (dlg, IDC_DECRYPT_SIGN_PWD));
143 SetDlgItemText (dlg, IDC_DECRYPT_SIGN_MSG, c->info);
144 }
145 center_window (dlg, NULL);
146 SetForegroundWindow (dlg);
147 set_active_window (dlg);
148 return FALSE;
149
150 case WM_SYSCOMMAND:
151 if (LOWORD (wparam) == SC_CLOSE) {
152 SetDlgItemText (dlg, item_ctrl_id( c->gpg_cmd ), "");
153 c->cancel = 1;
154 EndDialog (dlg, TRUE);
155 }
156 break;
157
158 case WM_COMMAND:
159 switch( HIWORD( wparam ) ) {
160 case BN_CLICKED:
161 if ( LOWORD( wparam ) == IDC_DECRYPT_HIDE
162 || LOWORD( wparam ) == IDC_DECRYPT_SIGN_HIDE ) {
163 HWND hwnd;
164 int hide = IsDlgButtonChecked (dlg, item_ctrl_id2 (c->gpg_cmd));
165 hwnd = GetDlgItem (dlg, item_ctrl_id (c->gpg_cmd));
166 SendMessage (hwnd, EM_SETPASSWORDCHAR, hide? '*' : 0, 0);
167 SetFocus (hwnd);
168 }
169 }
170
171 switch (LOWORD (wparam)) {
172 case IDOK:
173 /* fixme: the item is even cached when the passphrase is not
174 correct, which means that the user needs to delete all
175 cached entries to continue. */
176 if (c->pwd)
177 burn_passphrase (&c->pwd);
178 n = item_get_text_length (dlg, item_ctrl_id (c->gpg_cmd));
179 if (!n) {
180 c->pwd = new char[2];
181 if (!c->pwd)
182 BUG (NULL);
183 strcpy (c->pwd, "");
184 }
185 else {
186 c->pwd = new char[n+2];
187 if (!c->pwd)
188 BUG (NULL);
189 GetDlgItemText (dlg, item_ctrl_id (c->gpg_cmd), c->pwd, n+1);
190 }
191 res = gpgme_op_decrypt_result (c->gpg);
192 if (!res)
193 res_sig = gpgme_op_sign_result (c->gpg);
194 if (reg_prefs.cache_time > 0 && !c->is_card &&
195 ((res && res->recipients) || (res_sig && res_sig->signatures))) {
196 if (agent_get_cache (c->keyid, &item))
197 agent_unlock_cache_entry (&item);
198 else
199 agent_put_cache (c->keyid, c->pwd, reg_prefs.cache_time);
200 }
201 EndDialog (dlg, TRUE);
202 return TRUE;
203
204 case IDCANCEL:
205 SetDlgItemText (dlg, item_ctrl_id (c->gpg_cmd), "" );
206 c->cancel = 1;
207 EndDialog (dlg, FALSE);
208 return FALSE;
209 }
210 break;
211 }
212
213 return FALSE;
214 }
215
216
217 /* Extract the main keyid from @pass_info.
218 Return value: long main keyid or NULL for an error. */
219 static const char*
220 parse_gpg_keyid (const char *pass_info)
221 {
222 static char keyid[16+1];
223
224 /* XXX: check for leading alpha-chars? */
225 if (strlen (pass_info) < 16)
226 return NULL;
227 /* the format of the desc buffer looks like this:
228 request_keyid[16] main_keyid[16] keytype[1] keylength[4]
229 we use the main keyid to use only one cache entry. */
230 strncpy (keyid, pass_info+17, 16);
231 keyid[16] = 0;
232 return keyid;
233 }
234
235
236 /* Parse the information in @uid_hint and @pass_info to generate
237 a input message for the user in @desc. */
238 static int
239 parse_gpg_description (const char *uid_hint, const char *pass_info,
240 char *desc, int size)
241 {
242 gpgme_pubkey_algo_t algo;
243 char usedkey[16+1], mainkey[16+1];
244 char *uid, *p;
245 int n=0;
246
247 if (strlen (uid_hint) < 17) {
248 *desc = 0;
249 return -1;
250 }
251
252 while (p = strsep ((char**)&pass_info, " ")) {
253 switch (n++) {
254 case 0: strncpy (mainkey, p, 16); mainkey[16] = 0; break;
255 case 1: strncpy (usedkey, p, 16); usedkey[16] = 0; break;
256 case 2: algo = (gpgme_pubkey_algo_t)atol (p); break;
257 }
258 }
259 uid_hint += 16; /* skip keyid */
260 uid_hint += 1; /* space */
261
262 uid = utf8_to_wincp (uid_hint, strlen (uid_hint));
263
264 if (strcmp (usedkey, mainkey))
265 _snprintf (desc, size-1,
266 _("You need a passphrase to unlock the secret key for\n"
267 "user: \"%s\"\n"
268 "%s key, ID %s (main key ID %s)\n"),
269 uid, get_key_pubalgo (algo), usedkey+8, mainkey+8);
270 else if (!strcmp (usedkey, mainkey))
271 _snprintf (desc, size-1,
272 _("You need a passphrase to unlock the secret key for\n"
273 "user: \"%s\"\n"
274 "%s key, ID %s\n"),
275 uid, get_key_pubalgo (algo), usedkey+8);
276 free (uid);
277 return 0;
278 }
279
280
281 /*
282 static int inline
283 is_hexstring (const char * p)
284 {
285 size_t i;
286
287 for (i=0; i < strlen (p); i++) {
288 if (!isxdigit (p[i]))
289 return -1;
290 }
291 return 0;
292 }
293 */
294
295 /* Passphrase callback with the ability to support caching. */
296 gpgme_error_t
297 passphrase_cb (void *hook, const char *uid_hint,
298 const char *passphrase_info,
299 int prev_was_bad, int fd)
300 {
301 passphrase_cb_s *c = (passphrase_cb_s*)hook;
302 HANDLE hd = (HANDLE)fd;
303 void *item;
304 const char *keyid, *pass;
305 DWORD n;
306 int rc;
307
308 if (!c)
309 return gpg_error (GPG_ERR_INV_ARG);
310 c->bad_pwd = prev_was_bad? 1 : 0;
311 if (prev_was_bad && !c->cancel) {
312 if (c->pwd)
313 burn_passphrase (&c->pwd);
314 agent_del_cache (c->keyid);
315 c->pwd_init = 1;
316 }
317
318 if (passphrase_info) {
319 if (strlen (passphrase_info) < 16 &&
320 !strstr (passphrase_info, "OPENPGP")) {/* assume symetric encryption. */
321 int n=2;
322 c->sym.sym_algo = atoi (passphrase_info);
323 if (c->sym.sym_algo > 9)
324 n++;
325 /* XXX: be more strict. */
326 c->sym.s2k_mode = atoi (passphrase_info+n);
327 c->sym.s2k_hash = atoi (passphrase_info+n+2);
328 }
329
330 keyid = parse_gpg_keyid (passphrase_info);
331 pass = agent_get_cache (keyid+8, &item);
332 if (pass) {
333 agent_unlock_cache_entry (&item);
334 c->pwd_init = 0;
335 if (!WriteFile (hd, pass, strlen (pass), &n, NULL))
336 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
337 if (!WriteFile (hd, "\n", 1, &n, NULL))
338 log_debug ("passphrase_cb: WriteFile() failed ec=%d\n", w32_errno);
339 return 0;
340 }
341 }
342
343 if (c->pwd_init) {
344 if (keyid && strlen (keyid) == 16)
345 strcpy (c->keyid, keyid+8);
346
347 /* if @passphrase_info contains 'OPENPGP' we assume a smart card
348 has been used. */
349 if (strstr (passphrase_info, "OPENPGP")) {
350 char buf[16];
351 memset (buf, 0, sizeof buf);
352 strncpy (buf, "??", 2); /* XXX: fixme we need the serial no. */
353 _snprintf (c->info, sizeof c->info-1,
354 _("Please enter the PIN to unlock your secret card key\n"
355 "Card: %s"), buf);
356 c->is_card = 1;
357 }
358 else if (uid_hint)
359 parse_gpg_description (uid_hint, passphrase_info,
360 c->info, sizeof c->info - 1);
361 if (c->gpg_cmd == GPG_CMD_DECRYPT) {
362 rc = DialogBoxParam (glob_hinst, (LPCSTR)IDD_WINPT_DECRYPT,
363 (HWND)c->hwnd, passphrase_callback_proc,
364 (LPARAM)c);
365 }
366 else if (c->gpg_cmd == GPG_CMD_SIGN) {
367 rc = DialogBoxParam (glob_hinst, (LPCSTR)IDD_WINPT_DECRYPT_SIGN,
368 (HWND)c->hwnd, passphrase_callback_proc,
369 (LPARAM)c);
370 }
371 if (rc == -1) {
372 WriteFile (hd, "\n", 1, &n, NULL);
373 return 0;
374 }
375 c->pwd_init = 0;
376 }
377 if (c->cancel) {
378 WriteFile (hd, "\n", 1, &n, NULL);
379 return 0;
380 }
381
382 WriteFile (hd, c->pwd, strlen (c->pwd), &n, NULL);
383 WriteFile (hd, "\n", 1, &n, NULL);
384 return 0;
385 }
386
387
388 /* Initialize the given passphrase callback @cb with the
389 used gpgme context @ctx, the command @cmd and a title
390 @title for the dialog. */
391 void
392 set_gpg_passphrase_cb (passphrase_cb_s *cb, gpgme_ctx_t ctx,
393 int cmd, HWND hwnd, const char *title)
394 {
395 memset (cb, 0, sizeof *cb);
396 cb->gpg_cmd = cmd;
397 cb->bad_pwd = 0;
398 cb->is_card = 0;
399 cb->cancel = 0;
400 cb->hwnd = hwnd;
401 cb->pwd_init = 1;
402 free_if_alloc (cb->title);
403 cb->title = m_strdup (title);
404 if (!cb->title)
405 BUG (NULL);
406 gpgme_set_passphrase_cb (ctx, passphrase_cb, cb);
407 cb->gpg = ctx;
408 }
409
410
411 /* Release a passphrase callback @ctx. */
412 void
413 release_gpg_passphrase_cb (passphrase_cb_s *ctx)
414 {
415 if (!ctx)
416 return;
417 sfree_if_alloc (ctx->pwd);
418 free_if_alloc (ctx->title);
419 }
420
421
422 /* Simple check to measure passphrase (@pass) quality.
423 Return value: 0 on success. */
424 int
425 check_passwd_quality (const char *pass, int strict)
426 {
427 int i, nd=0, nc=0, n;
428
429 n = strlen (pass);
430 if (n < 8)
431 return -1;
432
433 for (i=0; i < n; i++) {
434 if (isdigit (pass[i])) nd++;
435 if (isalpha (pass[i])) nc++;
436 }
437
438 if (nd == n || nc == n)
439 return -1;
440
441 return 0;
442 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26