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