contrib: Add helper to build Android dependencies.
[libpwmd.git] / src / pinentry.c
blobec57a1b75496fb1fe322620a21812f700bdb47dc
1 /*
2 Copyright (C) 2006-2023 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 version 2.1 as published by the Free Software Foundation.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
18 USA
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 <string.h>
29 #ifdef HAVE_LIMITS_H
30 #include <limits.h>
31 #endif
33 #include "pinentry.h"
34 #include "misc.h"
36 #ifdef WITH_QUALITY
37 #include "zxcvbn.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 gpg_error_t rc;
46 assuan_context_t ctx;
47 assuan_fd_t child_list[] = { ASSUAN_INVALID_FD };
48 const char *argv[12];
49 const char **p = argv;
50 static struct assuan_malloc_hooks mhooks = {
51 pwmd_malloc, pwmd_realloc, pwmd_free
54 *p++ = "";
55 if (pwm->pinentry_display)
57 *p++ = "--display";
58 *p++ = pwm->pinentry_display;
61 if (pwm->pinentry_tty)
63 *p++ = "--ttyname";
64 *p++ = pwm->pinentry_tty;
67 if (pwm->pinentry_term)
69 *p++ = "--ttytype";
70 *p++ = pwm->pinentry_term;
73 if (pwm->pinentry_lcctype)
75 *p++ = "--lc-ctype";
76 *p++ = pwm->pinentry_lcctype;
79 if (pwm->pinentry_lcmessages)
81 *p++ = "--lc-messages";
82 *p++ = pwm->pinentry_lcmessages;
85 *p = NULL;
86 rc = assuan_new_ext (&ctx, GPG_ERR_SOURCE_PINENTRY, &mhooks, NULL, NULL);
87 if (rc)
88 return rc;
90 rc = assuan_pipe_connect (ctx, PINENTRY_PATH, argv, child_list, NULL, NULL, 0);
91 if (rc)
93 assuan_release (ctx);
94 return rc;
97 pwm->pinentry_pid = assuan_get_pid (ctx);
98 pwm->pctx = ctx;
99 rc = set_pinentry_strings (pwm, 0);
100 if (rc)
101 _pinentry_disconnect (pwm);
103 return rc;
106 static gpg_error_t
107 pinentry_command (pwm_t * pwm, char **result, size_t * len, const char *cmd)
109 if (len)
110 *len = 0;
112 if (result)
113 *result = NULL;
115 if (!pwm->pctx)
117 gpg_error_t rc = launch_pinentry (pwm);
119 if (rc)
120 return rc;
123 return _assuan_command (pwm, pwm->pctx, result, len, cmd);
126 #ifdef WITH_QUALITY
127 static gpg_error_t
128 quality_cb (void *data, const char *line)
130 #define MAX_ENTROPY 80 // in bits
131 pwm_t *pwm = data;
132 char buf[5];
133 double match;
135 if (strncmp (line, "QUALITY ", 8))
136 return GPG_ERR_INV_ARG;
138 match = ZxcvbnMatch (line+8, NULL, NULL);
139 match = (match/MAX_ENTROPY)*100;
140 snprintf (buf, sizeof (buf), "%u", (unsigned)match);
141 return assuan_send_data (pwm->pctx, buf, strlen (buf));
143 #endif
145 static gpg_error_t
146 set_pinentry_strings (pwm_t * pwm, pwmd_pinentry_t which)
148 char *tmp, *desc = NULL;
149 gpg_error_t rc;
151 if (which != PWMD_PINENTRY_USER)
153 rc = pinentry_command (pwm, NULL, NULL, "SETERROR ");
154 if (rc)
155 return rc;
158 tmp = pwmd_malloc (ASSUAN_LINELENGTH + 1);
159 if (!tmp)
160 return gpg_error_from_errno (ENOMEM);
162 if (!pwm->pinentry_prompt)
164 pwm->pinentry_prompt = pwmd_strdup (N_("Passphrase:"));
165 if (!pwm->pinentry_prompt)
167 pwmd_free (tmp);
168 return GPG_ERR_ENOMEM;
172 if (!pwm->pinentry_desc)
174 if (which == PWMD_PINENTRY_OPEN || which == PWMD_PINENTRY_OPEN_FAILED)
176 if (pwm->passphrase_hint && pwm->passphrase_info)
178 char *hint = pwmd_strdup (pwm->passphrase_hint), *hintp = hint;
179 char *info = pwmd_strdup (pwm->passphrase_info), *infop;
180 char *userid = strchr (hint, ' ');
182 if (userid && *userid)
183 *userid++ = 0;
185 infop = strchr (info, ' ');
186 if (infop && *infop)
188 char *p = strchr (++infop, ' ');
190 if (p)
191 infop[strlen(infop)-strlen(p)] = 0;
194 hintp += 8;
195 infop += 8;
196 desc = pwmd_strdup_printf (N_
197 ("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);
198 pwmd_free (hint);
199 pwmd_free (info);
201 else
203 desc = pwmd_strdup_printf (N_ ("A passphrase is required to decrypt the encrypted%%0Adata file \"%s\". Please enter the passphrase below."), pwm->filename);
206 else if (which == PWMD_PINENTRY_SAVE
207 || which == PWMD_PINENTRY_SAVE_FAILED)
209 if (pwm->bulk_id && !strcmp (pwm->bulk_id, "GENKEY"))
210 desc = pwmd_strdup_printf (N_("Please enter the passphrase to use to protect the newly generated keypair."));
211 else
212 desc = pwmd_strdup_printf (N_("Please enter the passphrase to use to encrypt%%0Athe data file \"%s\"."), 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)
247 snprintf (tmp, ASSUAN_LINELENGTH, "SETREPEAT %s", N_("Repeat:"));
248 rc = pinentry_command (pwm, NULL, NULL, tmp);
249 if (!rc)
251 snprintf (tmp, ASSUAN_LINELENGTH, "SETREPEATERROR %s", N_("Passphrases do not match."));
252 rc = pinentry_command (pwm, NULL, NULL, tmp);
253 if (!rc)
255 snprintf (tmp, ASSUAN_LINELENGTH, "SETREPEATOK %s", N_("Passphrases match."));
256 rc = pinentry_command (pwm, NULL, NULL, tmp);
257 if (gpg_err_code (rc) == GPG_ERR_ASS_UNKNOWN_CMD)
258 rc = 0;
263 if (!rc && (which == PWMD_PINENTRY_SAVE_FAILED
264 || which == PWMD_PINENTRY_OPEN_FAILED))
266 if (which == PWMD_PINENTRY_SAVE_FAILED)
267 snprintf (tmp, ASSUAN_LINELENGTH, "SETERROR %s",
268 N_("Passphrases do not match, try again."));
269 else
271 if (pwm->pinentry_tries)
273 strcpy (tmp, "SETERROR ");
274 snprintf (tmp + strlen ("SETERROR "), ASSUAN_LINELENGTH,
275 N_("Bad passphrase (try %i of %i)"),
276 pwm->pinentry_try + 1, pwm->pinentry_tries);
278 else
279 snprintf (tmp, ASSUAN_LINELENGTH, "SETERROR %s",
280 N_("Bad passphrase, try again"));
283 rc = pinentry_command (pwm, NULL, NULL, tmp);
284 if (rc)
286 pwmd_free (tmp);
287 return rc;
291 snprintf (tmp, ASSUAN_LINELENGTH, "SETDESC %s", desc);
293 if (pwm->pinentry_desc != desc)
294 pwmd_free (desc);
297 if (!rc)
298 rc = pinentry_command (pwm, NULL, NULL, tmp);
299 if (rc)
301 pwmd_free (tmp);
302 return rc;
305 snprintf (tmp, ASSUAN_LINELENGTH, "SETPROMPT %s", pwm->pinentry_prompt);
306 rc = pinentry_command (pwm, NULL, NULL, tmp);
307 pwmd_free (tmp);
308 #ifdef WITH_QUALITY
309 if (!rc && (which == PWMD_PINENTRY_SAVE ||
310 which == PWMD_PINENTRY_SAVE_FAILED))
312 rc = pinentry_command (pwm, NULL, NULL, "SETQUALITYBAR");
313 if (!rc)
315 pwm->_inquire_func = quality_cb;
316 pwm->_inquire_data = pwm;
319 #endif
321 if (!rc && pwm->pinentry_timeout >= 0)
323 char buf[32];
325 snprintf(buf, sizeof(buf), "SETTIMEOUT %i", pwm->pinentry_timeout);
326 rc = pinentry_command(pwm, NULL, NULL, buf);
329 return rc;
332 void
333 _pinentry_disconnect (pwm_t * pwm)
335 if (pwm->pctx)
336 assuan_release (pwm->pctx);
338 pwm->pctx = NULL;
339 pwm->pinentry_pid = -1;
342 gpg_error_t
343 _getpin (pwm_t * pwm, char **result, size_t * len, pwmd_pinentry_t which)
345 gpg_error_t rc = set_pinentry_strings (pwm, which);
347 if (rc)
349 _pinentry_disconnect (pwm);
350 return rc;
353 rc = pinentry_command (pwm, result, len,
354 which == PWMD_PINENTRY_CONFIRM ? "CONFIRM" : "GETPIN");
355 if (rc)
357 _pinentry_disconnect (pwm);
359 /* This lets PWMD_OPTION_PINENTRY_TIMEOUT work. Note that it is not
360 * thread safe due to the global variables. */
361 if (gpg_err_code (rc) == GPG_ERR_EOF)
362 rc = GPG_ERR_CANCELED;
364 else if (which != PWMD_PINENTRY_CONFIRM && len && result)
366 if (*len)
367 *len = strlen (*result); // remove the null byte
368 else
370 *result = pwmd_malloc (1);
371 *(*result) = 0;
372 *len = 1;
376 return rc;
379 gpg_error_t
380 _pwmd_getpin (pwm_t * pwm, const char *filename, char **result,
381 size_t * len, pwmd_pinentry_t which)
383 gpg_error_t rc;
384 char *p;
386 if (!pwm)
387 return GPG_ERR_INV_ARG;
389 p = pwm->filename;
390 if (which == PWMD_PINENTRY_CLOSE)
392 _pinentry_disconnect (pwm);
393 return 0;
396 if (!result && which != PWMD_PINENTRY_CONFIRM)
397 return GPG_ERR_INV_ARG;
399 pwm->filename = (char *) filename;
400 rc = _getpin (pwm, result, len, which);
401 pwm->filename = p;
402 return rc;