Use a custom log handler for libassuan.
[gnupg.git] / common / asshelp.c
1 /* asshelp.c - Helper functions for Assuan
2  * Copyright (C) 2002, 2004, 2007, 2009 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #ifdef HAVE_LOCALE_H
27 #include <locale.h>
28 #endif
29
30 #define JNLIB_NEED_LOG_LOGV
31 #include "i18n.h"
32 #include "util.h"
33 #include "exechelp.h"
34 #include "sysutils.h"
35 #include "status.h" 
36 #include "asshelp.h"
37
38
39 static int
40 my_libassuan_log_handler (assuan_context_t ctx, void *hook,
41                           unsigned int cat, const char *msg)
42 {
43   unsigned int dbgval;
44
45   if (cat != ASSUAN_LOG_CONTROL)
46     return 0; /* We only want the control channel messages.  */
47   dbgval = hook? *(unsigned int*)hook : 0;
48   if (!(dbgval & 1024))
49     return 0; /* Assuan debugging is not enabled.  */
50
51   if (msg)
52     log_string (JNLIB_LOG_DEBUG, msg);
53
54   return 1;
55 }
56
57
58 /* Setup libassuan to use our own logging functions.  Should be used
59    early at startup.  */
60 void
61 setup_libassuan_logging (unsigned int *debug_var_address)
62 {
63   assuan_set_log_cb (my_libassuan_log_handler, debug_var_address);
64 }
65
66
67
68 static gpg_error_t
69 send_one_option (assuan_context_t ctx, gpg_err_source_t errsource,
70                  const char *name, const char *value, int use_putenv)
71 {
72   gpg_error_t err;
73   char *optstr;
74
75   (void)errsource;
76
77   if (!value || !*value)
78     err = 0;  /* Avoid sending empty strings.  */
79   else if (asprintf (&optstr, "OPTION %s%s=%s", 
80                      use_putenv? "putenv=":"", name, value) < 0)
81     err = gpg_error_from_syserror ();
82   else
83     {
84       err = assuan_transact (ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL);
85       xfree (optstr);
86     }
87
88   return err;
89 }
90
91
92 /* Send the assuan commands pertaining to the pinentry environment.  The
93    OPT_* arguments are optional and may be used to override the
94    defaults taken from the current locale. */
95 gpg_error_t
96 send_pinentry_environment (assuan_context_t ctx,
97                            gpg_err_source_t errsource,
98                            const char *opt_lc_ctype,
99                            const char *opt_lc_messages,
100                            session_env_t session_env)
101
102 {
103   gpg_error_t err = 0;
104   char *old_lc = NULL; 
105   char *dft_lc = NULL;
106   const char *dft_ttyname;
107   int iterator;
108   const char *name, *assname, *value;
109   int is_default;
110
111   iterator = 0; 
112   while ((name = session_env_list_stdenvnames (&iterator, &assname)))
113     {
114       value = session_env_getenv_or_default (session_env, name, NULL);
115       if (!value)
116         continue;
117
118       if (assname)
119         err = send_one_option (ctx, errsource, assname, value, 0);
120       else
121         {
122           err = send_one_option (ctx, errsource, name, value, 1);
123           if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
124             err = 0;  /* Server too old; can't pass the new envvars.  */
125         }
126       if (err)
127         return err;
128     }
129
130
131   dft_ttyname = session_env_getenv_or_default (session_env, "GPG_TTY", 
132                                                &is_default);
133   if (dft_ttyname && !is_default)
134     dft_ttyname = NULL;  /* We need the default value.  */
135
136   /* Send the value for LC_CTYPE.  */
137 #if defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
138   old_lc = setlocale (LC_CTYPE, NULL);
139   if (old_lc)
140     {
141       old_lc = xtrystrdup (old_lc);
142       if (!old_lc)
143         return gpg_error_from_syserror ();
144     }
145   dft_lc = setlocale (LC_CTYPE, "");
146 #endif
147   if (opt_lc_ctype || (dft_ttyname && dft_lc))
148     {
149       err = send_one_option (ctx, errsource, "lc-ctype", 
150                              opt_lc_ctype ? opt_lc_ctype : dft_lc, 0);
151     }
152 #if defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
153   if (old_lc)
154     {
155       setlocale (LC_CTYPE, old_lc);
156       xfree (old_lc);
157     }
158 #endif
159   if (err)
160     return err;
161
162   /* Send the value for LC_MESSAGES.  */
163 #if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES)
164   old_lc = setlocale (LC_MESSAGES, NULL);
165   if (old_lc)
166     {
167       old_lc = xtrystrdup (old_lc);
168       if (!old_lc)
169         return gpg_error_from_syserror ();
170     }
171   dft_lc = setlocale (LC_MESSAGES, "");
172 #endif
173   if (opt_lc_messages || (dft_ttyname && dft_lc))
174     {
175       err = send_one_option (ctx, errsource, "lc-messages", 
176                              opt_lc_messages ? opt_lc_messages : dft_lc, 0);
177     }
178 #if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES)
179   if (old_lc)
180     {
181       setlocale (LC_MESSAGES, old_lc);
182       xfree (old_lc);
183     }
184 #endif
185   if (err)
186     return err;
187
188   return 0;
189 }
190
191
192 /* Try to connect to the agent via socket or fork it off and work by
193    pipes.  Handle the server's initial greeting.  Returns a new assuan
194    context at R_CTX or an error code. */
195 gpg_error_t
196 start_new_gpg_agent (assuan_context_t *r_ctx,
197                      gpg_err_source_t errsource,
198                      const char *homedir,
199                      const char *agent_program,
200                      const char *opt_lc_ctype,
201                      const char *opt_lc_messages,
202                      session_env_t session_env,
203                      int verbose, int debug,
204                      gpg_error_t (*status_cb)(ctrl_t, int, ...),
205                      ctrl_t status_cb_arg)
206 {
207   /* If we ever failed to connect via a socket we will force the use
208      of the pipe based server for the lifetime of the process.  */
209   static int force_pipe_server = 0;
210
211   gpg_error_t rc = 0;
212   char *infostr, *p;
213   assuan_context_t ctx;
214
215   *r_ctx = NULL;
216
217   rc = assuan_new (&ctx);
218   if (rc)
219     {
220       log_error ("error allocating assuan context: %s\n", gpg_strerror (rc));
221       return rc;
222     }
223
224  restart:
225   infostr = force_pipe_server? NULL : getenv ("GPG_AGENT_INFO");
226   if (!infostr || !*infostr)
227     {
228       char *sockname;
229
230       /* First check whether we can connect at the standard
231          socket.  */
232       sockname = make_filename (homedir, "S.gpg-agent", NULL);
233       rc = assuan_socket_connect (ctx, sockname, 0, 0);
234
235       if (rc)
236         {
237           /* With no success start a new server.  */
238           if (verbose)
239             log_info (_("no running gpg-agent - starting one\n"));
240           
241           if (status_cb)
242             status_cb (status_cb_arg, STATUS_PROGRESS, 
243                        "starting_agent ? 0 0", NULL);
244           
245           if (fflush (NULL))
246             {
247               gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
248               log_error ("error flushing pending output: %s\n",
249                          strerror (errno));
250               xfree (sockname);
251               assuan_release (ctx);
252               return tmperr;
253             }
254           
255           if (!agent_program || !*agent_program)
256             agent_program = gnupg_module_name (GNUPG_MODULE_NAME_AGENT);
257
258 #ifdef HAVE_W32_SYSTEM
259           {
260             /* Under Windows we start the server in daemon mode.  This
261                is because the default is to use the standard socket
262                and thus there is no need for the GPG_AGENT_INFO
263                envvar.  This is possible as we don't have a real unix
264                domain socket but use a plain file and thus there is no
265                need to care about non-local file systems. */
266             const char *argv[3];
267
268             argv[0] = "--daemon";
269             argv[1] = "--use-standard-socket"; 
270             argv[2] = NULL;  
271
272             rc = gnupg_spawn_process_detached (agent_program, argv, NULL);
273             if (rc)
274               log_debug ("failed to start agent `%s': %s\n",
275                          agent_program, gpg_strerror (rc));
276             else
277               {
278                 /* Give the agent some time to prepare itself. */
279                 gnupg_sleep (3);
280                 /* Now try again to connect the agent.  */
281                 rc = assuan_socket_connect (ctx, sockname, 0, 0);
282               }
283           }
284 #else /*!HAVE_W32_SYSTEM*/
285           {
286             const char *pgmname;
287             const char *argv[3];
288             int no_close_list[3];
289             int i;
290
291             if ( !(pgmname = strrchr (agent_program, '/')))
292               pgmname = agent_program;
293             else
294               pgmname++;
295             
296             argv[0] = pgmname;
297             argv[1] = "--server";
298             argv[2] = NULL;
299             
300             i=0;
301             if (log_get_fd () != -1)
302               no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
303             no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr));
304             no_close_list[i] = -1;
305             
306             /* Connect to the agent and perform initial handshaking. */
307             rc = assuan_pipe_connect (ctx, agent_program, argv,
308                                       no_close_list, NULL, NULL, 0);
309           }
310 #endif /*!HAVE_W32_SYSTEM*/
311         }
312       xfree (sockname);
313     }
314   else
315     {
316       int prot;
317       int pid;
318
319       infostr = xstrdup (infostr);
320       if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
321         {
322           log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
323           xfree (infostr);
324           force_pipe_server = 1;
325           goto restart;
326         }
327       *p++ = 0;
328       pid = atoi (p);
329       while (*p && *p != PATHSEP_C)
330         p++;
331       prot = *p? atoi (p+1) : 0;
332       if (prot != 1)
333         {
334           log_error (_("gpg-agent protocol version %d is not supported\n"),
335                      prot);
336           xfree (infostr);
337           force_pipe_server = 1;
338           goto restart;
339         }
340
341       rc = assuan_socket_connect (ctx, infostr, pid, 0);
342       xfree (infostr);
343       if (gpg_err_code (rc) == GPG_ERR_ASS_CONNECT_FAILED)
344         {
345           log_info (_("can't connect to the agent - trying fall back\n"));
346           force_pipe_server = 1;
347           goto restart;
348         }
349     }
350
351   if (rc)
352     {
353       log_error ("can't connect to the agent: %s\n", gpg_strerror (rc));
354       assuan_release (ctx);
355       return gpg_error (GPG_ERR_NO_AGENT);
356     }
357
358   if (debug)
359     log_debug ("connection to agent established\n");
360
361   rc = assuan_transact (ctx, "RESET",
362                         NULL, NULL, NULL, NULL, NULL, NULL);
363   if (!rc)
364     rc = send_pinentry_environment (ctx, errsource,
365                                     opt_lc_ctype, opt_lc_messages,
366                                     session_env);
367   if (rc)
368     {
369       assuan_release (ctx);
370       return rc;
371     }
372
373   *r_ctx = ctx;
374   return 0;
375 }
376