1 /* main.c - Secure W32 dialog for PIN entry.
2  * Copyright (C) 2004, 2007 g10 Code GmbH
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, see <https://www.gnu.org/licenses/>.
16  * SPDX-License-Identifier: GPL-2.0+
17  */
18 
19 #include <config.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #if WINVER < 0x0403
23 # define WINVER 0x0403  /* Required for SendInput.  */
24 #endif
25 #include <windows.h>
26 #ifdef HAVE_W32CE_SYSTEM
27 # include <winioctl.h>
28 # include <sipapi.h>
29 #endif
30 
31 #include "pinentry.h"
32 #include "memory.h"
33 
34 #include "resource.h"
35 /* #include "msgcodes.h" */
36 
37 #define PGMNAME "pinentry-w32"
38 
39 #ifndef LSFW_LOCK
40 # define LSFW_LOCK 1
41 # define LSFW_UNLOCK 2
42 #endif
43 
44 #ifndef debugfp
45 #define debugfp stderr
46 #endif
47 
48 
49 /* This function pointer gets initialized in main.  */
50 #ifndef HAVE_W32CE_SYSTEM
51 static BOOL WINAPI (*lock_set_foreground_window)(UINT);
52 #endif
53 
54 static int w32_cmd_handler (pinentry_t pe);
55 static void ok_button_clicked (HWND dlg, pinentry_t pe);
56 
57 
58 /* We use global variables for the state, because there should never
59    ever be a second instance.  */
60 static HWND dialog_handle;
61 static int confirm_mode;
62 static int passphrase_ok;
63 static int confirm_yes;
64 
65 /* The file descriptors for the loop.  */
66 static int w32_infd;
67 static int w32_outfd;
68 
69 
70 /* Connect this module to the pinentry framework.  */
71 pinentry_cmd_handler_t pinentry_cmd_handler = w32_cmd_handler;
72 
73 
74 
75 const char *
w32_strerror(int ec)76 w32_strerror (int ec)
77 {
78   static char strerr[256];
79 
80   if (ec == -1)
81     ec = (int)GetLastError ();
82 #ifdef HAVE_W32CE_SYSTEM
83   /* There is only a wchar_t FormatMessage.  It does not make much
84      sense to play the conversion game; we print only the code.  */
85   snprintf (strerr, sizeof strerr, "ec=%d", ec);
86   strerr[sizeof strerr -1] = 0;
87 #else
88   FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, ec,
89                  MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
90                  strerr, sizeof strerr - 1, NULL);
91 #endif
92   return strerr;
93 }
94 
95 
96 
97 #ifdef HAVE_W32CE_SYSTEM
98 /* Create a pipe.  WRITE_END shall have the opposite value of the one
99    pssed to _assuan_w32ce_prepare_pipe; see there for more
100    details.  */
101 #define GPGCEDEV_IOCTL_MAKE_PIPE                                        \
102   CTL_CODE (FILE_DEVICE_STREAMS, 2049, METHOD_BUFFERED, FILE_ANY_ACCESS)
103 static HANDLE
w32ce_finish_pipe(int rvid,int write_end)104 w32ce_finish_pipe (int rvid, int write_end)
105 {
106   HANDLE hd;
107 
108   hd = CreateFile (L"GPG1:", write_end? GENERIC_WRITE : GENERIC_READ,
109                    FILE_SHARE_READ | FILE_SHARE_WRITE,
110                    NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL);
111   if (hd != INVALID_HANDLE_VALUE)
112     {
113       if (!DeviceIoControl (hd, GPGCEDEV_IOCTL_MAKE_PIPE,
114                             &rvid, sizeof rvid, NULL, 0, NULL, NULL))
115         {
116           DWORD lastrc = GetLastError ();
117           CloseHandle (hd);
118           hd = INVALID_HANDLE_VALUE;
119           SetLastError (lastrc);
120         }
121     }
122 
123   return hd;
124 }
125 #endif /*HAVE_W32CE_SYSTEM*/
126 
127 
128 /* static HWND */
129 /* show_window_hierarchy (HWND parent, int level) */
130 /* { */
131 /*   HWND child; */
132 
133 /*   child = GetWindow (parent, GW_CHILD); */
134 /*   while (child) */
135 /*     { */
136 /*       char buf[1024+1]; */
137 /*       char name[200]; */
138 /*       int nname; */
139 /*       char *pname; */
140 
141 /*       memset (buf, 0, sizeof (buf)); */
142 /*       GetWindowText (child, buf, sizeof (buf)-1); */
143 /*       nname = GetClassName (child, name, sizeof (name)-1); */
144 /*       if (nname) */
145 /*         pname = name; */
146 /*       else */
147 /*         pname = NULL; */
148 /*       fprintf (debugfp, "### %*shwnd=%p (%s) `%s'\n", level*2, "", child, */
149 /*                pname? pname:"", buf); */
150 /*       show_window_hierarchy (child, level+1); */
151 /*       child = GetNextWindow (child, GW_HWNDNEXT);	 */
152 /*     } */
153 
154 /*   return NULL; */
155 /* } */
156 
157 
158 
159 /* Convert a wchar to UTF8.  Caller needs to release the string.
160    Returns NULL on error. */
161 static char *
wchar_to_utf8(const wchar_t * string,size_t len,int secure)162 wchar_to_utf8 (const wchar_t *string, size_t len, int secure)
163 {
164   int n;
165   char *result;
166 
167   /* Note, that CP_UTF8 is not defined in Windows versions earlier
168      than NT.  */
169   n = WideCharToMultiByte (CP_UTF8, 0, string, len, NULL, 0, NULL, NULL);
170   if (n < 0)
171     return NULL;
172 
173   result = secure? secmem_malloc (n+1) : malloc (n+1);
174   if (!result)
175     return NULL;
176   n = WideCharToMultiByte (CP_UTF8, 0, string, len, result, n, NULL, NULL);
177   if (n < 0)
178     {
179       if (secure)
180         secmem_free (result);
181       else
182         free (result);
183       return NULL;
184     }
185   return result;
186 }
187 
188 
189 /* Convert a UTF8 string to wchar.  Returns NULL on error. Caller
190    needs to free the returned value.  */
191 wchar_t *
utf8_to_wchar(const char * string)192 utf8_to_wchar (const char *string)
193 {
194   int n;
195   wchar_t *result;
196   size_t len = strlen (string);
197 
198   n = MultiByteToWideChar (CP_UTF8, 0, string, len, NULL, 0);
199   if (n < 0)
200     return NULL;
201 
202   result = calloc ((n+1), sizeof *result);
203   if (!result)
204     return NULL;
205   n = MultiByteToWideChar (CP_UTF8, 0, string, len, result, n);
206   if (n < 0)
207     {
208       free (result);
209       return NULL;
210     }
211   result[n] = 0;
212   return result;
213 }
214 
215 
216 /* Raise the software input panel.  */
217 static void
raise_sip(HWND dlg)218 raise_sip (HWND dlg)
219 {
220 #ifdef HAVE_W32CE_SYSTEM
221   SIPINFO si;
222 
223   SetForegroundWindow (dlg);
224 
225   memset (&si, 0, sizeof si);
226   si.cbSize = sizeof si;
227 
228   if (SipGetInfo (&si))
229     {
230       si.fdwFlags |= SIPF_ON;
231       SipSetInfo (&si);
232     }
233 #else
234   (void)dlg;
235 #endif
236 }
237 
238 /* Center the window CHILDWND with the desktop as its parent
239    window.  STYLE is passed as second arg to SetWindowPos.*/
240 static void
center_window(HWND childwnd,HWND style)241 center_window (HWND childwnd, HWND style)
242 {
243 #ifndef HAVE_W32CE_SYSTEM
244   HWND parwnd;
245   RECT rchild, rparent;
246   HDC hdc;
247   int wchild, hchild, wparent, hparent;
248   int wscreen, hscreen, xnew, ynew;
249   int flags = SWP_NOSIZE | SWP_NOZORDER;
250 
251   parwnd = GetDesktopWindow ();
252   GetWindowRect (childwnd, &rchild);
253   wchild = rchild.right - rchild.left;
254   hchild = rchild.bottom - rchild.top;
255 
256   GetWindowRect (parwnd, &rparent);
257   wparent = rparent.right - rparent.left;
258   hparent = rparent.bottom - rparent.top;
259 
260   hdc = GetDC (childwnd);
261   wscreen = GetDeviceCaps (hdc, HORZRES);
262   hscreen = GetDeviceCaps (hdc, VERTRES);
263   ReleaseDC (childwnd, hdc);
264   xnew = rparent.left + ((wparent - wchild) / 2);
265   if (xnew < 0)
266     xnew = 0;
267   else if ((xnew+wchild) > wscreen)
268     xnew = wscreen - wchild;
269   ynew = rparent.top  + ((hparent - hchild) / 2);
270   if (ynew < 0)
271     ynew = 0;
272   else if ((ynew+hchild) > hscreen)
273     ynew = hscreen - hchild;
274   if (style == HWND_TOPMOST || style == HWND_NOTOPMOST)
275     flags = SWP_NOMOVE | SWP_NOSIZE;
276   SetWindowPos (childwnd, style? style : NULL, xnew, ynew, 0, 0, flags);
277 #endif
278 }
279 
280 
281 
282 static void
move_mouse_and_click(HWND hwnd)283 move_mouse_and_click (HWND hwnd)
284 {
285 #ifndef HAVE_W32CE_SYSTEM
286   RECT rect;
287   HDC hdc;
288   int wscreen, hscreen, x, y, normx, normy;
289   INPUT inp[3];
290   int idx;
291 
292   hdc = GetDC (hwnd);
293   wscreen = GetDeviceCaps (hdc, HORZRES);
294   hscreen = GetDeviceCaps (hdc, VERTRES);
295   ReleaseDC (hwnd, hdc);
296   if (wscreen < 10 || hscreen < 10)
297     return;
298 
299   GetWindowRect (hwnd, &rect);
300   x = rect.left;
301   y = rect.bottom;
302 
303   normx = x * (65535 / wscreen);
304   if (normx < 0 || normx > 65535)
305     return;
306   normy = y * (65535 / hscreen);
307   if (normy < 0 || normy > 65535)
308     return;
309 
310   for (idx=0; idx < 3; idx++)
311     memset (&inp[idx], 0, sizeof inp[idx]);
312 
313   idx=0;
314   inp[idx].type = INPUT_MOUSE;
315   inp[idx].mi.dx = normx;
316   inp[idx].mi.dy = normy;
317   inp[idx].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
318   idx++;
319 
320   inp[idx].type = INPUT_MOUSE;
321   inp[idx].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
322   idx++;
323 
324   inp[idx].type = INPUT_MOUSE;
325   inp[idx].mi.dwFlags = MOUSEEVENTF_LEFTUP;
326   idx++;
327 
328   if ( (SendInput (idx, inp, sizeof (INPUT)) != idx) && debugfp)
329     fprintf (debugfp, "SendInput failed: %s\n", w32_strerror (-1));
330 #endif
331 }
332 
333 
334 
335 /* Resize the button so that STRING fits into it.   */
336 static void
resize_button(HWND hwnd,const char * string)337 resize_button (HWND hwnd, const char *string)
338 {
339   if (!hwnd)
340     return;
341 
342   /* FIXME: Need to figure out how to convert dialog coorddnates to
343      screen coordinates and how buttons should be placed.  */
344 /*   SetWindowPos (hbutton, NULL, */
345 /*                 10, 180,  */
346 /*                 strlen (string+2), 14, */
347 /*                 (SWP_NOZORDER)); */
348 }
349 
350 
351 
352 
353 
354 /* Call SetDlgItemTextW with an UTF8 string.  */
355 static void
set_dlg_item_text(HWND dlg,int item,const char * string)356 set_dlg_item_text (HWND dlg, int item, const char *string)
357 {
358   if (!string || !*string)
359     SetDlgItemTextW (dlg, item, L"");
360   else
361     {
362       wchar_t *wbuf;
363 
364       wbuf = utf8_to_wchar (string);
365       if (!wbuf)
366         SetDlgItemTextW (dlg, item, L"[out of core]");
367       else
368         {
369           SetDlgItemTextW (dlg, item, wbuf);
370           free (wbuf);
371         }
372     }
373 }
374 
375 
376 /* Load our butmapped icon from the resource and display it.  */
377 static void
set_bitmap(HWND dlg,int item)378 set_bitmap (HWND dlg, int item)
379 {
380   HWND hwnd;
381   HBITMAP bitmap;
382   RECT rect;
383   int resid;
384 
385   hwnd = GetDlgItem (dlg, item);
386   if (!hwnd)
387     return;
388 
389   rect.left = 0;
390   rect.top = 0;
391   rect.right = 32;
392   rect.bottom = 32;
393   if (!MapDialogRect (dlg, &rect))
394     {
395       fprintf (stderr, "MapDialogRect failed: %s\n",  w32_strerror (-1));
396       return;
397     }
398   /* fprintf (stderr, "MapDialogRect: %d/%d\n", rect.right, rect.bottom); */
399 
400   switch (rect.right)
401     {
402     case 32: resid = IDB_ICON_32; break;
403     case 48: resid = IDB_ICON_48; break;
404     case 64: resid = IDB_ICON_64; break;
405     case 96: resid = IDB_ICON_96; break;
406     default: resid = IDB_ICON_128;break;
407     }
408 
409   bitmap = LoadImage (GetModuleHandle (NULL),
410                       MAKEINTRESOURCE (resid),
411                       IMAGE_BITMAP,
412                       rect.right, rect.bottom,
413                       (LR_SHARED | LR_LOADTRANSPARENT | LR_LOADMAP3DCOLORS));
414   if (!bitmap)
415     {
416       fprintf (stderr, "LoadImage failed: %s\n",  w32_strerror (-1));
417       return;
418     }
419   SendMessage(hwnd, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)bitmap);
420 }
421 
422 
423 /* Dialog processing loop.  */
424 static BOOL CALLBACK
dlg_proc(HWND dlg,UINT msg,WPARAM wparam,LPARAM lparam)425 dlg_proc (HWND dlg, UINT msg, WPARAM wparam, LPARAM lparam)
426 {
427   static pinentry_t pe;
428   /* static int item; */
429 
430 
431 /*   { */
432 /*     int idx; */
433 
434 /*     for (idx=0; msgcodes[idx].string; idx++) */
435 /*       if (msg == msgcodes[idx].msg) */
436 /*         break; */
437 /*     if (msgcodes[idx].string) */
438 /*       fprintf (debugfp, "received %s\n", msgcodes[idx].string); */
439 /*     else */
440 /*       fprintf (debugfp, "received WM_%u\n", msg); */
441 /*   } */
442 
443   switch (msg)
444     {
445     case WM_INITDIALOG:
446       dialog_handle = dlg;
447       pe = (pinentry_t)lparam;
448       if (!pe)
449         abort ();
450       set_dlg_item_text (dlg, IDC_PINENT_PROMPT, pe->prompt);
451       set_dlg_item_text (dlg, IDC_PINENT_DESC, pe->description);
452       set_dlg_item_text (dlg, IDC_PINENT_TEXT, "");
453       set_bitmap (dlg, IDC_PINENT_ICON);
454       if (pe->ok)
455         {
456           set_dlg_item_text (dlg, IDOK, pe->ok);
457           resize_button (GetDlgItem (dlg, IDOK), pe->ok);
458         }
459       if (pe->cancel)
460         {
461           set_dlg_item_text (dlg, IDCANCEL, pe->cancel);
462           resize_button (GetDlgItem (dlg, IDCANCEL), pe->cancel);
463         }
464       if (pe->error)
465         set_dlg_item_text (dlg, IDC_PINENT_ERR, pe->error);
466 
467       if (confirm_mode)
468         {
469           EnableWindow (GetDlgItem (dlg, IDC_PINENT_TEXT), FALSE);
470           SetWindowPos (GetDlgItem (dlg, IDC_PINENT_TEXT), NULL, 0, 0, 0, 0,
471                         (SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_HIDEWINDOW));
472 
473           /* item = IDOK; */
474         }
475       /* else */
476       /*   item = IDC_PINENT_TEXT; */
477 
478       center_window (dlg, HWND_TOP);
479 
480       /* Unfortunately we can't use SetForegroundWindow because there
481          is no easy eay to have all the calling processes do an
482          AllowSetForegroundWindow.  What we do instead is to bad hack
483          by simulating a click to the Window. */
484 /*       if (SetForegroundWindow (dlg) && lock_set_foreground_window) */
485 /*         { */
486 /*           lock_set_foreground_window (LSFW_LOCK); */
487 /*         } */
488 
489 /*       show_window_hierarchy (GetDesktopWindow (), 0); */
490 
491       ShowWindow (dlg, SW_SHOW);
492       move_mouse_and_click ( GetDlgItem (dlg, IDC_PINENT_PROMPT) );
493       raise_sip (dlg);
494       break;
495 
496     case WM_COMMAND:
497       switch (LOWORD (wparam))
498 	{
499 	case IDOK:
500           if (confirm_mode)
501             confirm_yes = 1;
502           else
503             ok_button_clicked (dlg, pe);
504 	  EndDialog (dlg, TRUE);
505 	  break;
506 
507 	case IDCANCEL:
508           pe->result = -1;
509 	  EndDialog (dlg, FALSE);
510 	  break;
511 	}
512       break;
513 
514      case WM_KEYDOWN:
515        if (wparam == VK_RETURN)
516          {
517            if (confirm_mode)
518              confirm_yes = 1;
519            else
520              ok_button_clicked (dlg, pe);
521            EndDialog (dlg, TRUE);
522          }
523        break;
524 
525     case WM_CTLCOLORSTATIC:
526       if ((HWND)lparam == GetDlgItem (dlg, IDC_PINENT_ERR))
527         {
528           /* Display the error prompt in red.  */
529           SetTextColor ((HDC)wparam, RGB (255, 0, 0));
530           SetBkMode ((HDC)wparam, TRANSPARENT);
531           return (BOOL)GetStockObject (NULL_BRUSH);
532         }
533       break;
534 
535     }
536   return FALSE;
537 }
538 
539 
540 /* The okay button has been clicked or the enter enter key in the text
541    field.  */
542 static void
ok_button_clicked(HWND dlg,pinentry_t pe)543 ok_button_clicked (HWND dlg, pinentry_t pe)
544 {
545   char *s_utf8;
546   wchar_t *w_buffer;
547   size_t w_buffer_size = 255;
548   unsigned int nchar;
549 
550   pe->locale_err = 1;
551   w_buffer = secmem_malloc ((w_buffer_size + 1) * sizeof *w_buffer);
552   if (!w_buffer)
553     return;
554 
555   nchar = GetDlgItemTextW (dlg, IDC_PINENT_TEXT, w_buffer, w_buffer_size);
556   s_utf8 = wchar_to_utf8 (w_buffer, nchar, 1);
557   secmem_free (w_buffer);
558   if (s_utf8)
559     {
560       passphrase_ok = 1;
561       pinentry_setbufferlen (pe, strlen (s_utf8) + 1);
562       if (pe->pin)
563         strcpy (pe->pin, s_utf8);
564       secmem_free (s_utf8);
565       pe->locale_err = 0;
566       pe->result = pe->pin? strlen (pe->pin) : 0;
567     }
568 }
569 
570 
571 static int
w32_cmd_handler(pinentry_t pe)572 w32_cmd_handler (pinentry_t pe)
573 {
574 /*   HWND lastwindow = GetForegroundWindow (); */
575 
576   confirm_mode = !pe->pin;
577 
578   passphrase_ok = confirm_yes = 0;
579 
580   dialog_handle = NULL;
581   DialogBoxParam (GetModuleHandle (NULL), MAKEINTRESOURCE (IDD_PINENT),
582                   GetDesktopWindow (), dlg_proc, (LPARAM)pe);
583   if (dialog_handle)
584     {
585 /*       if (lock_set_foreground_window) */
586 /*         lock_set_foreground_window (LSFW_UNLOCK); */
587 /*       if (lastwindow) */
588 /*         SetForegroundWindow (lastwindow); */
589     }
590   else
591     return -1;
592 
593   if (confirm_mode)
594     return confirm_yes;
595   else if (passphrase_ok && pe->pin)
596     return strlen (pe->pin);
597   else
598     return -1;
599 }
600 
601 
602 /* WindowsCE uses a very strange way of handling the standard streams.
603    There is a function SetStdioPath to associate a standard stream
604    with a file or a device but what we really want is to use pipes as
605    standard streams.  Despite that we implement pipes using a device,
606    we would have some limitations on the number of open pipes due to
607    the 3 character limit of device file name.  Thus we don't take this
608    path.  Another option would be to install a file system driver with
609    support for pipes; this would allow us to get rid of the device
610    name length limitation.  However, with GnuPG we can get away be
611    redefining the standard streams and passing the handles to be used
612    on the command line.  This has also the advantage that it makes
613    creating a process much easier and does not require the
614    SetStdioPath set and restore game.  The caller needs to pass the
615    rendezvous ids using up to three options:
616 
617      -&S0=<rvid> -&S1=<rvid> -&S2=<rvid>
618 
619    They are all optional but they must be the first arguments on the
620    command line.  Parsing stops as soon as an invalid option is found.
621    These rendezvous ids are then used to finish the pipe creation.*/
622 #ifdef HAVE_W32CE_SYSTEM
623 static void
parse_std_file_handles(int * argcp,char *** argvp)624 parse_std_file_handles (int *argcp, char ***argvp)
625 {
626   int argc = *argcp;
627   char **argv = *argvp;
628   const char *s;
629   int fd;
630   int i;
631   int fixup = 0;
632 
633   if (!argc)
634     return;
635 
636   for (argc--, argv++; argc; argc--, argv++)
637     {
638       s = *argv;
639       if (*s == '-' && s[1] == '&' && s[2] == 'S'
640           && (s[3] == '0' || s[3] == '1' || s[3] == '2')
641           && s[4] == '='
642           && (strchr ("-01234567890", s[5]) || !strcmp (s+5, "null")))
643         {
644           if (s[5] == 'n')
645             fd = (int)(-1);
646           else
647             fd = (int)w32ce_finish_pipe (atoi (s+5), s[3] != '0');
648           if (s[3] == '0' && fd != -1)
649             w32_infd = fd;
650           else if (s[3] == '1' && fd != -1)
651             w32_outfd = fd;
652           fixup++;
653         }
654       else
655         break;
656     }
657 
658   if (fixup)
659     {
660       argc = *argcp;
661       argc -= fixup;
662       *argcp = argc;
663 
664       argv = *argvp;
665       for (i=1; i < argc; i++)
666         argv[i] = argv[i + fixup];
667       for (; i < argc + fixup; i++)
668         argv[i] = NULL;
669     }
670 
671 
672 }
673 #endif /*HAVE_W32CE_SYSTEM*/
674 
675 
676 int
main(int argc,char ** argv)677 main (int argc, char **argv)
678 {
679 #ifndef HAVE_W32CE_SYSTEM
680   void *handle;
681 #endif
682 
683   w32_infd = STDIN_FILENO;
684   w32_outfd = STDOUT_FILENO;
685 
686 #ifdef HAVE_W32CE_SYSTEM
687   parse_std_file_handles (&argc, &argv);
688 #endif
689 
690   pinentry_init (PGMNAME);
691 
692   pinentry_parse_opts (argc, argv);
693 
694 /*   debugfp = fopen ("pinentry.log", "w"); */
695 /*   if (!debugfp) */
696 /*     debugfp = stderr; */
697 
698   /* We need to load a function because that one is only available
699      since W2000 but not in older NTs.  */
700 #ifndef HAVE_W32CE_SYSTEM
701   handle = LoadLibrary ("user32.dll");
702   if (handle)
703     {
704       void *foo;
705       foo = GetProcAddress (handle, "LockSetForegroundWindow");
706       if (foo)
707         lock_set_foreground_window = foo;
708       else
709         CloseHandle (handle);
710     }
711 #endif
712 
713   if (pinentry_loop2 (w32_infd, w32_outfd))
714     return 1;
715 
716 #ifdef HAVE_W32CE_SYSTEM
717   Sleep (400);
718 #endif
719   return 0;
720 }
721