Fixed pwmd_connect() to acquire the libassuan context.
[libpwmd.git] / src / libpwmd.c
blobe6805574338b6399d87b81872be90a6f22c2f891
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 #ifndef LINE_MAX
52 #define LINE_MAX 2048
53 #endif
55 #include "mem.h"
56 #include "misc.h"
57 #include "types.h"
59 #ifdef WITH_PINENTRY
60 #include "pinentry.h"
61 #endif
63 #ifdef WITH_TCP
64 #include "ssh.h"
65 #endif
67 #define FINISH(rc) (gpg_err_source(rc) == GPG_ERR_SOURCE_UNKNOWN) \
68 ? gpg_error(rc) : rc
70 typedef struct {
71 size_t len;
72 void *buf;
73 } membuf_t;
75 ssize_t hook_read(assuan_context_t ctx, assuan_fd_t fd, void *data,
76 size_t len)
78 #ifdef WITH_TCP
79 pwm_t *pwm = assuan_get_pointer(ctx);
81 if (pwm && pwm->tcp_conn)
82 return read_hook_ssh(pwm, fd, data, len);
83 #endif
85 return read((int)fd, data, len);
88 ssize_t hook_write(assuan_context_t ctx, assuan_fd_t fd, const void *data,
89 size_t len)
91 ssize_t wrote;
92 #ifdef WITH_TCP
93 pwm_t *pwm = assuan_get_pointer(ctx);
95 if (pwm && pwm->tcp_conn)
96 return write_hook_ssh(pwm, fd, data, len);
97 #endif
99 /* libassuan cannot handle EAGAIN when doing writes. */
100 do {
101 wrote = write((int)fd, data, len);
103 if (wrote == -1 && errno == EAGAIN) {
104 usleep(50000);
106 } while (wrote == -1 && errno == EAGAIN);
108 return wrote;
111 pid_t hook_waitpid(assuan_context_t ctx, pid_t pid, int action, int *status,
112 int options)
114 return waitpid(pid, status, options);
117 gpg_error_t pwmd_init()
119 static int initialized;
121 if (initialized)
122 return 0;
124 #ifndef MEM_DEBUG
125 _xmem_init();
126 #endif
127 #ifdef ENABLE_NLS
128 bindtextdomain("libpwmd", LOCALEDIR);
129 #endif
130 #ifdef WITH_TCP
131 libssh2_init(0);
132 #endif
133 gpg_err_init();
134 initialized = 1;
135 return 0;
138 gpg_error_t _connect_finalize(pwm_t *pwm)
140 gpg_error_t rc = 0;
141 int active[2];
142 int n = assuan_get_active_fds(pwm->ctx, 0, active, N_ARRAY(active));
144 if (n <= 0)
145 return GPG_ERR_EBADFD;
147 pwm->fd = active[0];
148 #ifdef WITH_PINENTRY
149 pwm->pinentry_pid = -1;
150 #endif
152 if (pwm->name)
153 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION NAME=%s", pwm->name);
155 return rc;
158 static gpg_error_t connect_uds(pwm_t *pwm, const char *path)
160 char *socketpath = NULL;
161 struct passwd pw;
162 char *pwbuf;
163 gpg_error_t rc;
165 if (!pwm)
166 return GPG_ERR_INV_ARG;
168 pwbuf = _getpwuid(&pw);
169 if (!pwbuf)
170 return gpg_error_from_syserror();
172 if (!path || !*path)
173 socketpath = pwmd_strdup_printf("%s/.pwmd/socket", pw.pw_dir);
174 else
175 socketpath = _expand_homedir((char *)path, &pw);
177 pwmd_free(pwbuf);
178 if (!socketpath)
179 return GPG_ERR_ENOMEM;
181 rc = assuan_socket_connect(pwm->ctx, socketpath, ASSUAN_INVALID_FD, 0);
182 pwmd_free(socketpath);
183 return rc ? rc : _connect_finalize(pwm);
186 static gpg_error_t init_handle(pwm_t *pwm)
188 gpg_error_t rc;
189 static struct assuan_malloc_hooks mhooks = {
190 pwmd_malloc, pwmd_realloc, pwmd_free
192 static struct assuan_system_hooks shooks = {
193 ASSUAN_SYSTEM_HOOKS_VERSION,
194 __assuan_usleep,
195 __assuan_pipe,
196 __assuan_close,
197 hook_read,
198 hook_write,
199 //FIXME
200 NULL, //recvmsg
201 NULL, //sendmsg both are used for FD passing
202 __assuan_spawn,
203 hook_waitpid,
204 __assuan_socketpair,
205 __assuan_socket,
206 __assuan_connect
209 rc = assuan_new_ext(&pwm->ctx, GPG_ERR_SOURCE_DEFAULT, &mhooks, NULL, NULL);
210 if (rc)
211 return rc;
213 assuan_set_pointer(pwm->ctx, pwm);
214 assuan_ctx_set_system_hooks(pwm->ctx, &shooks);
215 return 0;
219 gpg_error_t pwmd_connect(pwm_t *pwm, const char *url, const char *identity,
220 const char *knownhosts)
222 char *p = (char *)url;
223 gpg_error_t rc;
225 if (!pwm)
226 return FINISH(GPG_ERR_INV_ARG);
227 else if (!pwm->ctx) {
228 rc = init_handle(pwm);
229 if (rc)
230 return rc;
233 rc = GPG_ERR_UNSUPPORTED_PROTOCOL;
235 if (p && *p == '/')
236 rc = connect_uds(pwm, p);
237 else if (!p || !strncmp(p, "file://", 7)) {
238 if (p)
239 p += 7;
240 rc = connect_uds(pwm, p);
242 else if (!strncmp(p, "ssh://", 6) || !strncmp(p, "ssh6://", 7) ||
243 !strncmp(p, "ssh4://", 7)) {
244 #ifndef WITH_TCP
245 return FINISH(GPG_ERR_NOT_IMPLEMENTED);
246 #else
247 char *host = NULL;
248 int port = -1;
249 char *username = NULL;
251 if (!strncmp(p, "ssh6://", 7)) {
252 pwm->prot = PWMD_IPV6;
253 p += 7;
255 else if (!strncmp(p, "ssh4://", 7)) {
256 pwm->prot = PWMD_IPV4;
257 p += 7;
259 else {
260 pwm->prot = PWMD_IP_ANY;
261 p += 6;
264 /* X11 forwarding is not supported. */
265 pwmd_setopt(pwm, PWMD_OPTION_NO_PINENTRY, 1);
266 rc = _parse_ssh_url(p, &host, &port, &username);
267 if (!rc) {
268 rc = _do_ssh_connect(pwm, host, port, identity, username, knownhosts);
269 if (!rc) {
270 rc = _connect_finalize(pwm);
271 if (rc) {
272 _free_ssh_conn(pwm->tcp_conn);
273 pwm->tcp_conn = NULL;
278 pwmd_free(host);
279 pwmd_free(username);
280 return FINISH(rc);
281 #endif
284 return FINISH(rc);
287 static void disconnect(pwm_t *pwm)
289 if (!pwm || !pwm->ctx)
290 return;
292 assuan_release(pwm->ctx);
293 #ifdef WITH_TCP
294 _ssh_disconnect(pwm);
295 #endif
296 pwm->ctx = NULL;
297 pwm->fd = -1;
300 void pwmd_close(pwm_t *pwm)
302 if (!pwm)
303 return;
305 disconnect(pwm);
306 pwmd_free(pwm->pinentry_error);
307 pwmd_free(pwm->pinentry_desc);
308 pwmd_free(pwm->pinentry_prompt);
309 pwmd_free(pwm->pinentry_tty);
310 pwmd_free(pwm->pinentry_display);
311 pwmd_free(pwm->pinentry_term);
312 pwmd_free(pwm->pinentry_lcctype);
313 pwmd_free(pwm->pinentry_lcmessages);
314 pwmd_free(pwm->filename);
315 pwmd_free(pwm->name);
317 #ifdef WITH_TCP
318 if (pwm->tcp_conn)
319 _free_ssh_conn(pwm->tcp_conn);
320 #endif
322 #ifdef WITH_PINENTRY
323 if (pwm->pctx)
324 _pinentry_disconnect(pwm);
325 #endif
327 pwmd_free(pwm);
330 static gpg_error_t inquire_realloc_cb(void *data, const void *buffer,
331 size_t len)
333 membuf_t *mem = (membuf_t *)data;
334 void *p;
336 if (!buffer)
337 return 0;
339 if ((p = pwmd_realloc(mem->buf, mem->len + len)) == NULL)
340 return gpg_error(GPG_ERR_ENOMEM);
342 mem->buf = p;
343 memcpy((char *)mem->buf + mem->len, buffer, len);
344 mem->len += len;
345 return 0;
348 static gpg_error_t get_password(pwm_t *pwm, char **result, pwmd_pinentry_t w,
349 int echo)
351 char buf[LINE_MAX] = {0}, *p;
352 struct termios told, tnew;
353 char *key = NULL;
355 *result = NULL;
357 if (!isatty(STDIN_FILENO)) {
358 fprintf(stderr, N_("Input is not from a terminal! Failing.\n"));
359 return GPG_ERR_ENOTTY;
362 if (!echo) {
363 if (tcgetattr(STDIN_FILENO, &told) == -1)
364 return gpg_error_from_syserror();
366 memcpy(&tnew, &told, sizeof(struct termios));
367 tnew.c_lflag &= ~(ECHO);
368 tnew.c_lflag |= ICANON|ECHONL;
370 if (tcsetattr(STDIN_FILENO, TCSANOW, &tnew) == -1) {
371 int n = errno;
373 tcsetattr(STDIN_FILENO, TCSANOW, &told);
374 return gpg_error_from_errno(n);
378 switch (w) {
379 case PWMD_PINENTRY_OPEN:
380 fprintf(stderr, N_("Password for %s: "), pwm->filename);
381 break;
382 case PWMD_PINENTRY_OPEN_FAILED:
383 fprintf(stderr, N_("Invalid password. Password for %s: "),
384 pwm->filename);
385 break;
386 case PWMD_PINENTRY_SAVE:
387 fprintf(stderr, N_("New password for %s: "), pwm->filename);
388 break;
389 case PWMD_PINENTRY_SAVE_CONFIRM:
390 fprintf(stderr, N_("Confirm password: "));
391 break;
392 default:
393 break;
396 if ((p = fgets(buf, sizeof(buf), stdin)) == NULL) {
397 tcsetattr(STDIN_FILENO, TCSANOW, &told);
398 return 0;
401 if (!echo)
402 tcsetattr(STDIN_FILENO, TCSANOW, &told);
404 if (feof(stdin)) {
405 clearerr(stdin);
406 return GPG_ERR_CANCELED;
409 p[strlen(p) - 1] = 0;
411 if (buf[0]) {
412 key = pwmd_strdup_printf("%s", p);
413 memset(&buf, 0, sizeof(buf));
415 if (!key)
416 return GPG_ERR_ENOMEM;
419 *result = key;
420 return 0;
423 gpg_error_t pwmd_password(pwm_t *pwm, const char *keyword, char **data,
424 size_t *size)
426 gpg_error_t rc;
427 int new_password = 0;
428 size_t len;
429 char *password = NULL, *newpass = NULL;
430 int error = 0;
432 if (!strcmp(keyword, "NEW_PASSPHRASE"))
433 new_password = 1;
435 if (!new_password && pwm->pinentry_try)
436 error = 1;
438 again:
439 if (pwm->disable_pinentry && !pwm->local_pinentry) {
440 rc = get_password(pwm, &password,
441 new_password ? PWMD_PINENTRY_SAVE : PWMD_PINENTRY_OPEN, 0);
442 if (!rc && new_password)
443 rc = get_password(pwm, &newpass, PWMD_PINENTRY_SAVE_CONFIRM, 0);
445 else {
446 pwmd_pinentry_t which;
448 if (error)
449 which = new_password ? PWMD_PINENTRY_SAVE_FAILED : PWMD_PINENTRY_OPEN_FAILED;
450 else
451 which = new_password ? PWMD_PINENTRY_SAVE : PWMD_PINENTRY_OPEN;
453 rc = pwmd_getpin(pwm, pwm->filename, &password, &len, which);
454 if (!rc && new_password)
455 rc = pwmd_getpin(pwm, pwm->filename, &newpass, &len,
456 PWMD_PINENTRY_SAVE_CONFIRM);
459 if (!rc && new_password) {
460 if ((!password && newpass) || (!newpass && password)
461 || strcmp(newpass, password)) {
462 if (pwm->disable_pinentry)
463 fprintf(stderr, N_("Passphrases do not match.\n"));
465 pwmd_free(password);
466 pwmd_free(newpass);
467 password = newpass = NULL;
468 error = 1;
469 goto again;
473 (void)pwmd_getpin(pwm, pwm->filename, NULL, NULL, PWMD_PINENTRY_CLOSE);
474 pwmd_free(newpass);
475 if (!rc) {
476 // An empty passphrase on a protected key is not allowed by gpg-agent.
477 if (!password && !new_password)
478 rc = GPG_ERR_CANCELED;
479 else {
480 *data = password;
481 *size = password ? strlen(password) : 0;
485 return rc;
488 static gpg_error_t inquire_cb(void *data, const char *keyword)
490 pwm_t *pwm = (pwm_t *)data;
491 gpg_error_t rc = 0;
492 int free_result = 0;
493 char *result = NULL;
495 /* Shouldn't get this far without a callback. */
496 if (!pwm->override_inquire && !pwm->inquire_func)
497 return gpg_error(GPG_ERR_ASS_NO_INQUIRE_CB);
499 for (;;) {
500 size_t len = 0;
501 gpg_error_t arc;
502 int is_password = 0;
503 int new_password = 0;
505 result = NULL;
507 if (!strcmp(keyword, "PASSPHRASE"))
508 is_password = 1;
509 else if (!strcmp(keyword, "NEW_PASSPHRASE"))
510 new_password = 1;
512 if (!pwm->override_inquire && (is_password || new_password)) {
513 free_result = 1;
514 rc = pwmd_password(data, keyword, &result, &len);
515 if (!rc)
516 rc = GPG_ERR_EOF;
518 else
519 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc, &result,
520 &len);
522 if (rc && gpg_err_code(rc) != GPG_ERR_EOF) {
523 gpg_error_t trc = rc;
525 /* Cancel this inquire. */
526 rc = assuan_send_data(pwm->ctx, NULL, 1);
527 if (!rc) {
528 char *line;
529 size_t len;
531 /* There is a bug (or feature?) in assuan_send_data() that
532 * when cancelling an inquire the next read from the server is
533 * not done until the next command making the next command
534 * fail with GPG_ERR_ASS_UNEXPECTED_CMD.
536 rc = assuan_read_line(pwm->ctx, &line, &len);
538 /* Restore the original error. This differs from the error
539 * returned from the pwmd command (GPG_ERR_CANCELED). This
540 * error is returned to the calling function.
542 if (!rc)
543 rc = trc;
546 break;
549 if (gpg_err_code(rc) == GPG_ERR_EOF || !rc) {
550 if (len <= 0 && !result) {
551 rc = 0;
552 break;
554 else if ((len <= 0 && result) || (len && !result)) {
555 rc = gpg_error(GPG_ERR_INV_ARG);
556 break;
559 arc = assuan_send_data(pwm->ctx, result, len);
560 if (gpg_err_code(rc) == GPG_ERR_EOF) {
561 rc = arc;
562 break;
565 rc = arc;
567 else if (rc)
568 break;
570 if (!rc) {
571 pwm->inquire_sent += len;
573 if (pwm->status_func) {
574 char buf[ASSUAN_LINELENGTH];
576 snprintf(buf, sizeof(buf), "XFER %u %u", pwm->inquire_sent,
577 pwm->inquire_total);
578 rc = pwm->status_func(pwm->status_data, buf);
579 if (rc)
580 continue;
585 if (free_result)
586 pwmd_free(result);
588 return rc;
591 static gpg_error_t parse_assuan_line(pwm_t *pwm)
593 gpg_error_t rc;
594 char *line;
595 size_t len;
597 rc = assuan_read_line(pwm->ctx, &line, &len);
598 if (!rc) {
599 if (line[0] == 'O' && line[1] == 'K' &&
600 (line[2] == 0 || line[2] == ' ')) {
602 else if (line[0] == '#') {
604 else if (line[0] == 'S' && (line[1] == 0 || line[1] == ' ')) {
605 if (pwm->status_func) {
606 rc = pwm->status_func(pwm->status_data,
607 line[1] == 0 ? line+1 : line+2);
610 else if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' &&
611 (line[3] == 0 || line[3] == ' ')) {
612 line += 4;
613 rc = atoi(line);
617 return rc;
620 static void reset_handle_state(pwm_t *pwm, int done)
622 #ifdef WITH_TCP
623 if (pwm->tcp_conn)
624 pwm->tcp_conn->rc = 0;
626 if (done && pwm->tcp_conn) {
627 _free_ssh_conn(pwm->tcp_conn);
628 pwm->tcp_conn = NULL;
630 #endif
633 static void reset_handle(pwm_t *h)
635 h->fd = -1;
636 #ifdef WITH_PINENTRY
637 if (h->pctx)
638 _pinentry_disconnect(h);
639 #endif
640 reset_handle_state(h, 0);
643 gpg_error_t pwmd_disconnect(pwm_t *pwm)
645 if (!pwm)
646 return FINISH(GPG_ERR_INV_ARG);
648 #ifdef WITH_TCP
649 if (pwm->fd == -1 && pwm->tcp_conn && pwm->tcp_conn->fd == -1)
650 #else
651 if (pwm->fd == -1)
652 #endif
653 return FINISH(GPG_ERR_INV_STATE);
655 if (pwm->fd != 1)
656 disconnect(pwm);
657 #ifdef WITH_TCP
658 else
659 _ssh_disconnect(pwm);
660 #endif
662 reset_handle(pwm);
663 return 0;
666 /* Note that this should only be called when not in a command. */
667 gpg_error_t pwmd_process(pwm_t *pwm)
669 gpg_error_t rc = 0;
670 fd_set fds;
671 struct timeval tv = {0, 0};
672 int n;
674 if (!pwm)
675 return FINISH(GPG_ERR_INV_ARG);
676 else if (!pwm->ctx)
677 return FINISH(GPG_ERR_INV_STATE);
679 #ifdef WITH_TCP
680 if (pwm->tcp_conn) {
681 int n = libssh2_keepalive_send(pwm->tcp_conn->session, NULL);
683 if (n)
684 return FINISH(GPG_ERR_ETIMEDOUT);
686 #endif
688 FD_ZERO(&fds);
689 FD_SET(pwm->fd, &fds);
690 n = select(pwm->fd+1, &fds, NULL, NULL, &tv);
692 if (n == -1)
693 return FINISH(gpg_error_from_syserror());
695 if (n > 0) {
696 if (FD_ISSET(pwm->fd, &fds))
697 rc = parse_assuan_line(pwm);
700 while (!rc && assuan_pending_line(pwm->ctx))
701 rc = parse_assuan_line(pwm);
703 return FINISH(rc);
706 gpg_error_t _assuan_command(pwm_t *pwm, assuan_context_t ctx,
707 char **result, size_t *len, const char *cmd)
709 membuf_t data;
710 gpg_error_t rc;
712 if (!cmd || !*cmd)
713 return FINISH(GPG_ERR_INV_ARG);
715 if (strlen(cmd) >= ASSUAN_LINELENGTH+1)
716 return FINISH(GPG_ERR_LINE_TOO_LONG);
718 data.len = 0;
719 data.buf = NULL;
720 rc = assuan_transact(ctx, cmd, inquire_realloc_cb, &data,
721 #ifdef WITH_QUALITY
722 pwm->pctx == ctx ? pwm->_inquire_func : inquire_cb,
723 pwm->pctx == ctx ? pwm->_inquire_data : pwm,
724 #else
725 inquire_cb, pwm,
726 #endif
727 pwm->status_func, pwm->status_data);
729 if (rc) {
730 if (data.buf) {
731 pwmd_free(data.buf);
732 data.buf = NULL;
735 else {
736 if (data.buf) {
737 inquire_realloc_cb(&data, "", 1);
739 if (result)
740 *result = (char *)data.buf;
741 else
742 pwmd_free(data.buf);
744 if (len)
745 *len = data.len;
749 return rc;
752 gpg_error_t pwmd_command_ap(pwm_t *pwm, char **result, size_t *rlen,
753 pwmd_inquire_cb_t func, void *user, const char *cmd, va_list ap)
755 char *buf;
756 size_t len;
757 va_list ap2;
759 if (!pwm || !cmd)
760 return FINISH(GPG_ERR_INV_ARG);
761 if (!pwm->ctx)
762 return FINISH(GPG_ERR_INV_STATE);
765 * C99 allows the dst pointer to be null which will calculate the length
766 * of the would-be result and return it.
768 va_copy(ap2, ap);
769 len = vsnprintf(NULL, 0, cmd, ap)+1;
770 buf = (char *)pwmd_malloc(len);
771 if (!buf) {
772 va_end(ap2);
773 return FINISH(GPG_ERR_ENOMEM);
776 len = vsnprintf(buf, len, cmd, ap2);
777 va_end(ap2);
779 if (buf[strlen(buf)-1] == '\n')
780 buf[strlen(buf)-1] = 0;
781 if (buf[strlen(buf)-1] == '\r')
782 buf[strlen(buf)-1] = 0;
784 pwm->inquire_func = func;
785 pwm->inquire_data = user;
786 pwm->inquire_sent = 0;
787 gpg_error_t rc = _assuan_command(pwm, pwm->ctx, result, rlen, buf);
788 pwmd_free(buf);
789 return rc;
792 gpg_error_t pwmd_command(pwm_t *pwm, char **result, size_t *len,
793 pwmd_inquire_cb_t func, void *user, const char *cmd, ...)
795 va_list ap;
797 if (!pwm || !cmd)
798 return FINISH(GPG_ERR_INV_ARG);
799 if (!pwm->ctx)
800 return FINISH(GPG_ERR_INV_STATE);
802 if (result)
803 *result = NULL;
805 va_start(ap, cmd);
806 gpg_error_t rc = pwmd_command_ap(pwm, result, len, func, user, cmd, ap);
807 va_end(ap);
808 return rc;
811 static gpg_error_t send_pinentry_options(pwm_t *pwm)
813 gpg_error_t rc;
815 if (pwm->pinentry_tty) {
816 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION TTYNAME=%s", pwm->pinentry_tty);
817 if (rc)
818 return rc;
821 if (pwm->pinentry_term) {
822 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION TTYTYPE=%s", pwm->pinentry_term);
823 if (rc)
824 return rc;
827 if (pwm->pinentry_display) {
828 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION DISPLAY=%s",
829 pwm->pinentry_display);
830 if (rc)
831 return rc;
834 if (pwm->pinentry_desc) {
835 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION DESC=%s",
836 pwm->pinentry_desc);
837 if (rc)
838 return rc;
841 if (pwm->pinentry_lcctype) {
842 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION LC_CTYPE=%s",
843 pwm->pinentry_lcctype);
844 if (rc)
845 return rc;
848 if (pwm->pinentry_lcmessages) {
849 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION LC_MESSAGES=%s",
850 pwm->pinentry_lcmessages);
851 if (rc)
852 return rc;
855 return 0;
858 gpg_error_t pwmd_socket_type(pwm_t *pwm, pwmd_socket_t *result)
860 if (!pwm || !result)
861 return FINISH(GPG_ERR_INV_ARG);
863 #ifdef WITH_TCP
864 if ((pwm->fd == -1 && !pwm->tcp_conn) ||
865 (pwm->fd == -1 && pwm->tcp_conn && pwm->tcp_conn->fd == -1))
866 #else
867 if (pwm->fd == -1)
868 #endif
869 return FINISH(GPG_ERR_INV_STATE);
871 #ifdef WITH_TCP
872 *result = pwm->tcp_conn ? PWMD_SOCKET_SSH : PWMD_SOCKET_LOCAL;
873 #else
874 *result = PWMD_SOCKET_LOCAL;
875 #endif
876 return 0;
879 gpg_error_t pwmd_open(pwm_t *pwm, const char *filename, pwmd_inquire_cb_t cb,
880 void *data)
882 gpg_error_t rc = 0;
883 #ifdef WITH_TCP
884 int no_pinentry = pwm->disable_pinentry || pwm->tcp_conn || pwm->local_pinentry;
885 #else
886 int no_pinentry = pwm->disable_pinentry || pwm->local_pinentry;
887 #endif
889 if (!pwm || !filename || !*filename)
890 return FINISH(GPG_ERR_INV_ARG);
892 if (!pwm->ctx)
893 return FINISH(GPG_ERR_INV_STATE);
895 if (!no_pinentry)
896 rc = send_pinentry_options(pwm);
898 if (!rc) {
899 pwm->pinentry_try = 0;
900 pwmd_free(pwm->filename);
901 pwm->filename = pwmd_strdup(filename);
903 do {
904 rc = pwmd_command(pwm, NULL, NULL, cb, data, "OPEN %s%s%s",
905 (pwm->opts & OPT_LOCK_ON_OPEN) ? "--lock " : "",
906 no_pinentry ? "--no-pinentry " : "", filename);
907 } while (gpg_err_code(rc) == GPG_ERR_BAD_PASSPHRASE
908 && no_pinentry && ++pwm->pinentry_try < pwm->pinentry_tries);
910 pwm->pinentry_try = 0;
912 if (rc) {
913 pwmd_free(pwm->filename);
914 pwm->filename = NULL;
918 return FINISH(rc);
921 gpg_error_t pwmd_save(pwm_t *pwm, const char *args, pwmd_inquire_cb_t cb,
922 void *data)
924 gpg_error_t rc;
926 if (!pwm)
927 return FINISH(GPG_ERR_INV_ARG);
928 if (!pwm->ctx)
929 return FINISH(GPG_ERR_INV_STATE);
931 rc = pwmd_command(pwm, NULL, NULL, cb, data, "SAVE %s",
932 args ? args : "");
933 return FINISH(rc);
936 gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...)
938 va_list ap;
939 int n;
940 char *arg1;
941 gpg_error_t rc = 0;
943 if (!pwm)
944 return FINISH(GPG_ERR_INV_ARG);
946 va_start(ap, opt);
948 switch (opt) {
949 case PWMD_OPTION_LOCK_ON_OPEN:
950 n = va_arg(ap, int);
952 if (n < 0 || n > 1)
953 rc = GPG_ERR_INV_VALUE;
955 if (n)
956 pwm->opts |= OPT_LOCK_ON_OPEN;
957 else
958 pwm->opts &= ~OPT_LOCK_ON_OPEN;
960 break;
961 case PWMD_OPTION_INQUIRE_TOTAL:
962 pwm->inquire_total = va_arg(ap, size_t);
963 break;
964 case PWMD_OPTION_STATUS_CB:
965 pwm->status_func = va_arg(ap, pwmd_status_cb_t);
966 break;
967 case PWMD_OPTION_STATUS_DATA:
968 pwm->status_data = va_arg(ap, void *);
969 break;
970 case PWMD_OPTION_NO_PINENTRY:
971 n = va_arg(ap, int);
973 if (n < 0 || n > 1)
974 rc = GPG_ERR_INV_VALUE;
975 else
976 pwm->disable_pinentry = n;
978 break;
979 case PWMD_OPTION_LOCAL_PINENTRY:
980 n = va_arg(ap, int);
982 if (n < 0 || n > 1)
983 rc = GPG_ERR_INV_VALUE;
984 else
985 pwm->local_pinentry = n;
987 break;
988 case PWMD_OPTION_PINENTRY_TIMEOUT:
989 n = va_arg(ap, int);
991 if (n < 0)
992 rc = GPG_ERR_INV_VALUE;
993 else
994 pwm->pinentry_timeout = n;
996 break;
997 case PWMD_OPTION_PINENTRY_TRIES:
998 n = va_arg(ap, int);
999 pwm->pinentry_tries = n;
1000 break;
1001 case PWMD_OPTION_PINENTRY_PATH:
1002 arg1 = va_arg(ap, char *);
1003 pwmd_free(pwm->pinentry_path);
1004 pwm->pinentry_path = arg1 ? _expand_homedir(arg1, NULL) : NULL;
1005 break;
1006 case PWMD_OPTION_PINENTRY_TTY:
1007 arg1 = va_arg(ap, char *);
1008 pwmd_free(pwm->pinentry_tty);
1009 pwm->pinentry_tty = arg1 ? pwmd_strdup(arg1) : NULL;
1010 break;
1011 case PWMD_OPTION_PINENTRY_DISPLAY:
1012 arg1 = va_arg(ap, char *);
1013 pwmd_free(pwm->pinentry_display);
1014 pwm->pinentry_display = arg1 ? pwmd_strdup(arg1) : NULL;
1015 break;
1016 case PWMD_OPTION_PINENTRY_TERM:
1017 arg1 = va_arg(ap, char *);
1018 pwmd_free(pwm->pinentry_term);
1019 pwm->pinentry_term = arg1 ? pwmd_strdup(arg1) : NULL;
1020 break;
1021 case PWMD_OPTION_PINENTRY_ERROR:
1022 arg1 = va_arg(ap, char *);
1023 pwmd_free(pwm->pinentry_error);
1024 pwm->pinentry_error = arg1 ? _percent_escape(arg1) : NULL;
1025 break;
1026 case PWMD_OPTION_PINENTRY_PROMPT:
1027 arg1 = va_arg(ap, char *);
1028 pwmd_free(pwm->pinentry_prompt);
1029 pwm->pinentry_prompt = arg1 ? _percent_escape(arg1) : NULL;
1030 break;
1031 case PWMD_OPTION_PINENTRY_DESC:
1032 arg1 = va_arg(ap, char *);
1033 pwmd_free(pwm->pinentry_desc);
1034 pwm->pinentry_desc = arg1 ? _percent_escape(arg1) : NULL;
1035 break;
1036 case PWMD_OPTION_PINENTRY_LC_CTYPE:
1037 arg1 = va_arg(ap, char *);
1038 pwmd_free(pwm->pinentry_lcctype);
1039 pwm->pinentry_lcctype = arg1 ? pwmd_strdup(arg1) : NULL;
1040 break;
1041 case PWMD_OPTION_PINENTRY_LC_MESSAGES:
1042 arg1 = va_arg(ap, char *);
1043 pwmd_free(pwm->pinentry_lcmessages);
1044 pwm->pinentry_lcmessages = arg1 ? pwmd_strdup(arg1) : NULL;
1045 break;
1046 #ifdef WITH_TCP
1047 case PWMD_OPTION_KNOWNHOST_CB:
1048 pwm->kh_cb = va_arg(ap, pwmd_knownhost_cb_t);
1049 break;
1050 case PWMD_OPTION_KNOWNHOST_DATA:
1051 pwm->kh_data = va_arg(ap, void *);
1052 break;
1053 case PWMD_OPTION_SSH_AGENT:
1054 pwm->use_agent = va_arg(ap, int);
1056 if (pwm->use_agent < 0 || pwm->use_agent > 1) {
1057 pwm->use_agent = 0;
1058 rc = GPG_ERR_INV_VALUE;
1061 break;
1062 case PWMD_OPTION_SSH_TIMEOUT:
1063 pwm->ssh_timeout = va_arg(ap, int);
1065 if (pwm->ssh_timeout < 0) {
1066 pwm->ssh_timeout = 0;
1067 rc = GPG_ERR_INV_VALUE;
1070 break;
1071 case PWMD_OPTION_SSH_KEEPALIVE:
1072 pwm->keepalive_interval = va_arg(ap, int);
1074 if (pwm->keepalive_interval < 0) {
1075 pwm->keepalive_interval = 0;
1076 rc = GPG_ERR_INV_VALUE;
1079 break;
1080 #else
1081 case PWMD_OPTION_KNOWNHOST_CB:
1082 case PWMD_OPTION_KNOWNHOST_DATA:
1083 case PWMD_OPTION_SSH_AGENT:
1084 case PWMD_OPTION_SSH_TIMEOUT:
1085 rc = GPG_ERR_NOT_IMPLEMENTED;
1086 break;
1087 #endif
1088 case PWMD_OPTION_OVERRIDE_INQUIRE:
1089 pwm->override_inquire = va_arg(ap, int);
1091 if (pwm->override_inquire < 0 || pwm->override_inquire > 1) {
1092 pwm->override_inquire = 0;
1093 rc = GPG_ERR_INV_VALUE;
1095 break;
1096 default:
1097 rc = GPG_ERR_UNKNOWN_OPTION;
1098 break;
1101 va_end(ap);
1102 return FINISH(rc);
1105 gpg_error_t pwmd_new(const char *name, pwm_t **pwm)
1107 pwm_t *h = pwmd_calloc(1, sizeof(pwm_t));
1108 gpg_error_t rc;
1110 if (!h)
1111 return FINISH(GPG_ERR_ENOMEM);
1113 if (name) {
1114 h->name = pwmd_strdup(name);
1115 if (!h->name) {
1116 pwmd_free(h);
1117 return FINISH(GPG_ERR_ENOMEM);
1121 reset_handle(h);
1122 h->pinentry_timeout = -30;
1123 h->pinentry_tries = 3;
1124 #ifdef WITH_TCP
1125 h->prot = PWMD_IP_ANY;
1126 #endif
1128 if (ttyname(STDOUT_FILENO)) {
1129 char buf[256];
1131 ttyname_r(STDOUT_FILENO, buf, sizeof(buf));
1132 h->pinentry_tty = pwmd_strdup(buf);
1133 if (!h->pinentry_tty) {
1134 rc = GPG_ERR_ENOMEM;
1135 goto fail;
1139 if (getenv("TERM") && h->pinentry_tty) {
1140 h->pinentry_term = pwmd_strdup(getenv("TERM"));
1141 if (!h->pinentry_term) {
1142 rc = GPG_ERR_ENOMEM;
1143 goto fail;
1147 if (getenv("DISPLAY")) {
1148 h->pinentry_display = pwmd_strdup(getenv("DISPLAY"));
1149 if (!h->pinentry_display) {
1150 rc = GPG_ERR_ENOMEM;
1151 goto fail;
1155 update_pinentry_settings(h);
1156 *pwm = h;
1157 return 0;
1159 fail:
1160 pwmd_close(h);
1161 return FINISH(rc);
1164 void pwmd_free(void *ptr)
1166 _xfree(ptr);
1169 void *pwmd_malloc(size_t size)
1171 return _xmalloc(size);
1174 void *pwmd_calloc(size_t nmemb, size_t size)
1176 return _xcalloc(nmemb, size);
1179 void *pwmd_realloc(void *ptr, size_t size)
1181 return _xrealloc(ptr, size);
1184 char *pwmd_strdup(const char *str)
1186 return _xstrdup(str);
1189 char *pwmd_strdup_printf(const char *fmt, ...)
1191 va_list ap, ap2;
1192 int len;
1193 char *buf;
1195 if (!fmt)
1196 return NULL;
1198 va_start(ap, fmt);
1199 va_copy(ap2, ap);
1200 len = vsnprintf(NULL, 0, fmt, ap);
1201 va_end(ap);
1202 buf = pwmd_malloc(++len);
1203 if (buf)
1204 vsnprintf(buf, len, fmt, ap2);
1206 va_end(ap2);
1207 return buf;
1210 gpg_error_t pwmd_getpin(pwm_t *pwm, const char *filename, char **result,
1211 size_t *len, pwmd_pinentry_t which)
1213 #ifndef WITH_PINENTRY
1214 return FINISH(GPG_ERR_NOT_IMPLEMENTED);
1215 #else
1216 gpg_error_t rc = _pwmd_getpin(pwm, filename, result, len, which);
1218 return FINISH(rc);
1219 #endif
1222 const char *pwmd_version()
1224 return LIBPWMD_VERSION_STR;
1227 unsigned int pwmd_features()
1229 unsigned int n = 0;
1231 #ifdef WITH_PINENTRY
1232 n |= PWMD_FEATURE_PINENTRY;
1233 #endif
1234 #ifdef WITH_TCP
1235 n |= PWMD_FEATURE_SSH;
1236 #endif
1237 #ifdef WITH_QUALITY
1238 n |= PWMD_FEATURE_CRACK;
1239 #endif
1240 return n;