Add code to allow for late memory cleanup.
[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 *homedir;
51   const char *agent_program;
52   const char *lc_ctype;
53   const char *lc_messages;
54   session_env_t session_env;
55   const char *pinentry_user_data;
56 } agentargs;
57
58
59 /* Set local variable to be used for a possible agent startup.  Note
60    that the strings are just pointers and should not anymore be
61    modified by the caller. */
62 void
63 gnupg_prepare_get_passphrase (gpg_err_source_t errsource,
64                               int verbosity,
65                               const char *homedir,
66                               const char *agent_program,
67                               const char *opt_lc_ctype,
68                               const char *opt_lc_messages,
69                               session_env_t session_env)
70 {
71   agentargs.errsource          = errsource;
72   agentargs.verbosity          = verbosity;
73   agentargs.homedir            = homedir;
74   agentargs.agent_program      = agent_program;
75   agentargs.lc_ctype           = opt_lc_ctype;
76   agentargs.lc_messages        = opt_lc_messages;
77   agentargs.session_env        = session_env;
78 }
79
80
81 /* Try to connect to the agent via socket or fork it off and work by
82    pipes.  Handle the server's initial greeting.  */
83 static gpg_error_t
84 start_agent (void)
85 {
86   gpg_error_t err;
87
88   /* Fixme: This code is not thread safe, thus we don't build it with
89      pth.  We will need a context for each thread or serialize the
90      access to the agent.  */
91   if (agent_ctx)
92     return 0;
93
94   err = start_new_gpg_agent (&agent_ctx,
95                              agentargs.errsource,
96                              agentargs.homedir,
97                              agentargs.agent_program,
98                              agentargs.lc_ctype,
99                              agentargs.lc_messages,
100                              agentargs.session_env,
101                              agentargs.verbosity, 0, NULL, NULL);
102   if (!err)
103     {
104       /* Tell the agent that we support Pinentry notifications.  No
105          error checking so that it will work with older agents.  */
106       assuan_transact (agent_ctx, "OPTION allow-pinentry-notify",
107                        NULL, NULL, NULL, NULL, NULL, NULL);
108     }
109
110   return err;
111 }
112
113
114 /* This is the default inquiry callback.  It merely handles the
115    Pinentry notification.  */
116 static gpg_error_t
117 default_inq_cb (void *opaque, const char *line)
118 {
119   (void)opaque;
120
121   if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
122     {
123       gnupg_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10));
124       /* We do not return errors to avoid breaking other code.  */
125     }
126   else
127     log_debug ("ignoring gpg-agent inquiry '%s'\n", line);
128
129   return 0;
130 }
131
132
133 static gpg_error_t
134 membuf_data_cb (void *opaque, const void *buffer, size_t length)
135 {
136   membuf_t *data = opaque;
137
138   if (buffer)
139     put_membuf (data, buffer, length);
140   return 0;
141 }
142
143
144 /* Ask for a passphrase via gpg-agent.  On success the caller needs to
145    free the string stored at R_PASSPHRASE.  On error NULL will be
146    stored at R_PASSPHRASE and an appropriate gpg error code is
147    returned.  With REPEAT set to 1, gpg-agent will ask the user to
148    repeat the just entered passphrase.  CACHE_ID is a gpg-agent style
149    passphrase cache id or NULL.  ERR_MSG is a error message to be
150    presented to the user (e.g. "bad passphrase - try again") or NULL.
151    PROMPT is the prompt string to label the entry box, it may be NULL
152    for a default one.  DESC_MSG is a longer description to be
153    displayed above the entry box, if may be NULL for a default one.
154    If USE_SECMEM is true, the returned passphrase is retruned in
155    secure memory.  The length of all these strings is limited; they
156    need to fit in their encoded form into a standard Assuan line (i.e
157    less then about 950 characters).  All strings shall be UTF-8.  */
158 gpg_error_t
159 gnupg_get_passphrase (const char *cache_id,
160                       const char *err_msg,
161                       const char *prompt,
162                       const char *desc_msg,
163                       int repeat,
164                       int check_quality,
165                       int use_secmem,
166                       char **r_passphrase)
167 {
168   gpg_error_t err;
169   char line[ASSUAN_LINELENGTH];
170   const char *arg1 = NULL;
171   char *arg2 = NULL;
172   char *arg3 = NULL;
173   char *arg4 = NULL;
174   membuf_t data;
175
176   *r_passphrase = NULL;
177
178   err = start_agent ();
179   if (err)
180     return err;
181
182   /* Check that the gpg-agent understands the repeat option.  */
183   if (assuan_transact (agent_ctx,
184                        "GETINFO cmd_has_option GET_PASSPHRASE repeat",
185                        NULL, NULL, NULL, NULL, NULL, NULL))
186     return gpg_error (GPG_ERR_NOT_SUPPORTED);
187
188   arg1 = cache_id && *cache_id? cache_id:NULL;
189   if (err_msg && *err_msg)
190     if (!(arg2 = percent_plus_escape (err_msg)))
191       goto no_mem;
192   if (prompt && *prompt)
193     if (!(arg3 = percent_plus_escape (prompt)))
194       goto no_mem;
195   if (desc_msg && *desc_msg)
196     if (!(arg4 = percent_plus_escape (desc_msg)))
197       goto no_mem;
198
199   snprintf (line, DIM(line)-1,
200             "GET_PASSPHRASE --data %s--repeat=%d -- %s %s %s %s",
201             check_quality? "--check ":"",
202             repeat,
203             arg1? arg1:"X",
204             arg2? arg2:"X",
205             arg3? arg3:"X",
206             arg4? arg4:"X");
207   line[DIM(line)-1] = 0;
208   xfree (arg2);
209   xfree (arg3);
210   xfree (arg4);
211
212   if (use_secmem)
213     init_membuf_secure (&data, 64);
214   else
215     init_membuf (&data, 64);
216   err = assuan_transact (agent_ctx, line,
217                          membuf_data_cb, &data,
218                          default_inq_cb, NULL, NULL, NULL);
219
220   /* Older Pinentries return the old assuan error code for canceled
221      which gets translated bt libassuan to GPG_ERR_ASS_CANCELED and
222      not to the code for a user cancel.  Fix this here. */
223   if (err && gpg_err_source (err)
224       && gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
225     err = gpg_err_make (gpg_err_source (err), GPG_ERR_CANCELED);
226
227   if (err)
228     {
229       void *p;
230       size_t n;
231
232       p = get_membuf (&data, &n);
233       if (p)
234         wipememory (p, n);
235       xfree (p);
236     }
237   else
238     {
239       put_membuf (&data, "", 1);
240       *r_passphrase = get_membuf (&data, NULL);
241       if (!*r_passphrase)
242         err = gpg_error_from_syserror ();
243     }
244   return err;
245  no_mem:
246   err = gpg_error_from_syserror ();
247   xfree (arg2);
248   xfree (arg3);
249   xfree (arg4);
250   return err;
251 }
252
253
254 /* Flush the passphrase cache with Id CACHE_ID.  */
255 gpg_error_t
256 gnupg_clear_passphrase (const char *cache_id)
257 {
258   gpg_error_t err;
259   char line[ASSUAN_LINELENGTH];
260
261   if (!cache_id || !*cache_id)
262     return 0;
263
264   err = start_agent ();
265   if (err)
266     return err;
267
268   snprintf (line, DIM(line)-1, "CLEAR_PASSPHRASE %s", cache_id);
269   line[DIM(line)-1] = 0;
270   return assuan_transact (agent_ctx, line, NULL, NULL,
271                           default_inq_cb, NULL, NULL, NULL);
272 }