Parse ~/.pwmd/pinentry.conf even when adding a file from the command
[pwmd.git] / src / pinentry.c
blobff73d4983423cfed379facd22af73cbb35193370
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2007-2008 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program 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
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
19 #include <stdlib.h>
20 #include <gcrypt.h>
21 #include <glib.h>
22 #include <errno.h>
23 #include <pth.h>
24 #include <pwd.h>
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
30 #ifdef WITH_QUALITY
31 #include <crack.h>
32 #endif
34 #include "mem.h"
35 #include "common.h"
36 #include "commands.h"
37 #include "pinentry.h"
38 #include "pwmd_error.h"
40 void free_client_list();
41 static gpg_error_t set_pinentry_strings(struct pinentry_s *pin, int which);
43 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
45 membuf_t *mem = (membuf_t *)data;
46 void *p;
48 if (!buffer)
49 return 0;
51 if ((p = xrealloc(mem->buf, mem->len + len)) == NULL)
52 return 1;
54 mem->buf = p;
55 memcpy((char *)mem->buf + mem->len, buffer, len);
56 mem->len += len;
57 return 0;
60 #ifdef WITH_QUALITY
61 static int quality_cb(void *data, const char *line)
63 struct pinentry_s *pin = data;
64 gpg_error_t rc;
65 const gchar *tmp;
66 gint score = 0;
67 gchar buf[5];
69 if (strncmp(line, "QUALITY ", 8) != 0)
70 return GPG_ERR_INV_ARG;
72 if (!(tmp = FascistCheck(line+8, CRACKLIB_DICT)))
73 return assuan_send_data(pin->ctx, "100", 3);
75 if (!strcmp(tmp, "it's WAY too short"))
76 score = 10;
77 else if (!strcmp(tmp, "it is too short"))
78 score = 20;
79 else if (!strcmp(tmp, "it is all whitespace"))
80 score = 25;
81 else if (!strcmp(tmp, "it is based on your username"))
82 score = 30;
83 else if (!strcmp(tmp, "it is based on a dictionary word"))
84 score = 40;
85 else if (!strcmp(tmp, "it is based upon your password entry"))
86 score = 50;
87 else if (!strcmp(tmp, "it's derived from your password entry"))
88 score = 50;
89 else if (!strcmp(tmp, "it is based on a (reversed) dictionary word"))
90 score = 60;
91 else if (!strcmp(tmp, "it is derivable from your password entry"))
92 score = 70;
93 else if (!strcmp(tmp, "it does not contain enough DIFFERENT characters"))
94 score = 80;
95 else if (!strcmp(tmp, "it is too simplistic/systematic"))
96 score = 90;
97 else
98 score = 0;
100 tmp = (const gchar *)print_fmt(buf, sizeof(buf), "%i", score);
101 return assuan_send_data(pin->ctx, tmp, strlen(tmp));
103 #endif
105 static gpg_error_t assuan_command(struct pinentry_s *pin, gchar **result,
106 const gchar *cmd)
108 gpg_error_t rc;
110 pin->data.len = 0;
111 pin->data.buf = NULL;
113 rc = assuan_transact(pin->ctx, cmd, mem_realloc_cb, &pin->data,
114 pin->inquire_cb, pin->inquire_data, NULL, NULL);
116 if (rc) {
117 if (pin->data.buf) {
118 xfree(pin->data.buf);
119 pin->data.buf = NULL;
122 else {
123 if (pin->data.buf) {
124 mem_realloc_cb(&pin->data, "", 1);
125 *result = (gchar *)pin->data.buf;
129 return rc;
132 static gpg_error_t set_pinentry_options(struct pinentry_s *pin)
134 gchar *display = getenv("DISPLAY");
135 gint have_display = 0;
136 gchar *tty = NULL, *ttytype = NULL;
137 gchar *opt, *val;
138 gpg_error_t rc;
139 gchar *result = NULL;
140 gchar cmd[ASSUAN_LINELENGTH];
142 if (pin->display || display)
143 have_display = 1;
144 else {
145 tty = pin->ttyname ? pin->ttyname : ttyname(STDOUT_FILENO);
147 if (!tty)
148 return GPG_ERR_CANCELED;
151 if (!have_display && !tty)
152 return GPG_ERR_CANCELED;
154 if (!have_display) {
155 gchar *p = getenv("TERM");
157 ttytype = pin->ttytype ? pin->ttytype : p;
159 if (!ttytype)
160 return GPG_ERR_CANCELED;
163 opt = have_display ? "DISPLAY" : "TTYNAME";
164 val = have_display ? pin->display ? pin->display : display : tty;
165 g_snprintf(cmd, sizeof(cmd), "OPTION %s=%s", g_ascii_strdown(opt, strlen(opt)), val);
166 rc = assuan_command(pin, &result, cmd);
168 if (rc)
169 return rc;
171 if (!have_display) {
172 g_snprintf(cmd, sizeof(cmd), "OPTION ttytype=%s", ttytype);
173 rc = assuan_command(pin, &result, cmd);
176 return rc;
179 static gpg_error_t launch_pinentry(struct pinentry_s *pin)
181 gpg_error_t rc;
182 assuan_context_t ctx;
183 gint child_list[] = {-1};
184 const gchar *argv[4];
186 argv[0] = "pinentry";
188 if (pin->display) {
189 argv[1] = "--display";
190 argv[2] = pin->display;
191 argv[3] = NULL;
193 else
194 argv[1] = NULL;
196 rc = assuan_pipe_connect(&ctx, pin->path ? pin->path : PINENTRY_PATH,
197 argv, child_list);
199 if (rc)
200 return rc;
202 pin->pid = assuan_get_pid(ctx);
203 pin->ctx = ctx;
204 rc = set_pinentry_options(pin);
205 return rc ? rc : set_pinentry_strings(pin, 0);
208 static gpg_error_t pinentry_command(struct pinentry_s *pin, gchar **result,
209 const gchar *cmd)
211 gpg_error_t rc = 0;
213 if (!pin->ctx)
214 rc = launch_pinentry(pin);
216 return rc ? rc : assuan_command(pin, result, cmd);
219 static gpg_error_t set_pinentry_strings(struct pinentry_s *pin, int which)
221 char *buf;
222 gpg_error_t rc;
223 gchar *title = NULL;
225 #ifdef WITH_QUALITY
226 if (pin->which == PINENTRY_SAVE && which != 2) {
227 rc = pinentry_command(pin, NULL, "SETQUALITYBAR");
229 if (rc)
230 goto done;
232 pin->inquire_cb = quality_cb;
233 pin->inquire_data = pin;
235 #endif
237 if (which == 1)
238 title = g_strdup(N_("Password mismatch, please try again."));
239 else if (!pin->title)
240 title = pin->title = g_strdup(N_("Password Manager Daemon"));
241 else
242 title = pin->title;
244 if (!pin->prompt)
245 pin->prompt = g_strdup(N_("Password:"));
247 if (!pin->desc && !which)
248 pin->desc = g_strdup_printf(pin->which == PINENTRY_OPEN ?
249 N_("A password is required to open the file \"%s\". Please%%0Aenter the password below.") :
250 N_("A password is required to save to the file \"%s\". Please%%0Aenter the password below."),
251 pin->filename);
253 if (which == 2)
254 buf = g_strdup_printf("SETERROR %s", N_("Please type the password again for confirmation."));
255 else
256 buf = g_strdup_printf("SETERROR %s", pin->desc);
258 rc = pinentry_command(pin, NULL, buf);
259 g_free(buf);
261 if (rc)
262 goto done;
264 buf = g_strdup_printf("SETPROMPT %s", pin->prompt);
265 rc = pinentry_command(pin, NULL, buf);
266 g_free(buf);
268 if (rc)
269 goto done;
271 buf = g_strdup_printf("SETDESC %s", title);
272 rc = pinentry_command(pin, NULL, buf);
273 g_free(buf);
275 done:
276 if (which == 1)
277 g_free(title);
279 return rc;
282 static void pinentry_disconnect(struct pinentry_s *pin)
284 if (!pin)
285 return;
287 if (pin->ctx)
288 assuan_disconnect(pin->ctx);
290 pin->ctx = NULL;
291 pin->pid = 0;
294 static gpg_error_t do_getpin(struct pinentry_s *pin, char **result)
296 gpg_error_t rc;
298 *result = NULL;
299 rc = pinentry_command(pin, result, "GETPIN");
301 if (!*result)
302 *result = xstrdup("");
304 return rc;
307 gpg_error_t pinentry_getpin(struct pinentry_s *pin, gchar **result)
309 gint which = 0;
310 gpg_error_t rc = set_pinentry_strings(pin, which);
311 gchar *result1 = NULL;
313 if (rc)
314 goto done;
316 again:
317 rc = do_getpin(pin, result);
319 if (rc)
320 goto done;
322 if (pin->which == PINENTRY_SAVE) {
323 if (!result1) {
324 rc = set_pinentry_strings(pin, 2);
326 if (rc)
327 goto done;
329 result1 = g_strdup(*result);
330 goto again;
333 if (strcmp(result1, *result)) {
334 g_free(result1);
335 xfree(*result);
336 result1 = *result = NULL;
337 rc = set_pinentry_strings(pin, 1);
339 if (rc)
340 goto done;
342 goto again;
346 done:
347 g_free(result1);
348 pinentry_disconnect(pin);
349 return rc;
352 static int write_result(int fd, pinentry_key_s *pk, char *result)
354 size_t len;
356 if (pk->error) {
358 * libassuan handles GPG_ERR_EOF in assuan_process_done() and
359 * will disconnect the client even if the error isn't related
360 * to it. Use GPG_ERR_CANCELED instead.
362 if (gpg_err_code(pk->error) == GPG_ERR_EOF)
363 pk->error = GPG_ERR_CANCELED;
365 len = pth_write(fd, pk, sizeof(pinentry_key_s));
366 close(fd);
368 if (len != sizeof(pinentry_key_s))
369 log_write("%s(%i): write: len != sizeof(pk)", __FUNCTION__, __LINE__);
371 return 1;
374 if (pk->status == PINENTRY_PID)
375 pk->what.pid = atoi(result);
376 else
377 g_strlcpy(pk->what.key, result, sizeof(pk->what.key));
379 xfree(result);
380 len = pth_write(fd, pk, sizeof(pinentry_key_s));
382 if (len != sizeof(pinentry_key_s)) {
383 memset(pk, 0, sizeof(pinentry_key_s));
384 log_write("%s(%i): write: len != sizeof(pk)", __FUNCTION__, __LINE__);
385 close(fd);
386 return 1;
389 if (pk->status != PINENTRY_PID)
390 close(fd);
392 memset(pk, 0, sizeof(pinentry_key_s));
393 return 0;
396 static void reset(struct pinentry_s *pin)
398 int status;
400 pth_waitpid(pin->pid, &status, 0);
401 close(pin->fd);
402 pin->fd = -1;
403 pin->pid = pin->pin_pid = 0;
404 pin->tid = 0;
405 pth_event_isolate(pin->ev);
406 pth_event_free(pin->ev, PTH_FREE_THIS);
407 pin->ev = NULL;
408 pin->status = PINENTRY_NONE;
411 static void *timeout_thread(void *arg)
413 struct pinentry_s *pin = arg;
414 pth_event_t ev = pth_event(PTH_EVENT_TIME, pth_timeout(pin->timeout, 0));
416 pth_wait(ev);
417 pth_event_free(ev, PTH_FREE_THIS);
418 pth_mutex_acquire(&pin->status_mutex, FALSE, NULL);
420 /* pth_cancel() was called from pinentry_iterate() (we have a key).
421 * pth_wait() was the cancelation point. */
422 if (pin->status == PINENTRY_NONE) {
423 reset(pin);
424 pth_mutex_release(&pin->status_mutex);
425 return NULL;
428 if (kill(pin->pin_pid, 0) == 0)
429 if (kill(pin->pin_pid, SIGTERM) == 0)
430 if (kill(pin->pin_pid, 0) == 0)
431 kill(pin->pin_pid, SIGKILL);
433 reset(pin);
434 pin->status = PINENTRY_TIMEOUT;
435 pth_mutex_release(&pin->status_mutex);
436 pth_exit(PTH_CANCELED);
437 return NULL;
440 gpg_error_t pinentry_fork(assuan_context_t ctx)
442 struct client_s *client = assuan_get_pointer(ctx);
443 struct pinentry_s *pin = client->pinentry;
444 gpg_error_t rc;
445 gint p[2];
446 pid_t pid;
447 pinentry_key_s pk;
448 gchar *result = NULL;
450 if (pipe(p) == -1)
451 return gpg_error_from_syserror();
453 pid = pth_fork();
455 switch (pid) {
456 case -1:
457 rc = gpg_error_from_syserror();
458 close(p[0]);
459 close(p[1]);
460 return rc;
461 case 0:
462 close(p[0]);
463 free_client_list();
465 if (pin->timeout > 0) {
467 * Send the pid of the pinentry process back to pwmd so it can
468 * handle the pinentry timeout properly.
470 pk.status = PINENTRY_PID;
471 pk.error = pinentry_command(pin, &result, "GETINFO pid");
473 if (write_result(p[1], &pk, result))
474 _exit(EXIT_FAILURE);
477 pk.status = PINENTRY_RUNNING;
478 pk.error = pinentry_getpin(pin, &result);
480 if (write_result(p[1], &pk, result))
481 _exit(EXIT_FAILURE);
483 _exit(EXIT_SUCCESS);
484 default:
485 close(p[1]);
486 client->pinentry->fd = p[0];
487 client->pinentry->pid = pid;
488 client->pinentry->status = PINENTRY_INIT;
489 break;
493 * Don't call assuan_process_done() here. That should be done in
494 * open_command_finalize() after the key has been read().
496 return 0;
499 gpg_error_t lock_pin_mutex(struct client_s *client)
501 while (pth_mutex_acquire(&pin_mutex, TRUE, NULL) == FALSE) {
502 if (client->ctx) {
503 gpg_error_t rc = send_status(client->ctx, STATUS_LOCKED);
505 if (rc)
506 return rc;
509 pth_sleep(1);
512 client->pinentry->has_lock = TRUE;
513 return 0;
516 void unlock_pin_mutex(struct pinentry_s *pin)
518 if (pin->has_lock == FALSE)
519 return;
521 pth_mutex_release(&pin_mutex);
522 pin->has_lock = FALSE;
525 void cleanup_pinentry(struct pinentry_s *pin)
527 if (!pin)
528 return;
530 unlock_pin_mutex(pin);
532 if (pin->ctx && pin->pid)
533 pinentry_disconnect(pin);
535 g_free(pin->ttyname);
536 g_free(pin->ttytype);
537 g_free(pin->desc);
538 g_free(pin->title);
539 g_free(pin->prompt);
540 g_free(pin->path);
541 g_free(pin->display);
542 g_free(pin->filename);
543 g_free(pin);
546 void set_pinentry_defaults(struct pinentry_s *pin)
548 FILE *fp;
549 gchar buf[PATH_MAX];
550 struct passwd *pw = getpwuid(getuid());
551 gchar *p;
553 pin->enable = -1;
554 g_snprintf(buf, sizeof(buf), "%s/.pwmd/pinentry.conf", pw->pw_dir);
555 fp = fopen(buf, "r");
557 if (fp) {
558 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
559 gchar name[32] = {0}, value[256] = {0};
561 if (*p == '#')
562 continue;
564 if (p[strlen(p)-1] == '\n')
565 p[strlen(p)-1] = 0;
567 if (sscanf(p, " %31[a-zA-Z] = %255s", name, value) != 2)
568 continue;
570 if (g_strcasecmp("TTYNAME", name) == 0)
571 pin->ttyname = g_strdup(value);
572 else if (g_strcasecmp("TTYTYPE", name) == 0)
573 pin->ttytype = g_strdup(value);
574 else if (g_strcasecmp("DISPLAY", name) == 0)
575 pin->display = g_strdup(value);
576 else if (g_strcasecmp("PATH", name) == 0)
577 pin->path = g_strdup(value);
580 fclose(fp);
583 pin->timeout = get_key_file_integer("global", "pinentry_timeout");
586 pth_event_t pinentry_iterate(struct client_s *cl, pth_event_t ev)
588 gpg_error_t rc;
590 pth_mutex_acquire(&cl->pinentry->status_mutex, FALSE, NULL);
592 /* Set from pinentry_timeout_thread(). */
593 if (cl->pinentry->status == PINENTRY_TIMEOUT) {
594 rc = send_error(cl->ctx, GPG_ERR_TIMEOUT);
596 if (cl->pinentry->which == PINENTRY_OPEN)
597 cleanup_client(cl);
599 reset(cl->pinentry);
600 unlock_pin_mutex(cl->pinentry);
603 if (cl->pinentry->status == PINENTRY_RUNNING) {
604 pth_event_isolate(cl->pinentry->ev);
606 if (pth_event_occurred(cl->pinentry->ev)) {
607 guchar shakey[gcrykeysize];
608 pinentry_key_s pk;
609 gsize len;
611 memset(&pk, 0, sizeof(pk));
612 len = pth_read(cl->pinentry->fd, &pk, sizeof(pk));
614 if (len == sizeof(pk)) {
615 if (pk.error) {
616 if (cl->pinentry->status == PINENTRY_TIMEOUT)
617 pk.error = GPG_ERR_TIMEOUT;
619 rc = send_error(cl->ctx, pk.error);
621 else if (pk.status == PINENTRY_PID) {
623 * Start the timeout thread for the pinentry process
624 * now that we know the pid of it.
626 pth_attr_t attr = pth_attr_new();
628 pth_attr_init(attr);
629 pth_attr_set(attr, PTH_ATTR_JOINABLE, 0);
630 pth_attr_set(attr, PTH_ATTR_CANCEL_STATE,
631 PTH_CANCEL_ASYNCHRONOUS);
632 cl->pinentry->pin_pid = pk.what.pid;
633 cl->pinentry->tid = pth_spawn(attr, timeout_thread,
634 cl->pinentry);
636 else {
637 if (cl->pinentry->tid) {
638 cl->pinentry->status = PINENTRY_NONE;
639 pth_cancel(cl->pinentry->tid);
642 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, pk.what.key,
643 strlen(pk.what.key) == 0 ? 1 : strlen(pk.what.key));
644 rc = cl->pinentry->cb(cl->ctx, shakey, FALSE);
645 memset(shakey, 0, sizeof(shakey));
648 else if (len == -1) {
649 if (cl->pinentry->status == PINENTRY_TIMEOUT)
650 pk.error = GPG_ERR_TIMEOUT;
652 pk.error = gpg_err_code_from_syserror();
653 log_write("%s", gpg_strerror(pk.error));
655 else if (len == 0) {
656 if (cl->pinentry->status == PINENTRY_TIMEOUT)
657 pk.error = GPG_ERR_TIMEOUT;
659 pk.error = GPG_ERR_EOF;
660 log_write("%s", gpg_strerror(pk.error));
662 else
663 log_write(N_("pth_read(): short byte count"));
665 if (pk.error) {
666 if (cl->pinentry->tid) {
667 pth_cancel(cl->pinentry->tid);
668 cl->pinentry->status = PINENTRY_NONE;
670 else
671 reset(cl->pinentry);
673 if (cl->pinentry->which == PINENTRY_OPEN)
674 cleanup_client(cl);
676 else if (pk.status == PINENTRY_RUNNING)
677 reset(cl->pinentry);
679 if (pk.error || pk.status == PINENTRY_RUNNING) {
680 unlock_file_mutex(cl);
681 unlock_pin_mutex(cl->pinentry);
684 memset(&pk, 0, sizeof(pk));
687 pth_event_concat(ev, cl->pinentry->ev, NULL);
690 pth_mutex_release(&cl->pinentry->status_mutex);
691 return ev;
694 struct pinentry_s *pinentry_init()
696 struct pinentry_s *pin = g_malloc0(sizeof(struct pinentry_s));
698 if (!pin)
699 return NULL;
701 pth_mutex_init(&pin->status_mutex);
702 set_pinentry_defaults(pin);
703 return pin;