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

Contents of /trunk/Src/wptPassphraseCB.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 25 - (show annotations)
Wed Oct 12 10:04:26 2005 UTC (19 years, 4 months ago) by twoaday
File size: 11991 byte(s)
First testing phase finished.
Provide bug fixes for a lot of (minor) problems.

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26