pwmc: Add --status-state.
[libpwmd.git] / src / pinentry.c
blobec6a408dc5d87ca15ca98ca507f691ac295ccf2c
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 update_pinentry_settings (pwm);
57 if (!pwm->pinentry_display && !pwm->pinentry_tty)
58 return GPG_ERR_ENOTTY;
60 *p++ = "pinentry";
61 *p++ = pwm->pinentry_display ? "--display" : "--ttyname";
62 *p++ = pwm->pinentry_display ? pwm->pinentry_display : pwm->pinentry_tty;
64 if (pwm->pinentry_lcctype)
66 *p++ = "--lc-ctype";
67 *p++ = pwm->pinentry_lcctype;
70 if (pwm->pinentry_lcmessages)
72 *p++ = "--lc-messages";
73 *p++ = pwm->pinentry_lcmessages;
76 *p = NULL;
78 if (!pwm->pinentry_display)
80 *p++ = "--ttytype";
81 *p++ = pwm->pinentry_term ? pwm->pinentry_term : getenv ("TERM");
82 *p = NULL;
85 rc = assuan_new_ext (&ctx, GPG_ERR_SOURCE_PINENTRY, &mhooks, NULL, NULL);
86 if (rc)
87 return rc;
89 rc = assuan_pipe_connect (ctx,
90 pwm->pinentry_path ? pwm->
91 pinentry_path : PINENTRY_PATH, argv, child_list,
92 NULL, NULL, 0);
93 if (rc)
95 assuan_release (ctx);
96 return rc;
99 pwm->pinentry_pid = assuan_get_pid (ctx);
100 pwm->pctx = ctx;
101 rc = set_pinentry_strings (pwm, 0);
102 if (rc)
103 _pinentry_disconnect (pwm);
105 return rc;
108 static gpg_error_t
109 pinentry_command (pwm_t * pwm, char **result, size_t * len, const char *cmd)
111 if (len)
112 *len = 0;
114 if (result)
115 *result = NULL;
117 if (!pwm->pctx)
119 gpg_error_t rc = launch_pinentry (pwm);
121 if (rc)
122 return rc;
125 return _assuan_command (pwm, pwm->pctx, result, len, cmd);
128 #ifdef WITH_QUALITY
129 static gpg_error_t
130 quality_cb (void *data, const char *line)
132 #define MAX_ENTROPY 80 // in bits
133 pwm_t *pwm = data;
134 char buf[5];
135 double match;
137 if (strncmp (line, "QUALITY ", 8))
138 return GPG_ERR_INV_ARG;
140 match = ZxcvbnMatch (line+8, NULL, NULL);
141 match = (match/MAX_ENTROPY)*100;
142 snprintf (buf, sizeof (buf), "%u", (unsigned)match);
143 return assuan_send_data (pwm->pctx, buf, strlen (buf));
145 #endif
147 static gpg_error_t
148 set_pinentry_strings (pwm_t * pwm, pwmd_pinentry_t which)
150 char *tmp, *desc = NULL;
151 gpg_error_t rc;
153 if (which != PWMD_PINENTRY_USER)
155 rc = pinentry_command (pwm, NULL, NULL, "SETERROR ");
156 if (rc)
157 return rc;
160 tmp = pwmd_malloc (ASSUAN_LINELENGTH + 1);
161 if (!tmp)
162 return gpg_error_from_errno (ENOMEM);
164 if (!pwm->pinentry_prompt)
166 pwm->pinentry_prompt = pwmd_strdup (N_("Passphrase:"));
167 if (!pwm->pinentry_prompt)
169 pwmd_free (tmp);
170 return GPG_ERR_ENOMEM;
174 if (!pwm->pinentry_desc)
176 if (which == PWMD_PINENTRY_OPEN || which == PWMD_PINENTRY_OPEN_FAILED)
178 if (pwm->passphrase_hint && pwm->passphrase_info)
180 char *hint = pwmd_strdup (pwm->passphrase_hint), *hintp = hint;
181 char *info = pwmd_strdup (pwm->passphrase_info), *infop = info;
182 char *userid = strchr (hint, ' ');
184 if (userid && *userid)
185 *userid++ = 0;
187 infop = strchr (info, ' ');
188 if (infop && *infop)
190 char *p = strchr (++infop, ' ');
192 if (p)
193 infop[strlen(infop)-strlen(p)] = 0;
196 hintp += 8;
197 infop += 8;
198 desc = pwmd_strdup_printf (N_
199 ("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);
200 pwmd_free (hint);
201 pwmd_free (info);
203 else
205 desc = pwmd_strdup_printf (N_ ("A passphrase is required to decrypt the encrypted data file \"%s\". Please enter the passphrase below."), pwm->filename);
208 else if (which == PWMD_PINENTRY_SAVE
209 || which == PWMD_PINENTRY_SAVE_FAILED)
210 desc =
211 pwmd_strdup_printf (N_
212 ("Please enter the passphrase to use to encrypt the data file \"%s\"."),
213 pwm->filename);
214 else if (which == PWMD_PINENTRY_SAVE_CONFIRM)
215 desc =
216 pwmd_strdup_printf (N_
217 ("Please re-enter the passphrase for confirmation."),
218 pwm->filename);
220 if (!desc)
222 pwmd_free (tmp);
223 return GPG_ERR_ENOMEM;
226 else
227 desc = pwm->pinentry_desc;
229 if (which == PWMD_PINENTRY_USER)
231 snprintf (tmp, ASSUAN_LINELENGTH, "SETERROR %s",
232 pwm->pinentry_error ? pwm->pinentry_error : "");
233 rc = pinentry_command (pwm, NULL, NULL, tmp);
234 if (rc)
236 pwmd_free (tmp);
237 return rc;
240 snprintf (tmp, ASSUAN_LINELENGTH, "SETDESC %s",
241 pwm->pinentry_desc ? pwm->pinentry_desc : "");
243 else
245 if (which == PWMD_PINENTRY_SAVE_FAILED
246 || which == PWMD_PINENTRY_OPEN_FAILED)
248 if (which == PWMD_PINENTRY_SAVE_FAILED)
249 snprintf (tmp, ASSUAN_LINELENGTH, "SETERROR %s",
250 N_("Passphrases do not match, try again."));
251 else
253 if (pwm->pinentry_tries)
255 strcpy (tmp, "SETERROR ");
256 snprintf (tmp + strlen ("SETERROR "), ASSUAN_LINELENGTH,
257 N_("Bad passphrase (try %i of %i)"),
258 pwm->pinentry_try + 1, pwm->pinentry_tries);
260 else
261 snprintf (tmp, ASSUAN_LINELENGTH, "SETERROR %s",
262 N_("Bad passphrase, try again"));
265 rc = pinentry_command (pwm, NULL, NULL, tmp);
266 if (rc)
268 pwmd_free (tmp);
269 return rc;
273 snprintf (tmp, ASSUAN_LINELENGTH, "SETDESC %s", desc);
275 if (pwm->pinentry_desc != desc)
276 pwmd_free (desc);
279 rc = pinentry_command (pwm, NULL, NULL, tmp);
280 if (rc)
282 pwmd_free (tmp);
283 return rc;
286 snprintf (tmp, ASSUAN_LINELENGTH, "SETPROMPT %s", pwm->pinentry_prompt);
287 rc = pinentry_command (pwm, NULL, NULL, tmp);
288 pwmd_free (tmp);
290 #ifdef WITH_QUALITY
291 if (!rc && (which == PWMD_PINENTRY_SAVE ||
292 which == PWMD_PINENTRY_SAVE_FAILED))
294 rc = pinentry_command (pwm, NULL, NULL, "SETQUALITYBAR");
295 if (!rc)
297 pwm->_inquire_func = quality_cb;
298 pwm->_inquire_data = pwm;
301 #endif
303 if (!rc && pwm->pinentry_timeout >= 0)
305 char buf[32];
307 snprintf(buf, sizeof(buf), "SETTIMEOUT %i", pwm->pinentry_timeout);
308 rc = pinentry_command(pwm, NULL, NULL, buf);
311 return rc;
314 void
315 _pinentry_disconnect (pwm_t * pwm)
317 if (pwm->pctx)
318 assuan_release (pwm->pctx);
320 pwm->pctx = NULL;
321 pwm->pinentry_pid = -1;
324 gpg_error_t
325 _getpin (pwm_t * pwm, char **result, size_t * len, pwmd_pinentry_t which)
327 gpg_error_t rc = set_pinentry_strings (pwm, which);
329 if (rc)
331 _pinentry_disconnect (pwm);
332 return rc;
335 rc = pinentry_command (pwm, result, len,
336 which == PWMD_PINENTRY_CONFIRM ? "CONFIRM" : "GETPIN");
337 if (rc)
339 _pinentry_disconnect (pwm);
341 /* This lets PWMD_OPTION_PINENTRY_TIMEOUT work. Note that it is not
342 * thread safe do to the global variables. */
343 if (gpg_err_code (rc) == GPG_ERR_EOF)
344 rc = GPG_ERR_CANCELED;
346 else if (which != PWMD_PINENTRY_CONFIRM && len && result)
348 if (*len)
349 *len = strlen (*result); // remove the null byte
350 else
352 *result = pwmd_malloc (1);
353 *(*result) = 0;
354 *len = 1;
358 return rc;
361 gpg_error_t
362 _pwmd_getpin (pwm_t * pwm, const char *filename, char **result,
363 size_t * len, pwmd_pinentry_t which)
365 gpg_error_t rc;
366 char *p;
368 if (!pwm)
369 return GPG_ERR_INV_ARG;
371 p = pwm->filename;
372 if (which == PWMD_PINENTRY_CLOSE)
374 _pinentry_disconnect (pwm);
375 return 0;
378 if (!result && which != PWMD_PINENTRY_CONFIRM)
379 return GPG_ERR_INV_ARG;
381 pwm->filename = (char *) filename;
382 rc = _getpin (pwm, result, len, which);
383 pwm->filename = p;
384 return rc;