Fixed pinentry when used without DISPLAY.
[pwmd.git] / src / pinentry.c
blob25c11d0720da769fd64f7afaf6d430801586f34f
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 02111-1307 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 #include "mem.h"
31 #include "common.h"
32 #include "pinentry.h"
33 #include "pwmd_error.h"
35 typedef struct {
36 size_t len;
37 void *buf;
38 } membuf_t;
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 static gpg_error_t assuan_command(struct pinentry_s *pin, gchar **result,
61 const gchar *cmd)
63 membuf_t data;
64 gpg_error_t rc;
66 data.len = 0;
67 data.buf = NULL;
69 rc = assuan_transact(pin->ctx, cmd, mem_realloc_cb, &data, NULL, NULL,
70 NULL, NULL);
72 if (rc) {
73 if (data.buf) {
74 xfree(data.buf);
75 data.buf = NULL;
78 else {
79 if (data.buf) {
80 mem_realloc_cb(&data, "", 1);
81 *result = (gchar *)data.buf;
85 return rc;
88 static gpg_error_t set_pinentry_options(struct pinentry_s *pin)
90 gchar *display = getenv("DISPLAY");
91 gint have_display = 0;
92 gchar *tty = NULL, *ttytype = NULL;
93 gchar *opt, *val;
94 gpg_error_t rc;
95 gchar *result = NULL;
96 gchar cmd[ASSUAN_LINELENGTH];
98 if (pin->display || display)
99 have_display = 1;
100 else {
101 tty = pin->ttyname ? pin->ttyname : ttyname(STDOUT_FILENO);
103 if (!tty)
104 return GPG_ERR_CANCELED;
107 if (!have_display && !tty)
108 return GPG_ERR_CANCELED;
110 if (!have_display) {
111 gchar *p = getenv("TERM");
113 ttytype = pin->ttytype ? pin->ttytype : p;
115 if (!ttytype)
116 return GPG_ERR_CANCELED;
119 opt = have_display ? "DISPLAY" : "TTYNAME";
120 val = have_display ? pin->display ? pin->display : display : tty;
121 g_snprintf(cmd, sizeof(cmd), "OPTION %s=%s", g_ascii_strdown(opt, strlen(opt)), val);
122 rc = assuan_command(pin, &result, cmd);
124 if (rc)
125 return rc;
127 if (!have_display) {
128 g_snprintf(cmd, sizeof(cmd), "OPTION ttytype=%s", ttytype);
129 rc = assuan_command(pin, &result, cmd);
132 return rc;
135 static gpg_error_t launch_pinentry(struct pinentry_s *pin)
137 gpg_error_t rc;
138 assuan_context_t ctx;
139 gint child_list[] = {-1};
140 const gchar *argv[4];
142 argv[0] = "pinentry";
144 if (pin->display) {
145 argv[1] = "--display";
146 argv[2] = pin->display;
147 argv[3] = NULL;
149 else
150 argv[1] = NULL;
152 rc = assuan_pipe_connect(&ctx, pin->path ? pin->path : PINENTRY_PATH,
153 argv, child_list);
155 if (rc)
156 return rc;
158 pin->pid = assuan_get_pid(ctx);
159 pin->ctx = ctx;
160 rc = set_pinentry_options(pin);
161 return rc ? rc : set_pinentry_strings(pin, 0);
164 static gpg_error_t pinentry_command(struct pinentry_s *pin, gchar **result,
165 const gchar *cmd)
167 gpg_error_t rc = 0;
169 if (!pin->ctx)
170 rc = launch_pinentry(pin);
172 return rc ? rc : assuan_command(pin, result, cmd);
175 static gpg_error_t set_pinentry_strings(struct pinentry_s *pin, int which)
177 char *buf;
178 gpg_error_t rc;
179 gchar *title = NULL;
181 if (which == 1)
182 title = g_strdup(N_("Password mismatch, please try again."));
183 else if (!pin->title)
184 title = pin->title = g_strdup(N_("Password Manager Daemon"));
185 else
186 title = pin->title;
188 if (!pin->prompt)
189 pin->prompt = g_strdup(N_("Password:"));
191 if (!pin->desc && !which)
192 pin->desc = g_strdup_printf(pin->which == PINENTRY_OPEN ?
193 N_("A password is required to open the file \"%s\". Please%%0Aenter the password below.") :
194 N_("A password is required to save to the file \"%s\". Please%%0Aenter the password below."),
195 pin->filename);
197 if (which == 2)
198 buf = g_strdup_printf("SETERROR %s", N_("Please type the password again for confirmation."));
199 else
200 buf = g_strdup_printf("SETERROR %s", pin->desc);
202 rc = pinentry_command(pin, NULL, buf);
203 g_free(buf);
205 if (rc)
206 goto done;
208 buf = g_strdup_printf("SETPROMPT %s", pin->prompt);
209 rc = pinentry_command(pin, NULL, buf);
210 g_free(buf);
212 if (rc)
213 goto done;
215 buf = g_strdup_printf("SETDESC %s", title);
216 rc = pinentry_command(pin, NULL, buf);
217 g_free(buf);
219 done:
220 if (which == 1)
221 g_free(title);
223 return rc;
226 static void pinentry_disconnect(struct pinentry_s *pin)
228 if (!pin)
229 return;
231 if (pin->ctx)
232 assuan_disconnect(pin->ctx);
234 pin->ctx = NULL;
235 pin->pid = 0;
238 static gpg_error_t do_getpin(struct pinentry_s *pin, char **result)
240 gpg_error_t rc;
242 *result = NULL;
243 rc = pinentry_command(pin, result, "GETPIN");
245 if (!*result)
246 *result = xstrdup("");
248 return rc;
251 gpg_error_t pinentry_getpin(struct pinentry_s *pin, gchar **result)
253 gint which = 0;
254 gpg_error_t rc = set_pinentry_strings(pin, which);
255 gchar *result1 = NULL;
257 if (rc) {
258 pinentry_disconnect(pin);
259 return rc;
262 again:
263 rc = do_getpin(pin, result);
265 if (rc)
266 goto done;
268 if (pin->which == PINENTRY_SAVE) {
269 if (!result1) {
270 rc = set_pinentry_strings(pin, 2);
272 if (rc)
273 goto done;
275 result1 = g_strdup(*result);
276 goto again;
279 if (strcmp(result1, *result)) {
280 g_free(result1);
281 xfree(*result);
282 result1 = *result = NULL;
283 rc = set_pinentry_strings(pin, 1);
285 if (rc)
286 goto done;
288 goto again;
292 done:
293 g_free(result1);
294 pinentry_disconnect(pin);
295 return rc;
298 static int write_result(int fd, pinentry_key_s *pk, char *result)
300 size_t len;
302 if (pk->error) {
304 * libassuan handles GPG_ERR_EOF in assuan_process_done() and
305 * will disconnect the client even if the error isn't related
306 * to it. Use GPG_ERR_CANCELED instead.
308 if (gpg_err_code(pk->error) == GPG_ERR_EOF)
309 pk->error = GPG_ERR_CANCELED;
311 len = pth_write(fd, pk, sizeof(pinentry_key_s));
312 close(fd);
314 if (len != sizeof(pinentry_key_s))
315 log_write("%s(%i): write: len != sizeof(pk)", __FUNCTION__, __LINE__);
317 return 1;
320 if (pk->status == PINENTRY_PID)
321 pk->what.pid = atoi(result);
322 else
323 g_strlcpy(pk->what.key, result, sizeof(pk->what.key));
325 xfree(result);
326 len = pth_write(fd, pk, sizeof(pinentry_key_s));
328 if (len != sizeof(pinentry_key_s)) {
329 memset(pk, 0, sizeof(pinentry_key_s));
330 log_write("%s(%i): write: len != sizeof(pk)", __FUNCTION__, __LINE__);
331 close(fd);
332 return 1;
335 if (pk->status != PINENTRY_PID)
336 close(fd);
338 memset(pk, 0, sizeof(pinentry_key_s));
339 return 0;
342 static void reset(struct pinentry_s *pin)
344 int status;
346 pth_waitpid(pin->pid, &status, 0);
347 close(pin->fd);
348 pin->fd = -1;
349 pin->pid = pin->pin_pid = 0;
350 pin->tid = 0;
351 pth_event_isolate(pin->ev);
352 pth_event_free(pin->ev, PTH_FREE_THIS);
353 pin->ev = NULL;
354 pin->status = PINENTRY_NONE;
357 void *pinentry_timeout_thread(void *arg)
359 struct pinentry_s *pin = arg;
360 pth_event_t ev = pth_event(PTH_EVENT_TIME, pth_timeout(pin->timeout, 0));
362 pth_wait(ev);
363 pth_event_free(ev, PTH_FREE_THIS);
366 * Don't give the forked process a chance to return a read error from the
367 * pinentry process. Kill the forked process first, then the pinentry
368 * process.
370 if (kill(pin->pid, 0) == 0)
371 if (kill(pin->pid, SIGTERM) == 0)
372 if (kill(pin->pid, 0) == 0)
373 kill(pin->pid, SIGKILL);
375 if (kill(pin->pin_pid, 0) == 0)
376 if (kill(pin->pin_pid, SIGTERM) == 0)
377 if (kill(pin->pin_pid, 0) == 0)
378 kill(pin->pin_pid, SIGKILL);
380 reset(pin);
381 pin->status = PINENTRY_TIMEOUT;
382 pth_exit(PTH_CANCELED);
383 return NULL;
386 gpg_error_t pinentry_fork(assuan_context_t ctx)
388 struct client_s *client = assuan_get_pointer(ctx);
389 struct pinentry_s *pin = client->pinentry;
390 gpg_error_t rc;
391 gint p[2];
392 pid_t pid;
393 pinentry_key_s pk;
394 gsize len;
395 gchar *result = NULL;
397 if (pipe(p) == -1)
398 return gpg_error_from_syserror();
400 pid = pth_fork();
402 switch (pid) {
403 case -1:
404 rc = gpg_error_from_syserror();
405 close(p[0]);
406 close(p[1]);
407 return rc;
408 case 0:
409 close(p[0]);
410 free_client_list();
412 if (pin->timeout > 0) {
414 * Send the pid of the pinentry process back to pwmd so it can
415 * handle the pinentry timeout properly.
417 pk.status = PINENTRY_PID;
418 pk.error = pinentry_command(pin, &result, "GETINFO pid");
421 * Support older pinentry's (< 0.7.5).
423 if (pk.error && gpg_err_code(pk.error) == GPG_ERR_ASS_UNKNOWN_CMD)
424 goto getpin;
426 if (write_result(p[1], &pk, result))
427 _exit(EXIT_FAILURE);
430 getpin:
431 pk.status = PINENTRY_RUNNING;
432 pk.error = pinentry_getpin(pin, &result);
434 if (write_result(p[1], &pk, result))
435 _exit(EXIT_FAILURE);
437 _exit(EXIT_SUCCESS);
438 default:
439 close(p[1]);
440 client->pinentry->fd = p[0];
441 client->pinentry->pid = pid;
442 client->pinentry->status = PINENTRY_INIT;
443 break;
447 * Don't call assuan_process_done() here. That should be done in
448 * open_command_finalize() after the key has been read().
450 return 0;
453 gpg_error_t lock_pin_mutex(struct client_s *client)
455 while (pth_mutex_acquire(&pin_mutex, TRUE, NULL) == FALSE) {
456 if (client->ctx) {
457 gpg_error_t rc = send_status(client->ctx, STATUS_LOCKED);
459 if (rc)
460 return rc;
463 pth_sleep(1);
466 client->pinentry->has_lock = TRUE;
467 return 0;
470 void unlock_pin_mutex(struct pinentry_s *pin)
472 if (pin->has_lock == FALSE)
473 return;
475 pth_mutex_release(&pin_mutex);
476 pin->has_lock = FALSE;
479 void cleanup_pinentry(struct pinentry_s *pin)
481 if (!pin)
482 return;
484 unlock_pin_mutex(pin);
486 if (pin->ctx && pin->pid)
487 pinentry_disconnect(pin);
489 g_free(pin->ttyname);
490 g_free(pin->ttytype);
491 g_free(pin->desc);
492 g_free(pin->title);
493 g_free(pin->prompt);
494 g_free(pin->path);
495 g_free(pin->display);
496 g_free(pin->filename);
497 g_free(pin);
500 static void set_pinentry_defaults(struct pinentry_s *pin)
502 FILE *fp;
503 gchar buf[PATH_MAX];
504 struct passwd *pw = getpwuid(getuid());
505 gchar *p;
507 pin->enable = -1;
508 g_snprintf(buf, sizeof(buf), "%s/.pwmd/pinentry.conf", pw->pw_dir);
509 fp = fopen(buf, "r");
511 if (fp) {
512 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
513 gchar name[32] = {0}, value[256] = {0};
515 if (*p == '#')
516 continue;
518 if (p[strlen(p)-1] == '\n')
519 p[strlen(p)-1] = 0;
521 if (sscanf(p, " %31[a-zA-Z] = %255s", name, value) != 2)
522 continue;
524 if (g_strcasecmp("TTYNAME", name) == 0)
525 pin->ttyname = g_strdup(value);
526 else if (g_strcasecmp("TTYTYPE", name) == 0)
527 pin->ttytype = g_strdup(value);
528 else if (g_strcasecmp("DISPLAY", name) == 0)
529 pin->display = g_strdup(value);
530 else if (g_strcasecmp("PATH", name) == 0)
531 pin->path = g_strdup(value);
534 fclose(fp);
537 pin->timeout = get_key_file_integer("global", "pinentry_timeout");
540 pth_event_t pinentry_iterate(struct client_s *cl, pth_event_t ev)
542 gpg_error_t rc;
544 /* Set from pinentry_timeout_thread(). */
545 if (cl->pinentry->status == PINENTRY_TIMEOUT) {
546 rc = send_error(cl->ctx, GPG_ERR_TIMEOUT);
548 if (cl->pinentry->which == PINENTRY_OPEN)
549 cleanup_client(cl);
551 reset(cl->pinentry);
552 unlock_pin_mutex(cl->pinentry);
555 if (cl->pinentry->status == PINENTRY_RUNNING) {
556 pth_event_isolate(cl->pinentry->ev);
558 if (pth_event_occurred(cl->pinentry->ev)) {
559 guchar shakey[gcrykeysize];
560 pinentry_key_s pk;
561 gsize len;
562 gint status;
564 memset(&pk, 0, sizeof(pk));
565 len = pth_read(cl->pinentry->fd, &pk, sizeof(pk));
567 if (len == sizeof(pk)) {
568 if (pk.error)
569 rc = send_error(cl->ctx, pk.error);
570 else if (pk.status == PINENTRY_PID) {
572 * Start the timeout thread for the pinentry process
573 * now that we know the pid of it.
575 pth_attr_t attr = pth_attr_new();
577 pth_attr_init(attr);
578 pth_attr_set(attr, PTH_ATTR_JOINABLE, 0);
579 pth_attr_set(attr, PTH_ATTR_CANCEL_STATE,
580 PTH_CANCEL_ASYNCHRONOUS);
581 cl->pinentry->pin_pid = pk.what.pid;
582 cl->pinentry->tid = pth_spawn(attr,
583 pinentry_timeout_thread, cl->pinentry);
585 else {
586 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, pk.what.key,
587 strlen(pk.what.key) == 0 ? 1 : strlen(pk.what.key));
589 if (cl->pinentry->tid)
590 pth_cancel(cl->pinentry->tid);
592 rc = cl->pinentry->cb(cl->ctx, shakey, FALSE);
593 memset(shakey, 0, sizeof(shakey));
596 else if (len == -1) {
597 pk.error = gpg_err_code_from_syserror();
598 log_write("%s", gpg_strerror(pk.error));
600 else if (len == 0) {
601 pk.error = GPG_ERR_EOF;
602 log_write("%s", gpg_strerror(pk.error));
604 else
605 log_write(N_("pth_read(): short byte count"));
607 if (pk.error) {
608 if (cl->pinentry->tid) {
609 pth_cancel(cl->pinentry->tid);
610 cl->pinentry->status = PINENTRY_NONE;
612 else
613 reset(cl->pinentry);
615 if (cl->pinentry->which == PINENTRY_OPEN)
616 cleanup_client(cl);
618 else if (pk.status == PINENTRY_RUNNING)
619 reset(cl->pinentry);
621 if (pk.error || pk.status == PINENTRY_RUNNING) {
622 unlock_file_mutex(cl);
623 unlock_pin_mutex(cl->pinentry);
626 memset(&pk, 0, sizeof(pk));
629 pth_event_concat(ev, cl->pinentry->ev, NULL);
632 return ev;
635 struct pinentry_s *pinentry_init()
637 struct pinentry_s *pin = g_malloc0(sizeof(struct pinentry_s));
639 if (!pin)
640 return NULL;
642 set_pinentry_defaults(pin);
643 return pin;