Now build "gpg" binary but install as "gpg2"
[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                              1, 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 /* Ask for a passphrase via gpg-agent.  On success the caller needs to
134    free the string stored at R_PASSPHRASE.  On error NULL will be
135    stored at R_PASSPHRASE and an appropriate gpg error code is
136    returned.  With REPEAT set to 1, gpg-agent will ask the user to
137    repeat the just entered passphrase.  CACHE_ID is a gpg-agent style
138    passphrase cache id or NULL.  ERR_MSG is a error message to be
139    presented to the user (e.g. "bad passphrase - try again") or NULL.
140    PROMPT is the prompt string to label the entry box, it may be NULL
141    for a default one.  DESC_MSG is a longer description to be
142    displayed above the entry box, if may be NULL for a default one.
143    If USE_SECMEM is true, the returned passphrase is retruned in
144    secure memory.  The length of all these strings is limited; they
145    need to fit in their encoded form into a standard Assuan line (i.e
146    less then about 950 characters).  All strings shall be UTF-8.  */
147 gpg_error_t
148 gnupg_get_passphrase (const char *cache_id,
149                       const char *err_msg,
150                       const char *prompt,
151                       const char *desc_msg,
152                       int repeat,
153                       int check_quality,
154                       int use_secmem,
155                       char **r_passphrase)
156 {
157   gpg_error_t err;
158   char line[ASSUAN_LINELENGTH];
159   const char *arg1 = NULL;
160   char *arg2 = NULL;
161   char *arg3 = NULL;
162   char *arg4 = NULL;
163   membuf_t data;
164
165   *r_passphrase = NULL;
166
167   err = start_agent ();
168   if (err)
169     return err;
170
171   /* Check that the gpg-agent understands the repeat option.  */
172   if (assuan_transact (agent_ctx,
173                        "GETINFO cmd_has_option GET_PASSPHRASE repeat",
174                        NULL, NULL, NULL, NULL, NULL, NULL))
175     return gpg_error (GPG_ERR_NOT_SUPPORTED);
176
177   arg1 = cache_id && *cache_id? cache_id:NULL;
178   if (err_msg && *err_msg)
179     if (!(arg2 = percent_plus_escape (err_msg)))
180       goto no_mem;
181   if (prompt && *prompt)
182     if (!(arg3 = percent_plus_escape (prompt)))
183       goto no_mem;
184   if (desc_msg && *desc_msg)
185     if (!(arg4 = percent_plus_escape (desc_msg)))
186       goto no_mem;
187
188   snprintf (line, DIM(line)-1,
189             "GET_PASSPHRASE --data %s--repeat=%d -- %s %s %s %s",
190             check_quality? "--check ":"",
191             repeat,
192             arg1? arg1:"X",
193             arg2? arg2:"X",
194             arg3? arg3:"X",
195             arg4? arg4:"X");
196   line[DIM(line)-1] = 0;
197   xfree (arg2);
198   xfree (arg3);
199   xfree (arg4);
200
201   if (use_secmem)
202     init_membuf_secure (&data, 64);
203   else
204     init_membuf (&data, 64);
205   err = assuan_transact (agent_ctx, line,
206                          put_membuf_cb, &data,
207                          default_inq_cb, NULL, NULL, NULL);
208
209   /* Older Pinentries return the old assuan error code for canceled
210      which gets translated bt libassuan to GPG_ERR_ASS_CANCELED and
211      not to the code for a user cancel.  Fix this here. */
212   if (err && gpg_err_source (err)
213       && gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
214     err = gpg_err_make (gpg_err_source (err), GPG_ERR_CANCELED);
215
216   if (err)
217     {
218       void *p;
219       size_t n;
220
221       p = get_membuf (&data, &n);
222       if (p)
223         wipememory (p, n);
224       xfree (p);
225     }
226   else
227     {
228       put_membuf (&data, "", 1);
229       *r_passphrase = get_membuf (&data, NULL);
230       if (!*r_passphrase)
231         err = gpg_error_from_syserror ();
232     }
233   return err;
234  no_mem:
235   err = gpg_error_from_syserror ();
236   xfree (arg2);
237   xfree (arg3);
238   xfree (arg4);
239   return err;
240 }
241
242
243 /* Flush the passphrase cache with Id CACHE_ID.  */
244 gpg_error_t
245 gnupg_clear_passphrase (const char *cache_id)
246 {
247   gpg_error_t err;
248   char line[ASSUAN_LINELENGTH];
249
250   if (!cache_id || !*cache_id)
251     return 0;
252
253   err = start_agent ();
254   if (err)
255     return err;
256
257   snprintf (line, DIM(line)-1, "CLEAR_PASSPHRASE %s", cache_id);
258   line[DIM(line)-1] = 0;
259   return assuan_transact (agent_ctx, line, NULL, NULL,
260                           default_inq_cb, NULL, NULL, NULL);
261 }