Version 3.0.0.
[pwmd.git] / src / pinentry.c
bloba98fcfbebed31d0cc4b2e36d95576377fd45849f
1 /*
2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
3 Ben Kibbey <bjk@luxsci.net>
5 This file is part of pwmd.
7 Pwmd 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 Pwmd 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 Pwmd. If not, see <http://www.gnu.org/licenses/>.
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <pwd.h>
27 #include <sys/types.h>
28 #include <signal.h>
29 #include <sys/wait.h>
31 #include "pwmd-error.h"
32 #include <gcrypt.h>
33 #include "mem.h"
34 #include "util-misc.h"
35 #include "common.h"
36 #include "commands.h"
37 #include "pinentry.h"
38 #include "mutex.h"
39 #include "rcfile.h"
40 #include "util-string.h"
42 #ifdef WITH_QUALITY
43 #include <crack.h>
44 #endif
46 void cleanup_all_clients (int atfork);
47 static gpg_error_t set_pinentry_strings (struct pinentry_s *pin, int which);
49 static gpg_error_t
50 mem_realloc_cb (void *data, const void *buffer, size_t len)
52 membuf_t *mem = (membuf_t *) data;
53 void *p;
55 if (!buffer)
56 return 0;
58 p = xrealloc (mem->buf, mem->len + len);
59 if (!p)
60 return 1;
62 mem->buf = p;
63 memcpy ((char *) mem->buf + mem->len, buffer, len);
64 mem->len += len;
65 return 0;
68 #ifdef WITH_QUALITY
69 static gpg_error_t
70 quality_cb (void *data, const char *line)
72 struct pinentry_s *pin = data;
73 const char *tmp;
74 int score = 0;
75 char buf[5];
77 if (strncmp (line, "QUALITY ", 8) != 0)
78 return GPG_ERR_INV_ARG;
80 tmp = FascistCheck (line + 8, CRACKLIB_DICT);
81 if (!tmp)
82 return assuan_send_data (pin->ctx, "100", 3);
84 if (!strcmp (tmp, _("it's WAY too short")))
85 score = 10;
86 else if (!strcmp (tmp, _("it is too short")))
87 score = 20;
88 else if (!strcmp (tmp, _("it is all whitespace")))
89 score = 25;
90 else if (!strcmp (tmp, _("it is based on your username")))
91 score = 30;
92 else if (!strcmp (tmp, _("it is based on a dictionary word")))
93 score = 40;
94 else if (!strcmp (tmp, _("it is based upon your password entry")))
95 score = 50;
96 else if (!strcmp (tmp, _("it's derived from your password entry")))
97 score = 50;
98 else if (!strcmp (tmp, _("it is based on a (reversed) dictionary word")))
99 score = 60;
100 else if (!strcmp (tmp, _("it is derivable from your password entry")))
101 score = 70;
102 else
103 if (!strcmp
104 (tmp, _("it does not contain enough DIFFERENT characters")))
105 score = 80;
106 else if (!strcmp (tmp, _("it is too simplistic/systematic")))
107 score = 90;
108 else
109 score = 0;
111 snprintf (buf, sizeof (buf), "%i", score);
112 tmp = buf;
113 return assuan_send_data (pin->ctx, tmp, strlen (tmp));
115 #endif
117 static gpg_error_t
118 assuan_command (struct pinentry_s *pin, char **result, const char *cmd)
120 gpg_error_t rc;
122 pin->data.len = 0;
123 pin->data.buf = NULL;
125 rc = assuan_transact (pin->ctx, cmd, mem_realloc_cb, &pin->data,
126 pin->inquire_cb, pin->inquire_data, NULL, NULL);
127 if (rc)
129 if (pin->data.buf)
131 xfree (pin->data.buf);
132 pin->data.buf = NULL;
135 else
137 if (pin->data.buf)
139 mem_realloc_cb (&pin->data, "", 1);
140 *result = (char *) pin->data.buf;
144 return rc;
147 static gpg_error_t
148 set_pinentry_options (struct pinentry_s *pin)
150 char *display = getenv ("DISPLAY");
151 int have_display = 0;
152 char *tty = NULL, *ttytype = NULL;
153 char *opt, *val;
154 gpg_error_t rc;
155 char *result = NULL;
156 char cmd[ASSUAN_LINELENGTH];
158 if (pin->pinentry_opts.display || display)
159 have_display = 1;
160 else
162 tty = pin->pinentry_opts.ttyname ? pin->pinentry_opts.ttyname
163 : ttyname (STDOUT_FILENO);
164 if (!tty)
165 return gpg_error_from_errno (errno);
168 if (!have_display && !tty)
169 return GPG_ERR_CANCELED;
171 if (!have_display)
173 char *p = getenv ("TERM");
175 ttytype = pin->pinentry_opts.ttytype ? pin->pinentry_opts.ttytype : p;
176 if (!ttytype)
177 return GPG_ERR_ENOTTY;
180 opt = have_display ? str_dup ("DISPLAY") : str_dup ("TTYNAME");
181 val = have_display ? pin->pinentry_opts.display ? pin->pinentry_opts.display
182 : display : tty;
183 snprintf (cmd, sizeof (cmd), "OPTION %s=%s", str_down (opt), val);
184 xfree (opt);
185 rc = assuan_command (pin, &result, cmd);
186 if (rc)
187 return rc;
189 if (!have_display)
191 snprintf (cmd, sizeof (cmd), "OPTION ttytype=%s", ttytype);
192 rc = assuan_command (pin, &result, cmd);
195 if (!rc)
197 snprintf (cmd, sizeof(cmd), "SETTIMEOUT %i", pin->pinentry_opts.timeout);
198 rc = assuan_command (pin, NULL, cmd);
199 /* Not all pinentrys support the SETTIMEOUT command. */
200 if (rc && gpg_err_source (rc) == GPG_ERR_SOURCE_PINENTRY
201 && gpg_err_code (rc) == GPG_ERR_ASS_UNKNOWN_CMD)
202 rc = 0;
205 return rc;
208 #ifdef HAVE_PTHREAD_ATFORK
209 static void
210 atfork_cb ()
212 cleanup_all_clients (1);
214 #else
215 static void
216 atfork_cb (void *data, int n)
218 (void)data;
219 if (!n)
220 cleanup_all_clients (1);
222 #endif
224 static gpg_error_t
225 launch_pinentry (struct pinentry_s *pin)
227 gpg_error_t rc;
228 assuan_context_t ctx;
229 static struct assuan_malloc_hooks mhooks = { xmalloc, xrealloc, xfree };
230 int child_list[] = { -1 };
231 const char *argv[8];
232 const char **p = argv;
233 char *tmp;
235 *p++ = "pinentry";
237 if (pin->pinentry_opts.display)
239 *p++ = "--display";
240 *p++ = pin->pinentry_opts.display;
243 if (pin->pinentry_opts.lc_ctype)
245 *p++ = "--lc-ctype";
246 *p++ = pin->pinentry_opts.lc_ctype;
249 if (pin->pinentry_opts.lc_messages)
251 *p++ = "--lc-messages";
252 *p++ = pin->pinentry_opts.lc_messages;
255 *p = NULL;
256 rc = assuan_new_ext (&ctx, GPG_ERR_SOURCE_DEFAULT, &mhooks, NULL, NULL);
257 if (rc)
258 return rc;
260 tmp = config_get_string ("global", "pinentry_path");
261 #ifdef HAVE_PTHREAD_ATFORK
262 pthread_atfork (NULL, NULL, atfork_cb);
263 rc = assuan_pipe_connect (ctx, tmp, argv, child_list, NULL, NULL, 0);
264 #else
265 rc = assuan_pipe_connect (ctx, tmp, argv, child_list, atfork_cb, NULL, 0);
266 #endif
267 xfree (tmp);
268 if (rc)
269 return rc;
271 pin->ctx = ctx;
272 rc = set_pinentry_options (pin);
273 return rc ? rc : set_pinentry_strings (pin, pin->which);
276 static gpg_error_t
277 pinentry_command (struct pinentry_s *pin, char **result, const char *cmd)
279 gpg_error_t rc = 0;
281 if (!pin->ctx)
282 rc = launch_pinentry (pin);
284 return rc ? rc : assuan_command (pin, result, cmd);
287 static gpg_error_t
288 set_pinentry_strings (struct pinentry_s *pin, int which)
290 char *buf;
291 gpg_error_t rc;
292 char *title = NULL;
294 /* The initial prompt from launch_pinentry(). */
295 pin->which = which;
297 if (which == PINENTRY_FAIL_CONFIRM)
299 title = str_dup (_("Passphrase mismatch, please try again."));
300 which = PINENTRY_SAVE;
302 else if (!pin->pinentry_opts.title)
303 title = str_asprintf (_("Password Manager Daemon%s%s"),
304 pin->name ? ": " : "",
305 pin->name ? pin->name : "");
306 else
307 title = str_dup (pin->pinentry_opts.title);
309 #ifdef WITH_QUALITY
310 if (which == PINENTRY_SAVE)
312 rc = pinentry_command (pin, NULL, "SETQUALITYBAR");
313 if (rc)
314 goto done;
316 pin->inquire_cb = quality_cb;
317 pin->inquire_data = pin;
319 #endif
321 if (!pin->pinentry_opts.prompt)
322 pin->pinentry_opts.prompt = str_dup (_("Passphrase:"));
324 if (!pin->pinentry_opts.desc)
325 pin->pinentry_opts.desc = str_asprintf (which == PINENTRY_OPEN ?
326 _("A passphrase is required to open the file "
327 "\"%s\". Please enter the passphrase below.")
329 _("A passphrase is required to save to the file "
330 "\"%s\". Please enter the passphrase below."),
331 pin->filename);
333 if (which == PINENTRY_SAVE_CONFIRM)
334 buf = str_asprintf ("SETDESC %s",
335 _("Please enter the passphrase again for confirmation."));
336 else
337 buf = str_asprintf ("SETDESC %s", pin->pinentry_opts.desc);
339 rc = pinentry_command (pin, NULL, buf);
340 xfree (buf);
341 if (rc)
342 goto done;
344 buf = str_asprintf ("SETPROMPT %s", pin->pinentry_opts.prompt);
345 rc = pinentry_command (pin, NULL, buf);
346 xfree (buf);
347 if (rc)
348 goto done;
350 buf = str_asprintf ("SETERROR %s", title);
351 rc = pinentry_command (pin, NULL, buf);
352 xfree (buf);
354 done:
355 free (title);
356 return rc;
359 static void
360 pinentry_disconnect (struct pinentry_s *pin)
362 if (!pin)
363 return;
365 if (pin->ctx)
366 assuan_release (pin->ctx);
368 pin->ctx = NULL;
371 static gpg_error_t
372 do_getpin (struct pinentry_s *pin, char **result)
374 gpg_error_t rc;
376 *result = NULL;
377 rc = pinentry_command (pin, result, "GETPIN");
378 if (rc)
379 return rc;
381 if (!*result)
382 *result = str_dup ("");
384 return rc;
387 gpg_error_t
388 pinentry_getpin (struct pinentry_s * pin, char **result, pinentry_cmd_t which)
390 gpg_error_t rc = set_pinentry_strings (pin, which);
391 char *result1 = NULL;
393 if (rc)
394 goto done;
396 again:
397 rc = do_getpin (pin, result);
398 if (rc)
399 goto done;
401 if (which == PINENTRY_SAVE)
403 if (!result1)
405 rc = set_pinentry_strings (pin, PINENTRY_SAVE_CONFIRM);
406 if (rc)
407 goto done;
409 result1 = str_dup (*result);
410 goto again;
413 if (strcmp (result1, *result))
415 xfree (result1);
416 xfree (*result);
417 result1 = *result = NULL;
418 rc = set_pinentry_strings (pin, PINENTRY_FAIL_CONFIRM);
419 if (rc)
420 goto done;
422 goto again;
426 done:
427 xfree (result1);
428 pinentry_disconnect (pin);
429 return rc;
432 gpg_error_t
433 pinentry_lock (assuan_context_t ctx, struct pinentry_s *pin)
435 gpg_error_t rc = 0;
437 MUTEX_TRYLOCK (ctx, &pin_mutex, rc, pin->pinentry_opts.timeout*10);
439 if (!rc)
440 pin->has_lock = 1;
442 return rc;
445 void
446 pinentry_unlock (struct pinentry_s *pin)
448 if (pin->has_lock == 0)
449 return;
451 MUTEX_UNLOCK (&pin_mutex);
452 pin->has_lock = 0;
455 void
456 pinentry_deinit (struct pinentry_s *pin)
458 if (!pin)
459 return;
461 pinentry_unlock (pin);
463 if (pin->ctx)
464 pinentry_disconnect (pin);
466 xfree (pin->name);
467 xfree (pin->filename);
468 pinentry_free_opts (&pin->pinentry_opts);
469 xfree (pin);
472 struct pinentry_s *
473 pinentry_init (const char *filename)
475 struct pinentry_s *pin = xcalloc (1, sizeof (struct pinentry_s));
477 if (!pin)
478 return NULL;
480 pin->filename = str_dup (filename);
481 if (!pin->filename)
483 xfree (pin);
484 return NULL;
487 return pin;
490 void
491 pinentry_free_opts (struct pinentry_option_s *opts)
493 xfree (opts->title);
494 xfree (opts->desc);
495 xfree (opts->prompt);
496 xfree (opts->ttyname);
497 xfree (opts->ttytype);
498 xfree (opts->display);
499 xfree (opts->lc_messages);
500 xfree (opts->lc_ctype);
503 gpg_error_t
504 pinentry_merge_options (struct pinentry_option_s *dst,
505 struct pinentry_option_s *src)
507 dst->timeout = src->timeout;
509 if (src->title)
511 xfree (dst->title);
512 dst->title = str_dup (src->title);
515 if (src->desc)
517 xfree (dst->desc);
518 dst->desc = str_dup (src->desc);
521 if (src->prompt)
523 xfree (dst->prompt);
524 dst->prompt = str_dup (src->prompt);
527 if (src->display)
529 xfree (dst->display);
530 dst->display = str_dup (src->display);
533 if (src->ttyname)
535 xfree (dst->ttyname);
536 dst->ttyname = str_dup (src->ttyname);
539 if (src->ttytype)
541 xfree (dst->ttytype);
542 dst->ttytype = str_dup (src->ttytype);
545 if (src->lc_ctype)
547 xfree (dst->lc_ctype);
548 dst->lc_ctype = str_dup (src->lc_ctype);
551 if (src->lc_messages)
553 xfree (dst->lc_messages);
554 dst->lc_messages = str_dup (src->lc_messages);
557 return 0;