pwmc: Reset filename upon .open failure.
[libpwmd.git] / src / pinentry.c
blobfdfaac84980c8a5c49018c17da011b37a6065002
1 /*
2 Copyright (C) 2006-2016 Ben Kibbey <bjk@luxsci.net>
4 This file is part of libpwmd.
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 This library 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 GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 USA
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <sys/types.h>
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <limits.h>
30 #ifdef HAVE_STRING_H
31 #include <string.h>
32 #endif
34 #include "pinentry.h"
35 #include "misc.h"
37 #ifdef WITH_QUALITY
38 #include "zxcvbn.h"
39 #endif
41 static gpg_error_t set_pinentry_strings (pwm_t * pwm, pwmd_pinentry_t which);
43 static gpg_error_t
44 launch_pinentry (pwm_t * pwm)
46 gpg_error_t rc;
47 assuan_context_t ctx;
48 int child_list[] = { -1 };
49 const char *argv[12];
50 const char **p = argv;
51 static struct assuan_malloc_hooks mhooks = {
52 pwmd_malloc, pwmd_realloc, pwmd_free
55 *p++ = "";
56 if (pwm->pinentry_display)
58 *p++ = "--display";
59 *p++ = pwm->pinentry_display;
62 if (pwm->pinentry_tty)
64 *p++ = "--ttyname";
65 *p++ = pwm->pinentry_tty;
68 if (pwm->pinentry_term)
70 *p++ = "--ttytype";
71 *p++ = pwm->pinentry_term;
74 if (pwm->pinentry_lcctype)
76 *p++ = "--lc-ctype";
77 *p++ = pwm->pinentry_lcctype;
80 if (pwm->pinentry_lcmessages)
82 *p++ = "--lc-messages";
83 *p++ = pwm->pinentry_lcmessages;
86 *p = NULL;
87 rc = assuan_new_ext (&ctx, GPG_ERR_SOURCE_PINENTRY, &mhooks, NULL, NULL);
88 if (rc)
89 return rc;
91 rc = assuan_pipe_connect (ctx,
92 pwm->pinentry_path ? pwm->pinentry_path
93 : PINENTRY_PATH, argv, child_list, NULL, NULL, 0);
94 if (rc)
96 assuan_release (ctx);
97 return rc;
100 pwm->pinentry_pid = assuan_get_pid (ctx);
101 pwm->pctx = ctx;
102 rc = set_pinentry_strings (pwm, 0);
103 if (rc)
104 _pinentry_disconnect (pwm);
106 return rc;
109 static gpg_error_t
110 pinentry_command (pwm_t * pwm, char **result, size_t * len, const char *cmd)
112 if (len)
113 *len = 0;
115 if (result)
116 *result = NULL;
118 if (!pwm->pctx)
120 gpg_error_t rc = launch_pinentry (pwm);
122 if (rc)
123 return rc;
126 return _assuan_command (pwm, pwm->pctx, result, len, cmd);
129 #ifdef WITH_QUALITY
130 static gpg_error_t
131 quality_cb (void *data, const char *line)
133 #define MAX_ENTROPY 80 // in bits
134 pwm_t *pwm = data;
135 char buf[5];
136 double match;
138 if (strncmp (line, "QUALITY ", 8))
139 return GPG_ERR_INV_ARG;
141 match = ZxcvbnMatch (line+8, NULL, NULL);
142 match = (match/MAX_ENTROPY)*100;
143 snprintf (buf, sizeof (buf), "%u", (unsigned)match);
144 return assuan_send_data (pwm->pctx, buf, strlen (buf));
146 #endif
148 static gpg_error_t
149 set_pinentry_strings (pwm_t * pwm, pwmd_pinentry_t which)
151 char *tmp, *desc = NULL;
152 gpg_error_t rc;
154 if (which != PWMD_PINENTRY_USER)
156 rc = pinentry_command (pwm, NULL, NULL, "SETERROR ");
157 if (rc)
158 return rc;
161 tmp = pwmd_malloc (ASSUAN_LINELENGTH + 1);
162 if (!tmp)
163 return gpg_error_from_errno (ENOMEM);
165 if (!pwm->pinentry_prompt)
167 pwm->pinentry_prompt = pwmd_strdup (N_("Passphrase:"));
168 if (!pwm->pinentry_prompt)
170 pwmd_free (tmp);
171 return GPG_ERR_ENOMEM;
175 if (!pwm->pinentry_desc)
177 if (which == PWMD_PINENTRY_OPEN || which == PWMD_PINENTRY_OPEN_FAILED)
179 if (pwm->passphrase_hint && pwm->passphrase_info)
181 char *hint = pwmd_strdup (pwm->passphrase_hint), *hintp = hint;
182 char *info = pwmd_strdup (pwm->passphrase_info), *infop = info;
183 char *userid = strchr (hint, ' ');
185 if (userid && *userid)
186 *userid++ = 0;
188 infop = strchr (info, ' ');
189 if (infop && *infop)
191 char *p = strchr (++infop, ' ');
193 if (p)
194 infop[strlen(infop)-strlen(p)] = 0;
197 hintp += 8;
198 infop += 8;
199 desc = pwmd_strdup_printf (N_
200 ("Please enter the passphrase to unlock the OpenPGP secret key:%%0A%%0AClient: %s%%0AData file: %s%%0AKey ID: %s (0x%s)%%0AMain key ID: 0x%s"), pwm->name ? pwm->name : N_("unknown"), pwm->filename, userid, hintp, infop);
201 pwmd_free (hint);
202 pwmd_free (info);
204 else
206 desc = pwmd_strdup_printf (N_ ("A passphrase is required to decrypt the encrypted%%0Adata file \"%s\". Please enter the passphrase below."), pwm->filename);
209 else if (which == PWMD_PINENTRY_SAVE
210 || which == PWMD_PINENTRY_SAVE_FAILED)
211 desc =
212 pwmd_strdup_printf (N_
213 ("Please enter the passphrase to use to encrypt%%0Athe data file \"%s\"."),
214 pwm->filename);
215 else if (which == PWMD_PINENTRY_SAVE_CONFIRM)
216 desc =
217 pwmd_strdup_printf (N_
218 ("Please re-enter the passphrase for confirmation."),
219 pwm->filename);
221 if (!desc)
223 pwmd_free (tmp);
224 return GPG_ERR_ENOMEM;
227 else
228 desc = pwm->pinentry_desc;
230 if (which == PWMD_PINENTRY_USER)
232 snprintf (tmp, ASSUAN_LINELENGTH, "SETERROR %s",
233 pwm->pinentry_error ? pwm->pinentry_error : "");
234 rc = pinentry_command (pwm, NULL, NULL, tmp);
235 if (rc)
237 pwmd_free (tmp);
238 return rc;
241 snprintf (tmp, ASSUAN_LINELENGTH, "SETDESC %s",
242 pwm->pinentry_desc ? pwm->pinentry_desc : "");
244 else
246 if (which == PWMD_PINENTRY_SAVE_FAILED
247 || which == PWMD_PINENTRY_OPEN_FAILED)
249 if (which == PWMD_PINENTRY_SAVE_FAILED)
250 snprintf (tmp, ASSUAN_LINELENGTH, "SETERROR %s",
251 N_("Passphrases do not match, try again."));
252 else
254 if (pwm->pinentry_tries)
256 strcpy (tmp, "SETERROR ");
257 snprintf (tmp + strlen ("SETERROR "), ASSUAN_LINELENGTH,
258 N_("Bad passphrase (try %i of %i)"),
259 pwm->pinentry_try + 1, pwm->pinentry_tries);
261 else
262 snprintf (tmp, ASSUAN_LINELENGTH, "SETERROR %s",
263 N_("Bad passphrase, try again"));
266 rc = pinentry_command (pwm, NULL, NULL, tmp);
267 if (rc)
269 pwmd_free (tmp);
270 return rc;
274 snprintf (tmp, ASSUAN_LINELENGTH, "SETDESC %s", desc);
276 if (pwm->pinentry_desc != desc)
277 pwmd_free (desc);
280 rc = pinentry_command (pwm, NULL, NULL, tmp);
281 if (rc)
283 pwmd_free (tmp);
284 return rc;
287 snprintf (tmp, ASSUAN_LINELENGTH, "SETPROMPT %s", pwm->pinentry_prompt);
288 rc = pinentry_command (pwm, NULL, NULL, tmp);
289 pwmd_free (tmp);
291 #ifdef WITH_QUALITY
292 if (!rc && (which == PWMD_PINENTRY_SAVE ||
293 which == PWMD_PINENTRY_SAVE_FAILED))
295 rc = pinentry_command (pwm, NULL, NULL, "SETQUALITYBAR");
296 if (!rc)
298 pwm->_inquire_func = quality_cb;
299 pwm->_inquire_data = pwm;
302 #endif
304 if (!rc && pwm->pinentry_timeout >= 0)
306 char buf[32];
308 snprintf(buf, sizeof(buf), "SETTIMEOUT %i", pwm->pinentry_timeout);
309 rc = pinentry_command(pwm, NULL, NULL, buf);
312 return rc;
315 void
316 _pinentry_disconnect (pwm_t * pwm)
318 if (pwm->pctx)
319 assuan_release (pwm->pctx);
321 pwm->pctx = NULL;
322 pwm->pinentry_pid = -1;
325 gpg_error_t
326 _getpin (pwm_t * pwm, char **result, size_t * len, pwmd_pinentry_t which)
328 gpg_error_t rc = set_pinentry_strings (pwm, which);
330 if (rc)
332 _pinentry_disconnect (pwm);
333 return rc;
336 rc = pinentry_command (pwm, result, len,
337 which == PWMD_PINENTRY_CONFIRM ? "CONFIRM" : "GETPIN");
338 if (rc)
340 _pinentry_disconnect (pwm);
342 /* This lets PWMD_OPTION_PINENTRY_TIMEOUT work. Note that it is not
343 * thread safe do to the global variables. */
344 if (gpg_err_code (rc) == GPG_ERR_EOF)
345 rc = GPG_ERR_CANCELED;
347 else if (which != PWMD_PINENTRY_CONFIRM && len && result)
349 if (*len)
350 *len = strlen (*result); // remove the null byte
351 else
353 *result = pwmd_malloc (1);
354 *(*result) = 0;
355 *len = 1;
359 return rc;
362 gpg_error_t
363 _pwmd_getpin (pwm_t * pwm, const char *filename, char **result,
364 size_t * len, pwmd_pinentry_t which)
366 gpg_error_t rc;
367 char *p;
369 if (!pwm)
370 return GPG_ERR_INV_ARG;
372 p = pwm->filename;
373 if (which == PWMD_PINENTRY_CLOSE)
375 _pinentry_disconnect (pwm);
376 return 0;
379 if (!result && which != PWMD_PINENTRY_CONFIRM)
380 return GPG_ERR_INV_ARG;
382 pwm->filename = (char *) filename;
383 rc = _getpin (pwm, result, len, which);
384 pwm->filename = p;
385 return rc;