Fix TLS socket timeouts and rename parameters.
[libpwmd.git] / src / pinentry.c
blob011b6160d917d02c7b3590e4f5ee2dc38af3c272
1 /*
2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
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 <signal.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 <crack.h>
39 #endif
41 static pwm_t *gpwm;
42 static int gelapsed, gtimeout;
43 static gpg_error_t grc;
45 static gpg_error_t set_pinentry_strings (pwm_t * pwm, pwmd_pinentry_t which);
47 static gpg_error_t
48 launch_pinentry (pwm_t * pwm)
50 int rc;
51 assuan_context_t ctx;
52 int child_list[] = { -1 };
53 const char *argv[10];
54 const char **p = argv;
55 static struct assuan_malloc_hooks mhooks = {
56 pwmd_malloc, pwmd_realloc, pwmd_free
59 update_pinentry_settings (pwm);
60 if (!pwm->pinentry_display && !pwm->pinentry_tty)
61 return GPG_ERR_ENOTTY;
63 *p++ = "pinentry";
64 *p++ = pwm->pinentry_display ? "--display" : "--ttyname";
65 *p++ = pwm->pinentry_display ? pwm->pinentry_display : pwm->pinentry_tty;
67 if (pwm->pinentry_lcctype)
69 *p++ = "--lc-ctype";
70 *p++ = pwm->pinentry_lcctype;
73 if (pwm->pinentry_lcmessages)
75 *p++ = "--lc-messages";
76 *p++ = pwm->pinentry_lcmessages;
79 *p = NULL;
81 if (!pwm->pinentry_display)
83 *p++ = "--ttytype";
84 *p++ = pwm->pinentry_term ? pwm->pinentry_term : getenv ("TERM");
85 *p = NULL;
88 rc = assuan_new_ext (&ctx, GPG_ERR_SOURCE_PINENTRY, &mhooks, NULL, NULL);
89 if (rc)
90 return rc;
92 rc = assuan_pipe_connect (ctx,
93 pwm->pinentry_path ? pwm->
94 pinentry_path : PINENTRY_PATH, argv, child_list,
95 NULL, NULL, 0);
96 if (rc)
98 assuan_release (ctx);
99 return rc;
102 pwm->pinentry_pid = assuan_get_pid (ctx);
103 pwm->pctx = ctx;
104 rc = set_pinentry_strings (pwm, 0);
105 if (rc)
106 _pinentry_disconnect (pwm);
108 return rc;
111 static gpg_error_t
112 pinentry_command (pwm_t * pwm, char **result, size_t * len, const char *cmd)
114 if (len)
115 *len = 0;
117 if (result)
118 *result = NULL;
120 if (!pwm->pctx)
122 gpg_error_t rc = launch_pinentry (pwm);
124 if (rc)
125 return rc;
128 return _assuan_command (pwm, pwm->pctx, result, len, cmd);
131 #ifdef WITH_QUALITY
132 static gpg_error_t
133 quality_cb (void *data, const char *line)
135 pwm_t *pwm = data;
136 int score = 0;
137 char buf[5];
138 const char *tmp;
140 if (strncmp (line, "QUALITY ", 8))
141 return GPG_ERR_INV_ARG;
143 if (!(tmp = FascistCheck (line + 8, CRACKLIB_DICT)))
144 return assuan_send_data (pwm->pctx, "100", 3);
146 if (!strcmp (tmp, N_("it is WAY too short")))
147 score = 10;
148 else if (!strcmp (tmp, N_("it is too short")))
149 score = 20;
150 else if (!strcmp (tmp, N_("it is all whitespace")))
151 score = 25;
152 else if (!strcmp (tmp, N_("it is based on your username")))
153 score = 30;
154 else
155 if (!strcmp (tmp, N_("it does not contain enough DIFFERENT characters")))
156 score = 35;
157 else if (!strcmp (tmp, N_("it is based on a dictionary word")))
158 score = 40;
159 else if (!strcmp (tmp, N_("it is based upon your password entry")))
160 score = 50;
161 else if (!strcmp (tmp, N_("it's derived from your password entry")))
162 score = 50;
163 else if (!strcmp (tmp, N_("it is derived from your password entry")))
164 score = 50;
165 else if (!strcmp (tmp, N_("it is based on a (reversed) dictionary word")))
166 score = 60;
167 else if (!strcmp (tmp, N_("it is derivable from your password entry")))
168 score = 70;
169 else if (!strcmp (tmp, N_("it's derivable from your password entry")))
170 score = 70;
171 else if (!strcmp (tmp, N_("it looks like a National Insurance number.")))
172 score = 80;
173 else if (!strcmp (tmp, N_("it is too simplistic/systematic")))
174 score = 90;
175 else
176 score = 0;
178 snprintf (buf, sizeof (buf), "%i", score);
179 return assuan_send_data (pwm->pctx, buf, strlen (buf));
181 #endif
183 static gpg_error_t
184 set_pinentry_strings (pwm_t * pwm, pwmd_pinentry_t which)
186 char *tmp, *desc = NULL;
187 gpg_error_t rc;
189 if (which != PWMD_PINENTRY_USER)
191 rc = pinentry_command (pwm, NULL, NULL, "SETERROR ");
192 if (rc)
193 return rc;
196 tmp = pwmd_malloc (ASSUAN_LINELENGTH + 1);
197 if (!tmp)
198 return gpg_error_from_errno (ENOMEM);
200 if (!pwm->pinentry_prompt)
202 pwm->pinentry_prompt = pwmd_strdup (N_("Passphrase:"));
203 if (!pwm->pinentry_prompt)
205 pwmd_free (tmp);
206 return GPG_ERR_ENOMEM;
210 if (!pwm->pinentry_desc)
212 if (which == PWMD_PINENTRY_OPEN || which == PWMD_PINENTRY_OPEN_FAILED)
213 desc =
214 pwmd_strdup_printf (N_
215 ("A passphrase is required to unlock the secret key for the encrypted data file \"%s\". Please enter the passphrase below."),
216 pwm->filename);
217 else if (which == PWMD_PINENTRY_SAVE
218 || which == PWMD_PINENTRY_SAVE_FAILED)
219 desc =
220 pwmd_strdup_printf (N_
221 ("Please enter the passphrase to use to encrypt the data file \"%s\"."),
222 pwm->filename);
223 else if (which == PWMD_PINENTRY_SAVE_CONFIRM)
224 desc =
225 pwmd_strdup_printf (N_
226 ("Please re-enter the passphrase for confirmation."),
227 pwm->filename);
229 if (!desc)
231 pwmd_free (tmp);
232 return GPG_ERR_ENOMEM;
236 if (pwm->pinentry_desc)
237 desc = pwm->pinentry_desc;
239 if (which == PWMD_PINENTRY_USER)
241 snprintf (tmp, ASSUAN_LINELENGTH, "SETERROR %s",
242 pwm->pinentry_error ? pwm->pinentry_error : "");
243 rc = pinentry_command (pwm, NULL, NULL, tmp);
244 if (rc)
246 pwmd_free (tmp);
247 return rc;
250 snprintf (tmp, ASSUAN_LINELENGTH, "SETDESC %s",
251 pwm->pinentry_desc ? pwm->pinentry_desc : "");
253 else
255 if (which == PWMD_PINENTRY_SAVE_FAILED
256 || which == PWMD_PINENTRY_OPEN_FAILED)
258 if (which == PWMD_PINENTRY_SAVE_FAILED)
259 snprintf (tmp, ASSUAN_LINELENGTH, "SETERROR %s",
260 N_("Passphrases do not match, try again."));
261 else
263 if (pwm->pinentry_tries)
265 strcpy (tmp, "SETERROR ");
266 snprintf (tmp + strlen ("SETERROR "), ASSUAN_LINELENGTH,
267 N_("Bad passphrase (try %i of %i)"),
268 pwm->pinentry_try + 1, pwm->pinentry_tries);
270 else
271 snprintf (tmp, ASSUAN_LINELENGTH, "SETERROR %s",
272 N_("Bad passphrase, try again"));
275 rc = pinentry_command (pwm, NULL, NULL, tmp);
276 if (rc)
278 pwmd_free (tmp);
279 return rc;
283 snprintf (tmp, ASSUAN_LINELENGTH, "SETDESC %s", desc);
285 if (pwm->pinentry_desc != desc)
286 pwmd_free (desc);
289 rc = pinentry_command (pwm, NULL, NULL, tmp);
290 if (rc)
292 pwmd_free (tmp);
293 return rc;
296 snprintf (tmp, ASSUAN_LINELENGTH, "SETPROMPT %s", pwm->pinentry_prompt);
297 rc = pinentry_command (pwm, NULL, NULL, tmp);
298 pwmd_free (tmp);
300 #ifdef WITH_QUALITY
301 if (!rc && (which == PWMD_PINENTRY_SAVE ||
302 which == PWMD_PINENTRY_SAVE_FAILED))
304 rc = pinentry_command (pwm, NULL, NULL, "SETQUALITYBAR");
305 if (!rc)
307 pwm->_inquire_func = quality_cb;
308 pwm->_inquire_data = pwm;
311 #endif
313 return rc;
316 static gpg_error_t
317 terminate_pinentry (pwm_t * pwm)
319 pid_t pid;
321 if (!pwm || pwm->pinentry_pid == -1)
322 return GPG_ERR_INV_ARG;
324 pid = pwm->pinentry_pid;
325 pwm->pinentry_pid = -1;
327 if (kill (pid, 0) == 0)
329 if (kill (pid, SIGTERM) == -1)
331 if (kill (pid, SIGKILL) == -1)
332 return gpg_error_from_errno (errno);
335 else
336 return gpg_error_from_errno (errno);
338 return 0;
341 void
342 _pinentry_disconnect (pwm_t * pwm)
344 if (pwm->pctx)
345 assuan_release (pwm->pctx);
347 pwm->pctx = NULL;
348 pwm->pinentry_pid = -1;
352 * Only called from a child process.
354 static void
355 catchsig (int sig)
357 switch (sig)
359 case SIGALRM:
360 if (++gelapsed >= gtimeout)
362 terminate_pinentry (gpwm);
363 grc = gpg_err_make (GPG_ERR_SOURCE_PINENTRY, GPG_ERR_CANCELED);
365 else
366 alarm (1);
367 break;
368 default:
369 break;
373 gpg_error_t
374 _getpin (pwm_t * pwm, char **result, size_t * len, pwmd_pinentry_t which)
376 grc = 0;
377 gpg_error_t rc = set_pinentry_strings (pwm, which);
379 if (rc)
381 _pinentry_disconnect (pwm);
382 return rc;
385 if (gtimeout)
387 signal (SIGALRM, catchsig);
388 alarm (1);
391 rc = pinentry_command (pwm, result, len,
392 which == PWMD_PINENTRY_CONFIRM ? "CONFIRM" : "GETPIN");
395 * Since there was input cancel any timeout setting.
397 alarm (0);
398 signal (SIGALRM, SIG_DFL);
400 if (rc)
402 _pinentry_disconnect (pwm);
404 /* This lets PWMD_OPTION_PINENTRY_TIMEOUT work. Note that it is not
405 * thread safe do to the global variables. */
406 if (gpg_err_code (rc) == GPG_ERR_EOF)
407 rc = GPG_ERR_CANCELED;
409 else if (which != PWMD_PINENTRY_CONFIRM && len && result)
411 if (*len)
412 *len = strlen (*result); // remove the null byte
413 else
415 /* pwmd allows for an empty passphrase when not using gpg-agent. */
416 *result = pwmd_malloc (1);
417 *(*result) = 0;
418 *len = 1;
422 return rc;
425 gpg_error_t
426 _pwmd_getpin (pwm_t * pwm, const char *filename, char **result,
427 size_t * len, pwmd_pinentry_t which)
429 gpg_error_t rc;
430 char *p;
432 if (!pwm)
433 return GPG_ERR_INV_ARG;
435 p = pwm->filename;
436 if (which == PWMD_PINENTRY_CLOSE)
438 _pinentry_disconnect (pwm);
439 return 0;
442 if (!result && which != PWMD_PINENTRY_CONFIRM)
443 return GPG_ERR_INV_ARG;
445 /* Another handle is using this pinentry method. Allowing this instance
446 * would reset the timeout and global handle which wouldn't be good. */
447 if (gpwm)
448 return GPG_ERR_PIN_BLOCKED;
450 if (pwm->pinentry_timeout != 0)
452 gpwm = pwm;
453 gtimeout = abs (pwm->pinentry_timeout);
454 gelapsed = 0;
457 pwm->filename = (char *) filename;
458 rc = _getpin (pwm, result, len, which);
459 pwm->filename = p;
461 /* Don't timeout when an invalid passphrase was entered. */
462 gtimeout = 0;
463 gpwm = NULL;
464 return rc;