Get GPG Agent's socket directly from the agent.
[scute.git] / src / get-path.c
1 /* agent.c - Talking to gpg-agent.
2    Copyright (C) 2008 g10 Code GmbH
3
4    This file is part of Scute.
5  
6    Scute is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    Scute is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with Scute; if not, write to the Free Software Foundation,
18    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
20    In addition, as a special exception, g10 Code GmbH gives permission
21    to link this library: with the Mozilla Foundation's code for
22    Mozilla (or with modified versions of it that use the same license
23    as the "Mozilla" code), and distribute the linked executables.  You
24    must obey the GNU General Public License in all respects for all of
25    the code used other than "Mozilla".  If you modify this file, you
26    may extend this exception to your version of the file, but you are
27    not obligated to do so.  If you do not wish to do so, delete this
28    exception statement from your version.  */
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <assert.h>
37 #include <errno.h>
38 #include <sys/time.h>
39 #include <sys/types.h>
40 #include <signal.h>
41 #include <fcntl.h>
42 #include <stdarg.h>
43 #ifdef HAVE_W32_SYSTEM
44 #include <windows.h>
45 #include <shlobj.h>
46 #include <io.h>
47 #endif
48
49 #include "support.h"
50
51 #ifdef HAVE_W32_SYSTEM
52 #define GNUPG_DEFAULT_HOMEDIR "c:/gnupg"
53 #elif defined(__VMS)
54 #define GNUPG_DEFAULT_HOMEDIR "/SYS\$LOGIN/gnupg" 
55 #else
56 #define GNUPG_DEFAULT_HOMEDIR "~/.gnupg"
57 #endif 
58
59 #ifdef HAVE_DOSISH_SYSTEM
60 #define DIRSEP_C '\\'
61 #define DIRSEP_S "\\"
62 #else
63 #define DIRSEP_C '/'
64 #define DIRSEP_S "/"
65 #endif
66
67 \f
68 #ifdef HAVE_W32_SYSTEM
69 #define RTLD_LAZY 0
70
71 static __inline__ void *
72 dlopen (const char * name, int flag)
73 {
74   void * hd = LoadLibrary (name);
75   return hd;
76 }
77
78 static __inline__ void *
79 dlsym (void * hd, const char * sym)
80 {
81   if (hd && sym)
82     {
83       void * fnc = GetProcAddress (hd, sym);
84       if (!fnc)
85         return NULL;
86       return fnc;
87     }
88   return NULL;
89 }
90
91 static __inline__ int
92 dlclose (void * hd)
93 {
94   if (hd)
95     {
96       FreeLibrary (hd);
97       return 0;
98     }
99   return -1;
100 }  
101
102
103 /* Return a string from the W32 Registry or NULL in case of error.
104    Caller must release the return value.  A NULL for root is an alias
105    for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. */
106 static char *
107 read_w32_registry_string (const char *root, const char *dir, const char *name)
108 {
109   HKEY root_key, key_handle;
110   DWORD n1, nbytes, type;
111   char *result = NULL;
112         
113   if ( !root )
114     root_key = HKEY_CURRENT_USER;
115   else if ( !strcmp( root, "HKEY_CLASSES_ROOT" ) )
116     root_key = HKEY_CLASSES_ROOT;
117   else if ( !strcmp( root, "HKEY_CURRENT_USER" ) )
118     root_key = HKEY_CURRENT_USER;
119   else if ( !strcmp( root, "HKEY_LOCAL_MACHINE" ) )
120     root_key = HKEY_LOCAL_MACHINE;
121   else if ( !strcmp( root, "HKEY_USERS" ) )
122     root_key = HKEY_USERS;
123   else if ( !strcmp( root, "HKEY_PERFORMANCE_DATA" ) )
124     root_key = HKEY_PERFORMANCE_DATA;
125   else if ( !strcmp( root, "HKEY_CURRENT_CONFIG" ) )
126     root_key = HKEY_CURRENT_CONFIG;
127   else
128     return NULL;
129         
130   if ( RegOpenKeyEx ( root_key, dir, 0, KEY_READ, &key_handle ) )
131     {
132       if (root)
133         return NULL; /* no need for a RegClose, so return direct */
134       /* It seems to be common practise to fall back to HKLM. */
135       if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
136         return NULL; /* still no need for a RegClose, so return direct */
137     }
138
139   nbytes = 1;
140   if ( RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes ) )
141     {
142       if (root)
143         goto leave;
144       /* Try to fallback to HKLM also vor a missing value.  */
145       RegCloseKey (key_handle);
146       if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
147         return NULL; /* Nope.  */
148       if (RegQueryValueEx ( key_handle, name, 0, NULL, NULL, &nbytes))
149         goto leave;
150     }
151   result = malloc ( (n1=nbytes+1) );
152   if ( !result )
153     goto leave;
154   if ( RegQueryValueEx ( key_handle, name, 0, &type, result, &n1 ) )
155     {
156       free(result); result = NULL;
157       goto leave;
158     }
159   result[nbytes] = 0; /* Make sure it is really a string.  */
160   if (type == REG_EXPAND_SZ && strchr (result, '%')) 
161     {
162       char *tmp;
163         
164       n1 += 1000;
165       tmp = malloc (n1+1);
166       if (!tmp)
167         goto leave;
168       nbytes = ExpandEnvironmentStrings (result, tmp, n1);
169       if (nbytes && nbytes > n1)
170         {
171           free (tmp);
172           n1 = nbytes;
173           tmp = malloc (n1 + 1);
174           if (!tmp)
175             goto leave;
176           nbytes = ExpandEnvironmentStrings (result, tmp, n1);
177           if (nbytes && nbytes > n1) {
178             free (tmp); /* Oops - truncated, better don't expand at all. */
179             goto leave;
180           }
181           tmp[nbytes] = 0;
182           free (result);
183           result = tmp;
184         }
185       else if (nbytes)  /* Okay, reduce the length. */
186         {
187           tmp[nbytes] = 0;
188           free (result);
189           result = malloc (strlen (tmp)+1);
190           if (!result)
191             result = tmp;
192           else 
193             {
194               strcpy (result, tmp);
195               free (tmp);
196             }
197         }
198       else  /* Error - don't expand. */
199         {
200           free (tmp);
201         }
202     }
203
204  leave:
205   RegCloseKey( key_handle );
206   return result;
207 }
208
209
210 /* This is a helper function to load and run a Windows function from
211    either of one DLLs. */
212 static HRESULT
213 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
214 {
215   static int initialized;
216   static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
217
218   if (!initialized)
219     {
220       static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
221       void *handle;
222       int i;
223
224       initialized = 1;
225
226       for (i=0, handle = NULL; !handle && dllnames[i]; i++)
227         {
228           handle = dlopen (dllnames[i], RTLD_LAZY);
229           if (handle)
230             {
231               func = dlsym (handle, "SHGetFolderPathA");
232               if (!func)
233                 {
234                   dlclose (handle);
235                   handle = NULL;
236                 }
237             }
238         }
239     }
240
241   if (func)
242     return func (a,b,c,d,e);
243   else
244     return -1;
245 }
246
247
248 static char *
249 find_program_in_inst_dir (const char *name)
250 {
251   char *result = NULL;
252   char *tmp;
253
254   tmp = read_w32_registry_string ("HKEY_LOCAL_MACHINE",
255                                   "Software\\GNU\\GnuPG",
256                                   "Install Directory");
257   if (!tmp)
258     return NULL;
259
260   result = malloc (strlen (tmp) + 1 + strlen (name) + 1);
261   if (!result)
262     {
263       free (tmp);
264       return NULL;
265     }
266
267   strcpy (stpcpy (stpcpy (result, tmp), "\\"), name);
268   free (tmp);
269   if (access (result, F_OK))
270     {
271       free (result);
272       return NULL;
273     }
274
275   return result;
276 }
277
278
279
280 static char *
281 find_program_at_standard_place (const char *name)
282 {
283   char path[MAX_PATH];
284   char *result = NULL;
285       
286   if (w32_shgetfolderpath (NULL, CSIDL_PROGRAM_FILES, NULL, 0, path) >= 0) 
287     {
288       result = malloc (strlen (path) + 1 + strlen (name) + 1);
289       if (result)
290         {
291           strcpy (stpcpy (stpcpy (result, path), "\\"), name);
292           if (access (result, F_OK))
293             {
294               free (result);
295               result = NULL;
296             }
297         }
298     }
299   return result;
300 }
301 #endif
302
303
304 const char *
305 get_gpgsm_path (void)
306 {
307   static const char *pgmname;
308
309 #ifdef HAVE_W32_SYSTEM
310   if (!pgmname)
311     pgmname = find_program_in_inst_dir ("gpgsm.exe");
312   if (!pgmname)
313     pgmname = find_program_at_standard_place ("GNU\\GnuPG\\gpgsm.exe");
314 #endif
315   if (!pgmname)
316     pgmname = GPGSM_PATH;
317   return pgmname;
318 }
319
320
321 const char *
322 get_gpg_agent_path (void)
323 {
324   static const char *pgmname;
325
326 #ifdef HAVE_W32_SYSTEM
327   if (!pgmname)
328     pgmname = find_program_in_inst_dir ("gpg-agent.exe");
329   if (!pgmname)
330     pgmname = find_program_at_standard_place ("GNU\\GnuPG\\gpg-agent.exe");
331 #endif
332   if (!pgmname)
333     pgmname = GPG_AGENT_PATH;
334   return pgmname;
335 }
336
337
338 const char *
339 get_gpg_connect_agent_path (void)
340 {
341   static const char *pgmname;
342
343 #ifdef HAVE_W32_SYSTEM
344   if (!pgmname)
345     pgmname = find_program_in_inst_dir ("gpg-connect-agent.exe");
346   if (!pgmname)
347     pgmname = find_program_at_standard_place ("GNU\\GnuPG\\gpg-connect-agent.exe");
348 #endif
349   if (!pgmname)
350     pgmname = GPG_CONNECT_AGENT_PATH;
351   return pgmname;
352 }
353
354
355 \f
356 /* Home directory.  */
357
358 #ifdef HAVE_W32_SYSTEM
359 #ifndef CSIDL_APPDATA
360 #define CSIDL_APPDATA 0x001a
361 #endif
362 #ifndef CSIDL_LOCAL_APPDATA
363 #define CSIDL_LOCAL_APPDATA 0x001c
364 #endif
365 #ifndef CSIDL_COMMON_APPDATA
366 #define CSIDL_COMMON_APPDATA 0x0023
367 #endif
368 #ifndef CSIDL_FLAG_CREATE
369 #define CSIDL_FLAG_CREATE 0x8000
370 #endif
371 #endif /*HAVE_W32_SYSTEM*/
372
373 /* Get the standard home directory.  In general this function should
374    not be used as it does not consider a registry value (under W32) or
375    the GNUPGHOME environment variable.  It is better to use
376    default_homedir(). */
377 const char *
378 standard_homedir (void)
379 {
380 #ifdef HAVE_W32_SYSTEM
381   static const char *dir;
382
383   if (!dir)
384     {
385       char path[MAX_PATH];
386       
387       /* It might be better to use LOCAL_APPDATA because this is
388          defined as "non roaming" and thus more likely to be kept
389          locally.  For private keys this is desired.  However, given
390          that many users copy private keys anyway forth and back,
391          using a system roaming services might be better than to let
392          them do it manually.  A security conscious user will anyway
393          use the registry entry to have better control.  */
394       if (w32_shgetfolderpath (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, 
395                                NULL, 0, path) >= 0) 
396         {
397           char *tmp = malloc (strlen (path) + 6 +1);
398           if (tmp)
399             {
400               strcpy (stpcpy (tmp, path), "\\gnupg");
401               dir = tmp;
402           
403               /* Try to create the directory if it does not yet exists.  */
404               if (access (dir, F_OK))
405                 CreateDirectory (dir, NULL);
406             }
407         }
408
409       if (!dir)
410         dir = GNUPG_DEFAULT_HOMEDIR;
411     }
412   return dir;
413 #else/*!HAVE_W32_SYSTEM*/
414   return GNUPG_DEFAULT_HOMEDIR;
415 #endif /*!HAVE_W32_SYSTEM*/
416 }
417
418 /* Set up the default home directory.  The usual --homedir option
419    should be parsed later. */
420 const char *
421 default_homedir (void)
422 {
423   const char *dir;
424
425   dir = getenv ("GNUPGHOME");
426 #ifdef HAVE_W32_SYSTEM
427   if (!dir || !*dir)
428     {
429       static const char *saved_dir;
430       
431       if (!saved_dir)
432         {
433           if (!dir || !*dir)
434             {
435               char *tmp;
436
437               tmp = read_w32_registry_string (NULL, "Software\\GNU\\GnuPG",
438                                               "HomeDir");
439               if (tmp && *tmp)
440                 {
441                   free (tmp);
442                   tmp = NULL;
443                 }
444                if (tmp)
445                 saved_dir = tmp;
446             }
447           
448           if (!saved_dir)
449             saved_dir = standard_homedir ();
450         }
451       dir = saved_dir;
452     }
453 #endif /*HAVE_W32_SYSTEM*/
454   if (!dir || !*dir)
455     dir = GNUPG_DEFAULT_HOMEDIR;
456
457   return dir;
458 }
459
460
461 /* Construct a filename from the NULL terminated list of parts.  Tilde
462    expansion is done here.  */
463 char *
464 make_filename (const char *first_part, ...)
465 {
466   va_list arg_ptr;
467   size_t n;
468   const char *s;
469   char *name;
470   char *home;
471   char *p;
472   
473   va_start (arg_ptr, first_part);
474   n = strlen (first_part) + 1;
475   while ((s = va_arg (arg_ptr, const char *)))
476     n += strlen (s) + 1;
477   va_end (arg_ptr);
478   
479   home = NULL;
480   if (*first_part == '~' && first_part[1] == '/'
481       && (home = getenv("HOME")) && *home)
482     n += strlen (home);
483
484   name = malloc (n);
485   if (! name)
486     return NULL;
487   p = (home 
488        ? stpcpy (stpcpy (name,home), first_part + 1)
489        : stpcpy (name, first_part));
490
491   va_start (arg_ptr, first_part);
492   while ((s = va_arg(arg_ptr, const char *)))
493     p = stpcpy (stpcpy (p,"/"), s);
494   va_end (arg_ptr);
495
496 #ifdef HAVE_W32_SYSTEM
497   /* We better avoid mixing slashes and backslashes and prefer
498      backslashes.  There is usual no problem with mixing them, however
499      a very few W32 API calls can't grok plain slashes.  Printing
500      filenames with mixed slashes also looks a bit strange. */
501   if (strchr (name, '\\'))
502     {
503       for (p = name; *p; p++)
504         if (*p == '/')
505           *p = '\\';
506     }
507 #endif
508
509   return name;
510 }