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