Always send pinentry options in pwmd_open().
[libpwmd.git] / src / libpwmd.c
blob72d506ed1d8d65d02e415ecea6c32b0e3a9544b4
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011
4 Ben Kibbey <bjk@luxsci.net>
6 This file is part of libpwmd.
8 Libpwmd is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
13 Libpwmd is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with Libpwmd. If not, see <http://www.gnu.org/licenses/>.
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <err.h>
29 #include <errno.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34 #include <signal.h>
35 #include <stdarg.h>
36 #include <string.h>
37 #include <sys/wait.h>
38 #include <fcntl.h>
39 #include <pwd.h>
40 #include <time.h>
41 #include <sys/types.h>
42 #include <limits.h>
43 #include <sys/select.h>
44 #include <termios.h>
45 #include <libpwmd.h>
47 #ifdef HAVE_ASSUAN_H
48 #include <assuan.h>
49 #endif
51 #include "mem.h"
52 #include "misc.h"
53 #include "types.h"
55 #ifdef WITH_PINENTRY
56 #include "pinentry.h"
57 #endif
59 #ifdef WITH_TCP
60 #include "ssh.h"
61 #endif
63 #define FINISH(rc) (gpg_err_source(rc) == GPG_ERR_SOURCE_UNKNOWN) \
64 ? gpg_error(rc) : rc
66 typedef struct {
67 size_t len;
68 void *buf;
69 } membuf_t;
71 ssize_t hook_read(assuan_context_t ctx, assuan_fd_t fd, void *data,
72 size_t len)
74 #ifdef WITH_TCP
75 pwm_t *pwm = assuan_get_pointer(ctx);
77 if (pwm && pwm->tcp_conn)
78 return read_hook_ssh(pwm, fd, data, len);
79 #endif
81 return read((int)fd, data, len);
84 ssize_t hook_write(assuan_context_t ctx, assuan_fd_t fd, const void *data,
85 size_t len)
87 ssize_t wrote;
88 #ifdef WITH_TCP
89 pwm_t *pwm = assuan_get_pointer(ctx);
91 if (pwm && pwm->tcp_conn)
92 return write_hook_ssh(pwm, fd, data, len);
93 #endif
95 /* libassuan cannot handle EAGAIN when doing writes. */
96 do {
97 wrote = write((int)fd, data, len);
99 if (wrote == -1 && errno == EAGAIN) {
100 usleep(50000);
102 } while (wrote == -1 && errno == EAGAIN);
104 return wrote;
107 pid_t hook_waitpid(assuan_context_t ctx, pid_t pid, int action, int *status,
108 int options)
110 return waitpid(pid, status, options);
113 gpg_error_t pwmd_init()
115 static int initialized;
117 if (initialized)
118 return 0;
120 #ifndef MEM_DEBUG
121 _xmem_init();
122 #endif
123 #ifdef ENABLE_NLS
124 bindtextdomain("libpwmd", LOCALEDIR);
125 #endif
126 #ifdef WITH_TCP
127 libssh2_init(0);
128 #endif
129 gpg_err_init();
130 initialized = 1;
131 return 0;
134 gpg_error_t _connect_finalize(pwm_t *pwm)
136 gpg_error_t rc = 0;
137 int active[2];
138 int n = assuan_get_active_fds(pwm->ctx, 0, active, N_ARRAY(active));
140 if (n <= 0)
141 return GPG_ERR_EBADFD;
143 pwm->fd = active[0];
144 #ifdef WITH_PINENTRY
145 pwm->pid = -1;
146 #endif
148 if (pwm->name)
149 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION NAME=%s", pwm->name);
151 return rc;
154 static gpg_error_t connect_uds(pwm_t *pwm, const char *path)
156 char *socketpath = NULL;
157 struct passwd pw;
158 char *pwbuf;
159 gpg_error_t rc;
161 if (!pwm)
162 return GPG_ERR_INV_ARG;
164 pwbuf = _getpwuid(&pw);
165 if (!pwbuf)
166 return gpg_error_from_syserror();
168 if (!path || !*path)
169 socketpath = pwmd_strdup_printf("%s/.pwmd/socket", pw.pw_dir);
170 else
171 socketpath = _expand_homedir((char *)path, &pw);
173 pwmd_free(pwbuf);
174 if (!socketpath)
175 return GPG_ERR_ENOMEM;
177 rc = assuan_socket_connect(pwm->ctx, socketpath, ASSUAN_INVALID_FD, 0);
178 pwmd_free(socketpath);
179 return rc ? rc : _connect_finalize(pwm);
182 gpg_error_t pwmd_connect(pwm_t *pwm, const char *url, const char *identity,
183 const char *knownhosts)
185 char *p = (char *)url;
186 gpg_error_t rc = GPG_ERR_UNSUPPORTED_PROTOCOL;
188 if (!pwm)
189 return FINISH(GPG_ERR_INV_ARG);
191 if (p && *p == '/')
192 rc = connect_uds(pwm, p);
193 else if (!p || !strncmp(p, "file://", 7)) {
194 if (p)
195 p += 7;
196 rc = connect_uds(pwm, p);
198 else if (!strncmp(p, "ssh://", 6) || !strncmp(p, "ssh6://", 7) ||
199 !strncmp(p, "ssh4://", 7)) {
200 #ifndef WITH_TCP
201 return FINISH(GPG_ERR_NOT_IMPLEMENTED);
202 #else
203 char *host = NULL;
204 int port = -1;
205 char *username = NULL;
207 if (!strncmp(p, "ssh6://", 7)) {
208 pwm->prot = PWMD_IPV6;
209 p += 7;
211 else if (!strncmp(p, "ssh4://", 7)) {
212 pwm->prot = PWMD_IPV4;
213 p += 7;
215 else {
216 pwm->prot = PWMD_IP_ANY;
217 p += 6;
220 /* X11 forwarding is not supported. */
221 pwmd_setopt(pwm, PWMD_OPTION_NO_PINENTRY, 1);
222 rc = _parse_ssh_url(p, &host, &port, &username);
223 if (!rc) {
224 rc = _do_ssh_connect(pwm, host, port, identity, username, knownhosts);
225 if (!rc) {
226 rc = _connect_finalize(pwm);
227 if (rc) {
228 _free_ssh_conn(pwm->tcp_conn);
229 pwm->tcp_conn = NULL;
234 pwmd_free(host);
235 pwmd_free(username);
236 return FINISH(rc);
237 #endif
240 return FINISH(rc);
243 static void disconnect(pwm_t *pwm)
245 if (!pwm || !pwm->ctx)
246 return;
248 assuan_release(pwm->ctx);
249 #ifdef WITH_TCP
250 _ssh_disconnect(pwm);
251 #endif
252 pwm->ctx = NULL;
253 pwm->fd = -1;
256 void pwmd_close(pwm_t *pwm)
258 if (!pwm)
259 return;
261 disconnect(pwm);
262 pwmd_free(pwm->error);
263 pwmd_free(pwm->desc);
264 pwmd_free(pwm->prompt);
265 pwmd_free(pwm->pinentry_tty);
266 pwmd_free(pwm->pinentry_display);
267 pwmd_free(pwm->pinentry_term);
268 pwmd_free(pwm->lcctype);
269 pwmd_free(pwm->lcmessages);
270 pwmd_free(pwm->filename);
271 pwmd_free(pwm->name);
273 #ifdef WITH_TCP
274 if (pwm->tcp_conn)
275 _free_ssh_conn(pwm->tcp_conn);
276 #endif
278 #ifdef WITH_PINENTRY
279 if (pwm->pctx)
280 _pinentry_disconnect(pwm);
281 #endif
283 pwmd_free(pwm);
286 static gpg_error_t inquire_realloc_cb(void *data, const void *buffer,
287 size_t len)
289 membuf_t *mem = (membuf_t *)data;
290 void *p;
292 if (!buffer)
293 return 0;
295 if ((p = pwmd_realloc(mem->buf, mem->len + len)) == NULL)
296 return gpg_error(GPG_ERR_ENOMEM);
298 mem->buf = p;
299 memcpy((char *)mem->buf + mem->len, buffer, len);
300 mem->len += len;
301 return 0;
304 static gpg_error_t get_password(pwm_t *pwm, char **result, pwmd_pinentry_t w,
305 int echo)
307 char buf[LINE_MAX] = {0}, *p;
308 struct termios told, tnew;
309 char *key = NULL;
311 *result = NULL;
313 if (!isatty(STDIN_FILENO)) {
314 fprintf(stderr, N_("Input is not from a terminal! Failing.\n"));
315 return GPG_ERR_ENOTTY;
318 if (!echo) {
319 if (tcgetattr(STDIN_FILENO, &told) == -1)
320 return gpg_error_from_syserror();
322 memcpy(&tnew, &told, sizeof(struct termios));
323 tnew.c_lflag &= ~(ECHO);
324 tnew.c_lflag |= ICANON|ECHONL;
326 if (tcsetattr(STDIN_FILENO, TCSANOW, &tnew) == -1) {
327 int n = errno;
329 tcsetattr(STDIN_FILENO, TCSANOW, &told);
330 return gpg_error_from_errno(n);
334 switch (w) {
335 case PWMD_PINENTRY_OPEN:
336 fprintf(stderr, N_("Password for %s: "), pwm->filename);
337 break;
338 case PWMD_PINENTRY_OPEN_FAILED:
339 fprintf(stderr, N_("Invalid password. Password for %s: "),
340 pwm->filename);
341 break;
342 case PWMD_PINENTRY_SAVE:
343 fprintf(stderr, N_("New password for %s: "), pwm->filename);
344 break;
345 case PWMD_PINENTRY_SAVE_CONFIRM:
346 fprintf(stderr, N_("Confirm password: "));
347 break;
348 default:
349 break;
352 if ((p = fgets(buf, sizeof(buf), stdin)) == NULL) {
353 tcsetattr(STDIN_FILENO, TCSANOW, &told);
354 return 0;
357 if (!echo)
358 tcsetattr(STDIN_FILENO, TCSANOW, &told);
360 if (feof(stdin)) {
361 clearerr(stdin);
362 return GPG_ERR_CANCELED;
365 p[strlen(p) - 1] = 0;
367 if (buf[0]) {
368 key = pwmd_strdup_printf("%s", p);
369 memset(&buf, 0, sizeof(buf));
371 if (!key)
372 return GPG_ERR_ENOMEM;
375 *result = key;
376 return 0;
379 static gpg_error_t inquire_password(pwm_t *pwm, const char *keyword,
380 char **data, size_t *size)
382 gpg_error_t rc;
383 int new_password = 0;
384 size_t len;
385 char *password = NULL, *newpass = NULL;
387 if (!strcmp(keyword, "NEW_PASSPHRASE"))
388 new_password = 1;
390 if (pwm->disable_pinentry && !pwm->local_pinentry) {
391 rc = get_password(pwm, &password,
392 new_password ? PWMD_PINENTRY_SAVE : PWMD_PINENTRY_OPEN, 0);
393 if (!rc && new_password)
394 rc = get_password(pwm, &newpass, PWMD_PINENTRY_SAVE_CONFIRM, 0);
396 else {
397 rc = pwmd_getpin(pwm, pwm->filename, &password, &len,
398 new_password ? PWMD_PINENTRY_SAVE : PWMD_PINENTRY_OPEN);
399 if (!rc && new_password)
400 rc = pwmd_getpin(pwm, pwm->filename, &newpass, &len,
401 PWMD_PINENTRY_SAVE_CONFIRM);
403 (void)pwmd_getpin(pwm, pwm->filename, NULL, NULL, PWMD_PINENTRY_CLOSE);
406 if (!rc && newpass) {
407 if ((!password && newpass) || (!newpass && password)
408 || strcmp(newpass, password)) {
409 fprintf(stderr, N_("Passphrases do not match.\n"));
410 pwmd_free(password);
411 rc = GPG_ERR_CANCELED;
415 pwmd_free(newpass);
416 if (!rc) {
417 rc = GPG_ERR_EOF;
418 *data = password;
419 *size = password ? strlen(password) : 0;
422 return rc;
425 static gpg_error_t inquire_cb(void *data, const char *keyword)
427 pwm_t *pwm = (pwm_t *)data;
428 gpg_error_t rc = 0;
429 int free_result = 0;
430 char *result = NULL;
432 /* Shouldn't get this far without a callback. */
433 if (!pwm->override_inquire && !pwm->inquire_func)
434 return gpg_error(GPG_ERR_ASS_NO_INQUIRE_CB);
436 for (;;) {
437 size_t len = 0;
438 gpg_error_t arc;
439 int is_password = 0;
440 int new_password = 0;
442 result = NULL;
444 if (!strcmp(keyword, "PASSPHRASE"))
445 is_password = 1;
446 else if (!strcmp(keyword, "NEW_PASSPHRASE"))
447 new_password = 1;
449 if (!pwm->override_inquire && (is_password || new_password)) {
450 free_result = 1;
451 rc = inquire_password(data, keyword, &result, &len);
453 else
454 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc, &result,
455 &len);
457 if (gpg_err_code(rc) == GPG_ERR_CANCELED) {
458 rc = assuan_send_data(pwm->ctx, NULL, 1);
459 if (!rc) {
460 char *line;
461 size_t len;
463 /* There is a bug (or feature?) in assuan_send_data() that
464 * when cancelling an inquire the next read from the server is
465 * not done until the next command making the next command
466 * fail.
468 rc = assuan_read_line(pwm->ctx, &line, &len);
469 if (!rc)
470 rc = gpg_error(GPG_ERR_CANCELED);
473 break;
476 if (gpg_err_code(rc) == GPG_ERR_EOF || !rc) {
477 if (len <= 0 && !result) {
478 rc = 0;
479 break;
481 else if ((len <= 0 && result) || (len && !result)) {
482 rc = gpg_error(GPG_ERR_INV_ARG);
483 break;
486 arc = assuan_send_data(pwm->ctx, result, len);
487 if (gpg_err_code(rc) == GPG_ERR_EOF) {
488 rc = arc;
489 break;
492 rc = arc;
494 else if (rc)
495 break;
497 if (!rc) {
498 pwm->inquire_sent += len;
500 if (pwm->status_func) {
501 char buf[ASSUAN_LINELENGTH];
503 snprintf(buf, sizeof(buf), "XFER %u %u", pwm->inquire_sent,
504 pwm->inquire_total);
505 rc = pwm->status_func(pwm->status_data, buf);
506 if (rc)
507 continue;
512 if (free_result)
513 pwmd_free(result);
515 return rc;
518 static gpg_error_t parse_assuan_line(pwm_t *pwm)
520 gpg_error_t rc;
521 char *line;
522 size_t len;
524 rc = assuan_read_line(pwm->ctx, &line, &len);
525 if (!rc) {
526 if (line[0] == 'O' && line[1] == 'K' &&
527 (line[2] == 0 || line[2] == ' ')) {
529 else if (line[0] == '#') {
531 else if (line[0] == 'S' && (line[1] == 0 || line[1] == ' ')) {
532 if (pwm->status_func) {
533 rc = pwm->status_func(pwm->status_data,
534 line[1] == 0 ? line+1 : line+2);
537 else if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' &&
538 (line[3] == 0 || line[3] == ' ')) {
539 line += 4;
540 rc = atoi(line);
544 return rc;
547 static void reset_handle_state(pwm_t *pwm, int done)
549 #ifdef WITH_TCP
550 if (pwm->tcp_conn)
551 pwm->tcp_conn->rc = 0;
553 if (done && pwm->tcp_conn) {
554 _free_ssh_conn(pwm->tcp_conn);
555 pwm->tcp_conn = NULL;
557 #endif
560 static void reset_handle(pwm_t *h)
562 h->fd = -1;
563 #ifdef WITH_PINENTRY
564 if (h->pctx)
565 _pinentry_disconnect(h);
566 #endif
567 h->pin_try = 0;
568 reset_handle_state(h, 0);
571 gpg_error_t pwmd_disconnect(pwm_t *pwm)
573 if (!pwm)
574 return FINISH(GPG_ERR_INV_ARG);
576 #ifdef WITH_TCP
577 if (pwm->fd == -1 && pwm->tcp_conn && pwm->tcp_conn->fd == -1)
578 #else
579 if (pwm->fd == -1)
580 #endif
581 return FINISH(GPG_ERR_INV_STATE);
583 if (pwm->fd != 1)
584 disconnect(pwm);
585 #ifdef WITH_TCP
586 else
587 _ssh_disconnect(pwm);
588 #endif
590 reset_handle(pwm);
591 return 0;
594 gpg_error_t pwmd_process(pwm_t *pwm)
596 gpg_error_t rc = 0;
597 fd_set fds;
598 struct timeval tv = {0, 0};
599 int n;
601 if (!pwm)
602 return FINISH(GPG_ERR_INV_ARG);
603 else if (!pwm->ctx)
604 return FINISH(GPG_ERR_INV_STATE);
606 FD_ZERO(&fds);
607 FD_SET(pwm->fd, &fds);
608 n = select(pwm->fd+1, &fds, NULL, NULL, &tv);
610 if (n == -1)
611 return FINISH(gpg_error_from_syserror());
613 if (n > 0) {
614 if (FD_ISSET(pwm->fd, &fds))
615 rc = parse_assuan_line(pwm);
618 while (!rc && assuan_pending_line(pwm->ctx))
619 rc = parse_assuan_line(pwm);
621 return FINISH(rc);
624 gpg_error_t _assuan_command(pwm_t *pwm, assuan_context_t ctx,
625 char **result, size_t *len, const char *cmd)
627 membuf_t data;
628 gpg_error_t rc;
630 if (!cmd || !*cmd)
631 return FINISH(GPG_ERR_INV_ARG);
633 if (strlen(cmd) >= ASSUAN_LINELENGTH+1)
634 return FINISH(GPG_ERR_LINE_TOO_LONG);
636 data.len = 0;
637 data.buf = NULL;
638 rc = assuan_transact(ctx, cmd, inquire_realloc_cb, &data,
639 #ifdef WITH_QUALITY
640 pwm->pctx == ctx ? pwm->_inquire_func : inquire_cb,
641 pwm->pctx == ctx ? pwm->_inquire_data : pwm,
642 #else
643 inquire_cb, pwm,
644 #endif
645 pwm->status_func, pwm->status_data);
647 if (rc) {
648 if (data.buf) {
649 pwmd_free(data.buf);
650 data.buf = NULL;
653 else {
654 if (data.buf) {
655 inquire_realloc_cb(&data, "", 1);
657 if (result)
658 *result = (char *)data.buf;
659 else
660 pwmd_free(data.buf);
662 if (len)
663 *len = data.len;
667 return rc;
670 gpg_error_t pwmd_command_ap(pwm_t *pwm, char **result, size_t *rlen,
671 pwmd_inquire_cb_t func, void *user, const char *cmd, va_list ap)
673 char *buf;
674 size_t len;
675 va_list ap2;
677 if (!pwm || !cmd)
678 return FINISH(GPG_ERR_INV_ARG);
679 if (!pwm->ctx)
680 return FINISH(GPG_ERR_INV_STATE);
683 * C99 allows the dst pointer to be null which will calculate the length
684 * of the would-be result and return it.
686 va_copy(ap2, ap);
687 len = vsnprintf(NULL, 0, cmd, ap)+1;
688 buf = (char *)pwmd_malloc(len);
689 if (!buf) {
690 va_end(ap2);
691 return FINISH(GPG_ERR_ENOMEM);
694 len = vsnprintf(buf, len, cmd, ap2);
695 va_end(ap2);
697 if (buf[strlen(buf)-1] == '\n')
698 buf[strlen(buf)-1] = 0;
699 if (buf[strlen(buf)-1] == '\r')
700 buf[strlen(buf)-1] = 0;
702 pwm->inquire_func = func;
703 pwm->inquire_data = user;
704 pwm->inquire_sent = 0;
705 gpg_error_t rc = _assuan_command(pwm, pwm->ctx, result, rlen, buf);
706 pwmd_free(buf);
707 return rc;
710 gpg_error_t pwmd_command(pwm_t *pwm, char **result, size_t *len,
711 pwmd_inquire_cb_t func, void *user, const char *cmd, ...)
713 va_list ap;
715 if (!pwm || !cmd)
716 return FINISH(GPG_ERR_INV_ARG);
717 if (!pwm->ctx)
718 return FINISH(GPG_ERR_INV_STATE);
720 if (result)
721 *result = NULL;
723 va_start(ap, cmd);
724 gpg_error_t rc = pwmd_command_ap(pwm, result, len, func, user, cmd, ap);
725 va_end(ap);
726 return rc;
729 static gpg_error_t send_pinentry_options(pwm_t *pwm)
731 gpg_error_t rc;
733 if (pwm->pinentry_tty) {
734 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION TTYNAME=%s", pwm->pinentry_tty);
735 if (rc)
736 return rc;
739 if (pwm->pinentry_term) {
740 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION TTYTYPE=%s", pwm->pinentry_term);
741 if (rc)
742 return rc;
745 if (pwm->pinentry_display) {
746 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION DISPLAY=%s",
747 pwm->pinentry_display);
748 if (rc)
749 return rc;
752 if (pwm->desc) {
753 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION DESC=%s", pwm->desc);
754 if (rc)
755 return rc;
758 if (pwm->lcctype) {
759 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION LC_CTYPE=%s", pwm->lcctype);
760 if (rc)
761 return rc;
764 if (pwm->lcmessages) {
765 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION LC_MESSAGES=%s", pwm->lcmessages);
766 if (rc)
767 return rc;
770 return 0;
773 gpg_error_t pwmd_socket_type(pwm_t *pwm, pwmd_socket_t *result)
775 if (!pwm || !result)
776 return FINISH(GPG_ERR_INV_ARG);
778 #ifdef WITH_TCP
779 if ((pwm->fd == -1 && !pwm->tcp_conn) ||
780 (pwm->fd == -1 && pwm->tcp_conn && pwm->tcp_conn->fd == -1))
781 #else
782 if (pwm->fd == -1)
783 #endif
784 return FINISH(GPG_ERR_INV_STATE);
786 #ifdef WITH_TCP
787 *result = pwm->tcp_conn ? PWMD_SOCKET_SSH : PWMD_SOCKET_LOCAL;
788 #else
789 *result = PWMD_SOCKET_LOCAL;
790 #endif
791 return 0;
794 gpg_error_t pwmd_open(pwm_t *pwm, const char *filename, pwmd_inquire_cb_t cb,
795 void *data)
797 gpg_error_t rc = 0;
798 #ifdef WITH_TCP
799 int no_pinentry = pwm->disable_pinentry || pwm->tcp_conn || pwm->local_pinentry;
800 #else
801 int no_pinentry = pwm->disable_pinentry || pwm->local_pinentry;
802 #endif
804 if (!pwm || !filename || !*filename)
805 return FINISH(GPG_ERR_INV_ARG);
807 if (!pwm->ctx)
808 return FINISH(GPG_ERR_INV_STATE);
810 if (!no_pinentry)
811 rc = send_pinentry_options(pwm);
813 if (!rc) {
814 pwmd_free(pwm->filename);
815 pwm->filename = pwmd_strdup(filename);
816 rc = pwmd_command(pwm, NULL, NULL, cb, data, "OPEN %s%s%s",
817 (pwm->opts & OPT_LOCK_ON_OPEN) ? "--lock " : "",
818 no_pinentry ? "--no-pinentry " : "", filename);
819 if (rc) {
820 pwmd_free(pwm->filename);
821 pwm->filename = NULL;
825 return FINISH(rc);
828 gpg_error_t pwmd_save(pwm_t *pwm, const char *args, pwmd_inquire_cb_t cb,
829 void *data)
831 gpg_error_t rc;
833 if (!pwm)
834 return FINISH(GPG_ERR_INV_ARG);
835 if (!pwm->ctx)
836 return FINISH(GPG_ERR_INV_STATE);
838 rc = pwmd_command(pwm, NULL, NULL, cb, data, "SAVE %s",
839 args ? args : "");
840 return FINISH(rc);
843 gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...)
845 va_list ap;
846 int n;
847 char *arg1;
848 gpg_error_t rc = 0;
850 if (!pwm)
851 return FINISH(GPG_ERR_INV_ARG);
853 va_start(ap, opt);
855 switch (opt) {
856 case PWMD_OPTION_LOCK_ON_OPEN:
857 n = va_arg(ap, int);
859 if (n < 0 || n > 1)
860 rc = GPG_ERR_INV_VALUE;
862 if (n)
863 pwm->opts |= OPT_LOCK_ON_OPEN;
864 else
865 pwm->opts &= ~OPT_LOCK_ON_OPEN;
867 break;
868 case PWMD_OPTION_INQUIRE_TOTAL:
869 pwm->inquire_total = va_arg(ap, size_t);
870 break;
871 case PWMD_OPTION_STATUS_CB:
872 pwm->status_func = va_arg(ap, pwmd_status_cb_t);
873 break;
874 case PWMD_OPTION_STATUS_DATA:
875 pwm->status_data = va_arg(ap, void *);
876 break;
877 case PWMD_OPTION_NO_PINENTRY:
878 n = va_arg(ap, int);
880 if (n < 0 || n > 1)
881 rc = GPG_ERR_INV_VALUE;
882 else
883 pwm->disable_pinentry = n;
885 break;
886 case PWMD_OPTION_LOCAL_PINENTRY:
887 n = va_arg(ap, int);
889 if (n < 0 || n > 1)
890 rc = GPG_ERR_INV_VALUE;
891 else
892 pwm->local_pinentry = n;
894 break;
895 case PWMD_OPTION_PINENTRY_TIMEOUT:
896 n = va_arg(ap, int);
898 if (n < 0)
899 rc = GPG_ERR_INV_VALUE;
900 else
901 pwm->pinentry_timeout = n;
903 break;
904 case PWMD_OPTION_PINENTRY_PATH:
905 if (pwm->pinentry_path)
906 pwmd_free(pwm->pinentry_path);
908 pwm->pinentry_path = _expand_homedir(va_arg(ap, char *), NULL);
909 break;
910 case PWMD_OPTION_PINENTRY_TTY:
911 arg1 = va_arg(ap, char *);
913 if (pwm->pinentry_tty)
914 pwmd_free(pwm->pinentry_tty);
916 pwm->pinentry_tty = arg1 ? pwmd_strdup(arg1) : NULL;
917 break;
918 case PWMD_OPTION_PINENTRY_DISPLAY:
919 if (pwm->pinentry_display)
920 pwmd_free(pwm->pinentry_display);
922 pwm->pinentry_display = pwmd_strdup(va_arg(ap, char *));
923 break;
924 case PWMD_OPTION_PINENTRY_TERM:
925 arg1 = va_arg(ap, char *);
927 if (pwm->pinentry_term)
928 pwmd_free(pwm->pinentry_term);
930 pwm->pinentry_term = arg1 ? pwmd_strdup(arg1) : NULL;
931 break;
932 case PWMD_OPTION_PINENTRY_ERROR:
933 if (pwm->error)
934 pwmd_free(pwm->error);
936 pwm->error = _percent_escape(va_arg(ap, char *));
937 break;
938 case PWMD_OPTION_PINENTRY_PROMPT:
939 if (pwm->prompt)
940 pwmd_free(pwm->prompt);
942 pwm->prompt = _percent_escape(va_arg(ap, char *));
943 break;
944 case PWMD_OPTION_PINENTRY_DESC:
945 if (pwm->desc)
946 pwmd_free(pwm->desc);
948 pwm->desc = _percent_escape(va_arg(ap, char *));
949 break;
950 case PWMD_OPTION_PINENTRY_LC_CTYPE:
951 arg1 = va_arg(ap, char *);
953 if (pwm->lcctype)
954 pwmd_free(pwm->lcctype);
956 pwm->lcctype = arg1 ? pwmd_strdup(arg1) : NULL;
957 break;
958 case PWMD_OPTION_PINENTRY_LC_MESSAGES:
959 arg1 = va_arg(ap, char *);
961 if (pwm->lcmessages)
962 pwmd_free(pwm->lcmessages);
964 pwm->lcmessages = arg1 ? pwmd_strdup(arg1) : NULL;
965 break;
966 #ifdef WITH_TCP
967 case PWMD_OPTION_KNOWNHOST_CB:
968 pwm->kh_cb = va_arg(ap, pwmd_knownhost_cb_t);
969 break;
970 case PWMD_OPTION_KNOWNHOST_DATA:
971 pwm->kh_data = va_arg(ap, void *);
972 break;
973 case PWMD_OPTION_SSH_AGENT:
974 pwm->use_agent = va_arg(ap, int);
976 if (pwm->use_agent < 0 || pwm->use_agent > 1) {
977 pwm->use_agent = 0;
978 rc = GPG_ERR_INV_VALUE;
981 break;
982 #else
983 case PWMD_OPTION_KNOWNHOST_CB:
984 case PWMD_OPTION_KNOWNHOST_DATA:
985 case PWMD_OPTION_SSH_AGENT:
986 rc = GPG_ERR_NOT_IMPLEMENTED;
987 break;
988 #endif
989 case PWMD_OPTION_OVERRIDE_INQUIRE:
990 pwm->override_inquire = va_arg(ap, int);
992 if (pwm->override_inquire < 0 || pwm->override_inquire > 1) {
993 pwm->override_inquire = 0;
994 rc = GPG_ERR_INV_VALUE;
996 break;
997 default:
998 rc = GPG_ERR_UNKNOWN_OPTION;
999 break;
1002 va_end(ap);
1003 return FINISH(rc);
1006 gpg_error_t pwmd_new(const char *name, pwm_t **pwm)
1008 pwm_t *h = pwmd_calloc(1, sizeof(pwm_t));
1009 gpg_error_t rc;
1010 static struct assuan_malloc_hooks mhooks = {
1011 pwmd_malloc, pwmd_realloc, pwmd_free
1013 static struct assuan_system_hooks shooks = {
1014 ASSUAN_SYSTEM_HOOKS_VERSION,
1015 __assuan_usleep,
1016 __assuan_pipe,
1017 __assuan_close,
1018 hook_read,
1019 hook_write,
1020 //FIXME
1021 NULL, //recvmsg
1022 NULL, //sendmsg both are used for FD passing
1023 __assuan_spawn,
1024 hook_waitpid,
1025 __assuan_socketpair,
1026 __assuan_socket,
1027 __assuan_connect
1030 if (!h)
1031 return FINISH(GPG_ERR_ENOMEM);
1033 if (name) {
1034 h->name = pwmd_strdup(name);
1035 if (!h->name) {
1036 pwmd_free(h);
1037 return FINISH(GPG_ERR_ENOMEM);
1041 reset_handle(h);
1042 h->pinentry_timeout = -30;
1043 #ifdef WITH_TCP
1044 h->prot = PWMD_IP_ANY;
1045 #endif
1047 if (ttyname(STDOUT_FILENO)) {
1048 char buf[256];
1050 ttyname_r(STDOUT_FILENO, buf, sizeof(buf));
1051 h->pinentry_tty = pwmd_strdup(buf);
1052 if (!h->pinentry_tty) {
1053 rc = GPG_ERR_ENOMEM;
1054 goto fail;
1058 if (getenv("TERM") && h->pinentry_tty) {
1059 h->pinentry_term = pwmd_strdup(getenv("TERM"));
1060 if (!h->pinentry_term) {
1061 rc = GPG_ERR_ENOMEM;
1062 goto fail;
1066 if (getenv("DISPLAY")) {
1067 h->pinentry_display = pwmd_strdup(getenv("DISPLAY"));
1068 if (!h->pinentry_display) {
1069 rc = GPG_ERR_ENOMEM;
1070 goto fail;
1074 update_pinentry_settings(h);
1075 rc = assuan_new_ext(&h->ctx, GPG_ERR_SOURCE_DEFAULT, &mhooks, NULL, NULL);
1076 if (rc)
1077 goto fail;
1079 assuan_set_pointer(h->ctx, h);
1080 assuan_ctx_set_system_hooks(h->ctx, &shooks);
1081 *pwm = h;
1082 return 0;
1084 fail:
1085 pwmd_close(h);
1086 return FINISH(rc);
1089 void pwmd_free(void *ptr)
1091 _xfree(ptr);
1094 void *pwmd_malloc(size_t size)
1096 return _xmalloc(size);
1099 void *pwmd_calloc(size_t nmemb, size_t size)
1101 return _xcalloc(nmemb, size);
1104 void *pwmd_realloc(void *ptr, size_t size)
1106 return _xrealloc(ptr, size);
1109 char *pwmd_strdup(const char *str)
1111 return _xstrdup(str);
1114 char *pwmd_strdup_printf(const char *fmt, ...)
1116 va_list ap, ap2;
1117 int len;
1118 char *buf;
1120 if (!fmt)
1121 return NULL;
1123 va_start(ap, fmt);
1124 va_copy(ap2, ap);
1125 len = vsnprintf(NULL, 0, fmt, ap);
1126 va_end(ap);
1127 buf = pwmd_malloc(++len);
1128 if (buf)
1129 vsnprintf(buf, len, fmt, ap2);
1131 va_end(ap2);
1132 return buf;
1135 gpg_error_t pwmd_getpin(pwm_t *pwm, const char *filename, char **result,
1136 size_t *len, pwmd_pinentry_t which)
1138 #ifndef WITH_PINENTRY
1139 return FINISH(GPG_ERR_NOT_IMPLEMENTED);
1140 #else
1141 gpg_error_t rc = _pwmd_getpin(pwm, filename, result, len, which);
1143 return FINISH(rc);
1144 #endif
1147 const char *pwmd_version()
1149 return LIBPWMD_VERSION_STR;
1152 unsigned int pwmd_features()
1154 unsigned int n = 0;
1156 #ifdef WITH_PINENTRY
1157 n |= PWMD_FEATURE_PINENTRY;
1158 #endif
1159 #ifdef WITH_TCP
1160 n |= PWMD_FEATURE_SSH;
1161 #endif
1162 #ifdef WITH_QUALITY
1163 n |= PWMD_FEATURE_CRACK;
1164 #endif
1165 return n;