Reworked passing of envars to Pinentry.
[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  restart:
187   infostr = force_pipe_server? NULL : getenv ("GPG_AGENT_INFO");
188   if (!infostr || !*infostr)
189     {
190       char *sockname;
191
192       /* First check whether we can connect at the standard
193          socket.  */
194       sockname = make_filename (homedir, "S.gpg-agent", NULL);
195       rc = assuan_socket_connect (&ctx, sockname, 0);
196
197       if (rc)
198         {
199           /* With no success start a new server.  */
200           if (verbose)
201             log_info (_("no running gpg-agent - starting one\n"));
202           
203           if (status_cb)
204             status_cb (status_cb_arg, STATUS_PROGRESS, 
205                        "starting_agent ? 0 0", NULL);
206           
207           if (fflush (NULL))
208             {
209               gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
210               log_error ("error flushing pending output: %s\n",
211                          strerror (errno));
212               xfree (sockname);
213               return tmperr;
214             }
215           
216           if (!agent_program || !*agent_program)
217             agent_program = gnupg_module_name (GNUPG_MODULE_NAME_AGENT);
218
219 #ifdef HAVE_W32_SYSTEM
220           {
221             /* Under Windows we start the server in daemon mode.  This
222                is because the default is to use the standard socket
223                and thus there is no need for the GPG_AGENT_INFO
224                envvar.  This is possible as we don't have a real unix
225                domain socket but use a plain file and thus there is no
226                need to care about non-local file systems. */
227             const char *argv[3];
228
229             argv[0] = "--daemon";
230             argv[1] = "--use-standard-socket"; 
231             argv[2] = NULL;  
232
233             rc = gnupg_spawn_process_detached (agent_program, argv, NULL);
234             if (rc)
235               log_debug ("failed to start agent `%s': %s\n",
236                          agent_program, gpg_strerror (rc));
237             else
238               {
239                 /* Give the agent some time to prepare itself. */
240                 gnupg_sleep (3);
241                 /* Now try again to connect the agent.  */
242                 rc = assuan_socket_connect (&ctx, sockname, 0);
243               }
244           }
245 #else /*!HAVE_W32_SYSTEM*/
246           {
247             const char *pgmname;
248             const char *argv[3];
249             int no_close_list[3];
250             int i;
251
252             if ( !(pgmname = strrchr (agent_program, '/')))
253               pgmname = agent_program;
254             else
255               pgmname++;
256             
257             argv[0] = pgmname;
258             argv[1] = "--server";
259             argv[2] = NULL;
260             
261             i=0;
262             if (log_get_fd () != -1)
263               no_close_list[i++] = log_get_fd ();
264             no_close_list[i++] = fileno (stderr);
265             no_close_list[i] = -1;
266             
267             /* Connect to the agent and perform initial handshaking. */
268             rc = assuan_pipe_connect (&ctx, agent_program, argv,
269                                       no_close_list);
270           }
271 #endif /*!HAVE_W32_SYSTEM*/
272         }
273       xfree (sockname);
274     }
275   else
276     {
277       int prot;
278       int pid;
279
280       infostr = xstrdup (infostr);
281       if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
282         {
283           log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
284           xfree (infostr);
285           force_pipe_server = 1;
286           goto restart;
287         }
288       *p++ = 0;
289       pid = atoi (p);
290       while (*p && *p != PATHSEP_C)
291         p++;
292       prot = *p? atoi (p+1) : 0;
293       if (prot != 1)
294         {
295           log_error (_("gpg-agent protocol version %d is not supported\n"),
296                      prot);
297           xfree (infostr);
298           force_pipe_server = 1;
299           goto restart;
300         }
301
302       rc = assuan_socket_connect (&ctx, infostr, pid);
303       xfree (infostr);
304       if (gpg_err_code (rc) == GPG_ERR_ASS_CONNECT_FAILED)
305         {
306           log_info (_("can't connect to the agent - trying fall back\n"));
307           force_pipe_server = 1;
308           goto restart;
309         }
310     }
311
312   if (rc)
313     {
314       log_error ("can't connect to the agent: %s\n", gpg_strerror (rc));
315       return gpg_error (GPG_ERR_NO_AGENT);
316     }
317
318   if (debug)
319     log_debug ("connection to agent established\n");
320
321   rc = assuan_transact (ctx, "RESET",
322                         NULL, NULL, NULL, NULL, NULL, NULL);
323   if (!rc)
324     rc = send_pinentry_environment (ctx, errsource,
325                                     opt_lc_ctype, opt_lc_messages,
326                                     session_env);
327   if (rc)
328     {
329       assuan_disconnect (ctx);
330       return rc;
331     }
332
333   *r_ctx = ctx;
334   return 0;
335 }
336