common: Remove homedir arg from start_new_{dirmngr,gpg_agent}.
[gnupg.git] / common / get-passphrase.c
1 /* get-passphrase.c - Ask for a passphrase via the agent
2  * Copyright (C) 2009 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * This file is free software; you can redistribute it and/or modify
7  * it under the terms of either
8  *
9  *   - the GNU Lesser General Public License as published by the Free
10  *     Software Foundation; either version 3 of the License, or (at
11  *     your option) any later version.
12  *
13  * or
14  *
15  *   - the GNU General Public License as published by the Free
16  *     Software Foundation; either version 2 of the License, or (at
17  *     your option) any later version.
18  *
19  * or both in parallel, as here.
20  *
21  * This file is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, see <http://www.gnu.org/licenses/>.
28  */
29
30 #include <config.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <assert.h>
35 #include <assuan.h>
36
37 #include "util.h"
38 #include "i18n.h"
39 #include "asshelp.h"
40 #include "membuf.h"
41 #include "sysutils.h"
42 #include "get-passphrase.h"
43
44 /* The context used by this process to ask for the passphrase.  */
45 static assuan_context_t agent_ctx;
46 static struct
47 {
48   gpg_err_source_t errsource;
49   int verbosity;
50   const char *agent_program;
51   const char *lc_ctype;
52   const char *lc_messages;
53   session_env_t session_env;
54   const char *pinentry_user_data;
55 } agentargs;
56
57
58 /* Set local variable to be used for a possible agent startup.  Note
59    that the strings are just pointers and should not anymore be
60    modified by the caller. */
61 void
62 gnupg_prepare_get_passphrase (gpg_err_source_t errsource,
63                               int verbosity,
64                               const char *agent_program,
65                               const char *opt_lc_ctype,
66                               const char *opt_lc_messages,
67                               session_env_t session_env)
68 {
69   agentargs.errsource          = errsource;
70   agentargs.verbosity          = verbosity;
71   agentargs.agent_program      = agent_program;
72   agentargs.lc_ctype           = opt_lc_ctype;
73   agentargs.lc_messages        = opt_lc_messages;
74   agentargs.session_env        = session_env;
75 }
76
77
78 /* Try to connect to the agent via socket or fork it off and work by
79    pipes.  Handle the server's initial greeting.  */
80 static gpg_error_t
81 start_agent (void)
82 {
83   gpg_error_t err;
84
85   /* Fixme: This code is not thread safe, thus we don't build it with
86      pth.  We will need a context for each thread or serialize the
87      access to the agent.  */
88   if (agent_ctx)
89     return 0;
90
91   err = start_new_gpg_agent (&agent_ctx,
92                              agentargs.errsource,
93                              agentargs.agent_program,
94                              agentargs.lc_ctype,
95                              agentargs.lc_messages,
96                              agentargs.session_env,
97                              1, agentargs.verbosity, 0, NULL, NULL);
98   if (!err)
99     {
100       /* Tell the agent that we support Pinentry notifications.  No
101          error checking so that it will work with older agents.  */
102       assuan_transact (agent_ctx, "OPTION allow-pinentry-notify",
103                        NULL, NULL, NULL, NULL, NULL, NULL);
104     }
105
106   return err;
107 }
108
109
110 /* This is the default inquiry callback.  It merely handles the
111    Pinentry notification.  */
112 static gpg_error_t
113 default_inq_cb (void *opaque, const char *line)
114 {
115   (void)opaque;
116
117   if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
118     {
119       gnupg_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10));
120       /* We do not return errors to avoid breaking other code.  */
121     }
122   else
123     log_debug ("ignoring gpg-agent inquiry '%s'\n", line);
124
125   return 0;
126 }
127
128
129 /* Ask for a passphrase via gpg-agent.  On success the caller needs to
130    free the string stored at R_PASSPHRASE.  On error NULL will be
131    stored at R_PASSPHRASE and an appropriate gpg error code is
132    returned.  With REPEAT set to 1, gpg-agent will ask the user to
133    repeat the just entered passphrase.  CACHE_ID is a gpg-agent style
134    passphrase cache id or NULL.  ERR_MSG is a error message to be
135    presented to the user (e.g. "bad passphrase - try again") or NULL.
136    PROMPT is the prompt string to label the entry box, it may be NULL
137    for a default one.  DESC_MSG is a longer description to be
138    displayed above the entry box, if may be NULL for a default one.
139    If USE_SECMEM is true, the returned passphrase is retruned in
140    secure memory.  The length of all these strings is limited; they
141    need to fit in their encoded form into a standard Assuan line (i.e
142    less then about 950 characters).  All strings shall be UTF-8.  */
143 gpg_error_t
144 gnupg_get_passphrase (const char *cache_id,
145                       const char *err_msg,
146                       const char *prompt,
147                       const char *desc_msg,
148                       int repeat,
149                       int check_quality,
150                       int use_secmem,
151                       char **r_passphrase)
152 {
153   gpg_error_t err;
154   char line[ASSUAN_LINELENGTH];
155   const char *arg1 = NULL;
156   char *arg2 = NULL;
157   char *arg3 = NULL;
158   char *arg4 = NULL;
159   membuf_t data;
160
161   *r_passphrase = NULL;
162
163   err = start_agent ();
164   if (err)
165     return err;
166
167   /* Check that the gpg-agent understands the repeat option.  */
168   if (assuan_transact (agent_ctx,
169                        "GETINFO cmd_has_option GET_PASSPHRASE repeat",
170                        NULL, NULL, NULL, NULL, NULL, NULL))
171     return gpg_error (GPG_ERR_NOT_SUPPORTED);
172
173   arg1 = cache_id && *cache_id? cache_id:NULL;
174   if (err_msg && *err_msg)
175     if (!(arg2 = percent_plus_escape (err_msg)))
176       goto no_mem;
177   if (prompt && *prompt)
178     if (!(arg3 = percent_plus_escape (prompt)))
179       goto no_mem;
180   if (desc_msg && *desc_msg)
181     if (!(arg4 = percent_plus_escape (desc_msg)))
182       goto no_mem;
183
184   snprintf (line, DIM(line)-1,
185             "GET_PASSPHRASE --data %s--repeat=%d -- %s %s %s %s",
186             check_quality? "--check ":"",
187             repeat,
188             arg1? arg1:"X",
189             arg2? arg2:"X",
190             arg3? arg3:"X",
191             arg4? arg4:"X");
192   line[DIM(line)-1] = 0;
193   xfree (arg2);
194   xfree (arg3);
195   xfree (arg4);
196
197   if (use_secmem)
198     init_membuf_secure (&data, 64);
199   else
200     init_membuf (&data, 64);
201   err = assuan_transact (agent_ctx, line,
202                          put_membuf_cb, &data,
203                          default_inq_cb, NULL, NULL, NULL);
204
205   /* Older Pinentries return the old assuan error code for canceled
206      which gets translated bt libassuan to GPG_ERR_ASS_CANCELED and
207      not to the code for a user cancel.  Fix this here. */
208   if (err && gpg_err_source (err)
209       && gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
210     err = gpg_err_make (gpg_err_source (err), GPG_ERR_CANCELED);
211
212   if (err)
213     {
214       void *p;
215       size_t n;
216
217       p = get_membuf (&data, &n);
218       if (p)
219         wipememory (p, n);
220       xfree (p);
221     }
222   else
223     {
224       put_membuf (&data, "", 1);
225       *r_passphrase = get_membuf (&data, NULL);
226       if (!*r_passphrase)
227         err = gpg_error_from_syserror ();
228     }
229   return err;
230  no_mem:
231   err = gpg_error_from_syserror ();
232   xfree (arg2);
233   xfree (arg3);
234   xfree (arg4);
235   return err;
236 }
237
238
239 /* Flush the passphrase cache with Id CACHE_ID.  */
240 gpg_error_t
241 gnupg_clear_passphrase (const char *cache_id)
242 {
243   gpg_error_t err;
244   char line[ASSUAN_LINELENGTH];
245
246   if (!cache_id || !*cache_id)
247     return 0;
248
249   err = start_agent ();
250   if (err)
251     return err;
252
253   snprintf (line, DIM(line)-1, "CLEAR_PASSPHRASE %s", cache_id);
254   line[DIM(line)-1] = 0;
255   return assuan_transact (agent_ctx, line, NULL, NULL,
256                           default_inq_cb, NULL, NULL, NULL);
257 }