w32: Remove all support for WindowsCE
[gpgme.git] / src / w32-util.c
1 /* w32-util.c - Utility functions for the W32 API
2  * Copyright (C) 1999 Free Software Foundation, Inc
3  * Copyright (C) 2001 Werner Koch (dd9jn)
4  * Copyright (C) 2001, 2002, 2003, 2004, 2007, 2013 g10 Code GmbH
5  *
6  * This file is part of GPGME.
7  *
8  * GPGME is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as
10  * published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * GPGME is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this program; if not, see <https://www.gnu.org/licenses/>.
20  **/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <assert.h>
29 #include <errno.h>
30 #include <stdint.h>
31 #ifdef HAVE_SYS_TIME_H
32 # include <sys/time.h>
33 #endif
34 #ifdef HAVE_SYS_TYPES_H
35 # include <sys/types.h>
36 #endif
37 #ifdef HAVE_SYS_STAT_H
38 # include <sys/stat.h>
39 #endif
40 #ifdef HAVE_UNISTD_H
41 # include <unistd.h>
42 #endif
43 #include <fcntl.h>
44 #include <io.h>
45
46 #if __MINGW64_VERSION_MAJOR >= 2
47 # define _WIN32_IE 0x0501 /* Required by mingw64 toolkit.  */
48 #else
49 # define _WIN32_IE 0x0400 /* Required for SHGetSpecialFolderPathA.  */
50 #endif
51
52 /* We need to include the windows stuff here prior to shlobj.h so that
53    we get the right winsock version.  This is usually done in util.h
54    but that header also redefines some Windows functions which we need
55    to avoid unless having included shlobj.h.  */
56 #include <winsock2.h>
57 #include <ws2tcpip.h>
58 #include <windows.h>
59 #include <shlobj.h>
60
61 #include "util.h"
62 #include "ath.h"
63 #include "sema.h"
64 #include "debug.h"
65 #include "sys-util.h"
66
67
68 #define HAVE_ALLOW_SET_FOREGROUND_WINDOW 1
69 #ifndef F_OK
70 # define F_OK 0
71 #endif
72
73 /* The Registry key used by GNUPG.  */
74 #ifdef _WIN64
75 # define GNUPG_REGKEY_2  "Software\\Wow6432Node\\GNU\\GnuPG"
76 #else
77 # define GNUPG_REGKEY_2  "Software\\GNU\\GnuPG"
78 #endif
79 #ifdef _WIN64
80 # define GNUPG_REGKEY_3  "Software\\Wow6432Node\\GnuPG"
81 #else
82 # define GNUPG_REGKEY_3  "Software\\GnuPG"
83 #endif
84
85 DEFINE_STATIC_LOCK (get_path_lock);
86
87 /* The module handle of this DLL.  If we are linked statically,
88    dllmain does not exists and thus the value of my_hmodule will be
89    NULL.  The effect is that a GetModuleFileName always returns the
90    file name of the DLL or executable which contains the gpgme code.  */
91 static HMODULE my_hmodule;
92
93 /* These variables store the malloced name of alternative default
94    binaries.  The are set only once by gpgme_set_global_flag.  */
95 static char *default_gpg_name;
96 static char *default_gpgconf_name;
97 /* If this variable is not NULL the value is assumed to be the
98    installation directory.  The variable may only be set once by
99    gpgme_set_global_flag and accessed by _gpgme_get_inst_dir.  */
100 static char *override_inst_dir;
101
102 #ifdef HAVE_ALLOW_SET_FOREGROUND_WINDOW
103
104 #define RTLD_LAZY 0
105
106 static GPG_ERR_INLINE void *
107 dlopen (const char * name, int flag)
108 {
109   void * hd = LoadLibrary (name);
110
111   (void)flag;
112   return hd;
113 }
114
115 static GPG_ERR_INLINE void *
116 dlsym (void * hd, const char * sym)
117 {
118   if (hd && sym)
119     {
120       void * fnc = GetProcAddress (hd, sym);
121       if (!fnc)
122         return NULL;
123       return fnc;
124     }
125   return NULL;
126 }
127
128 static GPG_ERR_INLINE int
129 dlclose (void * hd)
130 {
131   if (hd)
132     {
133       FreeLibrary (hd);
134       return 0;
135     }
136   return -1;
137 }
138 #endif /* HAVE_ALLOW_SET_FOREGROUND_WINDOW */
139
140
141 /* Return a malloced string encoded in UTF-8 from the wide char input
142    string STRING.  Caller must free this value.  Returns NULL and sets
143    ERRNO on failure.  Calling this function with STRING set to NULL is
144    not defined.  */
145 static char *
146 wchar_to_utf8 (const wchar_t *string)
147 {
148   int n;
149   char *result;
150
151   n = WideCharToMultiByte (CP_UTF8, 0, string, -1, NULL, 0, NULL, NULL);
152   if (n < 0)
153     {
154       gpg_err_set_errno (EINVAL);
155       return NULL;
156     }
157
158   result = malloc (n+1);
159   if (!result)
160     return NULL;
161
162   n = WideCharToMultiByte (CP_UTF8, 0, string, -1, result, n, NULL, NULL);
163   if (n < 0)
164     {
165       free (result);
166       gpg_err_set_errno (EINVAL);
167       result = NULL;
168     }
169   return result;
170 }
171
172
173 /* Replace all forward slashes by backslashes.  */
174 static void
175 replace_slashes (char *string)
176 {
177   for (; *string; string++)
178     if (*string == '/')
179       *string = '\\';
180 }
181
182
183 /* Get the base name of NAME.  Returns a pointer into NAME right after
184    the last slash or backslash or to NAME if no slash or backslash
185    exists.  */
186 static const char *
187 get_basename (const char *name)
188 {
189   const char *mark, *s;
190
191   for (mark=NULL, s=name; *s; s++)
192     if (*s == '/' || *s == '\\')
193       mark = s;
194
195   return mark? mark+1 : name;
196 }
197
198
199 void
200 _gpgme_allow_set_foreground_window (pid_t pid)
201 {
202 #ifdef HAVE_ALLOW_SET_FOREGROUND_WINDOW
203   static int initialized;
204   static BOOL (WINAPI * func)(DWORD);
205   void *handle;
206
207   if (!initialized)
208     {
209       /* Available since W2000; thus we dynload it.  */
210       initialized = 1;
211       handle = dlopen ("user32.dll", RTLD_LAZY);
212       if (handle)
213         {
214           func = dlsym (handle, "AllowSetForegroundWindow");
215           if (!func)
216             {
217               dlclose (handle);
218               handle = NULL;
219             }
220         }
221     }
222
223   if (!pid || pid == (pid_t)(-1))
224     {
225       TRACE1 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
226               "no action for pid %d", (int)pid);
227     }
228   else if (func)
229     {
230       int rc = func (pid);
231       TRACE2 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
232               "called for pid %d; result=%d", (int)pid, rc);
233
234     }
235   else
236     {
237       TRACE0 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
238               "function not available");
239     }
240 #endif /* HAVE_ALLOW_SET_FOREGROUND_WINDOW */
241 }
242
243
244 /* Return a string from the W32 Registry or NULL in case of error.
245    Caller must release the return value.  A NULL for root is an alias
246    for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. */
247 static char *
248 read_w32_registry_string (const char *root, const char *dir, const char *name)
249 {
250   HKEY root_key, key_handle;
251   DWORD n1, nbytes, type;
252   char *result = NULL;
253
254   if (!root)
255     root_key = HKEY_CURRENT_USER;
256   else if (!strcmp( root, "HKEY_CLASSES_ROOT"))
257     root_key = HKEY_CLASSES_ROOT;
258   else if (!strcmp( root, "HKEY_CURRENT_USER"))
259     root_key = HKEY_CURRENT_USER;
260   else if (!strcmp( root, "HKEY_LOCAL_MACHINE"))
261     root_key = HKEY_LOCAL_MACHINE;
262   else if (!strcmp( root, "HKEY_USERS"))
263     root_key = HKEY_USERS;
264   else if (!strcmp( root, "HKEY_PERFORMANCE_DATA"))
265     root_key = HKEY_PERFORMANCE_DATA;
266   else if (!strcmp( root, "HKEY_CURRENT_CONFIG"))
267     root_key = HKEY_CURRENT_CONFIG;
268   else
269     return NULL;
270
271   if (RegOpenKeyExA (root_key, dir, 0, KEY_READ, &key_handle))
272     {
273       if (root)
274         return NULL; /* no need for a RegClose, so return direct */
275       /* It seems to be common practise to fall back to HKLM. */
276       if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle))
277         return NULL; /* still no need for a RegClose, so return direct */
278     }
279
280   nbytes = 1;
281   if (RegQueryValueExA (key_handle, name, 0, NULL, NULL, &nbytes))
282     {
283       if (root)
284         goto leave;
285       /* Try to fallback to HKLM also vor a missing value.  */
286       RegCloseKey (key_handle);
287       if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle))
288         return NULL; /* Nope.  */
289       if (RegQueryValueExA (key_handle, name, 0, NULL, NULL, &nbytes))
290         goto leave;
291     }
292   n1 = nbytes + 1;
293   result = malloc (n1);
294   if (!result)
295     goto leave;
296   if (RegQueryValueExA (key_handle, name, 0, &type, (LPBYTE) result, &n1))
297     {
298       free (result);
299       result = NULL;
300       goto leave;
301     }
302   result[nbytes] = 0; /* Make sure it is really a string.  */
303
304  leave:
305   RegCloseKey (key_handle);
306   return result;
307 }
308
309
310 /* Return the name of the directory with the gpgme DLL or the EXE (if
311    statically linked).  May return NULL on severe errors. */
312 const char *
313 _gpgme_get_inst_dir (void)
314 {
315   static char *inst_dir;
316
317   if (override_inst_dir)
318     return override_inst_dir;
319
320   LOCK (get_path_lock);
321   if (!inst_dir)
322     {
323       wchar_t *moddir;
324
325       moddir = malloc ((MAX_PATH+5) * sizeof *moddir);
326       if (moddir)
327         {
328           if (!GetModuleFileNameW (my_hmodule, moddir, MAX_PATH))
329             *moddir = 0;
330           if (!*moddir)
331             gpg_err_set_errno (ENOENT);
332           else
333             {
334               inst_dir = wchar_to_utf8 (moddir);
335               if (inst_dir)
336                 {
337                   char *p = strrchr (inst_dir, '\\');
338                   if (p)
339                     *p = 0;
340                 }
341             }
342           free (moddir);
343         }
344     }
345   UNLOCK (get_path_lock);
346   return inst_dir;
347 }
348
349
350 static char *
351 find_program_in_dir (const char *dir, const char *name)
352 {
353   char *result;
354
355   result = _gpgme_strconcat (dir, "\\", name, NULL);
356   if (!result)
357     return NULL;
358
359   if (access (result, F_OK))
360     {
361       free (result);
362       return NULL;
363     }
364
365   return result;
366 }
367
368
369 static char *
370 find_program_at_standard_place (const char *name)
371 {
372   char path[MAX_PATH];
373   char *result = NULL;
374
375   /* See https://wiki.tcl-lang.org/page/Getting+Windows+%22special+folders%22+with+Ffidl for details on compatibility.
376
377      We First try the generic place and then fallback to the x86
378      (i.e. 32 bit) place.  This will prefer a 64 bit of the program
379      over a 32 bit version on 64 bit Windows if installed.  */
380   if (SHGetSpecialFolderPathA (NULL, path, CSIDL_PROGRAM_FILES, 0))
381     {
382       result = _gpgme_strconcat (path, "\\", name, NULL);
383       if (result && access (result, F_OK))
384         {
385           free (result);
386           result = NULL;
387         }
388     }
389   if (!result
390       && SHGetSpecialFolderPathA (NULL, path, CSIDL_PROGRAM_FILESX86, 0))
391     {
392       result = _gpgme_strconcat (path, "\\", name, NULL);
393       if (result && access (result, F_OK))
394         {
395           free (result);
396           result = NULL;
397         }
398     }
399   return result;
400 }
401
402
403 /* Set the default name for the gpg binary.  This function may only be
404    called by gpgme_set_global_flag.  Returns 0 on success.  */
405 int
406 _gpgme_set_default_gpg_name (const char *name)
407 {
408   if (!default_gpg_name)
409     {
410       default_gpg_name = _gpgme_strconcat (name, ".exe", NULL);
411       if (default_gpg_name)
412         replace_slashes (default_gpg_name);
413     }
414   return !default_gpg_name;
415 }
416
417 /* Set the default name for the gpgconf binary.  This function may only be
418    called by gpgme_set_global_flag.  Returns 0 on success.  */
419 int
420 _gpgme_set_default_gpgconf_name (const char *name)
421 {
422   if (!default_gpgconf_name)
423     {
424       default_gpgconf_name = _gpgme_strconcat (name, ".exe", NULL);
425       if (default_gpgconf_name)
426         replace_slashes (default_gpgconf_name);
427     }
428   return !default_gpgconf_name;
429 }
430
431
432 /* Set the override installation directory.  This function may only be
433    called by gpgme_set_global_flag.  Returns 0 on success.  */
434 int
435 _gpgme_set_override_inst_dir (const char *dir)
436 {
437   if (!override_inst_dir)
438     {
439       override_inst_dir = strdup (dir);
440       if (override_inst_dir)
441         {
442           replace_slashes (override_inst_dir);
443           /* Remove a trailing slash.  */
444           if (*override_inst_dir
445               && override_inst_dir[strlen (override_inst_dir)-1] == '\\')
446             override_inst_dir[strlen (override_inst_dir)-1] = 0;
447         }
448     }
449   return !override_inst_dir;
450 }
451
452
453 /* Return the full file name of the GPG binary.  This function is used
454    iff gpgconf was not found and thus it can be assumed that gpg2 is
455    not installed.  This function is only called by get_gpgconf_item
456    and may not be called concurrently. */
457 char *
458 _gpgme_get_gpg_path (void)
459 {
460   char *gpg = NULL;
461   const char *name, *inst_dir;
462
463   name = default_gpg_name? get_basename (default_gpg_name) : "gpg.exe";
464
465   /* 1. Try to find gpg.exe in the installation directory of gpgme.  */
466   inst_dir = _gpgme_get_inst_dir ();
467   if (inst_dir)
468     {
469       gpg = find_program_in_dir (inst_dir, name);
470     }
471
472   /* 2. Try to find gpg.exe using that ancient registry key.  */
473   if (!gpg)
474     {
475       char *dir;
476
477       dir = read_w32_registry_string ("HKEY_LOCAL_MACHINE",
478                                       GNUPG_REGKEY_2,
479                                       "Install Directory");
480       if (dir)
481         {
482           gpg = find_program_in_dir (dir, name);
483           free (dir);
484         }
485     }
486
487   /* 3. Try to find gpg.exe below CSIDL_PROGRAM_FILES.  */
488   if (!gpg)
489     {
490       name = default_gpg_name? default_gpg_name : "GNU\\GnuPG\\gpg.exe";
491       gpg = find_program_at_standard_place (name);
492     }
493
494   /* 4. Print a debug message if not found.  */
495   if (!gpg)
496     _gpgme_debug (DEBUG_ENGINE, "_gpgme_get_gpg_path: '%s' not found", name);
497
498   return gpg;
499 }
500
501
502 /* This function is only called by get_gpgconf_item and may not be
503    called concurrently.  */
504 char *
505 _gpgme_get_gpgconf_path (void)
506 {
507   char *gpgconf = NULL;
508   const char *inst_dir, *name;
509
510   name = default_gpgconf_name? get_basename(default_gpgconf_name):"gpgconf.exe";
511
512   /* 1. Try to find gpgconf.exe in the installation directory of gpgme.  */
513   inst_dir = _gpgme_get_inst_dir ();
514   if (inst_dir)
515     {
516       gpgconf = find_program_in_dir (inst_dir, name);
517     }
518
519   /* 2. Try to find gpgconf.exe from GnuPG >= 2.1 below CSIDL_PROGRAM_FILES. */
520   if (!gpgconf)
521     {
522       const char *name2 = (default_gpgconf_name ? default_gpgconf_name
523                            /**/                 : "GnuPG\\bin\\gpgconf.exe");
524       gpgconf = find_program_at_standard_place (name2);
525     }
526
527   /* 3. Try to find gpgconf.exe using the Windows registry. */
528   if (!gpgconf)
529     {
530       char *dir;
531
532       dir = read_w32_registry_string (NULL,
533                                       GNUPG_REGKEY_2,
534                                       "Install Directory");
535       if (!dir)
536         {
537           char *tmp = read_w32_registry_string (NULL,
538                                                 GNUPG_REGKEY_3,
539                                                 "Install Directory");
540           if (tmp)
541             {
542               dir = _gpgme_strconcat (tmp, "\\bin", NULL);
543               free (tmp);
544               if (!dir)
545                 return NULL;
546             }
547         }
548       if (dir)
549         {
550           gpgconf = find_program_in_dir (dir, name);
551           free (dir);
552         }
553     }
554
555   /* 4. Try to find gpgconf.exe from Gpg4win below CSIDL_PROGRAM_FILES.  */
556   if (!gpgconf)
557     {
558       gpgconf = find_program_at_standard_place ("GNU\\GnuPG\\gpgconf.exe");
559     }
560
561   /* 5. Try to find gpgconf.exe relative to us.  */
562   if (!gpgconf && inst_dir)
563     {
564       char *dir = _gpgme_strconcat (inst_dir, "\\..\\..\\GnuPG\\bin");
565       gpgconf = find_program_in_dir (dir, name);
566       free (dir);
567     }
568
569   /* 5. Print a debug message if not found.  */
570   if (!gpgconf)
571     _gpgme_debug (DEBUG_ENGINE, "_gpgme_get_gpgconf_path: '%s' not found",name);
572
573   return gpgconf;
574 }
575
576
577 const char *
578 _gpgme_get_w32spawn_path (void)
579 {
580   static char *w32spawn_program;
581   const char *inst_dir;
582
583   inst_dir = _gpgme_get_inst_dir ();
584   LOCK (get_path_lock);
585   if (!w32spawn_program)
586     w32spawn_program = find_program_in_dir (inst_dir, "gpgme-w32spawn.exe");
587   UNLOCK (get_path_lock);
588   return w32spawn_program;
589 }
590
591
592 /* Return an integer value from gpgme specific configuration
593    entries. VALUE receives that value; function returns true if a value
594    has been configured and false if not. */
595 int
596 _gpgme_get_conf_int (const char *key, int *value)
597 {
598   char *tmp = read_w32_registry_string (NULL, "Software\\GNU\\gpgme", key);
599   if (!tmp)
600     return 0;
601   *value = atoi (tmp);
602   free (tmp);
603   return 1;
604 }
605
606
607 \f
608 /* mkstemp extracted from libc/sysdeps/posix/tempname.c.  Copyright
609    (C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc.
610
611    The GNU C Library is free software; you can redistribute it and/or
612    modify it under the terms of the GNU Lesser General Public
613    License as published by the Free Software Foundation; either
614    version 2.1 of the License, or (at your option) any later version.  */
615
616 static const char letters[] =
617 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
618
619 /* Generate a temporary file name based on TMPL.  TMPL must match the
620    rules for mk[s]temp (i.e. end in "XXXXXX").  The name constructed
621    does not exist at the time of the call to mkstemp.  TMPL is
622    overwritten with the result.  */
623 static int
624 my_mkstemp (char *tmpl)
625 {
626   int len;
627   char *XXXXXX;
628   static uint64_t value;
629   uint64_t random_time_bits;
630   unsigned int count;
631   int fd = -1;
632   int save_errno = errno;
633
634   /* A lower bound on the number of temporary files to attempt to
635      generate.  The maximum total number of temporary file names that
636      can exist for a given template is 62**6.  It should never be
637      necessary to try all these combinations.  Instead if a reasonable
638      number of names is tried (we define reasonable as 62**3) fail to
639      give the system administrator the chance to remove the problems.  */
640 #define ATTEMPTS_MIN (62 * 62 * 62)
641
642   /* The number of times to attempt to generate a temporary file.  To
643      conform to POSIX, this must be no smaller than TMP_MAX.  */
644 #if ATTEMPTS_MIN < TMP_MAX
645   unsigned int attempts = TMP_MAX;
646 #else
647   unsigned int attempts = ATTEMPTS_MIN;
648 #endif
649
650   len = strlen (tmpl);
651   if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
652     {
653       gpg_err_set_errno (EINVAL);
654       return -1;
655     }
656
657   /* This is where the Xs start.  */
658   XXXXXX = &tmpl[len - 6];
659
660   /* Get some more or less random data.  */
661   {
662     FILETIME ft;
663
664     GetSystemTimeAsFileTime (&ft);
665     random_time_bits = (((uint64_t)ft.dwHighDateTime << 32)
666                         | (uint64_t)ft.dwLowDateTime);
667   }
668   value += random_time_bits ^ ath_self ();
669
670   for (count = 0; count < attempts; value += 7777, ++count)
671     {
672       uint64_t v = value;
673
674       /* Fill in the random bits.  */
675       XXXXXX[0] = letters[v % 62];
676       v /= 62;
677       XXXXXX[1] = letters[v % 62];
678       v /= 62;
679       XXXXXX[2] = letters[v % 62];
680       v /= 62;
681       XXXXXX[3] = letters[v % 62];
682       v /= 62;
683       XXXXXX[4] = letters[v % 62];
684       v /= 62;
685       XXXXXX[5] = letters[v % 62];
686
687       fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
688       if (fd >= 0)
689         {
690           gpg_err_set_errno (save_errno);
691           return fd;
692         }
693       else if (errno != EEXIST)
694         return -1;
695     }
696
697   /* We got out of the loop because we ran out of combinations to try.  */
698   gpg_err_set_errno (EEXIST);
699   return -1;
700 }
701
702 \f
703 int
704 _gpgme_mkstemp (int *fd, char **name)
705 {
706   char tmp[MAX_PATH + 2];
707   char *tmpname;
708   int err;
709
710   *fd = -1;
711   *name = NULL;
712
713   err = GetTempPathA (MAX_PATH + 1, tmp);
714   if (err == 0 || err > MAX_PATH + 1)
715     strcpy (tmp,"c:\\windows\\temp");
716   else
717     {
718       int len = strlen(tmp);
719
720       /* GetTempPath may return with \ on the end */
721       while(len > 0 && tmp[len - 1] == '\\')
722         {
723           tmp[len-1] = '\0';
724           len--;
725         }
726     }
727
728   tmpname = _gpgme_strconcat (tmp, "\\gpgme-XXXXXX", NULL);
729   if (!tmpname)
730     return -1;
731   *fd = my_mkstemp (tmpname);
732   if (*fd < 0)
733     {
734       free (tmpname);
735       return -1;
736     }
737
738   *name = tmpname;
739   return 0;
740 }
741
742
743 \f
744 /* Entry point called by the DLL loader.  */
745 #ifdef DLL_EXPORT
746 int WINAPI
747 DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved)
748 {
749   (void)reserved;
750
751   if (reason == DLL_PROCESS_ATTACH)
752     my_hmodule = hinst;
753
754   return TRUE;
755 }
756 #endif /*DLL_EXPORT*/