Replace cracklib with built-in zxcvbn-c.
[libpwmd.git] / src / pinentry.c
blobc70732d446a927242d5b3415f3d757f19f6ed2fe
1 /*
2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015,
3 2016
4 Ben Kibbey <bjk@luxsci.net>
6 This file is part of libpwmd.
8 Libpwmd is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
13 Libpwmd is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with Libpwmd. If not, see <http://www.gnu.org/licenses/>.
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 #include <crack.h>
40 #endif
42 static gpg_error_t set_pinentry_strings (pwm_t * pwm, pwmd_pinentry_t which);
44 static gpg_error_t
45 launch_pinentry (pwm_t * pwm)
47 int rc;
48 assuan_context_t ctx;
49 int child_list[] = { -1 };
50 const char *argv[10];
51 const char **p = argv;
52 static struct assuan_malloc_hooks mhooks = {
53 pwmd_malloc, pwmd_realloc, pwmd_free
56 if (!pwm->pinentry_display && !pwm->pinentry_tty)
57 return GPG_ERR_ENOTTY;
59 *p++ = "pinentry";
60 *p++ = pwm->pinentry_display ? "--display" : "--ttyname";
61 *p++ = pwm->pinentry_display ? pwm->pinentry_display : pwm->pinentry_tty;
63 if (pwm->pinentry_lcctype)
65 *p++ = "--lc-ctype";
66 *p++ = pwm->pinentry_lcctype;
69 if (pwm->pinentry_lcmessages)
71 *p++ = "--lc-messages";
72 *p++ = pwm->pinentry_lcmessages;
75 *p = NULL;
77 if (!pwm->pinentry_display)
79 *p++ = "--ttytype";
80 *p++ = pwm->pinentry_term ? pwm->pinentry_term : getenv ("TERM");
81 *p = NULL;
84 rc = assuan_new_ext (&ctx, GPG_ERR_SOURCE_PINENTRY, &mhooks, NULL, NULL);
85 if (rc)
86 return rc;
88 rc = assuan_pipe_connect (ctx,
89 pwm->pinentry_path ? pwm->
90 pinentry_path : PINENTRY_PATH, argv, child_list,
91 NULL, NULL, 0);
92 if (rc)
94 assuan_release (ctx);
95 return rc;
98 pwm->pinentry_pid = assuan_get_pid (ctx);
99 pwm->pctx = ctx;
100 rc = set_pinentry_strings (pwm, 0);
101 if (rc)
102 _pinentry_disconnect (pwm);
104 return rc;
107 static gpg_error_t
108 pinentry_command (pwm_t * pwm, char **result, size_t * len, const char *cmd)
110 if (len)
111 *len = 0;
113 if (result)
114 *result = NULL;
116 if (!pwm->pctx)
118 gpg_error_t rc = launch_pinentry (pwm);
120 if (rc)
121 return rc;
124 return _assuan_command (pwm, pwm->pctx, result, len, cmd);
127 #ifdef WITH_QUALITY
128 static gpg_error_t
129 quality_cb (void *data, const char *line)
131 #define MAX_ENTROPY 80 // in bits
132 pwm_t *pwm = data;
133 char buf[5];
134 double match;
136 if (strncmp (line, "QUALITY ", 8))
137 return GPG_ERR_INV_ARG;
139 match = ZxcvbnMatch (line+8, NULL, NULL);
140 match = (match/MAX_ENTROPY)*100;
141 snprintf (buf, sizeof (buf), "%u", (unsigned)match);
142 return assuan_send_data (pwm->pctx, buf, strlen (buf));
144 #endif
146 static gpg_error_t
147 set_pinentry_strings (pwm_t * pwm, pwmd_pinentry_t which)
149 char *tmp, *desc = NULL;
150 gpg_error_t rc;
152 if (which != PWMD_PINENTRY_USER)
154 rc = pinentry_command (pwm, NULL, NULL, "SETERROR ");
155 if (rc)
156 return rc;
159 tmp = pwmd_malloc (ASSUAN_LINELENGTH + 1);
160 if (!tmp)
161 return gpg_error_from_errno (ENOMEM);
163 if (!pwm->pinentry_prompt)
165 pwm->pinentry_prompt = pwmd_strdup (N_("Passphrase:"));
166 if (!pwm->pinentry_prompt)
168 pwmd_free (tmp);
169 return GPG_ERR_ENOMEM;
173 if (!pwm->pinentry_desc)
175 if (which == PWMD_PINENTRY_OPEN || which == PWMD_PINENTRY_OPEN_FAILED)
177 if (pwm->passphrase_hint && pwm->passphrase_info)
179 char *hint = pwmd_strdup (pwm->passphrase_hint), *hintp = hint;
180 char *info = pwmd_strdup (pwm->passphrase_info), *infop = info;
181 char *userid = strchr (hint, ' ');
183 if (userid && *userid)
184 *userid++ = 0;
186 infop = strchr (info, ' ');
187 if (infop && *infop)
189 char *p = strchr (++infop, ' ');
191 if (p)
192 infop[strlen(infop)-strlen(p)] = 0;
195 hintp += 8;
196 infop += 8;
197 desc = pwmd_strdup_printf (N_
198 ("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);
199 pwmd_free (hint);
200 pwmd_free (info);
202 else
204 desc = pwmd_strdup_printf (N_ ("A passphrase is required to decrypt the encrypted data file \"%s\". Please enter the passphrase below."), pwm->filename);
207 else if (which == PWMD_PINENTRY_SAVE
208 || which == PWMD_PINENTRY_SAVE_FAILED)
209 desc =
210 pwmd_strdup_printf (N_
211 ("Please enter the passphrase to use to encrypt the data file \"%s\"."),
212 pwm->filename);
213 else if (which == PWMD_PINENTRY_SAVE_CONFIRM)
214 desc =
215 pwmd_strdup_printf (N_
216 ("Please re-enter the passphrase for confirmation."),
217 pwm->filename);
219 if (!desc)
221 pwmd_free (tmp);
222 return GPG_ERR_ENOMEM;
225 else
226 desc = pwm->pinentry_desc;
228 if (which == PWMD_PINENTRY_USER)
230 snprintf (tmp, ASSUAN_LINELENGTH, "SETERROR %s",
231 pwm->pinentry_error ? pwm->pinentry_error : "");
232 rc = pinentry_command (pwm, NULL, NULL, tmp);
233 if (rc)
235 pwmd_free (tmp);
236 return rc;
239 snprintf (tmp, ASSUAN_LINELENGTH, "SETDESC %s",
240 pwm->pinentry_desc ? pwm->pinentry_desc : "");
242 else
244 if (which == PWMD_PINENTRY_SAVE_FAILED
245 || which == PWMD_PINENTRY_OPEN_FAILED)
247 if (which == PWMD_PINENTRY_SAVE_FAILED)
248 snprintf (tmp, ASSUAN_LINELENGTH, "SETERROR %s",
249 N_("Passphrases do not match, try again."));
250 else
252 if (pwm->pinentry_tries)
254 strcpy (tmp, "SETERROR ");
255 snprintf (tmp + strlen ("SETERROR "), ASSUAN_LINELENGTH,
256 N_("Bad passphrase (try %i of %i)"),
257 pwm->pinentry_try + 1, pwm->pinentry_tries);
259 else
260 snprintf (tmp, ASSUAN_LINELENGTH, "SETERROR %s",
261 N_("Bad passphrase, try again"));
264 rc = pinentry_command (pwm, NULL, NULL, tmp);
265 if (rc)
267 pwmd_free (tmp);
268 return rc;
272 snprintf (tmp, ASSUAN_LINELENGTH, "SETDESC %s", desc);
274 if (pwm->pinentry_desc != desc)
275 pwmd_free (desc);
278 rc = pinentry_command (pwm, NULL, NULL, tmp);
279 if (rc)
281 pwmd_free (tmp);
282 return rc;
285 snprintf (tmp, ASSUAN_LINELENGTH, "SETPROMPT %s", pwm->pinentry_prompt);
286 rc = pinentry_command (pwm, NULL, NULL, tmp);
287 pwmd_free (tmp);
289 #ifdef WITH_QUALITY
290 if (!rc && (which == PWMD_PINENTRY_SAVE ||
291 which == PWMD_PINENTRY_SAVE_FAILED))
293 rc = pinentry_command (pwm, NULL, NULL, "SETQUALITYBAR");
294 if (!rc)
296 pwm->_inquire_func = quality_cb;
297 pwm->_inquire_data = pwm;
300 #endif
302 if (!rc && pwm->pinentry_timeout >= 0)
304 char buf[32];
306 snprintf(buf, sizeof(buf), "SETTIMEOUT %i", pwm->pinentry_timeout);
307 rc = pinentry_command(pwm, NULL, NULL, buf);
310 return rc;
313 void
314 _pinentry_disconnect (pwm_t * pwm)
316 if (pwm->pctx)
317 assuan_release (pwm->pctx);
319 pwm->pctx = NULL;
320 pwm->pinentry_pid = -1;
323 gpg_error_t
324 _getpin (pwm_t * pwm, char **result, size_t * len, pwmd_pinentry_t which)
326 gpg_error_t rc = set_pinentry_strings (pwm, which);
328 if (rc)
330 _pinentry_disconnect (pwm);
331 return rc;
334 rc = pinentry_command (pwm, result, len,
335 which == PWMD_PINENTRY_CONFIRM ? "CONFIRM" : "GETPIN");
336 if (rc)
338 _pinentry_disconnect (pwm);
340 /* This lets PWMD_OPTION_PINENTRY_TIMEOUT work. Note that it is not
341 * thread safe do to the global variables. */
342 if (gpg_err_code (rc) == GPG_ERR_EOF)
343 rc = GPG_ERR_CANCELED;
345 else if (which != PWMD_PINENTRY_CONFIRM && len && result)
347 if (*len)
348 *len = strlen (*result); // remove the null byte
349 else
351 *result = pwmd_malloc (1);
352 *(*result) = 0;
353 *len = 1;
357 return rc;
360 gpg_error_t
361 _pwmd_getpin (pwm_t * pwm, const char *filename, char **result,
362 size_t * len, pwmd_pinentry_t which)
364 gpg_error_t rc;
365 char *p;
367 if (!pwm)
368 return GPG_ERR_INV_ARG;
370 p = pwm->filename;
371 if (which == PWMD_PINENTRY_CLOSE)
373 _pinentry_disconnect (pwm);
374 return 0;
377 if (!result && which != PWMD_PINENTRY_CONFIRM)
378 return GPG_ERR_INV_ARG;
380 pwm->filename = (char *) filename;
381 rc = _getpin (pwm, result, len, which);
382 pwm->filename = p;
383 return rc;