Fix resetting key files for non-interactive mode.
[libpwmd.git] / src / pinentry.c
blob360abd5d2c9a00b93dd443255f19b46b3f75e5c7
1 /*
2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015
3 Ben Kibbey <bjk@luxsci.net>
5 This file is part of libpwmd.
7 Libpwmd is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 2 of the License, or
10 (at your option) any later version.
12 Libpwmd is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Libpwmd. If not, see <http://www.gnu.org/licenses/>.
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
24 #include <sys/types.h>
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <limits.h>
29 #ifdef HAVE_STRING_H
30 #include <string.h>
31 #endif
33 #include "pinentry.h"
34 #include "misc.h"
36 #ifdef WITH_QUALITY
37 #include <crack.h>
38 #endif
40 static gpg_error_t set_pinentry_strings (pwm_t * pwm, pwmd_pinentry_t which);
42 static gpg_error_t
43 launch_pinentry (pwm_t * pwm)
45 int rc;
46 assuan_context_t ctx;
47 int child_list[] = { -1 };
48 const char *argv[10];
49 const char **p = argv;
50 static struct assuan_malloc_hooks mhooks = {
51 pwmd_malloc, pwmd_realloc, pwmd_free
54 if (!pwm->pinentry_display && !pwm->pinentry_tty)
55 return GPG_ERR_ENOTTY;
57 *p++ = "pinentry";
58 *p++ = pwm->pinentry_display ? "--display" : "--ttyname";
59 *p++ = pwm->pinentry_display ? pwm->pinentry_display : pwm->pinentry_tty;
61 if (pwm->pinentry_lcctype)
63 *p++ = "--lc-ctype";
64 *p++ = pwm->pinentry_lcctype;
67 if (pwm->pinentry_lcmessages)
69 *p++ = "--lc-messages";
70 *p++ = pwm->pinentry_lcmessages;
73 *p = NULL;
75 if (!pwm->pinentry_display)
77 *p++ = "--ttytype";
78 *p++ = pwm->pinentry_term ? pwm->pinentry_term : getenv ("TERM");
79 *p = NULL;
82 rc = assuan_new_ext (&ctx, GPG_ERR_SOURCE_PINENTRY, &mhooks, NULL, NULL);
83 if (rc)
84 return rc;
86 rc = assuan_pipe_connect (ctx,
87 pwm->pinentry_path ? pwm->
88 pinentry_path : PINENTRY_PATH, argv, child_list,
89 NULL, NULL, 0);
90 if (rc)
92 assuan_release (ctx);
93 return rc;
96 pwm->pinentry_pid = assuan_get_pid (ctx);
97 pwm->pctx = ctx;
98 rc = set_pinentry_strings (pwm, 0);
99 if (rc)
100 _pinentry_disconnect (pwm);
102 return rc;
105 static gpg_error_t
106 pinentry_command (pwm_t * pwm, char **result, size_t * len, const char *cmd)
108 if (len)
109 *len = 0;
111 if (result)
112 *result = NULL;
114 if (!pwm->pctx)
116 gpg_error_t rc = launch_pinentry (pwm);
118 if (rc)
119 return rc;
122 return _assuan_command (pwm, pwm->pctx, result, len, cmd);
125 #ifdef WITH_QUALITY
126 static gpg_error_t
127 quality_cb (void *data, const char *line)
129 pwm_t *pwm = data;
130 int score = 0;
131 char buf[5];
132 const char *tmp;
134 if (strncmp (line, "QUALITY ", 8))
135 return GPG_ERR_INV_ARG;
137 if (!(tmp = FascistCheck (line + 8, CRACKLIB_DICT)))
138 return assuan_send_data (pwm->pctx, "100", 3);
140 if (!strcmp (tmp, N_("it is WAY too short")))
141 score = 10;
142 else if (!strcmp (tmp, N_("it is too short")))
143 score = 20;
144 else if (!strcmp (tmp, N_("it is all whitespace")))
145 score = 25;
146 else if (!strcmp (tmp, N_("it is based on your username")))
147 score = 30;
148 else if (!strcmp (tmp, N_("it does not contain enough DIFFERENT characters")))
149 score = 35;
150 else if (!strcmp (tmp, N_("it is based on a dictionary word")))
151 score = 40;
152 else if (!strcmp (tmp, N_("it is based upon your password entry")))
153 score = 50;
154 else if (!strcmp (tmp, N_("it's derived from your password entry")))
155 score = 50;
156 else if (!strcmp (tmp, N_("it is derived from your password entry")))
157 score = 50;
158 else if (!strcmp (tmp, N_("it is based on a (reversed) dictionary word")))
159 score = 60;
160 else if (!strcmp (tmp, N_("it is derivable from your password entry")))
161 score = 70;
162 else if (!strcmp (tmp, N_("it's derivable from your password entry")))
163 score = 70;
164 else if (!strcmp (tmp, N_("it looks like a National Insurance number.")))
165 score = 80;
166 else if (!strcmp (tmp, N_("it is too simplistic/systematic")))
167 score = 90;
168 else
169 score = 0;
171 snprintf (buf, sizeof (buf), "%i", score);
172 return assuan_send_data (pwm->pctx, buf, strlen (buf));
174 #endif
176 static gpg_error_t
177 set_pinentry_strings (pwm_t * pwm, pwmd_pinentry_t which)
179 char *tmp, *desc = NULL;
180 gpg_error_t rc;
182 if (which != PWMD_PINENTRY_USER)
184 rc = pinentry_command (pwm, NULL, NULL, "SETERROR ");
185 if (rc)
186 return rc;
189 tmp = pwmd_malloc (ASSUAN_LINELENGTH + 1);
190 if (!tmp)
191 return gpg_error_from_errno (ENOMEM);
193 if (!pwm->pinentry_prompt)
195 pwm->pinentry_prompt = pwmd_strdup (N_("Passphrase:"));
196 if (!pwm->pinentry_prompt)
198 pwmd_free (tmp);
199 return GPG_ERR_ENOMEM;
203 if (!pwm->pinentry_desc)
205 if (which == PWMD_PINENTRY_OPEN || which == PWMD_PINENTRY_OPEN_FAILED)
207 if (pwm->passphrase_hint && pwm->passphrase_info)
209 char *hint = pwmd_strdup (pwm->passphrase_hint), *hintp = hint;
210 char *info = pwmd_strdup (pwm->passphrase_info), *infop = info;
211 char *userid = strchr (hint, ' ');
213 if (userid && *userid)
214 *userid++ = 0;
216 infop = strchr (info, ' ');
217 if (infop && *infop)
219 char *p = strchr (++infop, ' ');
221 if (p)
222 infop[strlen(infop)-strlen(p)] = 0;
225 hintp += 8;
226 infop += 8;
227 desc = pwmd_strdup_printf (N_
228 ("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);
229 pwmd_free (hint);
230 pwmd_free (info);
232 else
234 desc = pwmd_strdup_printf (N_ ("A passphrase is required to decrypt the encrypted data file \"%s\". Please enter the passphrase below."), pwm->filename);
237 else if (which == PWMD_PINENTRY_SAVE
238 || which == PWMD_PINENTRY_SAVE_FAILED)
239 desc =
240 pwmd_strdup_printf (N_
241 ("Please enter the passphrase to use to encrypt the data file \"%s\"."),
242 pwm->filename);
243 else if (which == PWMD_PINENTRY_SAVE_CONFIRM)
244 desc =
245 pwmd_strdup_printf (N_
246 ("Please re-enter the passphrase for confirmation."),
247 pwm->filename);
249 if (!desc)
251 pwmd_free (tmp);
252 return GPG_ERR_ENOMEM;
256 if (pwm->pinentry_desc)
257 desc = pwm->pinentry_desc;
259 if (which == PWMD_PINENTRY_USER)
261 snprintf (tmp, ASSUAN_LINELENGTH, "SETERROR %s",
262 pwm->pinentry_error ? pwm->pinentry_error : "");
263 rc = pinentry_command (pwm, NULL, NULL, tmp);
264 if (rc)
266 pwmd_free (tmp);
267 return rc;
270 snprintf (tmp, ASSUAN_LINELENGTH, "SETDESC %s",
271 pwm->pinentry_desc ? pwm->pinentry_desc : "");
273 else
275 if (which == PWMD_PINENTRY_SAVE_FAILED
276 || which == PWMD_PINENTRY_OPEN_FAILED)
278 if (which == PWMD_PINENTRY_SAVE_FAILED)
279 snprintf (tmp, ASSUAN_LINELENGTH, "SETERROR %s",
280 N_("Passphrases do not match, try again."));
281 else
283 if (pwm->pinentry_tries)
285 strcpy (tmp, "SETERROR ");
286 snprintf (tmp + strlen ("SETERROR "), ASSUAN_LINELENGTH,
287 N_("Bad passphrase (try %i of %i)"),
288 pwm->pinentry_try + 1, pwm->pinentry_tries);
290 else
291 snprintf (tmp, ASSUAN_LINELENGTH, "SETERROR %s",
292 N_("Bad passphrase, try again"));
295 rc = pinentry_command (pwm, NULL, NULL, tmp);
296 if (rc)
298 pwmd_free (tmp);
299 return rc;
303 snprintf (tmp, ASSUAN_LINELENGTH, "SETDESC %s", desc);
305 if (pwm->pinentry_desc != desc)
306 pwmd_free (desc);
309 rc = pinentry_command (pwm, NULL, NULL, tmp);
310 if (rc)
312 pwmd_free (tmp);
313 return rc;
316 snprintf (tmp, ASSUAN_LINELENGTH, "SETPROMPT %s", pwm->pinentry_prompt);
317 rc = pinentry_command (pwm, NULL, NULL, tmp);
318 pwmd_free (tmp);
320 #ifdef WITH_QUALITY
321 if (!rc && (which == PWMD_PINENTRY_SAVE ||
322 which == PWMD_PINENTRY_SAVE_FAILED))
324 rc = pinentry_command (pwm, NULL, NULL, "SETQUALITYBAR");
325 if (!rc)
327 pwm->_inquire_func = quality_cb;
328 pwm->_inquire_data = pwm;
331 #endif
333 if (pwm->pinentry_timeout >= 0)
335 char buf[32];
337 snprintf(buf, sizeof(buf), "SETTIMEOUT %i", pwm->pinentry_timeout);
338 rc = pinentry_command(pwm, NULL, NULL, buf);
341 return rc;
344 void
345 _pinentry_disconnect (pwm_t * pwm)
347 if (pwm->pctx)
348 assuan_release (pwm->pctx);
350 pwm->pctx = NULL;
351 pwm->pinentry_pid = -1;
354 gpg_error_t
355 _getpin (pwm_t * pwm, char **result, size_t * len, pwmd_pinentry_t which)
357 gpg_error_t rc = set_pinentry_strings (pwm, which);
359 if (rc)
361 _pinentry_disconnect (pwm);
362 return rc;
365 rc = pinentry_command (pwm, result, len,
366 which == PWMD_PINENTRY_CONFIRM ? "CONFIRM" : "GETPIN");
367 if (rc)
369 _pinentry_disconnect (pwm);
371 /* This lets PWMD_OPTION_PINENTRY_TIMEOUT work. Note that it is not
372 * thread safe do to the global variables. */
373 if (gpg_err_code (rc) == GPG_ERR_EOF)
374 rc = GPG_ERR_CANCELED;
376 else if (which != PWMD_PINENTRY_CONFIRM && len && result)
378 if (*len)
379 *len = strlen (*result); // remove the null byte
380 else
382 *result = pwmd_malloc (1);
383 *(*result) = 0;
384 *len = 1;
388 return rc;
391 gpg_error_t
392 _pwmd_getpin (pwm_t * pwm, const char *filename, char **result,
393 size_t * len, pwmd_pinentry_t which)
395 gpg_error_t rc;
396 char *p;
398 if (!pwm)
399 return GPG_ERR_INV_ARG;
401 p = pwm->filename;
402 if (which == PWMD_PINENTRY_CLOSE)
404 _pinentry_disconnect (pwm);
405 return 0;
408 if (!result && which != PWMD_PINENTRY_CONFIRM)
409 return GPG_ERR_INV_ARG;
411 pwm->filename = (char *) filename;
412 rc = _getpin (pwm, result, len, which);
413 pwm->filename = p;
414 return rc;