1 /* get-passphrase.c - Ask for a passphrase via the agent
2 * Copyright (C) 2009 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
6 * This file is free software; you can redistribute it and/or modify
7 * it under the terms of either
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.
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.
19 * or both in parallel, as here.
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.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, see <https://www.gnu.org/licenses/>.
42 #include "get-passphrase.h"
44 /* The context used by this process to ask for the passphrase. */
45 static assuan_context_t agent_ctx;
48 gpg_err_source_t errsource;
50 const char *agent_program;
52 const char *lc_messages;
53 session_env_t session_env;
54 const char *pinentry_user_data;
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. */
62 gnupg_prepare_get_passphrase (gpg_err_source_t errsource,
64 const char *agent_program,
65 const char *opt_lc_ctype,
66 const char *opt_lc_messages,
67 session_env_t session_env)
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;
78 /* Try to connect to the agent via socket or fork it off and work by
79 pipes. Handle the server's initial greeting. */
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. */
91 err = start_new_gpg_agent (&agent_ctx,
93 agentargs.agent_program,
95 agentargs.lc_messages,
96 agentargs.session_env,
97 1, agentargs.verbosity, 0, NULL, NULL);
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);
110 /* This is the default inquiry callback. It merely handles the
111 Pinentry notification. */
113 default_inq_cb (void *opaque, const char *line)
117 if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
119 gnupg_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10));
120 /* We do not return errors to avoid breaking other code. */
123 log_debug ("ignoring gpg-agent inquiry '%s'\n", line);
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 returned 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. */
144 gnupg_get_passphrase (const char *cache_id,
147 const char *desc_msg,
154 char line[ASSUAN_LINELENGTH];
155 const char *arg1 = NULL;
161 *r_passphrase = NULL;
163 err = start_agent ();
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);
173 arg1 = cache_id && *cache_id? cache_id:NULL;
174 if (err_msg && *err_msg)
175 if (!(arg2 = percent_plus_escape (err_msg)))
177 if (prompt && *prompt)
178 if (!(arg3 = percent_plus_escape (prompt)))
180 if (desc_msg && *desc_msg)
181 if (!(arg4 = percent_plus_escape (desc_msg)))
184 snprintf (line, DIM(line),
185 "GET_PASSPHRASE --data %s--repeat=%d -- %s %s %s %s",
186 check_quality? "--check ":"",
197 init_membuf_secure (&data, 64);
199 init_membuf (&data, 64);
200 err = assuan_transact (agent_ctx, line,
201 put_membuf_cb, &data,
202 default_inq_cb, NULL, NULL, NULL);
204 /* Older Pinentries return the old assuan error code for canceled
205 which gets translated by libassuan to GPG_ERR_ASS_CANCELED and
206 not to the code for a user cancel. Fix this here. */
207 if (err && gpg_err_source (err)
208 && gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
209 err = gpg_err_make (gpg_err_source (err), GPG_ERR_CANCELED);
216 p = get_membuf (&data, &n);
223 put_membuf (&data, "", 1);
224 *r_passphrase = get_membuf (&data, NULL);
226 err = gpg_error_from_syserror ();
230 err = gpg_error_from_syserror ();
238 /* Flush the passphrase cache with Id CACHE_ID. */
240 gnupg_clear_passphrase (const char *cache_id)
243 char line[ASSUAN_LINELENGTH];
245 if (!cache_id || !*cache_id)
248 err = start_agent ();
252 snprintf (line, DIM(line), "CLEAR_PASSPHRASE %s", cache_id);
253 return assuan_transact (agent_ctx, line, NULL, NULL,
254 default_inq_cb, NULL, NULL, NULL);