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